diff --git a/src/app/[locale]/courses/[share_token]/results/page.tsx b/src/app/[locale]/results/[courseId]/exam/page.tsx
similarity index 93%
rename from src/app/[locale]/courses/[share_token]/results/page.tsx
rename to src/app/[locale]/results/[courseId]/exam/page.tsx
index dc27ba454..b30508b93 100644
--- a/src/app/[locale]/courses/[share_token]/results/page.tsx
+++ b/src/app/[locale]/results/[courseId]/exam/page.tsx
@@ -1,13 +1,14 @@
'use server'
import { forbidden, notFound } from 'next/navigation'
-import { getCourseByShareToken } from '@/database/course/select'
+import { getCourseById } from '@/database/course/select'
import { getCourseExaminationAttempts } from '@/database/examination/select'
import { ExaminationSuccessPieChart } from '@/src/components/charts/ExaminationSuccessPieChart'
import { ExamQuestionDurationChart } from '@/src/components/charts/QuestionDurationChart'
import { QuestionScoresLineChartCard } from '@/src/components/charts/QuestionScoresLineChart'
import { UserTypePieChart } from '@/src/components/charts/UserTypePieChart'
import { ExaminationAttemptTable } from '@/src/components/courses/[share_token]/ExaminationAttemptTable'
+import { ExamResultsBreadcrumbs } from '@/src/components/results/examination/ExamResultsBreadcrumbs'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/src/components/shadcn/card'
import PageHeading from '@/src/components/Shared/PageHeading'
import { getScopedI18n } from '@/src/i18n/server-localization'
@@ -24,11 +25,11 @@ export type ExaminationAttemptTableStructure = Pick
}) {
- const { share_token } = await params
+export default async function ExaminationResultsPage({ params }: { params: Promise<{ courseId: string }> }) {
+ const { courseId } = await params
const { user } = await requireAuthentication()
- const course = await getCourseByShareToken(share_token)
+ const course = await getCourseById(courseId)
if (!course) notFound()
if (!hasCollaborativePermissions(course, user.id)) forbidden()
@@ -38,6 +39,7 @@ export default async function ExaminationResultsPage({ params }: { params: Promi
return (
<>
+
diff --git a/src/app/[locale]/results/[courseId]/practice/page.tsx b/src/app/[locale]/results/[courseId]/practice/page.tsx
index 2560ca55b..2b554e006 100644
--- a/src/app/[locale]/results/[courseId]/practice/page.tsx
+++ b/src/app/[locale]/results/[courseId]/practice/page.tsx
@@ -3,7 +3,7 @@ import { getCourseById } from '@/database/course/select'
import { QuestionCorrectnessPieChart } from '@/src/components/charts/QuestionCorrectnessPieChart'
import { ExamQuestionDurationChart } from '@/src/components/charts/QuestionDurationChart'
import { QuestionScorePlotCard } from '@/src/components/charts/QuestionScorePlot'
-import ExamQuestionResultTable from '@/src/components/courses/[share_token]/results/ExamQuestionResultTable'
+import ExamQuestionResultTable from '@/src/components/results/examination/ExamQuestionResultTable'
import { PracticeResultsBreadcrumbs } from '@/src/components/results/practice/PracticeResultsBreadcrumbs'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/src/components/shadcn/card'
import PageHeading from '@/src/components/Shared/PageHeading'
diff --git a/src/components/courses/(hamburger-menu)/CourseActionMenu.tsx b/src/components/courses/(hamburger-menu)/CourseActionMenu.tsx
index 6af272500..467ae6961 100644
--- a/src/components/courses/(hamburger-menu)/CourseActionMenu.tsx
+++ b/src/components/courses/(hamburger-menu)/CourseActionMenu.tsx
@@ -1,7 +1,7 @@
'use client'
import { useState } from 'react'
-import { ArrowUpRightIcon, CopyPlusIcon, EllipsisIcon, Share2Icon, SquarePenIcon, TrashIcon } from 'lucide-react'
+import { ArrowUpRightIcon, ChartSplineIcon, CopyPlusIcon, EllipsisIcon, Share2Icon, SquarePenIcon, TrashIcon } from 'lucide-react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { toast } from 'react-toastify'
@@ -142,6 +142,13 @@ export default function CourseActionMenu({ id, questions, share_key, owner_id, c
+
+
+
+ {t('show_exam_results.label')}
+
+
+
{t('clone_course.label')}
diff --git a/src/components/courses/[share_token]/ExaminationAttemptTable.tsx b/src/components/courses/[share_token]/ExaminationAttemptTable.tsx
index c5b979e0a..3c9a96e51 100644
--- a/src/components/courses/[share_token]/ExaminationAttemptTable.tsx
+++ b/src/components/courses/[share_token]/ExaminationAttemptTable.tsx
@@ -157,7 +157,7 @@ export function ExaminationAttemptTable({ data: initialData }: { data: ExamAttem
-
+
{t('columns.actions_menu.show_results_label')}
diff --git a/src/components/courses/[share_token]/results/ExamQuestionResultTable.tsx b/src/components/results/examination/ExamQuestionResultTable.tsx
similarity index 96%
rename from src/components/courses/[share_token]/results/ExamQuestionResultTable.tsx
rename to src/components/results/examination/ExamQuestionResultTable.tsx
index 9c24f0794..26c73f3f6 100644
--- a/src/components/courses/[share_token]/results/ExamQuestionResultTable.tsx
+++ b/src/components/results/examination/ExamQuestionResultTable.tsx
@@ -3,8 +3,8 @@
import { IconCircleCheckFilled } from '@tabler/icons-react'
import { ColumnDef } from '@tanstack/react-table'
import { EyeIcon, XIcon } from 'lucide-react'
-import ExamQuestionTable_ActionMenu from '@/src/components/courses/[share_token]/results/ExamQuestionTable_ActionMenu'
-import ShowAnswerDrawer_TableCell from '@/src/components/courses/[share_token]/results/PreviewQuestionResultDrawer'
+import ExamQuestionTable_ActionMenu from '@/src/components/results/examination/ExamQuestionTable_ActionMenu'
+import ShowAnswerDrawer_TableCell from '@/src/components/results/examination/PreviewQuestionResultDrawer'
import { Badge } from '@/src/components/shadcn/badge'
import { Button } from '@/src/components/shadcn/button'
import { Checkbox } from '@/src/components/shadcn/checkbox'
diff --git a/src/components/courses/[share_token]/results/ExamQuestionTable_ActionMenu.tsx b/src/components/results/examination/ExamQuestionTable_ActionMenu.tsx
similarity index 91%
rename from src/components/courses/[share_token]/results/ExamQuestionTable_ActionMenu.tsx
rename to src/components/results/examination/ExamQuestionTable_ActionMenu.tsx
index 08b3b56ce..458cf2e38 100644
--- a/src/components/courses/[share_token]/results/ExamQuestionTable_ActionMenu.tsx
+++ b/src/components/results/examination/ExamQuestionTable_ActionMenu.tsx
@@ -2,8 +2,8 @@ import { useState } from 'react'
import { IconDotsVertical } from '@tabler/icons-react'
import { Row } from '@tanstack/react-table'
import { ChartColumnIcon, TrashIcon } from 'lucide-react'
-import { PreviewQuestionResult_QuestionItem } from '@/src/components/courses/[share_token]/results/ExamQuestionResultTable'
-import ShowAnswerDrawer_TableCell from '@/src/components/courses/[share_token]/results/PreviewQuestionResultDrawer'
+import { PreviewQuestionResult_QuestionItem } from '@/src/components/results/examination/ExamQuestionResultTable'
+import ShowAnswerDrawer_TableCell from '@/src/components/results/examination/PreviewQuestionResultDrawer'
import { Button } from '@/src/components/shadcn/button'
import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger } from '@/src/components/shadcn/dropdown-menu'
import { useCurrentLocale, useScopedI18n } from '@/src/i18n/client-localization'
diff --git a/src/components/results/examination/ExamResultsBreadcrumbs.tsx b/src/components/results/examination/ExamResultsBreadcrumbs.tsx
new file mode 100644
index 000000000..6462d0b9e
--- /dev/null
+++ b/src/components/results/examination/ExamResultsBreadcrumbs.tsx
@@ -0,0 +1,79 @@
+'use server'
+
+import { DetailedHTMLProps, HTMLAttributes } from 'react'
+import Link from 'next/link'
+import { getCourseById } from '@/database/course/select'
+import { Breadcrumb, BreadcrumbItem, BreadcrumbLink, BreadcrumbList, BreadcrumbPage, BreadcrumbSeparator } from '@/src/components/shadcn/breadcrumb'
+import { getScopedI18n } from '@/src/i18n/server-localization'
+import { Course } from '@/src/schemas/CourseSchema'
+
+async function BaseBreadcrumbs({
+ courseName,
+ courseId,
+ children,
+ ...props
+}: { children: React.ReactNode; courseId: Course['id']; courseName: Course['name'] } & DetailedHTMLProps, HTMLElement>) {
+ const t = await getScopedI18n('Shared.Breadcrumbs')
+ return (
+
+
+
+
+ {t('root')}
+
+
+
+
+
+ {t('courses')}
+
+
+
+
+
+ {courseName}
+
+
+
+ {children}
+
+
+ )
+}
+
+export async function ExamResultsBreadcrumbs({ courseId, ...props }: { courseId: string } & DetailedHTMLProps, HTMLElement>) {
+ const t = await getScopedI18n('Shared.Breadcrumbs')
+ const course = await getCourseById(courseId)
+ if (!course) return null
+
+ return (
+
+
+ {t('examination_results')}
+
+
+ )
+}
+
+export async function ExamResultAttemptResultsBreadcrumbs({ courseId, userName, ...props }: { courseId: string; userName: string } & DetailedHTMLProps, HTMLElement>) {
+ const t = await getScopedI18n('Shared.Breadcrumbs')
+ const course = await getCourseById(courseId)
+ if (!course) return null
+
+ return (
+
+
+
+ {t('examination_results')}
+
+
+
+
+
+
+ {userName}'s {t('results')}
+
+
+
+ )
+}
diff --git a/src/components/courses/[share_token]/results/PreviewQuestionResultDrawer.tsx b/src/components/results/examination/PreviewQuestionResultDrawer.tsx
similarity index 99%
rename from src/components/courses/[share_token]/results/PreviewQuestionResultDrawer.tsx
rename to src/components/results/examination/PreviewQuestionResultDrawer.tsx
index 496169df4..da8d351ca 100644
--- a/src/components/courses/[share_token]/results/PreviewQuestionResultDrawer.tsx
+++ b/src/components/results/examination/PreviewQuestionResultDrawer.tsx
@@ -3,7 +3,7 @@
import React, { Dispatch, SetStateAction, useState } from 'react'
import { ChevronRightIcon, UserCheckIcon, UserIcon } from 'lucide-react'
import { QuestionScoresLineChart } from '@/src/components/charts/QuestionScoresLineChart'
-import { PreviewQuestionResult_QuestionItem } from '@/src/components/courses/[share_token]/results/ExamQuestionResultTable'
+import { PreviewQuestionResult_QuestionItem } from '@/src/components/results/examination/ExamQuestionResultTable'
import { Button } from '@/src/components/shadcn/button'
import { Drawer, DrawerClose, DrawerContent, DrawerDescription, DrawerFooter, DrawerHeader, DrawerTitle, DrawerTrigger } from '@/src/components/shadcn/drawer'
import { Input } from '@/src/components/shadcn/input'
diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json
index c80dd818c..bec865c47 100644
--- a/src/i18n/locales/de.json
+++ b/src/i18n/locales/de.json
@@ -38,7 +38,23 @@
"minute_label#other": "minuten",
"minute_label": "minuten"
},
- "jump_back_button_label": "Bearbeiten"
+ "jump_back_button_label": "Bearbeiten",
+ "Breadcrumbs": {
+ "root": "Home",
+ "courses": "Kurse",
+ "discover": "Entdecken",
+ "practice": "Üben",
+ "edit": "Bearbeiten",
+ "create": "Erstellen",
+ "account": "Account",
+ "start": "Start",
+ "results": "Ergebnisse",
+ "examination_results": "Prüfungsergebnisse",
+ "practice_results": "Üben",
+ "not-allowed": "Nicht erlaubt",
+ "attempt-limit": "Versuchslimit erreicht",
+ "attempt-not-possible": "Versuch abgebrochen"
+ }
},
"Home": {
"description": "Create your own Course to boost your knowledge to the next level. (de)"
@@ -421,6 +437,10 @@
"clone_course": {
"label": "Kurs duplizieren"
},
+ "show_exam_results": {
+ "label": "Prüfungs Ergebnisse",
+ "tooltip": "Dir fehlen Berechtigungen um Prüfungsergebnisse einzusehen!"
+ },
"inspect_statistics": {
"label": "Statistik anzeigen"
},
diff --git a/src/i18n/locales/de.ts b/src/i18n/locales/de.ts
index ff514c093..85ea860eb 100644
--- a/src/i18n/locales/de.ts
+++ b/src/i18n/locales/de.ts
@@ -39,7 +39,23 @@ export default {
'minute_label#other': 'minuten',
minute_label: 'minuten'
},
- jump_back_button_label: 'Bearbeiten'
+ jump_back_button_label: 'Bearbeiten',
+ Breadcrumbs: {
+ root: 'Home',
+ courses: 'Kurse',
+ discover: 'Entdecken',
+ practice: 'Üben',
+ edit: 'Bearbeiten',
+ create: 'Erstellen',
+ account: 'Account',
+ start: 'Start',
+ results: 'Ergebnisse',
+ examination_results: 'Prüfungsergebnisse',
+ practice_results: 'Üben',
+ 'not-allowed': 'Nicht erlaubt',
+ 'attempt-limit': 'Versuchslimit erreicht',
+ 'attempt-not-possible': 'Versuch abgebrochen'
+ }
},
Home: {
description: 'Create your own Course to boost your knowledge to the next level. (de)'
@@ -425,6 +441,10 @@ export default {
clone_course: {
label: 'Kurs duplizieren'
},
+ show_exam_results: {
+ label: 'Prüfungs Ergebnisse',
+ tooltip: 'Dir fehlen Berechtigungen um Prüfungsergebnisse einzusehen!'
+ },
inspect_statistics: {
label: 'Statistik anzeigen'
},
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
index e39d762c6..ac3800e91 100644
--- a/src/i18n/locales/en.json
+++ b/src/i18n/locales/en.json
@@ -38,7 +38,24 @@
"minute_label#other": "minutes",
"minute_label": "minutes"
},
- "jump_back_button_label": "Jump back"
+ "jump_back_button_label": "Jump back",
+ "Breadcrumbs": {
+ "root": "Home",
+ "courses": "Courses",
+ "discover": "Discover",
+ "practice": "Practice",
+ "edit": "Edit",
+ "create": "Create",
+ "account": "Account",
+ "start": "Start",
+ "results": "Results",
+ "examination_results": "Examination Results",
+ "practice_results": "Practice",
+
+ "not-allowed": "Not Allowed",
+ "attempt-limit": "Attempt Limit Reached",
+ "attempt-not-possible": "Attempt aborted"
+ }
},
"Home": {
"description": "Create your own Courses to boost your knowledge to the next level. (en)"
@@ -430,6 +447,10 @@
"clone_course": {
"label": "Clone Course"
},
+ "show_exam_results": {
+ "label": "Show Examination Results",
+ "tooltip": "You are not allowed to view exam results!"
+ },
"inspect_statistics": {
"label": "Inspect Statistics"
},
diff --git a/src/i18n/locales/en.ts b/src/i18n/locales/en.ts
index c434aa3a2..d0bb0788f 100644
--- a/src/i18n/locales/en.ts
+++ b/src/i18n/locales/en.ts
@@ -39,7 +39,23 @@ export default {
'minute_label#other': 'minutes',
minute_label: 'minutes'
},
- jump_back_button_label: 'Jump back'
+ jump_back_button_label: 'Jump back',
+ Breadcrumbs: {
+ root: 'Home',
+ courses: 'Courses',
+ discover: 'Discover',
+ practice: 'Practice',
+ edit: 'Edit',
+ create: 'Create',
+ account: 'Account',
+ start: 'Start',
+ results: 'Results',
+ examination_results: 'Examination Results',
+ practice_results: 'Practice',
+ 'not-allowed': 'Not Allowed',
+ 'attempt-limit': 'Attempt Limit Reached',
+ 'attempt-not-possible': 'Attempt aborted'
+ }
},
Home: {
description: 'Create your own Courses to boost your knowledge to the next level. (en)'
@@ -431,6 +447,10 @@ export default {
clone_course: {
label: 'Clone Course'
},
+ show_exam_results: {
+ label: 'Show Examination Results',
+ tooltip: 'You are not allowed to view exam results!'
+ },
inspect_statistics: {
label: 'Inspect Statistics'
},