diff --git a/client/src/components/ui/ConfirmDialog.vue b/client/src/components/ui/ConfirmDialog.vue new file mode 100644 index 00000000..b138b1e8 --- /dev/null +++ b/client/src/components/ui/ConfirmDialog.vue @@ -0,0 +1,35 @@ + + + + + + + {{ title }} + + + + Löschen + Abbrechen + + + + + + + diff --git a/client/src/components/ui/ItModal.vue b/client/src/components/ui/ItModal.vue index f962eb96..1930002e 100644 --- a/client/src/components/ui/ItModal.vue +++ b/client/src/components/ui/ItModal.vue @@ -1,11 +1,11 @@ @@ -213,60 +176,7 @@ async function uploadDocument(data: DocumentUploadData) { {{ $t("circlePage.learnMore") }} - - - {{ $t("circlePage.documents.title") }} - - - - {{ $t("circlePage.documents.userDescription") }} - - - - - {{ learningSequence.title }} - - - - {{ document.name }} - - - - - - - - - - - {{ $t("circlePage.documents.expertDescription") }} - - - {{ $t("circlePage.documents.action") }} - - - - + {{ $t("circlePage.gotQuestions") }} @@ -312,17 +222,6 @@ async function uploadDocument(data: DocumentUploadData) { - - {{ $t("circlePage.documents.action") }} - - - - diff --git a/client/src/pages/learningPath/circlePage/DocumentListItem.vue b/client/src/pages/learningPath/circlePage/DocumentListItem.vue new file mode 100644 index 00000000..c9b4632f --- /dev/null +++ b/client/src/pages/learningPath/circlePage/DocumentListItem.vue @@ -0,0 +1,33 @@ + + + + {{ doc.name }} + + {{ subtitle }} + + + + + + + + + + + + + diff --git a/client/src/pages/learningPath/circlePage/DocumentSection.vue b/client/src/pages/learningPath/circlePage/DocumentSection.vue new file mode 100644 index 00000000..3275fded --- /dev/null +++ b/client/src/pages/learningPath/circlePage/DocumentSection.vue @@ -0,0 +1,128 @@ + + + + {{ $t("circlePage.documents.title") }} + + + + + {{ $t("circlePage.documents.userDescription") }} + + + {{ $t("circlePage.documents.expertDescription") }} + + + + + + + + + + + {{ $t("circlePage.documents.action") }} + + + + + {{ $t("circlePage.documents.action") }} + + + + + + + {{ $t("circlePage.documents.trainerTitle") }} + + + {{ $t("circlePage.documents.trainerDescription") }} + + + {{ $t("circlePage.documents.trainerLinkText") }} + + + + + diff --git a/client/src/pages/learningPath/circlePage/DocumentUploadForm.vue b/client/src/pages/learningPath/circlePage/DocumentUploadForm.vue index 35b9736d..f268ee71 100644 --- a/client/src/pages/learningPath/circlePage/DocumentUploadForm.vue +++ b/client/src/pages/learningPath/circlePage/DocumentUploadForm.vue @@ -4,7 +4,7 @@ import type { DocumentUploadData, DropdownSelectable } from "@/types"; import { reactive } from "vue"; import { useI18n } from "vue-i18n"; -interface Props { +export interface Props { learningSequences: DropdownSelectable[]; showUploadErrorMessage: boolean; isUploading: boolean; diff --git a/client/src/stores/courseSessions.ts b/client/src/stores/courseSessions.ts index c928586a..3124a1e2 100644 --- a/client/src/stores/courseSessions.ts +++ b/client/src/stores/courseSessions.ts @@ -182,21 +182,23 @@ export const useCourseSessionsStore = defineStore("courseSessions", () => { const circleDocuments = computed(() => { const circleStore = useCircleStore(); - return circleStore.circle?.learningSequences - .map((ls) => ({ id: ls.id, title: ls.title, documents: [] })) - .map((ls: { id: number; title: string; documents: CircleDocument[] }) => { - if (currentCourseSession.value === undefined) { - return ls; - } - - for (const document of currentCourseSession.value.documents) { - if (document.learning_sequence === ls.id) { - ls.documents.push(document); + return ( + circleStore.circle?.learningSequences + .map((ls) => ({ id: ls.id, title: ls.title, documents: [] })) + .map((ls: { id: number; title: string; documents: CircleDocument[] }) => { + if (currentCourseSession.value === undefined) { + return ls; } - } - return ls; - }) - .filter((ls) => ls.documents.length > 0); + + for (const document of currentCourseSession.value.documents) { + if (document.learning_sequence === ls.id) { + ls.documents.push(document); + } + } + return ls; + }) + .filter((ls) => ls.documents.length > 0) || [] + ); }); function addDocument(document: CircleDocument) { diff --git a/client/src/utils/confirm-dialog.ts b/client/src/utils/confirm-dialog.ts new file mode 100644 index 00000000..dac8d906 --- /dev/null +++ b/client/src/utils/confirm-dialog.ts @@ -0,0 +1,67 @@ +import ConfirmDialog from "@/components/ui/ConfirmDialog.vue"; +import { createApp } from "vue"; + +interface ConfirmDialogOptions { + title: string; + content: string; +} +type ResolveReject = (v?: unknown) => void; + +// inspired by https://stackoverflow.com/a/69773076/6071058 +/* + * We need a separate service for the ConfirmDialog, because we don't want the + * boilerplate of including the component and the handling of the promises inside + * of every component where we need a confirm dialog. + * + * With this service, one can simply import the dialog and use it, e.g. + * +import dialog from "@/utils/confirm-dialog"; +const someMethodToConfirm = async () => { + const options = { + title: 'Dialog Title', + content: 'Do you really wanna?' + }; + try { + await dialog.confirm(options); + doSomethingWhenConfirmed(); + } catch (e) { + log.debug("rejected"); + doSomethingWhenRejected() + } +}; + + */ +export default { + confirm(options: ConfirmDialogOptions) { + const mountEl = document.createElement("div"); + document.body.appendChild(mountEl); + + let _resolve: ResolveReject, _reject: ResolveReject; + + const promise = new Promise((resolve, reject) => { + _resolve = resolve; + _reject = reject; + }); + + const cleanUp = () => { + mountEl?.parentNode?.removeChild(mountEl); + dialog.unmount(); + }; + + const dialog = createApp(ConfirmDialog, { + isOpen: true, + title: options.title, + content: options.content, + onClose() { + cleanUp(); + _reject(); + }, + onConfirm() { + cleanUp(); + _resolve(); + }, + }); + dialog.mount(mountEl); + return promise; + }, +}; diff --git a/client/tailwind.config.js b/client/tailwind.config.js index baef2d07..6b0011e2 100644 --- a/client/tailwind.config.js +++ b/client/tailwind.config.js @@ -51,11 +51,13 @@ module.exports = { ], "rating-scale-slim": ["bar bar bar", "fst mid fth"], "icon-card": ["icon title", "icon value"], + "document-list-item": ["title icons", "subtitle icons"], }, gridTemplateColumns: { "horizontal-bar-chart": "50px 1fr 300px 4fr 300px 1fr", "horizontal-bar-chart-slim": "50px 1fr 78px 4fr 78px 1fr", "icon-card": "60px auto", + "document-list-item": "1fr 100px", }, gridTemplateRows: { "horizontal-bar-chart": "200px 40px", diff --git a/server/vbv_lernwelt/static/icons/icon-delete.svg b/server/vbv_lernwelt/static/icons/icon-delete.svg new file mode 100644 index 00000000..39ec0664 --- /dev/null +++ b/server/vbv_lernwelt/static/icons/icon-delete.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/server/vbv_lernwelt/static/icons/icon-download.svg b/server/vbv_lernwelt/static/icons/icon-download.svg new file mode 100644 index 00000000..5b63d09a --- /dev/null +++ b/server/vbv_lernwelt/static/icons/icon-download.svg @@ -0,0 +1 @@ + \ No newline at end of file