Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unmet file requirements op submitpagina #211

Merged
merged 23 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5,601 changes: 5,214 additions & 387 deletions frontend/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"vue-i18n": "^9.10.1",
"vue-query": "^1.26.0",
"vue-router": "^4.2.5",
"vuetify": "^3.0.0-beta.0",
"vuetify": "3.5.*",
"webfontloader": "^1.0.0"
},
"devDependencies": {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/project/ProjectInfo.vue
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
<script setup lang="ts">
import type Project from "@/models/Project";
import type Group from "@/models/Group";
import SubmitInfo from "@/components/project/submit/SubmitInfo.vue";
import SubmitInfo from "@/components/submission/SubmitInfo.vue";
import { toRefs } from "vue";
import { Quill } from "@vueup/vue-quill";
import type User from "@/models/User";
Expand Down
101 changes: 101 additions & 0 deletions frontend/src/components/project/RequirementsCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
<template>
<v-card variant="flat">
<v-card-item>
<v-card-title>
{{ $t("project.requirements") }}
</v-card-title>
</v-card-item>
<v-card-text>
<v-card-subtitle v-if="mandatory.length > 0">{{
$t("project.mandatory")
}}</v-card-subtitle>
<v-list>
<v-list-item v-for="req in mandatory" :key="req.value">
<v-list-item-title>{{ req.value }}</v-list-item-title>

<template v-if="unmet_extensions.includes(req.value)" v-slot:append>
<v-tooltip>
<p>{{ $t("project.unmet_mandatory") }}</p>
<template v-slot:activator="{ props }">
<v-icon
v-bind="props"
icon="mdi-alert"
class="warning-content"
></v-icon>
</template>
</v-tooltip>
</template>
</v-list-item>
</v-list>

<v-card-subtitle v-if="forbidden.length > 0">{{
$t("project.forbidden")
}}</v-card-subtitle>
<v-list>
<v-list-item v-for="req in forbidden" :key="req.value">
<v-list-item-title>{{ req.value }}</v-list-item-title>

<template v-if="unmet_extensions.includes(req.value)" v-slot:append>
<v-tooltip>
<p>{{ $t("project.unmet_forbidden") }}</p>
<ul>
<li
v-for="illegal_file in unmetRequirements.find(
(r) => r.requirement.value == req.value
)!.files"
:key="illegal_file"
>
{{ illegal_file }}
</li>
</ul>

<template v-slot:activator="{ props }">
<v-icon
v-bind="props"
icon="mdi-alert"
class="warning-content"
></v-icon>
</template>
</v-tooltip>
</template>
</v-list-item>
</v-list>
<v-card-subtitle v-if="unmetRequirements.length > 0" class="warning-content">{{
$t("project.unmet_reqs_warning")
}}</v-card-subtitle>
</v-card-text>
</v-card>
</template>

<script setup lang="ts">
import { computed, ref, toRefs } from "vue";
import type { Requirement, UnmetRequirement } from "@/models/Project";

const props = defineProps<{
requirements: Requirement[];
unmetRequirements: UnmetRequirement[];
}>();

const { requirements, unmetRequirements } = toRefs(props);

const mandatory = ref<Requirement[]>(requirements.value.filter((r) => r.mandatory));
const forbidden = ref<Requirement[]>(requirements.value.filter((r) => !r.mandatory));

const unmet_extensions = computed(() => unmetRequirements.value.map((r) => r.requirement.value));
</script>

<style scoped>
.v-card,
.v-list {
width: 100%;
background-color: rgb(var(--v-theme-secondary));
}

ul li {
list-style-position: inside;
}

.warning-content {
color: red;
}
</style>
38 changes: 0 additions & 38 deletions frontend/src/components/project/submit/SubmitCard.vue

This file was deleted.

54 changes: 0 additions & 54 deletions frontend/src/components/project/submit/SubmitForm.vue

This file was deleted.

41 changes: 41 additions & 0 deletions frontend/src/components/submission/SubmitCard.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<template>
<v-skeleton-loader :loading="isLoading" type="article">
<v-card class="submit-card">
<v-card-item>
<h1>{{ $t("submit.submit_title") }}</h1>
<h1 v-if="isError">Error</h1>
<ProjectMiniCard :project="project!" class="mini-card" />
</v-card-item>
<v-card-item>
<SubmitForm :project="project!" class="submit-form" />
</v-card-item>
</v-card>
</v-skeleton-loader>
</template>

<script setup lang="ts">
import ProjectMiniCard from "@/components/project/ProjectMiniCard.vue";
import SubmitForm from "@/components/submission/SubmitForm.vue";
import { useProjectQuery } from "@/queries/Project";
import { toRefs } from "vue";

