fix: UI-imperfecties & diverse bugs omtrent het verwijderen en editeren van leerpaden opgelost
This commit is contained in:
parent
9400b7f33c
commit
9a58126c7c
6 changed files with 126 additions and 28 deletions
|
@ -4,7 +4,6 @@ import { Language } from '@dwengo-1/common/util/language';
|
|||
import { LearningPathNode } from '../../entities/content/learning-path-node.entity.js';
|
||||
import { RequiredEntityData } from '@mikro-orm/core';
|
||||
import { LearningPathTransition } from '../../entities/content/learning-path-transition.entity.js';
|
||||
import { EntityAlreadyExistsException } from '../../exceptions/entity-already-exists-exception.js';
|
||||
|
||||
export class LearningPathRepository extends DwengoEntityRepository<LearningPath> {
|
||||
public async findByHruidAndLanguage(hruid: string, language: Language): Promise<LearningPath | null> {
|
||||
|
@ -53,21 +52,6 @@ export class LearningPathRepository extends DwengoEntityRepository<LearningPath>
|
|||
return this.em.create(LearningPathTransition, transitionData);
|
||||
}
|
||||
|
||||
public async saveLearningPathNodesAndTransitions(
|
||||
path: LearningPath,
|
||||
nodes: LearningPathNode[],
|
||||
transitions: LearningPathTransition[],
|
||||
options?: { preventOverwrite?: boolean }
|
||||
): Promise<void> {
|
||||
if (options?.preventOverwrite && (await this.findOne(path))) {
|
||||
throw new EntityAlreadyExistsException('A learning path with this hruid/language combination already exists.');
|
||||
}
|
||||
const em = this.getEntityManager();
|
||||
await em.persistAndFlush(path);
|
||||
await Promise.all(nodes.map(async (it) => em.persistAndFlush(it)));
|
||||
await Promise.all(transitions.map(async (it) => em.persistAndFlush(it)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the learning path with the given hruid and language.
|
||||
* @returns the deleted learning path or null if it was not found.
|
||||
|
|
|
@ -134,8 +134,14 @@ const learningPathService = {
|
|||
*/
|
||||
async createNewLearningPath(dto: LearningPath, admins: TeacherDTO[]): Promise<LearningPathEntity> {
|
||||
const repo = getLearningPathRepository();
|
||||
|
||||
const path = mapToLearningPath(dto, admins);
|
||||
await repo.save(path, { preventOverwrite: true });
|
||||
try {
|
||||
await repo.save(path, { preventOverwrite: true });
|
||||
} catch (e: unknown) {
|
||||
repo.getEntityManager().clear();
|
||||
throw e;
|
||||
}
|
||||
return path;
|
||||
},
|
||||
|
||||
|
@ -146,6 +152,7 @@ const learningPathService = {
|
|||
*/
|
||||
async deleteLearningPath(id: LearningPathIdentifier): Promise<LearningPathEntity> {
|
||||
const repo = getLearningPathRepository();
|
||||
|
||||
const deletedPath = await repo.deleteByHruidAndLanguage(id.hruid, id.language);
|
||||
if (deletedPath) {
|
||||
return deletedPath;
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<script setup lang="ts">
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
const props = defineProps<{
|
||||
text: string,
|
||||
prependIcon?: string,
|
||||
appendIcon?: string,
|
||||
confirmQueryText: string,
|
||||
variant?: "flat" | "text" | "elevated" | "tonal" | "outlined" | "plain" | undefined,
|
||||
color?: string,
|
||||
disabled?: boolean
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{ (e: 'confirm'): void }>()
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
function confirm(): void {
|
||||
emit("confirm");
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-dialog max-width="500">
|
||||
<template v-slot:activator="{ props: activatorProps }">
|
||||
<v-btn
|
||||
v-bind="activatorProps"
|
||||
:text="props.text"
|
||||
:prependIcon="props.prependIcon"
|
||||
:appendIcon="props.appendIcon"
|
||||
:variant="props.variant"
|
||||
:color="color"
|
||||
:disabled="props.disabled"
|
||||
></v-btn>
|
||||
</template>
|
||||
|
||||
<template v-slot:default="{ isActive }">
|
||||
<v-card :title="t('confirmDialogTitle')">
|
||||
<v-card-text>
|
||||
{{ props.confirmQueryText }}
|
||||
</v-card-text>
|
||||
|
||||
<v-card-actions>
|
||||
<v-spacer></v-spacer>
|
||||
|
||||
<v-btn
|
||||
:text="t('yes')"
|
||||
@click="confirm(); isActive.value = false"
|
||||
></v-btn>
|
||||
<v-btn
|
||||
:text="t('cancel')"
|
||||
@click="isActive.value = false"
|
||||
></v-btn>
|
||||
</v-card-actions>
|
||||
</v-card>
|
||||
</template>
|
||||
</v-dialog>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
</style>
|
|
@ -139,5 +139,10 @@
|
|||
"editLearningPath": "Edit learning path",
|
||||
"newLearningPath": "Create a new learning path",
|
||||
"saveChanges": "Save changes",
|
||||
"newLearningObject": "Upload learning object"
|
||||
"newLearningObject": "Upload learning object",
|
||||
"confirmDialogTitle": "Please confirm",
|
||||
"learningPathDeleteQuery": "Are you sure you want to delete this learning path?",
|
||||
"learningObjectDeleteQuery": "Are you sure you want to delete this learning object?",
|
||||
"learningPathCantModifyId": "The HRUID or language of a learning path cannot be modified.",
|
||||
"error": "Error"
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
import type { LearningObject } from '@/data-objects/learning-objects/learning-object';
|
||||
import UsingQueryResult from '@/components/UsingQueryResult.vue';
|
||||
import LearningObjectContentView from '../../learning-paths/learning-object/content/LearningObjectContentView.vue';
|
||||
import ButtonWithConfirmation from '@/components/ButtonWithConfirmation.vue';
|
||||
import { useDeleteLearningObjectMutation, useLearningObjectHTMLQuery } from '@/queries/learning-objects';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
|
||||
|
@ -41,7 +42,13 @@
|
|||
</using-query-result>
|
||||
</template>
|
||||
<template v-slot:actions>
|
||||
<v-btn text="Delete" @click="deleteLearningObject()"></v-btn>
|
||||
<button-with-confirmation
|
||||
@confirm="deleteLearningObject"
|
||||
prepend-icon="mdi mdi-delete"
|
||||
color="red"
|
||||
:text="t('delete')"
|
||||
:confirmQueryText="t('learningObjectDeleteQuery')"
|
||||
/>
|
||||
</template>
|
||||
</v-card>
|
||||
</template>
|
||||
|
|
|
@ -2,10 +2,12 @@
|
|||
import { useI18n } from 'vue-i18n';
|
||||
import { computed, ref, watch, type Ref } from 'vue';
|
||||
import JsonEditorVue from 'json-editor-vue'
|
||||
import ButtonWithConfirmation from '@/components/ButtonWithConfirmation.vue'
|
||||
import { useDeleteLearningPathMutation, usePostLearningPathMutation, usePutLearningPathMutation } from '@/queries/learning-paths';
|
||||
import { Language } from '@/data-objects/language';
|
||||
import type { LearningPath } from '@dwengo-1/common/interfaces/learning-content';
|
||||
import type { AxiosError } from 'axios';
|
||||
import { parse } from 'uuid';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
|
@ -63,7 +65,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
function deleteLearningObject(): void {
|
||||
function deleteLearningPath(): void {
|
||||
if (props.selectedLearningPath) {
|
||||
mutate({
|
||||
hruid: props.selectedLearningPath.hruid,
|
||||
|
@ -72,8 +74,28 @@
|
|||
}
|
||||
}
|
||||
|
||||
function extractErrorMessage(error: AxiosError | null): string | undefined {
|
||||
return (error?.response?.data as {error: string}).error ?? error?.message;
|
||||
function extractErrorMessage(error: AxiosError): string {
|
||||
return (error.response?.data as {error: string}).error ?? error.message;
|
||||
}
|
||||
|
||||
const isIdModified = computed(() =>
|
||||
props.selectedLearningPath !== undefined && (
|
||||
props.selectedLearningPath.hruid !== parsedLearningPath.value.hruid
|
||||
|| props.selectedLearningPath.language !== parsedLearningPath.value.language
|
||||
)
|
||||
);
|
||||
|
||||
function getErrorMessage(): string | null {
|
||||
if (postError.value) {
|
||||
return t(extractErrorMessage(postError.value));
|
||||
} else if (putError.value) {
|
||||
return t(extractErrorMessage(putError.value));
|
||||
} else if (deleteError.value) {
|
||||
return t(extractErrorMessage(deleteError.value));
|
||||
} else if (isIdModified.value) {
|
||||
return t('learningPathCantModifyId');
|
||||
}
|
||||
return null;
|
||||
}
|
||||
</script>
|
||||
|
||||
|
@ -84,22 +106,34 @@
|
|||
<template v-slot:text>
|
||||
<json-editor-vue v-model="learningPath"></json-editor-vue>
|
||||
<v-alert
|
||||
v-if="postError || putError || deleteError"
|
||||
v-if="postError || putError || deleteError || isIdModified"
|
||||
icon="mdi mdi-alert-circle"
|
||||
type="error"
|
||||
:title="t('error')"
|
||||
:text="t(extractErrorMessage(postError) || extractErrorMessage(putError) || extractErrorMessage(deleteError)!)"
|
||||
:text="getErrorMessage()!"
|
||||
></v-alert>
|
||||
</template>
|
||||
<template v-slot:actions>
|
||||
<v-btn @click="uploadLearningPath" :loading="isPostPending || isPutPending" :disabled="parsedLearningPath.hruid === DEFAULT_LEARNING_PATH.hruid">
|
||||
<v-btn
|
||||
@click="uploadLearningPath"
|
||||
prependIcon="mdi mdi-check"
|
||||
:loading="isPostPending || isPutPending"
|
||||
:disabled="parsedLearningPath.hruid === DEFAULT_LEARNING_PATH.hruid || isIdModified"
|
||||
>
|
||||
{{ props.selectedLearningPath ? t('saveChanges') : t('create') }}
|
||||
</v-btn>
|
||||
<v-btn @click="deleteLearningObject" :disabled="!props.selectedLearningPath">
|
||||
{{ t('delete') }}
|
||||
</v-btn>
|
||||
<button-with-confirmation
|
||||
@confirm="deleteLearningPath"
|
||||
:disabled="!props.selectedLearningPath"
|
||||
:text="t('delete')"
|
||||
color="red"
|
||||
prependIcon="mdi mdi-delete"
|
||||
:confirmQueryText="t('learningPathDeleteQuery')"
|
||||
/>
|
||||
<v-btn
|
||||
:href="`/learningPath/${props.selectedLearningPath?.hruid}/${props.selectedLearningPath?.language}/start`"
|
||||
target="_blank"
|
||||
prepend-icon="mdi mdi-open-in-new"
|
||||
:disabled="!props.selectedLearningPath"
|
||||
>
|
||||
{{ t('open') }}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue