feat(frontend): leerkracht kan alle groepen binnen een assignment zien en leerling kan zijn group zien

This commit is contained in:
Joyelle Ndagijimana 2025-04-18 23:16:59 +02:00
parent 83f01830e3
commit 20cf276faf
15 changed files with 397 additions and 341 deletions

View file

@ -1,226 +0,0 @@
<script setup lang="ts">
import {useI18n} from "vue-i18n";
import {computed, onMounted, ref, watch} from "vue";
import GroupSelector from "@/components/assignments/GroupSelector.vue";
import {assignmentTitleRules, classRules, descriptionRules, learningPathRules} from "@/utils/assignment-rules.ts";
import DeadlineSelector from "@/components/assignments/DeadlineSelector.vue";
import auth from "@/services/auth/auth-service.ts";
import {useTeacherClassesQuery} from "@/queries/teachers.ts";
import {useRouter} from "vue-router";
import {useGetAllLearningPaths} from "@/queries/learning-paths.ts";
import UsingQueryResult from "@/components/UsingQueryResult.vue";
import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts";
import type {ClassesResponse} from "@/controllers/classes.ts";
import type {AssignmentDTO} from "@dwengo-1/common/interfaces/assignment";
import {AssignmentController} from "@/controllers/assignments.ts";
/***
TODO: when clicking the assign button from lp page pass the lp-object in a state:
*/
const router = useRouter();
const {t, locale} = useI18n();
const role = ref(auth.authState.activeRole);
const username = ref<string>("");
async function submitForm(assignmentDTO: AssignmentDTO): Promise<void> {
//TODO: replace with query function
const controller: AssignmentController = new AssignmentController(assignmentDTO.class);
await controller.createAssignment(assignmentDTO);
// Navigate back to all assignments
await router.push('/user/assignment');
}
onMounted(async () => {
// Redirect student
if (role.value === 'student') {
await router.push('/user');
}
// Get the user's username
const user = await auth.loadUser();
username.value = user?.profile?.preferred_username ?? "";
});
const language = computed(() => locale.value);
const form = ref();
//Fetch all learning paths
const learningPathsQueryResults = useGetAllLearningPaths(language);
// Fetch and store all the teacher's classes
const classesQueryResults = useTeacherClassesQuery(username, true);
const selectedClass = ref(undefined);
const assignmentTitle = ref('');
const selectedLearningPath = ref<LearningPath | null>(props.learningPath ?? null);
// Disable combobox when learningPath prop is passed
const isLearningPathSelected = props.learningPath !== null;
const deadline = ref(null);
const description = ref('');
const groups = ref<string[][]>([]);
// New group is added to the list
function addGroupToList(students: string[]): void {
if (students.length) {
groups.value = [...groups.value, students];
}
}
watch(selectedClass, () => {
groups.value = [];
});
async function submitFormHandler(): Promise<void> {
const {valid} = await form.value.validate();
// Don't submit the form if all rules don't apply
if (!valid) return;
const assignmentDTO: AssignmentDTO = {
id: 0,
class: selectedClass.value?.id || "",
title: assignmentTitle.value,
description: description.value,
learningPath: selectedLearningPath.value?.hruid || "",
language: language.value
}
await submitForm(assignmentDTO);
}
</script>
<template>
<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>
<using-query-result
:query-result="learningPathsQueryResults"
v-slot="{ data }: { data: LearningPath[] }"
>
<v-card-text>
<v-combobox
v-model="selectedLearningPath"
:items="data"
:label="t('choose-lp')"
:rules="learningPathRules"
variant="outlined"
clearable
hide-details
density="compact"
append-inner-icon="mdi-magnify"
item-title="title"
item-value="hruid"
required
:disabled="isLearningPathSelected"
:filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())"
></v-combobox>
</v-card-text>
</using-query-result>
<using-query-result
:query-result="classesQueryResults"
v-slot="{ data }: {data: ClassesResponse}"
>
<v-card-text>
<v-combobox
v-model="selectedClass"
:items="data?.classes ?? []"
:label="t('pick-class')"
:rules="classRules"
variant="outlined"
clearable
hide-details
density="compact"
append-inner-icon="mdi-magnify"
item-title="displayName"
item-value="id"
required
></v-combobox>
</v-card-text>
</using-query-result>
<GroupSelector
:classId="selectedClass?.id"
:groups="groups"
@groupCreated="addGroupToList"
/>
<!-- Counter for created groups -->
<v-card-text v-if="groups.length">
<strong>Created Groups: {{ groups.length }}</strong>
</v-card-text>
<DeadlineSelector v-model:deadline="deadline"/>
<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;
}
.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>