const props = defineProps<{
projectId: number;
}>();

const { projectId } = toRefs(props);

const { data: project, isLoading, isError } = useProjectQuery(projectId);
</script>

<style scoped>
.submit-card {
width: 100%;
}
.mini-card {
margin: 15px 0 30px 0;
}
.submit-form {
margin-top: 15px;
}
</style>
81 changes: 81 additions & 0 deletions frontend/src/components/submission/SubmitForm.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<template>
<div>
<RequirementsCard
v-if="project.requirements.length > 0"
:requirements="project.requirements"
:unmetRequirements="unmetRequirements"
/>
<v-form validate-on="submit lazy" @submit.prevent="formOnSubmit" class="submission-form">
<FilesInput v-model="inputFiles" />
<v-textarea :label="$t('submit.remarks')" name="remarks" v-model="remarksInput" />
<v-btn variant="flat" type="submit">{{ $t("submit.submit_button") }}</v-btn>
</v-form>
</div>
</template>

<script setup lang="ts">
import { computed, ref, toRefs } from "vue";
import FilesInput from "@/components/form_elements/FilesInput.vue";
import { useRouter } from "vue-router";
import { UnmetRequirementsError, useCreateSubmissionMutation } from "@/queries/Submission";
import { useProjectGroupQuery } from "@/queries/Group";
import { useI18n } from "vue-i18n";
import RequirementsCard from "@/components/project/RequirementsCard.vue";
import type Project from "@/models/Project";
import type { UnmetRequirement } from "@/models/Project";

const props = defineProps<{
project: Project;
}>();

const { project } = toRefs(props);
const projectId = computed(() => project.value.id);

const router = useRouter();
const { t } = useI18n();

const inputFiles = ref<File[]>([]);
const remarksInput = ref<string | null>(null);
const unmetRequirements = ref<UnmetRequirement[]>([]);

const { data: group } = useProjectGroupQuery(projectId);
const groupId = computed(() => group.value?.id);
const { mutateAsync } = useCreateSubmissionMutation(groupId);

async function formOnSubmit(event: SubmitEvent) {
if (inputFiles.value.length === 0) {
alert(t("submit.no_files_warning"));
return;
}
const formData = new FormData(event.target as HTMLFormElement);

for (const inputFile of inputFiles.value) {
formData.append("files", inputFile);
}

try {
await mutateAsync(formData);
await router.push(`/groups/${group.value?.id}/submissions`);
} catch (error) {
if (error instanceof UnmetRequirementsError) {
unmetRequirements.value = error.unmetRequirements;
} else {
throw error;
}
}
}
</script>

<style scoped>
.v-btn {
background-color: rgb(var(--v-theme-secondary));
}

.v-textarea {
margin-top: 30px;
}

.submission-form {
margin-top: 30px;
}
</style>
6 changes: 6 additions & 0 deletions frontend/src/i18n/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ export default {
not_found: "No projects found.",
finished: "Finished",
not_found2: "Project not found",
requirements: "File requirements",
mandatory: "Mandatory",
forbidden: "Forbidden",
unmet_mandatory: "This mandatory file was not included in your submission.",
unmet_forbidden: "These submitted files are not allowed:",
unmet_reqs_warning: "Your submission did not satisfy all file requirements.",
to_subject: "To subject",
to_groups: "To groups",
},
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/i18n/locales/nl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ export default {
not_found: "Geen projecten teruggevonden.",
finished: "Afgerond",
not_found2: "Project niet teruggevonden",
requirements: "Bestandsvereisten",
mandatory: "Verplicht",
forbidden: "Verboden",
unmet_mandatory: "Dit verplichte bestand is niet aanwezig in je indiening.",
unmet_forbidden: "Deze ingediende bestanden zijn verboden:",
unmet_reqs_warning: "Opgelet: je indiening voldoet niet aan alle bestandsvereisten.",
to_subject: "Naar vak",
to_groups: "Naar groepen",
},
Expand Down
12 changes: 11 additions & 1 deletion frontend/src/models/Project.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export default interface Project {
// groupProjectType: string;
// selectedTeachers: string[]; // Assuming you store only teacher IDs
subject_id: number;
requirements: [];
requirements: Requirement[];
description: string;
capacity: number;
}
Expand All @@ -31,6 +31,16 @@ export interface Deadline {
status: string;
}

export interface Requirement {
mandatory: boolean;
value: string;
}

export interface UnmetRequirement {
requirement: Requirement;
files: string[] | undefined;
}

export enum FilterOptions {
All = "All",
Completed = "Completed",
Expand Down
Loading
Loading