Skip to content

Commit 5774efa

Browse files
authored
BC-10396_BC-10397-move-or-import-card-to-other-boards (#3973)
Introducing the features: - sharing and importing a card from a board - moving a card from one board to another Highlights: - Start share card from board detail view - Import card using import url and choosing the destination - Move card from board detail view - Choose card destination from within a dialog - Notifications/Alerts are now supporting replacements and routerlinks using a {link} replacement - ShareModal supports card type now - Its possible to just fetch rooms via rooms.store and not update cached rooms response
1 parent ecc40fa commit 5774efa

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

57 files changed

+1473
-97
lines changed

src/components/molecules/AlertContainer.unit.ts

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ import { mount } from "@vue/test-utils";
77
import { setActivePinia } from "pinia";
88
import { beforeEach } from "vitest";
99
import { nextTick } from "vue";
10+
import { I18nT } from "vue-i18n";
11+
import { createRouterMock, injectRouterMock } from "vue-router-mock";
1012
import { VAlert, VIcon } from "vuetify/lib/components/index";
1113

1214
describe("AlertContainer", () => {
@@ -15,6 +17,7 @@ describe("AlertContainer", () => {
1517
});
1618

1719
const getWrapper = () => {
20+
injectRouterMock(createRouterMock({}));
1821
const wrapper = mount(AlertContainer, {
1922
global: {
2023
plugins: [createTestingVuetify(), createTestingI18n()],
@@ -83,4 +86,39 @@ describe("AlertContainer", () => {
8386
const result = wrapper.find(".alert-wrapper");
8487
expect(result.exists()).toBe(true);
8588
});
89+
90+
describe("i18n-t component with links and replacements", () => {
91+
it("should render i18n-t with link slot when notification contains link", async () => {
92+
const { wrapper } = getWrapper();
93+
useNotificationStore().notify({
94+
text: "notification.with.{link}",
95+
status: "info",
96+
link: {
97+
to: "/test-route",
98+
text: "Click here",
99+
},
100+
});
101+
await nextTick();
102+
103+
const i18n = wrapper.findComponent(I18nT);
104+
expect(i18n.exists()).toBe(true);
105+
});
106+
107+
it("should render i18n-t with replace slots when notification contains replacements", async () => {
108+
const { wrapper } = getWrapper();
109+
110+
useNotificationStore().notify({
111+
text: "notification.user.{userName}.{action}",
112+
status: "info",
113+
replace: {
114+
userName: "TestUser",
115+
action: "deleted",
116+
},
117+
});
118+
await nextTick();
119+
120+
const i18n = wrapper.findComponent(I18nT);
121+
expect(i18n.exists()).toBe(true);
122+
});
123+
});
86124
});

src/components/molecules/AlertContainer.vue

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,25 @@
1919
@click:close="removeNotifier(notification.id)"
2020
>
2121
<div class="alert-text mr-2" data-testId="alert-text">
22-
{{ getNotificationText(notification.text) }}
22+
<i18n-t
23+
v-if="notification.link || notification.replace"
24+
:keypath="notification.text"
25+
tag="span"
26+
scope="global"
27+
>
28+
<template v-if="notification.link" #link>
29+
<RouterLink data-testId="alert-link" :to="notification.link.to">
30+
{{ notification.link.text }}
31+
</RouterLink>
32+
</template>
33+
<template v-for="(value, key) in notification.replace" #[key]>
34+
{{ value }}
35+
</template>
36+
</i18n-t>
37+
38+
<template v-else>
39+
{{ getNotificationText(notification.text) }}
40+
</template>
2341
</div>
2442
</v-alert>
2543
</transition-group>

src/components/share/ShareModal.unit.ts

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,47 @@ describe("@/components/share/ShareModal", () => {
223223
});
224224
});
225225

226+
describe("when card is shared", () => {
227+
const setup = () => {
228+
const shareModuleMock = createModuleMocks(ShareModule, {
229+
getIsShareModalOpen: true,
230+
getParentType: ShareTokenBodyParamsParentTypeEnum.Card,
231+
createShareUrl: vi.fn(),
232+
resetShareFlow: vi.fn(),
233+
});
234+
235+
const wrapper = mount(ShareModal, {
236+
global: {
237+
plugins: [createTestingVuetify(), createTestingI18n()],
238+
provide: {
239+
[SHARE_MODULE_KEY.valueOf()]: shareModuleMock,
240+
},
241+
},
242+
props: {
243+
type: ShareTokenBodyParamsParentTypeEnum.Card,
244+
},
245+
});
246+
247+
return {
248+
wrapper,
249+
};
250+
};
251+
252+
it("should show copyright and privacy info alert", () => {
253+
const { wrapper } = setup();
254+
const infoAlert = wrapper.findComponent(InfoAlert);
255+
256+
expect(infoAlert.text()).toBe("components.molecules.share.checkPrivacyAndCopyright");
257+
});
258+
259+
it("should show warning alert", () => {
260+
const { wrapper } = setup();
261+
const warningAlert = wrapper.findComponent(WarningAlert);
262+
263+
expect(warningAlert.exists()).toBe(true);
264+
});
265+
});
266+
226267
describe("when lesson is shared", () => {
227268
const setup = () => {
228269
const shareModuleMock = createModuleMocks(ShareModule, {

src/components/share/ShareModal.vue

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,17 @@ const showAlertInfo = computed(
151151
props.type === ShareTokenBodyParamsParentTypeEnum.Courses ||
152152
props.type === ShareTokenBodyParamsParentTypeEnum.ColumnBoard ||
153153
props.type === ShareTokenBodyParamsParentTypeEnum.Lessons ||
154-
props.type === ShareTokenBodyParamsParentTypeEnum.Room
154+
props.type === ShareTokenBodyParamsParentTypeEnum.Room ||
155+
props.type === ShareTokenBodyParamsParentTypeEnum.Card
155156
);
156157
157158
const showCourseInfo = computed(() => props.type === ShareTokenBodyParamsParentTypeEnum.Courses);
158159
159-
const showBoardInfo = computed(() => props.type === ShareTokenBodyParamsParentTypeEnum.ColumnBoard);
160+
const showBoardInfo = computed(
161+
() =>
162+
props.type === ShareTokenBodyParamsParentTypeEnum.ColumnBoard ||
163+
props.type === ShareTokenBodyParamsParentTypeEnum.Card
164+
);
160165
161166
const showLessonInfo = computed(() => props.type === ShareTokenBodyParamsParentTypeEnum.Lessons);
162167

src/locales/de.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export default {
77
"common.actions.continue": "Weiter",
88
"common.actions.copy": "Kopieren",
99
"common.actions.duplicate": "Duplizieren",
10+
"common.actions.move": "Verschieben",
1011
"common.actions.create": "Erstellen",
1112
"common.actions.delete": "Löschen",
1213
"common.actions.discard": "Verwerfen",
@@ -196,7 +197,7 @@ export default {
196197
"common.validation.nonEmptyString": "Dies ist ein Pflichtfeld und darf nicht nur Leerzeichen enthalten.",
197198
"common.words.also": "auch",
198199
"common.words.and": "und",
199-
"common.words.board": "Bereich",
200+
"common.words.board": "Bereich | Bereiche",
200201
"common.words.classes": "Klassen",
201202
"common.words.color": "Farbe",
202203
"common.words.color.blue": "Blau",
@@ -247,6 +248,7 @@ export default {
247248
"common.words.topic": "Thema",
248249
"common.words.topics": "Themen",
249250
"common.words.yes": "Ja",
251+
"common.alerts.room.not.available": "Kein Raum verfügbar. | Keine Räume verfügbar.",
250252
"components.administration.adminMigrationSection.description.firstPart":
251253
"Bei der Migration wird das Anmeldesystem der Schüler:innen und Lehrkräfte zu moin.schule gewechselt. Die zu den betroffenen Accounts gehörenden Daten bleiben erhalten.",
252254
"components.administration.adminMigrationSection.description.secondPart":
@@ -493,6 +495,8 @@ export default {
493495
"In Abschnitt {columnPosition} wurde eine Karte an Position {newPosition} verschoben.",
494496
"components.board.screenReader.notification.cardMovedToAnotherColumn.success":
495497
"Eine Karte wurde von Abschnitt {fromColumnPosition} in Abschnitt {toColumnPosition} verschoben.",
498+
"components.board.screenReader.notification.cardMovedToBoard.success":
499+
"Eine Karte wurde in Abschnitt {toColumnId} verschoben.",
496500
"components.board.screenReader.notification.columnMoved.success":
497501
"Ein Abschnitt wurde von Position {oldPosition} an Position {newPosition} verschoben.",
498502
"components.board.screenReader.notification.boardTitleUpdated.success":
@@ -658,6 +662,20 @@ export default {
658662
"components.molecules.copyResult.followingNotCopied": "Folgendes wurde nicht kopiert:",
659663
"components.molecules.EdusharingFooter.img_alt": "edusharing-logo",
660664
"components.molecules.EdusharingFooter.text": "powered by",
665+
"components.molecules.label.room": "Raum wählen",
666+
"components.molecules.label.board": "Bereich wählen",
667+
"components.molecules.label.section": "Abschnitt wählen",
668+
"components.molecules.import.card.question": "Wohin soll die Karte{title} importiert werden?",
669+
"components.molecules.import.card.hint.restriction": "Folgende Inhalte werden nicht übernommen:",
670+
"components.molecules.import.card.hint.etherpad": "Inhalte aus Etherpads",
671+
"components.molecules.import.card.hint.whiteboard": "Inhalte aus Whiteboards",
672+
"components.molecules.import.card.hint.ctltools": "Geschützte Einstellungen externer Tools",
673+
"components.molecules.move.card.title": "Karte verschieben",
674+
"components.molecules.move.card.question": "Wohin soll die Karte{title} verschoben werden?",
675+
"components.molecules.move.card.hint.restriction":
676+
"Mit der Berechtigung „Bearbeiten“ können Karten nur innerhalb des selben Raumes in andere Bereiche verschoben werden.",
677+
"components.molecules.move.card.message.success": "Karte erfolgreich in den Bereich {link} - {column} verschoben",
678+
"components.molecules.import.card.options.title": "Karte importieren",
661679
"components.molecules.import.columnBoard.label": "Titel des Bereichs",
662680
"components.molecules.import.columnBoard.rename": "Bei Bedarf kann der Name des Bereiches umbenannt werden: ",
663681
"components.molecules.import.columnBoard.options.infoText": "Der Bereich kann im Folgenden umbenannt werden. ",
@@ -714,6 +732,9 @@ export default {
714732
"components.molecules.importUsersMatch.externalRoleName.schulconnex.orgAdmin": "Organisationsadministrator",
715733
"components.molecules.importUsersMatch.externalRoleName.schulconnex.manager": "Organisationsleitung",
716734
"components.molecules.MintEcFooter.chapters": "Kapitelübersicht",
735+
"components.molecules.share.card.options.infoText":
736+
"Mit dem folgenden Link kann diese Karte von anderen Personen importiert werden.",
737+
"components.molecules.share.card.result.linkLabel": "Link Karten-Kopie",
717738
"components.molecules.share.columnBoard.options.infoText":
718739
"Mit dem folgenden Link kann der Bereich als Kopie von anderen Lehrkräften importiert werden.",
719740
"components.molecules.share.columnBoard.result.linkLabel": "Link Bereich-Kopie",
@@ -1839,6 +1860,7 @@ export default {
18391860
"pages.rooms.invitationlinks.error.create": "Das Erstellen des Einladungslinks ist fehlgeschlagen.",
18401861
"pages.rooms.invitationlinks.error.update": "Das Aktualisieren des Einladungslinks ist fehlgeschlagen.",
18411862
"pages.rooms.invitationlinks.error.load": "Die Einladungsliste konnte nicht geladen werden.",
1863+
"pages.rooms.invitationlinks.error.delete": "Die Einladungsliste konnte nicht gelöscht werden.",
18421864
"pages.rooms.title": "Räume",
18431865
"pages.rooms.administration.title": "Räume verwalten",
18441866
"pages.rooms.administration.table.header.roomName": "Raumname",

src/locales/en.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export default {
77
"common.actions.continue": "Continue",
88
"common.actions.copy": "Copy",
99
"common.actions.duplicate": "Duplicate",
10+
"common.actions.move": "Move",
1011
"common.actions.create": "Create",
1112
"common.actions.delete": "Delete",
1213
"common.actions.discard": "Discard",
@@ -189,7 +190,7 @@ export default {
189190
"common.validation.nonEmptyString": "This is a mandatory field and must not contain only spaces.",
190191
"common.words.also": "also",
191192
"common.words.and": "and",
192-
"common.words.board": "board",
193+
"common.words.board": "board | boards",
193194
"common.words.classes": "Classes",
194195
"common.words.color": "Color",
195196
"common.words.color.blue": "Blue",
@@ -239,6 +240,7 @@ export default {
239240
"common.words.topics": "Topics",
240241
"common.words.yes": "Yes",
241242
"common.words.export": "Course export is downloading",
243+
"common.alerts.room.not.available": "No room available. | No rooms available.",
242244
"components.administration.adminMigrationSection.description.firstPart":
243245
"During the migration, the registration system for students and teachers is changed to moin.schule. The data belonging to the affected accounts will be preserved.",
244246
"components.administration.adminMigrationSection.description.secondPart":
@@ -480,6 +482,7 @@ export default {
480482
"In column {columnPosition}, a card was moved to position {newPosition}.",
481483
"components.board.screenReader.notification.cardMovedToAnotherColumn.success":
482484
"A card was moved from column {fromColumnPosition} to column {toColumnPosition}.",
485+
"components.board.screenReader.notification.cardMovedToBoard.success": "A card was moved to section {toColumnId}.",
483486
"components.board.screenReader.notification.columnMoved.success":
484487
"A column was moved from position {oldPosition} to position {newPosition}.",
485488
"components.board.screenReader.notification.boardTitleUpdated.success":
@@ -646,7 +649,20 @@ export default {
646649
"components.molecules.copyResult.followingNotCopied": "The following was not copied:",
647650
"components.molecules.EdusharingFooter.img_alt": "edusharing-logo",
648651
"components.molecules.EdusharingFooter.text": "powered by",
652+
"components.molecules.import.card.options.title": "Import card",
649653
"components.molecules.import.columnBoard.label": "Board title",
654+
"components.molecules.label.room": "Select room",
655+
"components.molecules.label.board": "Select board",
656+
"components.molecules.label.section": "Select section",
657+
"components.molecules.import.card.question": "Where should the card{title} be imported?",
658+
"components.molecules.import.card.hint.restriction": "The following contents are not transferred:",
659+
"components.molecules.import.card.hint.etherpad": "Content from Etherpads",
660+
"components.molecules.import.card.hint.whiteboard": "Content from Whiteboards",
661+
"components.molecules.import.card.hint.ctltools": "Protected settings of external tools",
662+
"components.molecules.move.card.title": "Move card",
663+
"components.molecules.move.card.question": "Where should the card{title} be moved to?",
664+
"components.molecules.move.card.hint.restriction": `With the "Edit" permission, cards can only be moved to other boards within the same room.`,
665+
"components.molecules.move.card.message.success": "Card successfully moved to the board {link} - {column}",
650666
"components.molecules.import.columnBoard.rename": "If necessary, the name of the board can be renamed: ",
651667
"components.molecules.import.columnBoard.options.infoText": "The board can be renamed below.",
652668
"components.molecules.import.columnBoard.options.title": "Import board",
@@ -707,6 +723,9 @@ export default {
707723
"components.molecules.shareImport.options.ctlTools.infoText.unavailable":
708724
"External tools not available in the target school",
709725
"components.molecules.shareImport.options.ctlTools.infoText.protected": "Protected settings of external tools",
726+
"components.molecules.share.card.options.infoText":
727+
"With the following link, the card can be imported by other people.",
728+
"components.molecules.share.card.result.linkLabel": "Link card copy",
710729
"components.molecules.share.courses.options.infoText":
711730
"With the following link, the course can be imported as a copy by other teachers.",
712731
"components.molecules.shareImport.options.restrictions.infoText.personalData": "Personal data",
@@ -1809,6 +1828,7 @@ export default {
18091828
"pages.rooms.invitationlinks.error.create": "Creating the invitation link has failed.",
18101829
"pages.rooms.invitationlinks.error.update": "Updating the invitation link has failed.",
18111830
"pages.rooms.invitationlinks.error.load": "The invitation list could not be loaded.",
1831+
"pages.rooms.invitationlinks.error.delete": "The invitation list could not be deleted.",
18121832
"pages.rooms.title": "Rooms",
18131833
"pages.rooms.administration.title": "Room administration",
18141834
"pages.rooms.administration.table.header.roomName": "Room name",

src/locales/es.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ export default {
77
"common.actions.continue": "Continuar",
88
"common.actions.copy": "Copiar",
99
"common.actions.duplicate": "Duplicar",
10+
"common.actions.move": "Mover",
1011
"common.actions.create": "Crear",
1112
"common.actions.delete": "Borrar",
1213
"common.actions.discard": "Descartar",
@@ -191,7 +192,7 @@ export default {
191192
"common.validation.nonEmptyString": "Este campo es obligatorio y no debe contener sólo espacios.",
192193
"common.words.also": "también",
193194
"common.words.and": "y",
194-
"common.words.board": "tablero",
195+
"common.words.board": "tablero | tableros",
195196
"common.words.classes": "Clases",
196197
"common.words.color": "Color",
197198
"common.words.color.blue": "Azul",
@@ -241,6 +242,7 @@ export default {
241242
"common.words.topics": "Temas",
242243
"common.words.yes": "Sí",
243244
"common.words.export": "La exportación del curso se está descargando",
245+
"common.alerts.room.not.available": "No hay sala disponible. | No hay salas disponibles.",
244246
"components.administration.adminMigrationSection.description.firstPart":
245247
"Durante la migración se cambia el sistema de registro de alumnos y profesores a moin.schule. Los datos pertenecientes a las cuentas afectadas se conservarán.",
246248
"components.administration.adminMigrationSection.description.secondPart":
@@ -488,6 +490,8 @@ export default {
488490
"En la columna {columnPosition}, se ha movido una carta a la posición {newPosition}.",
489491
"components.board.screenReader.notification.cardMovedToAnotherColumn.success":
490492
"Se ha movido una tarjeta de la columna {fromColumnPosition} a la columna {toColumnPosition}.",
493+
"components.board.screenReader.notification.cardMovedToBoard.success":
494+
"Una tarjeta fue movida a la sección {toColumnId}.",
491495
"components.board.screenReader.notification.columnMoved.success":
492496
"Se ha movido una columna de la posición {oldPosition} a la posición {newPosition}.",
493497
"components.board.screenReader.notification.boardTitleUpdated.success":
@@ -655,7 +659,21 @@ export default {
655659
"components.molecules.copyResult.followingNotCopied": "No se ha copiado lo siguiente:",
656660
"components.molecules.EdusharingFooter.img_alt": "edusharing-logotipo",
657661
"components.molecules.EdusharingFooter.text": "desarrollado por",
662+
"components.molecules.import.card.options.title": "Importar tarjeta",
658663
"components.molecules.import.columnBoard.label": "Título del tablero",
664+
"components.molecules.label.room": "Seleccionar sala",
665+
"components.molecules.label.board": "Seleccionar tablero",
666+
"components.molecules.label.section": "Seleccionar sección",
667+
"components.molecules.import.card.question": "¿A dónde se debe importar la tarjeta{title}?",
668+
"components.molecules.import.card.hint.restriction": "Los siguientes contenidos no se transfieren:",
669+
"components.molecules.import.card.hint.etherpad": "Contenido de Etherpads",
670+
"components.molecules.import.card.hint.whiteboard": "Contenido de Whiteboards",
671+
"components.molecules.import.card.hint.ctltools": "Configuraciones protegidas de herramientas externas",
672+
"components.molecules.move.card.title": "Mover tarjeta",
673+
"components.molecules.move.card.question": "¿A dónde se debe mover la tarjeta{title}?",
674+
"components.molecules.move.card.message.success": "Tarjeta movida con éxito al tablero {link} - {column}",
675+
"components.molecules.move.card.hint.restriction":
676+
"Con el permiso «editar», las tarjetas solo pueden moverse a otras tableros dentro del mismo espacio.",
659677
"components.molecules.import.columnBoard.rename": "Si es necesario, se puede cambiar el nombre del tablero: ",
660678
"components.molecules.import.columnBoard.options.infoText": "Puede cambiar el nombre del tablero a continuación.",
661679
"components.molecules.import.columnBoard.options.title": "Importar tablero",
@@ -708,6 +726,9 @@ export default {
708726
"components.molecules.importUsersMatch.externalRoleName.schulconnex.orgAdmin": "Administrador de organización",
709727
"components.molecules.importUsersMatch.externalRoleName.schulconnex.manager": "Gestión organizativa",
710728
"components.molecules.MintEcFooter.chapters": "Resumen del capítulo",
729+
"components.molecules.share.card.options.infoText":
730+
"Con el siguiente enlace, la tarjeta puede ser importada como copia por otros personas.",
731+
"components.molecules.share.card.result.linkLabel": "Enlace a la copia de la tarjeta",
711732
"components.molecules.share.columnBoard.options.infoText":
712733
"Con el siguiente enlace, el tablero puede ser importado como copia por otros profesores. Los datos personales no se importarán.",
713734
"components.molecules.share.columnBoard.result.linkLabel": "Enlace a la copia del tablón",
@@ -1855,6 +1876,7 @@ export default {
18551876
"pages.rooms.invitationlinks.error.create": "No se ha podido crear el enlace de invitación.",
18561877
"pages.rooms.invitationlinks.error.update": "Se ha producido un error al actualizar el enlace de invitación.",
18571878
"pages.rooms.invitationlinks.error.load": "No se pudo cargar la lista de invitados.",
1879+
"pages.rooms.invitationlinks.error.delete": "No se pudo eliminar la lista de invitaciones.",
18581880
"pages.rooms.title": "Salas",
18591881
"pages.rooms.administration.title": "Administración de salas",
18601882
"pages.rooms.administration.table.header.roomName": "Nombre de la sala",

0 commit comments

Comments
 (0)