feat(frontend): klas tonen bij assignment card
This commit is contained in:
		
							parent
							
								
									3423bd2724
								
							
						
					
					
						commit
						16f8aa449e
					
				
					 9 changed files with 321 additions and 289 deletions
				
			
		| 
						 | 
				
			
			@ -52,5 +52,6 @@
 | 
			
		|||
    "title": "Titel",
 | 
			
		||||
    "pick-class": "Wählen Sie eine klasse",
 | 
			
		||||
    "choose-students": "Studenten auswählen",
 | 
			
		||||
    "create-group": "Gruppe erstellen"
 | 
			
		||||
    "create-group": "Gruppe erstellen",
 | 
			
		||||
    "class": "klasse"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,5 +52,6 @@
 | 
			
		|||
    "title": "Title",
 | 
			
		||||
    "pick-class": "Pick a class",
 | 
			
		||||
    "choose-students": "Select students",
 | 
			
		||||
    "create-group": "Create group"
 | 
			
		||||
    "create-group": "Create group",
 | 
			
		||||
    "class": "class"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,5 +52,6 @@
 | 
			
		|||
    "title": "Titre",
 | 
			
		||||
    "pick-class": "Choisissez une classe",
 | 
			
		||||
    "choose-students": "Sélectionnez des élèves",
 | 
			
		||||
    "create-group": "Créer un groupe"
 | 
			
		||||
    "create-group": "Créer un groupe",
 | 
			
		||||
    "class": "classe"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,5 +52,6 @@
 | 
			
		|||
    "title": "Titel",
 | 
			
		||||
    "pick-class": "Kies een klas",
 | 
			
		||||
    "choose-students": "Studenten selecteren",
 | 
			
		||||
    "create-group": "Groep aanmaken"
 | 
			
		||||
    "create-group": "Groep aanmaken",
 | 
			
		||||
    "class": "klas"
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -65,6 +65,7 @@ export const assignments: Assignment[] = Array.from({length: 4}, (_, i) => ({
 | 
			
		|||
    id: `assignment${i}`,
 | 
			
		||||
    title: `Assignment ${i}`,
 | 
			
		||||
    learningPathHruid: 'lphruid',
 | 
			
		||||
    class: `class 0${i+1}`,
 | 
			
		||||
    description:
 | 
			
		||||
        "Lorem ipsum dolor sit amet, consectetuer adipiscing elit. " +
 | 
			
		||||
        "Aenean commodo ligula eget dolor. Aenean massa. Cum sociis natoque penatibus et magnis dis parturient montes, " +
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,238 +0,0 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
    import {useI18n} from "vue-i18n";
 | 
			
		||||
    import {computed, onMounted, ref, watch, defineProps, defineEmits} from "vue";
 | 
			
		||||
    import GroupSelector from "@/components/GroupSelector.vue";
 | 
			
		||||
    import DeadlineSelector from "@/components/DeadlineSelector.vue";
 | 
			
		||||
    import {
 | 
			
		||||
        assignmentTitleRules,
 | 
			
		||||
        classesRules,
 | 
			
		||||
        descriptionRules,
 | 
			
		||||
        learningPathRules
 | 
			
		||||
    } from "@/utils/assignmentForm.ts";
 | 
			
		||||
    import {classes} from "@/utils/tempData.ts";
 | 
			
		||||
 | 
			
		||||
    const {t, locale} = useI18n();
 | 
			
		||||
    const emit = defineEmits(["submit"]);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    const props = defineProps({
 | 
			
		||||
        sort: {type: String},
 | 
			
		||||
        initialTitle: {type: String, default: ""},
 | 
			
		||||
        initialDeadline: {type: Date, default: null},
 | 
			
		||||
        initialDescription: {type: String, default: ""},
 | 
			
		||||
        initialLearningPath: {type: Object, default: null},
 | 
			
		||||
        initialClass: {type: Object, default: null},
 | 
			
		||||
        initialGroups: {type: Array, default: () => []}
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const form = ref();
 | 
			
		||||
    const language = ref(locale.value);
 | 
			
		||||
    const searchQuery = ref("");
 | 
			
		||||
    const assignmentTitle = ref(props.initialTitle);
 | 
			
		||||
    const deadline = ref(props.initialDeadline);
 | 
			
		||||
    const description = ref(props.initialDescription);
 | 
			
		||||
    const selectedLearningPath = ref(props.initialLearningPath);
 | 
			
		||||
    const selectedClass = ref(props.initialClass);
 | 
			
		||||
    const groups = ref(props.initialGroups);
 | 
			
		||||
    const allLearningPaths = ref([]);
 | 
			
		||||
    const filteredLearningPaths = ref([]);
 | 
			
		||||
    //TODO: replace by real data
 | 
			
		||||
    const allClasses = ref([...classes.map(cl => ({title: cl.displayName, value: cl.id}))]);
 | 
			
		||||
 | 
			
		||||
    const availableClass = computed(() => {
 | 
			
		||||
        //TODO: replace by real data
 | 
			
		||||
        return classes.find(cl => selectedClass.value?.value === cl.id) || null;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const allStudents = computed(() => {
 | 
			
		||||
        //TODO: replace by real data
 | 
			
		||||
        if (!selectedClass.value) return [];
 | 
			
		||||
        const cl = classes.find(c => c.id === selectedClass.value.value);
 | 
			
		||||
        return cl ? cl.students.map(st => ({
 | 
			
		||||
            title: `${st.firstName} ${st.lastName}`,
 | 
			
		||||
            value: st.username,
 | 
			
		||||
            classes: cl
 | 
			
		||||
        })) : [];
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    const addGroupToList = (students: string[]) => {
 | 
			
		||||
        if (students.length) {
 | 
			
		||||
            groups.value = [...groups.value, students];
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    async function fetchAllLearningPaths() {
 | 
			
		||||
        try {
 | 
			
		||||
            //TODO: replace by function from controller
 | 
			
		||||
            const response = await fetch(`http://localhost:3000/api/learningPath?language=${language.value}`);
 | 
			
		||||
            if (!response.ok) throw new Error("Failed to fetch learning paths");
 | 
			
		||||
            const data = await response.json();
 | 
			
		||||
            allLearningPaths.value = data.map((lp: { hruid: string; title: string }) => ({
 | 
			
		||||
                hruid: lp.hruid,
 | 
			
		||||
                title: lp.title
 | 
			
		||||
            }));
 | 
			
		||||
            filteredLearningPaths.value = [...allLearningPaths.value];
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error(error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    watch(
 | 
			
		||||
        () => locale.value,
 | 
			
		||||
        (newLocale) => {
 | 
			
		||||
            if (!["nl", "en"].includes(newLocale)) {
 | 
			
		||||
                language.value = "en";
 | 
			
		||||
            }
 | 
			
		||||
            fetchAllLearningPaths();
 | 
			
		||||
        },
 | 
			
		||||
        {immediate: true}
 | 
			
		||||
    );
 | 
			
		||||
    watch(selectedClass, () => groups.value = []);
 | 
			
		||||
 | 
			
		||||
    // Seach queries for the dropdown search bar
 | 
			
		||||
    const searchResults = computed(() => filteredLearningPaths.value.filter((lp) =>
 | 
			
		||||
        lp.title.toLowerCase().includes(searchQuery.value.toLowerCase())
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    onMounted(fetchAllLearningPaths);
 | 
			
		||||
 | 
			
		||||
    const submitFormHandler = async () => {
 | 
			
		||||
        const {valid} = await form.value.validate();
 | 
			
		||||
        if (!valid) return;
 | 
			
		||||
        emit("submit", {
 | 
			
		||||
            title: assignmentTitle.value,
 | 
			
		||||
            description: description,
 | 
			
		||||
            learningPathHruid: selectedLearningPath.value,
 | 
			
		||||
            class: selectedClass.value,
 | 
			
		||||
            groups: groups.value,
 | 
			
		||||
            deadline: deadline.value
 | 
			
		||||
        });
 | 
			
		||||
    };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
    <div class="main-container">
 | 
			
		||||
        <h1 class="title">{{ t(`${sort}-assignment`) }}</h1>
 | 
			
		||||
        <v-card class="form-card">
 | 
			
		||||
            <v-form ref="form" class="form-container" validate-on="submit lazy" @submit.prevent="submitFormHandler">
 | 
			
		||||
                <v-container class="step-container">
 | 
			
		||||
                    <v-card-text>
 | 
			
		||||
                        <v-text-field
 | 
			
		||||
                            v-model="assignmentTitle"
 | 
			
		||||
                            :label="t('title')"
 | 
			
		||||
                            :rules="assignmentTitleRules"
 | 
			
		||||
                            density="compact"
 | 
			
		||||
                            variant="outlined"
 | 
			
		||||
                            clearable
 | 
			
		||||
                            required
 | 
			
		||||
                        ></v-text-field>
 | 
			
		||||
                    </v-card-text>
 | 
			
		||||
 | 
			
		||||
                    <v-card-text>
 | 
			
		||||
                        <v-combobox
 | 
			
		||||
                            v-model="selectedLearningPath"
 | 
			
		||||
                            :items="searchResults"
 | 
			
		||||
                            :label="t('choose-lp')"
 | 
			
		||||
                            :rules="learningPathRules"
 | 
			
		||||
                            variant="outlined"
 | 
			
		||||
                            clearable
 | 
			
		||||
                            hide-details
 | 
			
		||||
                            density="compact"
 | 
			
		||||
                            append-inner-icon="mdi-magnify"
 | 
			
		||||
                            item-title="title"
 | 
			
		||||
                            item-value="value"
 | 
			
		||||
                            required
 | 
			
		||||
                            :filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())"
 | 
			
		||||
                        ></v-combobox>
 | 
			
		||||
                    </v-card-text>
 | 
			
		||||
 | 
			
		||||
                    <v-card-text>
 | 
			
		||||
                        <v-combobox
 | 
			
		||||
                            v-model="selectedClass"
 | 
			
		||||
                            :items="allClasses"
 | 
			
		||||
                            item-title="title"
 | 
			
		||||
                            item-value="value"
 | 
			
		||||
                            :label="t('pick-class')"
 | 
			
		||||
                            :rules="classesRules"
 | 
			
		||||
                            variant="outlined"
 | 
			
		||||
                            clearable
 | 
			
		||||
                            hide-details
 | 
			
		||||
                            density="compact"
 | 
			
		||||
                            append-inner-icon="mdi-magnify"
 | 
			
		||||
                            required></v-combobox>
 | 
			
		||||
                    </v-card-text>
 | 
			
		||||
 | 
			
		||||
                    <DeadlineSelector v-model:deadline="deadline"/>
 | 
			
		||||
 | 
			
		||||
                    <h3>{{ t('create-groups') }}</h3>
 | 
			
		||||
 | 
			
		||||
                    <GroupSelector
 | 
			
		||||
                        :students="allStudents"
 | 
			
		||||
                        :availableClass="availableClass"
 | 
			
		||||
                        :groups="groups"
 | 
			
		||||
                        @groupCreated="addGroupToList"/>
 | 
			
		||||
 | 
			
		||||
                    <v-card-text v-if="groups.length">
 | 
			
		||||
                        <strong>Created Groups: {{ groups.length }}</strong>
 | 
			
		||||
                    </v-card-text>
 | 
			
		||||
 | 
			
		||||
                    <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-container>
 | 
			
		||||
            </v-form>
 | 
			
		||||
        </v-card>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.main-container {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.title {
 | 
			
		||||
    margin-bottom: 1%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.form-card {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    width: 55%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.form-container {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.step-container {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    min-height: 200px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 1000px) {
 | 
			
		||||
    .form-card {
 | 
			
		||||
        width: 70%;
 | 
			
		||||
        padding: 1%;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 650px) {
 | 
			
		||||
    .form-card {
 | 
			
		||||
        width: 95%;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			@ -1,20 +1,239 @@
 | 
			
		|||
<script setup lang="ts">
 | 
			
		||||
    import AssignmentForm from "@/views/assignments/AssignmentForm.vue";
 | 
			
		||||
    const sort = "new";
 | 
			
		||||
    import {useI18n} from "vue-i18n";
 | 
			
		||||
    import {computed, onMounted, ref, watch} from "vue";
 | 
			
		||||
    import GroupSelector from "@/components/GroupSelector.vue";
 | 
			
		||||
    import {classes} from "@/utils/tempData.ts";
 | 
			
		||||
    import {
 | 
			
		||||
        assignmentTitleRules,
 | 
			
		||||
        classesRules,
 | 
			
		||||
        descriptionRules,
 | 
			
		||||
        learningPathRules,
 | 
			
		||||
        submitForm
 | 
			
		||||
    } from "@/utils/assignmentForm.ts";
 | 
			
		||||
    import DeadlineSelector from "@/components/DeadlineSelector.vue";
 | 
			
		||||
 | 
			
		||||
    const handleSubmit = async (formData) => {
 | 
			
		||||
        /*await fetch('/api/assignments', {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            body: JSON.stringify(formData),
 | 
			
		||||
            headers: { 'Content-Type': 'application/json' }
 | 
			
		||||
        });*/
 | 
			
		||||
    const {t, locale} = useI18n();
 | 
			
		||||
 | 
			
		||||
    const form = ref();
 | 
			
		||||
 | 
			
		||||
    const language = ref(locale.value);
 | 
			
		||||
 | 
			
		||||
    const searchQuery = ref('');
 | 
			
		||||
 | 
			
		||||
    const assignmentTitle = ref('');
 | 
			
		||||
    const deadline = ref(null);
 | 
			
		||||
    const description = ref('');
 | 
			
		||||
    const allLearningPaths = ref([]);
 | 
			
		||||
    const filteredLearningPaths = ref([]);
 | 
			
		||||
    const selectedLearningPath = ref(null);
 | 
			
		||||
    const allClasses = ref([...classes.map(cl => ({title: cl.displayName, value: cl.id}))]);
 | 
			
		||||
    const selectedClass = ref(null);
 | 
			
		||||
    const groups = ref<string[][]>([]);
 | 
			
		||||
 | 
			
		||||
    const availableClass = computed(() => {
 | 
			
		||||
        //TODO: replace by real data
 | 
			
		||||
        return classes.find(cl => selectedClass.value?.value === cl.id) || null;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const allStudents = computed(() => {
 | 
			
		||||
        //TODO: replace by real data
 | 
			
		||||
        if (!selectedClass.value) return [];
 | 
			
		||||
        const cl = classes.find(c => c.id === selectedClass.value.value);
 | 
			
		||||
        return cl ? cl.students.map(st => ({
 | 
			
		||||
            title: `${st.firstName} ${st.lastName}`,
 | 
			
		||||
            value: st.username,
 | 
			
		||||
            classes: cl
 | 
			
		||||
        })) : [];
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    // New group is added to the list
 | 
			
		||||
    const addGroupToList = (students: string[]) => {
 | 
			
		||||
        if (students.length) {
 | 
			
		||||
            groups.value = [...groups.value, students];
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    async function fetchAllLearningPaths() {
 | 
			
		||||
        try {
 | 
			
		||||
            //TODO: replace by function from controller
 | 
			
		||||
            const response = await fetch(`http://localhost:3000/api/learningPath?language=${language.value}`);
 | 
			
		||||
            if (!response.ok) throw new Error("Failed to fetch learning paths");
 | 
			
		||||
            const data = await response.json();
 | 
			
		||||
            allLearningPaths.value = data.map((lp: { hruid: string; title: string }) => ({
 | 
			
		||||
                hruid: lp.hruid,
 | 
			
		||||
                title: lp.title
 | 
			
		||||
            }));
 | 
			
		||||
            filteredLearningPaths.value = [...allLearningPaths.value];
 | 
			
		||||
        } catch (error) {
 | 
			
		||||
            console.error(error);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    watch(
 | 
			
		||||
        () => locale.value,
 | 
			
		||||
        (newLocale) => {
 | 
			
		||||
            if (!["nl", "en"].includes(newLocale)) {
 | 
			
		||||
                language.value = "en";
 | 
			
		||||
            }
 | 
			
		||||
            fetchAllLearningPaths();
 | 
			
		||||
        },
 | 
			
		||||
        {immediate: true}
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    watch(selectedClass, () => {
 | 
			
		||||
        groups.value = [];
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    const searchResults = computed(() => {
 | 
			
		||||
        return filteredLearningPaths.value.filter((lp: { hruid: string; title: string }) =>
 | 
			
		||||
            lp.title.toLowerCase().includes(searchQuery.value.toLowerCase())
 | 
			
		||||
        );
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    onMounted(fetchAllLearningPaths);
 | 
			
		||||
 | 
			
		||||
    const submitFormHandler = async () => {
 | 
			
		||||
        const { valid } = await form.value.validate();
 | 
			
		||||
        // Don't submit thr form if all rules don't apply
 | 
			
		||||
        if (!valid) return;
 | 
			
		||||
        submitForm(assignmentTitle.value, selectedLearningPath.value, selectedClass.value, groups.value, deadline.value, description.value);
 | 
			
		||||
    };
 | 
			
		||||
</script>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<template>
 | 
			
		||||
    <AssignmentForm :sort="sort" @submit="handleSubmit"></AssignmentForm>
 | 
			
		||||
    <div class="main-container">
 | 
			
		||||
        <h1 class="title">{{ t("new-assignment") }}</h1>
 | 
			
		||||
        <v-card class="form-card">
 | 
			
		||||
            <v-form ref="form" class="form-container" validate-on="submit lazy" @submit.prevent="submitFormHandler">
 | 
			
		||||
                <v-container class="step-container">
 | 
			
		||||
                    <v-card-text>
 | 
			
		||||
                        <v-text-field :v-model="assignmentTitle" :label="t('title')" :rules="assignmentTitleRules"
 | 
			
		||||
                                      density="compact" variant="outlined" clearable required></v-text-field>
 | 
			
		||||
                    </v-card-text>
 | 
			
		||||
 | 
			
		||||
                    <v-card-text>
 | 
			
		||||
                        <v-combobox
 | 
			
		||||
                            v-model="selectedLearningPath"
 | 
			
		||||
                            :items="searchResults"
 | 
			
		||||
                            :label="t('choose-lp')"
 | 
			
		||||
                            :rules="learningPathRules"
 | 
			
		||||
                            variant="outlined"
 | 
			
		||||
                            clearable
 | 
			
		||||
                            hide-details
 | 
			
		||||
                            density="compact"
 | 
			
		||||
                            append-inner-icon="mdi-magnify"
 | 
			
		||||
                            item-title="title"
 | 
			
		||||
                            item-value="value"
 | 
			
		||||
                            required
 | 
			
		||||
                            :filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())"
 | 
			
		||||
                        ></v-combobox>
 | 
			
		||||
                    </v-card-text>
 | 
			
		||||
 | 
			
		||||
                    <v-card-text>
 | 
			
		||||
                        <v-combobox
 | 
			
		||||
                            v-model="selectedClass"
 | 
			
		||||
                            :items="allClasses"
 | 
			
		||||
                            :label="t('pick-class')"
 | 
			
		||||
                            :rules="classesRules"
 | 
			
		||||
                            variant="outlined"
 | 
			
		||||
                            clearable
 | 
			
		||||
                            hide-details
 | 
			
		||||
                            density="compact"
 | 
			
		||||
                            append-inner-icon="mdi-magnify"
 | 
			
		||||
                            item-title="title"
 | 
			
		||||
                            item-value="value"
 | 
			
		||||
                            required
 | 
			
		||||
                        ></v-combobox>
 | 
			
		||||
 | 
			
		||||
                    </v-card-text>
 | 
			
		||||
 | 
			
		||||
                    <DeadlineSelector v-model:deadline="deadline" />
 | 
			
		||||
 | 
			
		||||
                    <h3>{{ t('create-groups') }}</h3>
 | 
			
		||||
 | 
			
		||||
                    <GroupSelector
 | 
			
		||||
                        :students="allStudents"
 | 
			
		||||
                        :availableClass="availableClass"
 | 
			
		||||
                        :groups="groups"
 | 
			
		||||
                        @groupCreated="addGroupToList"
 | 
			
		||||
                    />
 | 
			
		||||
 | 
			
		||||
                    <!-- Counter for created groups -->
 | 
			
		||||
                    <v-card-text v-if="groups.length">
 | 
			
		||||
                        <strong>Created Groups: {{ groups.length }}</strong>
 | 
			
		||||
                    </v-card-text>
 | 
			
		||||
 | 
			
		||||
                    <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-container>
 | 
			
		||||
            </v-form>
 | 
			
		||||
        </v-card>
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.main-container {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.title {
 | 
			
		||||
    margin-bottom: 1%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.form-card {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    width: 55%;
 | 
			
		||||
    /*padding: 1%;*/
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.form-container {
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.step-container {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: center;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    min-height: 200px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 1000px) {
 | 
			
		||||
    .form-card {
 | 
			
		||||
        width: 70%;
 | 
			
		||||
        padding: 1%;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    .step-container {
 | 
			
		||||
        min-height: 300px;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@media (max-width: 650px) {
 | 
			
		||||
    .form-card {
 | 
			
		||||
        width: 95%;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -50,6 +50,12 @@
 | 
			
		|||
                </v-btn>
 | 
			
		||||
            </div>
 | 
			
		||||
            <v-card-title class="text-h4">{{ assignment.title }}</v-card-title>
 | 
			
		||||
            <v-container class="assignment-class">
 | 
			
		||||
                {{ t('class') }}:
 | 
			
		||||
                <span class="class-name">
 | 
			
		||||
                    {{ assignment.class }}
 | 
			
		||||
                  </span>
 | 
			
		||||
            </v-container>
 | 
			
		||||
            <v-card-subtitle>
 | 
			
		||||
                <v-btn
 | 
			
		||||
                    :to="`/learningPath/${assignment.learningPathHruid}`"
 | 
			
		||||
| 
						 | 
				
			
			@ -77,8 +83,7 @@
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
.assignment-card {
 | 
			
		||||
    width: 90%;
 | 
			
		||||
    max-width: 900px;
 | 
			
		||||
    width: 85%;
 | 
			
		||||
    padding: 2%;
 | 
			
		||||
    border-radius: 12px;
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -95,5 +100,14 @@
 | 
			
		|||
    color: red;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.assignment-class {
 | 
			
		||||
    color: #666;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.class-name {
 | 
			
		||||
    font-weight: 500;
 | 
			
		||||
    color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
</style>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,67 +42,99 @@
 | 
			
		|||
    <div class="assignments-container">
 | 
			
		||||
        <h1>{{ t('assignments') }}</h1>
 | 
			
		||||
 | 
			
		||||
        <v-btn v-if="isTeacher" color="primary" class="mb-4" @click="goToCreateAssignment">
 | 
			
		||||
        <v-btn
 | 
			
		||||
            v-if="isTeacher"
 | 
			
		||||
            color="primary"
 | 
			
		||||
            class="mb-4"
 | 
			
		||||
            @click="goToCreateAssignment"
 | 
			
		||||
        >
 | 
			
		||||
            {{ t('new-assignment') }}
 | 
			
		||||
        </v-btn>
 | 
			
		||||
 | 
			
		||||
        <v-container>
 | 
			
		||||
            <v-row>
 | 
			
		||||
                <v-col v-for="assignment in allAssignments" :key="assignment.id" cols="12">
 | 
			
		||||
                <v-col
 | 
			
		||||
                    v-for="assignment in allAssignments"
 | 
			
		||||
                    :key="assignment.id"
 | 
			
		||||
                    cols="12"
 | 
			
		||||
                >
 | 
			
		||||
                    <v-card class="assignment-card" variant="outlined">
 | 
			
		||||
                        <v-card-title class="title">{{ assignment.title }}</v-card-title>
 | 
			
		||||
                        <v-divider></v-divider>
 | 
			
		||||
                        <v-card-text class="description-text" >{{ assignment.description }}</v-card-text>
 | 
			
		||||
 | 
			
		||||
                        <v-card-actions>
 | 
			
		||||
                            <v-btn color="primary" @click="goToAssignmentDetails(assignment.id)">
 | 
			
		||||
                                {{ t('view-assignment') }}
 | 
			
		||||
                            </v-btn>
 | 
			
		||||
                            <v-btn v-if="isTeacher" color="secondary" @click="goToDeleteAssignment(assignment.id)">
 | 
			
		||||
                                {{ t('delete') }}
 | 
			
		||||
                            </v-btn>
 | 
			
		||||
                        </v-card-actions>
 | 
			
		||||
                        <v-card-text class="card-content">
 | 
			
		||||
                            <div class="left-content">
 | 
			
		||||
                                <div class="assignment-title">{{ assignment.title }}</div>
 | 
			
		||||
                                <div class="assignment-class">
 | 
			
		||||
                                    {{ t('class') }}:
 | 
			
		||||
                                    <span class="class-name">
 | 
			
		||||
                    {{ assignment.class }}
 | 
			
		||||
                  </span>
 | 
			
		||||
                                </div>
 | 
			
		||||
                            </div>
 | 
			
		||||
                            <div class="right-content">
 | 
			
		||||
                                <v-btn
 | 
			
		||||
                                    color="primary"
 | 
			
		||||
                                    @click="goToAssignmentDetails(assignment.id)"
 | 
			
		||||
                                >
 | 
			
		||||
                                    {{ t('view-assignment') }}
 | 
			
		||||
                                </v-btn>
 | 
			
		||||
                            </div>
 | 
			
		||||
                        </v-card-text>
 | 
			
		||||
                    </v-card>
 | 
			
		||||
                </v-col>
 | 
			
		||||
            </v-row>
 | 
			
		||||
        </v-container>
 | 
			
		||||
 | 
			
		||||
    </div>
 | 
			
		||||
</template>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<style scoped>
 | 
			
		||||
.assignments-container {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    padding: 2%;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
    margin: 0 auto;
 | 
			
		||||
    padding: 2% 4%;
 | 
			
		||||
    box-sizing: border-box;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.assignment-card {
 | 
			
		||||
    padding: 1%;
 | 
			
		||||
    padding: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.card-content {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    justify-content: space-between;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    flex-wrap: wrap;
 | 
			
		||||
    gap: 1rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.left-content {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    flex-direction: column;
 | 
			
		||||
    justify-content: space-between;
 | 
			
		||||
    flex: 1;
 | 
			
		||||
    min-width: 200px;
 | 
			
		||||
    max-width: 70%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
h1 {
 | 
			
		||||
    text-align: left;
 | 
			
		||||
    width: 100%;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.title {
 | 
			
		||||
    flex-grow: 1;
 | 
			
		||||
    white-space: normal;
 | 
			
		||||
    overflow-wrap: break-word;
 | 
			
		||||
.assignment-title {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    font-size: 1.5rem;
 | 
			
		||||
    margin-bottom: 0.3rem;
 | 
			
		||||
    word-break: break-word;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.description-text {
 | 
			
		||||
    display: -webkit-box;
 | 
			
		||||
    -webkit-line-clamp: 3;  /* Limit to 3 lines */
 | 
			
		||||
    -webkit-box-orient: vertical;
 | 
			
		||||
    overflow: hidden;
 | 
			
		||||
    text-overflow: ellipsis;
 | 
			
		||||
.assignment-class {
 | 
			
		||||
    color: #666;
 | 
			
		||||
    font-size: 0.95rem;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.class-name {
 | 
			
		||||
    font-weight: 500;
 | 
			
		||||
    color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.right-content {
 | 
			
		||||
    display: flex;
 | 
			
		||||
    align-items: center;
 | 
			
		||||
    justify-content: flex-end;
 | 
			
		||||
    flex-shrink: 0;
 | 
			
		||||
}
 | 
			
		||||
</style>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Reference in a new issue