feat: kleine correctie in form component

This commit is contained in:
Joyelle Ndagijimana 2025-03-29 22:32:16 +01:00
parent 36f7cfa4bd
commit 83b15a392d
3 changed files with 64 additions and 53 deletions

View file

@ -4,37 +4,30 @@ import { useI18n } from 'vue-i18n';
const props = defineProps({ const props = defineProps({
students: Array, // All students students: Array, // All students
availableClasses: Array, // Selected classes availableClass: Object, // Selected class
groups: Array, // All groups groups: Array, // All groups
}); });
const emit = defineEmits(['groupCreated']); const emit = defineEmits(['groupCreated']);
const { t } = useI18n(); const { t } = useI18n();
const selectedClass = ref(null);
const selectedStudents = ref([]); const selectedStudents = ref([]);
// Filter students based on the selected class and exclude students already in a group // Filter students based on the selected class and exclude students already in a group
const filteredStudents = computed(() => { const filteredStudents = computed(() => {
if (selectedClass.value) { if (props.availableClass) {
// Find the class based on selectedClass id const studentsInClass = props.availableClass.students.map(st => ({
const selected = props.availableClasses.find(cl => cl.id === selectedClass.value.id);
if (selected) {
// Get all students from the selected class
const studentsInClass = selected.students.map(st => ({
title: `${st.firstName} ${st.lastName}`, title: `${st.firstName} ${st.lastName}`,
value: st.username, value: st.username,
})); }));
// Get the list of students already in any group
const studentsInGroups = props.groups.flat(); const studentsInGroups = props.groups.flat();
// Filter out students that are already in a group
return studentsInClass.filter(student => !studentsInGroups.includes(student.value)); return studentsInClass.filter(student => !studentsInGroups.includes(student.value));
} }
}
return []; return [];
}); });
const createGroup = () => { const createGroup = () => {
if (selectedStudents.value.length) { if (selectedStudents.value.length) {
// Extract only usernames (student.value) // Extract only usernames (student.value)
@ -48,19 +41,6 @@ const createGroup = () => {
<template> <template>
<v-card-text> <v-card-text>
<v-combobox
v-model="selectedClass"
:items="props.availableClasses"
item-title="displayName"
item-value="id"
:label="t('pick-class')"
variant="outlined"
clearable
hide-details
density="compact"
class="mb-4"
></v-combobox>
<v-combobox <v-combobox
v-model="selectedStudents" v-model="selectedStudents"
:items="filteredStudents" :items="filteredStudents"

View file

@ -6,6 +6,7 @@
* @param selectedClasses - The selected classes, an array of class objects. * @param selectedClasses - The selected classes, an array of class objects.
* @param groups - An array of groups, each containing student IDs. * @param groups - An array of groups, each containing student IDs.
* @param deadline - The deadline of the assignment in ISO format. * @param deadline - The deadline of the assignment in ISO format.
* @param description - The description of the aasignment
* Sends a POST request to the backend with the form data. * Sends a POST request to the backend with the form data.
*/ */
export const submitForm = async ( export const submitForm = async (
@ -13,14 +14,16 @@ export const submitForm = async (
selectedLearningPath: any, selectedLearningPath: any,
selectedClasses: any[], selectedClasses: any[],
groups: string[][], groups: string[][],
deadline: string deadline: string,
description: string
) => { ) => {
const formData = { const formData = {
title: assignmentTitle, title: assignmentTitle,
hruid: selectedLearningPath?.hruid, hruid: selectedLearningPath?.hruid,
classes: selectedClasses.map(cl => cl.value), classes: selectedClasses.map(cl => cl.value),
groups: groups, groups: groups,
deadline: deadline deadline: deadline,
description: description
}; };
try { try {
@ -93,3 +96,10 @@ export const deadlineRules = [
return true; return true;
}, },
]; ];
export const descriptionRules = [
(value: string) => {
if (!value || value.trim() === "") return "Description cannot be empty.";
return true;
},
];

View file

@ -3,7 +3,13 @@
import {computed, onMounted, ref, watch} from "vue"; import {computed, onMounted, ref, watch} from "vue";
import GroupSelector from "@/components/GroupSelector.vue"; import GroupSelector from "@/components/GroupSelector.vue";
import {classes} from "@/utils/tempData.ts"; import {classes} from "@/utils/tempData.ts";
import {assignmentTitleRules, classesRules, learningPathRules, submitForm} from "@/utils/assignmentForm.ts"; import {
assignmentTitleRules,
classesRules,
descriptionRules,
learningPathRules,
submitForm
} from "@/utils/assignmentForm.ts";
import DeadlineSelector from "@/components/DeadlineSelector.vue"; import DeadlineSelector from "@/components/DeadlineSelector.vue";
const {t, locale} = useI18n(); const {t, locale} = useI18n();
@ -16,27 +22,28 @@
const assignmentTitle = ref(''); const assignmentTitle = ref('');
const deadline = ref(null); const deadline = ref(null);
const description = ref('');
const allLearningPaths = ref([]); const allLearningPaths = ref([]);
const filteredLearningPaths = ref([]); const filteredLearningPaths = ref([]);
const selectedLearningPath = ref(null); const selectedLearningPath = ref(null);
const allClasses = ref([...classes.map(cl => ({title: cl.displayName, value: cl.id}))]); const allClasses = ref([...classes.map(cl => ({title: cl.displayName, value: cl.id}))]);
const selectedClasses = ref([]); const selectedClass = ref(null);
const groups = ref<string[][]>([]); const groups = ref<string[][]>([]);
const availableClasses = computed(() => { const availableClass = computed(() => {
//TODO: replace by real data //TODO: replace by real data
return classes.filter(cl => selectedClasses.value.some(c => c.value === cl.id)); return classes.find(cl => selectedClass.value?.value === cl.id) || null;
}); });
const allStudents = computed(() => { const allStudents = computed(() => {
//TODO: replace by real data //TODO: replace by real data
return classes if (!selectedClass.value) return [];
.filter(cl => selectedClasses.value.some(c => c.value === cl.id)) const cl = classes.find(c => c.id === selectedClass.value.value);
.flatMap(cl => cl.students.map(st => ({ return cl ? cl.students.map(st => ({
title: `${st.firstName} ${st.lastName}`, title: `${st.firstName} ${st.lastName}`,
value: st.username, value: st.username,
classes: cl classes: cl
}))); })) : [];
}); });
@ -74,6 +81,10 @@
{immediate: true} {immediate: true}
); );
watch(selectedClass, () => {
groups.value = [];
});
const searchResults = computed(() => { const searchResults = computed(() => {
return filteredLearningPaths.value.filter((lp: { hruid: string; title: string }) => return filteredLearningPaths.value.filter((lp: { hruid: string; title: string }) =>
lp.title.toLowerCase().includes(searchQuery.value.toLowerCase()) lp.title.toLowerCase().includes(searchQuery.value.toLowerCase())
@ -84,10 +95,9 @@
const submitFormHandler = async () => { const submitFormHandler = async () => {
const { valid } = await form.value.validate(); const { valid } = await form.value.validate();
console.log(valid); // Don't submit thr form if all rules don't apply
console.log(deadline);
if (!valid) return; if (!valid) return;
submitForm(assignmentTitle.value, selectedLearningPath.value, selectedClasses.value, groups.value, deadline.value); submitForm(assignmentTitle.value, selectedLearningPath.value, selectedClass.value, groups.value, deadline.value, description.value);
}; };
</script> </script>
@ -123,21 +133,20 @@
<v-card-text> <v-card-text>
<v-combobox <v-combobox
v-model="selectedClasses" v-model="selectedClass"
:items="allClasses" :items="allClasses"
:label="t('choose-classes')" :label="t('pick-class')"
:rules="classesRules" :rules="classesRules"
variant="outlined" variant="outlined"
clearable clearable
multiple
hide-details hide-details
density="compact" density="compact"
chips
append-inner-icon="mdi-magnify" append-inner-icon="mdi-magnify"
item-title="title" item-title="title"
item-value="value" item-value="value"
required required
></v-combobox> ></v-combobox>
</v-card-text> </v-card-text>
<DeadlineSelector v-model:deadline="deadline" /> <DeadlineSelector v-model:deadline="deadline" />
@ -146,7 +155,7 @@
<GroupSelector <GroupSelector
:students="allStudents" :students="allStudents"
:availableClasses="availableClasses" :availableClass="availableClass"
:groups="groups" :groups="groups"
@groupCreated="addGroupToList" @groupCreated="addGroupToList"
/> />
@ -156,8 +165,21 @@
<strong>Created Groups: {{ groups.length }}</strong> <strong>Created Groups: {{ groups.length }}</strong>
</v-card-text> </v-card-text>
</v-container> <v-card-text>
<v-textarea
v-model="description"
:label="t('description')"
variant="outlined"
density="compact"
auto-grow
rows="3"
:rules="descriptionRules"
></v-textarea>
</v-card-text>
<v-btn class="mt-2" color="secondary" type="submit" block>Submit</v-btn> <v-btn class="mt-2" color="secondary" type="submit" block>Submit</v-btn>
</v-container>
</v-form> </v-form>
</v-card> </v-card>
</div> </div>
@ -169,7 +191,6 @@
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100vh;
text-align: center; text-align: center;
} }
@ -212,7 +233,7 @@
} }
/* Responsive adjustments */ /* Responsive adjustments */
@media (max-width: 700px) { @media (max-width: 650px) {
.form-card { .form-card {
width: 95%; width: 95%;
} }