View file

@ -1,5 +1,5 @@
<script setup lang="ts">
import { ref, computed, defineEmits } from "vue";
import {ref, computed, defineEmits} from "vue";
import {deadlineRules} from "@/utils/assignment-rules.ts";
const date = ref("");
@ -11,7 +11,7 @@ const formattedDeadline = computed(() => {
return `${date.value} ${time.value}`;
});
const updateDeadline = () => {
function updateDeadline(): void {
if (date.value && time.value) {
emit("update:deadline", formattedDeadline.value);
}

View file

@ -29,7 +29,7 @@ function filterStudents(data: StudentsResponse): { title: string, value: string
}
const createGroup = () => {
function createGroup(): void {
if (selectedStudents.value.length) {
// Extract only usernames (student.value)
const usernames = selectedStudents.value.map(student => student.value);

View file

@ -70,7 +70,7 @@
"create-group": "Gruppe erstellen",
"class": "klasse",
"delete": "löschen",
"view-assignment": "Auftrag anzeigen"
"view-assignment": "Auftrag anzeigen",
"legendTeacherExclusive": "Information für Lehrkräfte",
"code": "code",
"class": "Klasse",

View file

@ -70,10 +70,8 @@
"create-group": "Create group",
"class": "class",
"delete": "delete",
"view-assignment": "View assignment"
"read-more": "Read more",
"view-assignment": "View assignment",
"code": "code",
"class": "class",
"invitations": "invitations",
"createClass": "create class",
"classname": "classname",

View file

@ -70,7 +70,7 @@
"create-group": "Créer un groupe",
"class": "classe",
"delete": "supprimer",
"view-assignment": "Voir le travail"
"view-assignment": "Voir le travail",
"read-more": "En savoir plus",
"code": "code",
"class": "classe",

View file

@ -70,7 +70,7 @@
"create-group": "Groep aanmaken",
"class": "klas",
"delete": "verwijderen",
"view-assignment": "Opdracht bekijken"
"view-assignment": "Opdracht bekijken",
"read-more": "Lees meer",
"code": "code",
"class": "klas",

View file

@ -104,7 +104,7 @@ export function useAssignmentsQuery(
export function useAssignmentQuery(
classid: MaybeRefOrGetter<string | undefined>,
assignmentNumber: MaybeRefOrGetter<number | undefined>,
): UseQueryReturnType<AssignmentsResponse, Error> {
): UseQueryReturnType<AssignmentResponse, Error> {
const { cid, an } = toValues(classid, assignmentNumber, 1, true);
return useQuery({

View file

@ -1,8 +1,9 @@
import { computed, toValue } from "vue";
import {computed, type Ref, toValue} from "vue";
import type { MaybeRefOrGetter } from "vue";
import {
type QueryObserverResult,
useMutation,
type UseMutationReturnType,
type UseMutationReturnType, useQueries,
useQuery,
useQueryClient,
type UseQueryReturnType,
@ -69,6 +70,20 @@ export function useStudentQuery(
});
}
export function useStudentsByUsernamesQuery(
usernames: MaybeRefOrGetter<string[] | undefined>
): Ref<QueryObserverResult<StudentResponse>[]> {
const resolvedUsernames = toValue(usernames) ?? [];
return useQueries({
queries: resolvedUsernames?.map((username) => ({
queryKey: computed(() => studentQueryKey(toValue(username))),
queryFn: async () => studentController.getByUsername(toValue(username)),
enabled: Boolean(toValue(username)),
})),
});
}
export function useStudentClassesQuery(
username: MaybeRefOrGetter<string | undefined>,
full: MaybeRefOrGetter<boolean> = true,

View file

@ -88,7 +88,7 @@ const router = createRouter({
component: CreateAssignment,
},
{
path: ":id",
path: ":classId/:id",
name: "SingleAssigment",
component: SingleAssignment,
},

View file

@ -1,10 +1,225 @@
<script setup lang="ts">
import AssignmentForm from "@/components/assignments/AssignmentForm.vue";
import {useI18n} from "vue-i18n";
import {computed, onMounted, ref, watch} from "vue";
import GroupSelector from "@/components/assignments/GroupSelector.vue";
import {assignmentTitleRules, classRules, descriptionRules, learningPathRules} from "@/utils/assignment-rules.ts";
import DeadlineSelector from "@/components/assignments/DeadlineSelector.vue";
import auth from "@/services/auth/auth-service.ts";
import {useTeacherClassesQuery} from "@/queries/teachers.ts";
import {useRouter} from "vue-router";
import {useGetAllLearningPaths} from "@/queries/learning-paths.ts";
import UsingQueryResult from "@/components/UsingQueryResult.vue";
import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts";
import type {ClassesResponse} from "@/controllers/classes.ts";
import type {AssignmentDTO} from "@dwengo-1/common/interfaces/assignment";
import {AssignmentController} from "@/controllers/assignments.ts";
import {useCreateAssignmentMutation} from "@/queries/assignments.ts";
/***
TODO: when clicking the assign button from lp page pass the lp-object in a state:
*/
const router = useRouter();
const {t, locale} = useI18n();
const role = ref(auth.authState.activeRole);
const username = ref<string>("");
onMounted(async () => {
// Redirect student
if (role.value === 'student') {
await router.push('/user');
}
// Get the user's username
const user = await auth.loadUser();
username.value = user?.profile?.preferred_username ?? "";
});
const language = computed(() => locale.value);
const form = ref();
//Fetch all learning paths
const learningPathsQueryResults = useGetAllLearningPaths(language);
// Fetch and store all the teacher's classes
const classesQueryResults = useTeacherClassesQuery(username, true);
const selectedClass = ref(undefined);
const assignmentTitle = ref('');
const selectedLearningPath = ref<LearningPath | null>(window.history.state?.learningPath ?? null);
// Disable combobox when learningPath prop is passed
const lpIsSelected = window.history.state?.learningPath !== undefined;
const deadline = ref(null);
const description = ref('');
const groups = ref<string[][]>([]);
// New group is added to the list
function addGroupToList(students: string[]): void {
if (students.length) {
groups.value = [...groups.value, students];
}
}
watch(selectedClass, () => {
groups.value = [];
});
const {mutate, isSuccess} = useCreateAssignmentMutation();
async function submitFormHandler(): Promise<void> {
const {valid} = await form.value.validate();
if (!valid) return;
const assignmentDTO: AssignmentDTO = {
id: 0,
within: selectedClass.value?.id || "",
title: assignmentTitle.value,
description: description.value,
learningPath: selectedLearningPath.value?.hruid || "",
language: language.value,
groups: groups.value
};
mutate({cid: assignmentDTO.within, data: assignmentDTO});
if (isSuccess) await router.push("/user/assignment");
}
</script>
<template>
<AssignmentForm :learning-path="null"/>
<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>
<using-query-result
:query-result="learningPathsQueryResults"
v-slot="{ data }: { data: LearningPath[] }"
>
<v-card-text>
<v-combobox
v-model="selectedLearningPath"
:items="data"
:label="t('choose-lp')"
:rules="learningPathRules"
variant="outlined"
clearable
hide-details
density="compact"
append-inner-icon="mdi-magnify"
item-title="title"
item-value="hruid"
required
:disabled="lpIsSelected"
:filter="(item, query: string) => item.title.toLowerCase().includes(query.toLowerCase())"
></v-combobox>
</v-card-text>
</using-query-result>
<using-query-result
:query-result="classesQueryResults"
v-slot="{ data }: {data: ClassesResponse}"
>
<v-card-text>
<v-combobox
v-model="selectedClass"
:items="data?.classes ?? []"
:label="t('pick-class')"
:rules="classRules"
variant="outlined"
clearable
hide-details
density="compact"
append-inner-icon="mdi-magnify"
item-title="displayName"
item-value="id"
required
></v-combobox>
</v-card-text>
</using-query-result>
<GroupSelector
:classId="selectedClass?.id"
:groups="groups"
@groupCreated="addGroupToList"
/>
<!-- Counter for created groups -->
<v-card-text v-if="groups.length">
<strong>Created Groups: {{ groups.length }}</strong>
</v-card-text>
<DeadlineSelector v-model:deadline="deadline"/>
<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;
}
.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>

View file

@ -4,27 +4,14 @@ import auth from "@/services/auth/auth-service.ts";
import {computed, ref} from "vue";
import StudentAssignment from "@/views/assignments/StudentAssignment.vue";
import TeacherAssignment from "@/views/assignments/TeacherAssignment.vue";
import {asyncComputed} from "@vueuse/core";
import {GroupController} from "@/controllers/groups.ts";
import {useRoute} from "vue-router";
const role = auth.authState.activeRole;
const isTeacher = computed(() => role === 'teacher');
// Get the user's username/id
const username = asyncComputed(async () => {
const user = await auth.loadUser();
return user?.profile?.preferred_username ?? undefined
});
const route = useRoute();
const classId = ref<string>(route.params.classId as string);
const assignmentId = ref(Number(route.params.id));
const classId = window.history.state?.class_id;
const groupController = new GroupController(classId, assignmentId.value);
const groupDTOs = asyncComputed(async () => await groupController.getAll(true));
console.log(groupDTOs.value);
</script>
@ -32,14 +19,12 @@ console.log(groupDTOs.value);
<TeacherAssignment
:class-id="classId"
:assignment-id="assignmentId"
:groups="groupDTOs"
v-if="isTeacher"
>
</TeacherAssignment>
<StudentAssignment
:class-id="classId"
:assignment-id="assignmentId"
:groups="groupDTOs"
v-else
>
</StudentAssignment>

View file

@ -5,16 +5,13 @@ import {useI18n} from "vue-i18n";
import {useAssignmentQuery} from "@/queries/assignments.ts";
import UsingQueryResult from "@/components/UsingQueryResult.vue";
import type {AssignmentResponse} from "@/controllers/assignments.ts";
import type {GroupDTO} from "@dwengo-1/common/interfaces/group";
import {asyncComputed} from "@vueuse/core";
import {useStudentsByUsernamesQuery} from "@/queries/students.ts";
import {AssignmentDTO} from "@dwengo-1/common/dist/interfaces/assignment.ts";
import {StudentDTO} from "@dwengo-1/common/dist/interfaces/student.ts";
import {useGroupsQuery} from "@/queries/groups.ts";
const props = defineProps<{
classId: string
assignmentId: number
groups: GroupDTO[] | undefined
}>();
const {t, locale} = useI18n();
@ -26,23 +23,19 @@ const username = asyncComputed(async () => {
});
const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId);
const submitted = ref(false);//TODO: update by fetching submissions and check if group submitted
const submitted = ref(true);//TODO: update by fetching submissions and check group
const submitAssignment = async () => {
//TODO
};
const group = computed(() => {
return props?.groups?.find(group =>
group.members.some(m => m.username === username.value)
);
const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true);
const group = computed(() =>
groupsQueryResult?.data.value?.groups.find(group =>
group.members?.some(m => m.username === username.value)
)
/** For testing
return {assignment: 1,
groupNumber: 1,
members: ["testleerling1"]}
*/
});
return {assignment: 1,
groupNumber: 1,
members: ["testleerling1"]}
*/
);
// Assuming group.value.members is a list of usernames TODO: case when it's StudentDTO's
const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as string[]);
@ -75,10 +68,10 @@ const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as
{{ t("submitted") }}
</v-chip>
</div>
<v-card-title class="text-h4">{{ data.title }}</v-card-title>
<v-card-title class="text-h4">{{ data.assignment.title }}</v-card-title>
<v-card-subtitle class="subtitle-section">
<v-btn
:to="`/learningPath/${language}/${data.learningPath}`"
:to="`/learningPath/${language}/${data.assignment.learningPath}`"
variant="tonal"
color="primary"
>
@ -87,30 +80,20 @@ const studentQueries = useStudentsByUsernamesQuery(() => group.value?.members as
</v-card-subtitle>
<v-card-text class="description">
{{ data.description }}
{{ data.assignment.description }}
</v-card-text>
<v-card-text class="group-section">
<h3>{{ t("group") }}</h3>
<div v-if="studentQueries">
<ul>
<li v-for="student in studentQueries" :key="student.data?.student.id">
{{ student.data?.student.firstName + ' ' + student.data?.student.lastName }}
<li v-for="student in group?.members" :key="student.username">
{{ student.firstName + ' ' + student.lastName }}
</li>
</ul>
</div>
</v-card-text>
<v-card-actions class="justify-end">
<v-btn
size="large"
color="success"
variant="flat"
@click="submitAssignment"
>
{{ t("submit") }}
</v-btn>
</v-card-actions>
</v-card>
</using-query-result>

View file

@ -1,15 +1,17 @@
<script setup lang="ts">
import {computed, defineProps} from "vue";
import {computed, defineProps, ref} from "vue";
import {useI18n} from "vue-i18n";
import {AssignmentController, type AssignmentResponse} from "@/controllers/assignments.ts";
import {useAssignmentQuery} from "@/queries/assignments.ts";
import UsingQueryResult from "@/components/UsingQueryResult.vue";
import type {GroupDTO} from "@dwengo-1/common/interfaces/group";
import {useGroupsQuery} from "@/queries/groups.ts";
import {useGetLearningPathQuery} from "@/queries/learning-paths.ts";
import type {Language} from "@/data-objects/language.ts";
import type {LearningPath} from "@/data-objects/learning-paths/learning-path.ts";
const props = defineProps<{
classId: string
assignmentId: number
groups: GroupDTO[] | undefined
}>();
const {t, locale} = useI18n();
@ -17,10 +19,43 @@ const language = computed(() => locale.value);
const controller = new AssignmentController(props.classId);
const assignmentQueryResult = useAssignmentQuery(() => props.classId, props.assignmentId);
const groupsQueryResult = useGroupsQuery(props.classId, props.assignmentId, true);
const deleteAssignment = async () => {
await controller.deleteAssignment(props.assignmentId.value);
};
const lpQueryResult = useGetLearningPathQuery(
computed(() => assignmentQueryResult.data.value?.assignment?.learningPath ?? ""),
computed(() => language.value as Language)
);
const allGroups = computed(() => {
const groups = groupsQueryResult.data.value?.groups;
if (!groups) return [];
return groups.map(group => ({
name: `${t('group')} ${group.groupNumber}`,
progress: 0,
members: group.members,
submitted: false,//TODO: fetch from submission
}));
});
const dialog = ref(false);
const selectedGroup = ref({});
function openGroupDetails(group): void {
selectedGroup.value = group;
dialog.value = true;
}
const headers = ref([
{title: t('group'), align: 'start', key: 'name'},
{title: t('progress'), align: 'center', key: 'progress'},
{title: t('submission'), align: 'center', key: 'submission'}
]);
async function deleteAssignment(): Promise<void> {
await controller.deleteAssignment(props.assignmentId);
}
</script>
@ -50,45 +85,93 @@ const deleteAssignment = async () => {
<v-icon>mdi-delete</v-icon>
</v-btn>
</div>
<v-card-title class="text-h4">{{ data.title }}</v-card-title>
<v-card-title class="text-h4">{{ data.assignment.title }}</v-card-title>
<v-card-subtitle class="subtitle-section">
<v-btn
:to="`/learningPath/${language}/${data.learningPath}`"
variant="tonal"
color="primary"
<using-query-result
:query-result="lpQueryResult"
v-slot="{ data: lpData }"
>
{{ t("learning-path") }}
</v-btn>
<v-btn v-if="lpData"
:to="`/learningPath/${language}/${lpData.hruid}/${lpData.startNode.learningobjectHruid}`"
variant="tonal"
color="primary"
>
{{ t("learning-path") }}
</v-btn>
</using-query-result>
</v-card-subtitle>
<v-card-text class="description">
{{ data.description }}
{{ data.assignment.description }}
</v-card-text>
<v-card-text class="group-section">
<h3>{{ t("group") }}</h3>
<h3>{{ t("groups") }}</h3>
<div class="table-scroll">
<v-data-table
:headers="headers"
:items="allGroups"
item-key="id"
class="elevation-1"
>
<template v-slot:item.name="{ item }">
<v-btn @click="openGroupDetails(item)" variant="text" color="primary">
{{ item.name }}
</v-btn>
</template>
<!-- Teacher view
<div v-if="isTeacher">
<v-expansion-panels>
<v-expansion-panel
v-for="(group, index) in assignment.groups"
:key="group.id"
>
<v-expansion-panel-title>
{{ t("group") }} {{ index + 1 }}
</v-expansion-panel-title>
<v-expansion-panel-text>
<ul>
<li v-for="student in group.members" :key="student.username">
{{ student.firstName + ' ' + student.lastName }}
</li>
</ul>
</v-expansion-panel-text>
</v-expansion-panel>
</v-expansion-panels>
</div>-->
<template v-slot:item.progress="{ item }">
<v-progress-linear
:model-value="item.progress"
color="blue-grey"
height="25"
>
<template v-slot:default="{ value }">
<strong>{{ Math.ceil(value) }}%</strong>
</template>
</v-progress-linear>
</template>
<template v-slot:item.submission="{ item }">
<v-btn
:to="item.submitted ? `${props.assignmentId}/submissions/` : undefined"
:color="item.submitted ? 'green' : 'red'"
variant="text"
class="text-capitalize"
>
{{ item.submitted ? t('see-submission') : t('not-submitted') }}
</v-btn>
</template>
</v-data-table>
</div>
</v-card-text>
<v-dialog v-model="dialog" max-width="50%">
<v-card>
<v-card-title class="headline">Group Members</v-card-title>
<v-card-text>
<v-list>
<v-list-item
v-for="(member, index) in selectedGroup.members"
:key="index"
>
<v-list-item-content>
<v-list-item-title>{{
member.firstName + ' ' + member.lastName
}}
</v-list-item-title>
</v-list-item-content>
</v-list-item>
</v-list>
</v-card-text>
<v-card-actions>
<v-btn color="primary" @click="dialog = false">Close</v-btn>
</v-card-actions>
</v-card>
</v-dialog>
<!--
<v-card-actions class="justify-end">
<v-btn
size="large"
@ -98,6 +181,7 @@ const deleteAssignment = async () => {
{{ t("view-submissions") }}
</v-btn>
</v-card-actions>
-->
</v-card>
</using-query-result>
</div>
@ -105,5 +189,10 @@ const deleteAssignment = async () => {
<style scoped>
@import "@/assets/assignment.css";
.table-scroll {
overflow-x: auto;
-webkit-overflow-scrolling: touch;
}
</style>

View file

@ -27,25 +27,25 @@ if (isTeacher.value) {
classesQueryResults = useStudentClassesQuery(username, true);
}
//TODO: replace with query from classes
//TODO: remove later
const classController = new ClassController();
//TODO: replace by query that fetches all user's assignment
const assignments = asyncComputed(async () => {
const classes = classesQueryResults?.data?.value?.classes;
if (!classes) return [];
const result = await Promise.all(
(classes as ClassDTO[]).map(async (cls) => {
//TODO: replace by class queries
const {assignments} = await classController.getAssignments(cls.id);
return assignments.map(a => ({
id: a.id,
class: cls, // replace by the whole ClassDTO object
class: cls,
title: a.title,
description: a.description,
learningPath: a.learningPath,
language: a.language,
groups: []
groups: a.groups
}));
})
);
@ -54,23 +54,20 @@ const assignments = asyncComputed(async () => {
}, []);
const goToCreateAssignment = async () => {
async function goToCreateAssignment(): Promise<void> {
await router.push('/assignment/create');
};
}
const goToAssignmentDetails = async (id: number, class_id: string) => {
await router.push({
path: `/assignment/${id}`,
state: {class_id},
});
};
async function goToAssignmentDetails(id: number, clsId: string): Promise<void> {
await router.push(`/assignment/${clsId}/${id}`);
}
const goToDeleteAssignment = async (id: number, class_id: string) => {
async function goToDeleteAssignment(id: number, clsId: string): Promise<void> {
//TODO: replace with query
const controller = new AssignmentController(class_id);
const controller = new AssignmentController(clsId);
await controller.deleteAssignment(id);
};
}
onMounted(async () => {
const user = await auth.loadUser();