} query
+ * @return {boolean}
+ */
+export function filterAndQueryAreEqual(filter, query) {
+ if (JSON.stringify(filter) === JSON.stringify(query)) return true
+ const arrayFiltersAreEqual = ['category', 'responsible', 'progressLabel']
+ .map((key) => ({
+ a: getValueAsArrayForKey(query, key),
+ b: getValueAsArrayForKey(filter, key),
+ }))
+ .map(({ a, b }) => JSON.stringify(a) === JSON.stringify(b))
+ .reduce((accum, curr) => accum && curr, true)
+ return arrayFiltersAreEqual && filter.period === query.period
+}
+
+/**
+ * @internal
+ * @template T
+ * @param {T} obj
+ * @param {keyof T} key
+ * @return {string[] | undefined}
+ */
+function getValueAsArrayForKey(obj, key) {
+ const val = obj[key]
+ if (Array.isArray(val)) return val.filter((v) => !!v && typeof v === 'string').sort()
+ if (typeof val === 'string') return [val]
+ return undefined
+}
diff --git a/frontend-old/src/helpers/scheduleEntryRouteChange.js b/frontend-old/src/helpers/scheduleEntryRouteChange.js
new file mode 100644
index 0000000000..b3eb932feb
--- /dev/null
+++ b/frontend-old/src/helpers/scheduleEntryRouteChange.js
@@ -0,0 +1,37 @@
+import { firstActivityScheduleEntry } from '@/router.js'
+import { apiStore } from '@/plugins/store'
+
+/**
+ * Loads first valid schedule entry for activity if current one is missing
+ * @param activity
+ * @param to
+ * @param from
+ * @param next
+ * @return {Promise<*>}
+ */
+export default async function scheduleEntryRouteChange(activity, to, from, next) {
+ if (
+ to.params.scheduleEntryId !== from.params.scheduleEntryId ||
+ to.params.activityId !== from.params.activityId
+ ) {
+ apiStore.get().activities({ id: to.params.activityId }).$reload()
+ // activity reload doesn't need to be awaited, but for scheduleEntry we want to
+ // ensure it exists and otherwise reroute
+ return await apiStore
+ .get()
+ .scheduleEntries({ id: to.params.scheduleEntryId })
+ .$reload()
+ .then(() => next())
+ .catch(async () => {
+ return next({
+ name: 'camp/activity',
+ params: {
+ ...to.params,
+ scheduleEntryId: (await firstActivityScheduleEntry(to.params.activityId)).id,
+ },
+ })
+ })
+ } else {
+ return next()
+ }
+}
diff --git a/frontend-old/src/helpers/serverError.js b/frontend-old/src/helpers/serverError.js
new file mode 100644
index 0000000000..137eefe51a
--- /dev/null
+++ b/frontend-old/src/helpers/serverError.js
@@ -0,0 +1,93 @@
+/**
+ * Parses an error object returned by hal-json-vuex and returns as message string
+ */
+import fallbackLocalesFor from '@/plugins/i18n/apiFallbackLocalesFor'
+
+const serverErrorToString = (error) => {
+ // API error
+ // error.response is in API Problem Details format: https://www.rfc-editor.org/rfc/rfc7807
+ if (error.name === 'ServerException' && error.response) {
+ if (
+ error.response?.headers?.['content-type']?.startsWith('application/problem+json')
+ ) {
+ return error.response.data.detail
+ }
+
+ return error.response?.data?.message || error.toString()
+ }
+
+ // other error thrown directly by Javascript (e.g. connection error)
+ return error.message
+}
+
+function getApiTranslation(locale, violation) {
+ if (!locale) {
+ return null
+ }
+ const matchingTranslation = [locale, ...fallbackLocalesFor(locale)]
+ .map((locale) => violation?.i18n?.translations?.[locale])
+ .find((value) => value !== undefined)
+ return matchingTranslation ?? null
+}
+
+/**
+ * Parses an error object returned by hal-json-vuex and returns an object of propertypath => violationMessages[]
+ * i18n is nullable if someone wants to use it without initialized localisation
+ */
+const transformViolations = (error, i18n = null) => {
+ const serverErrorMessages = {}
+
+ if (error.name === 'ServerException' && error.response?.status !== 422) {
+ serverErrorMessages[0] = serverErrorToString(error)
+ return serverErrorMessages
+ }
+
+ const violations = error.response?.data?.violations ?? []
+
+ for (const i in violations) {
+ const violation = violations[i]
+ const propertyPath = violation.propertyPath
+ if (!serverErrorMessages[propertyPath]) {
+ serverErrorMessages[propertyPath] = []
+ }
+ const i18nKey = `api.${violation.i18n?.key}`
+ const locale = i18n?.locale?.replaceAll('-', '_')
+ const apiTranslation = getApiTranslation(locale, violation)
+
+ if (i18n?.te(i18nKey)) {
+ const parameters = violation.i18n?.parameters ?? {}
+ serverErrorMessages[propertyPath].push(i18n.tc(i18nKey, parameters))
+ } else if (apiTranslation) {
+ serverErrorMessages[propertyPath].push(apiTranslation)
+ } else {
+ serverErrorMessages[propertyPath].push(violation.message)
+ }
+ }
+ return serverErrorMessages
+}
+
+function violationsToFlatArray(e, i18n) {
+ const violationsObject = transformViolations(e, i18n)
+ const violations = Object.entries(violationsObject)
+ if (violations.length === 0) {
+ return []
+ }
+ if (violations.length === 1 && violationsObject[0]) {
+ return [violationsObject[0]]
+ }
+ const toArray = (element) => {
+ if (Array.isArray(element)) {
+ return element
+ }
+ return [element]
+ }
+ const result = []
+ for (const [key, value] of Object.entries(violationsObject)) {
+ for (const message of toArray(value)) {
+ result.push(`${key}: ${message}`)
+ }
+ }
+ return result
+}
+
+export { serverErrorToString, transformViolations, violationsToFlatArray }
diff --git a/frontend-old/src/helpers/vCalendarDragAndDrop.js b/frontend-old/src/helpers/vCalendarDragAndDrop.js
new file mode 100644
index 0000000000..7d5ffae3c7
--- /dev/null
+++ b/frontend-old/src/helpers/vCalendarDragAndDrop.js
@@ -0,0 +1,30 @@
+/**
+ * helpers for VCalendar DragAndDrop composables
+ */
+
+const ONE_MINUTE_IN_MILLISECONDS = 60 * 1000
+const ONE_HOUR_IN_MILLISECONDS = 60 * ONE_MINUTE_IN_MILLISECONDS
+const ONE_DAY_IN_MILLISECONDS = 24 * ONE_HOUR_IN_MILLISECONDS
+const QUARTER_HOUR_IN_MILLISECONDS = 15 * ONE_MINUTE_IN_MILLISECONDS
+
+// helper function to convert Vuetify day & time object into timestamp
+const toTime = (tms) => {
+ return new Date(tms.year, tms.month - 1, tms.day, tms.hour, tms.minute).getTime()
+}
+
+const roundTimeToNearestQuarterHour = (time) => {
+ return Math.round(time / QUARTER_HOUR_IN_MILLISECONDS) * QUARTER_HOUR_IN_MILLISECONDS
+}
+
+const roundTimeUpToNextQuarterHour = (time) => {
+ return Math.ceil(time / QUARTER_HOUR_IN_MILLISECONDS) * QUARTER_HOUR_IN_MILLISECONDS
+}
+
+export {
+ toTime,
+ roundTimeToNearestQuarterHour,
+ roundTimeUpToNextQuarterHour,
+ ONE_MINUTE_IN_MILLISECONDS,
+ ONE_HOUR_IN_MILLISECONDS,
+ ONE_DAY_IN_MILLISECONDS,
+}
diff --git a/frontend-old/src/locales/de-CH-scout.json b/frontend-old/src/locales/de-CH-scout.json
new file mode 100644
index 0000000000..d808ffcec3
--- /dev/null
+++ b/frontend-old/src/locales/de-CH-scout.json
@@ -0,0 +1,31 @@
+{
+ "components": {
+ "campCreate": {
+ "campCreateStep1": {
+ "titlePlaceholder": "Sommerlager 2024 Pfadistufe"
+ }
+ }
+ },
+ "contentNode": {
+ "storycontext": {
+ "name": "Roter Faden"
+ }
+ },
+ "entity": {
+ "user": {
+ "fields": {
+ "nickname": "Pfadiname"
+ }
+ }
+ },
+ "global": {
+ "language": "Deutsch (Pfadi)"
+ },
+ "views": {
+ "camp": {
+ "story": {
+ "title": "Roter Faden"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend-old/src/locales/de.json b/frontend-old/src/locales/de.json
new file mode 100644
index 0000000000..43d5062f53
--- /dev/null
+++ b/frontend-old/src/locales/de.json
@@ -0,0 +1,867 @@
+{
+ "components": {
+ "activity": {
+ "content": {
+ "checklist": {
+ "title": "Checkliste bearbeiten"
+ },
+ "columnLayout": {
+ "columnOperations": {
+ "addColumn": "Spalte hinzufügen",
+ "removeColumn": "Leere Spalte entfernen"
+ }
+ },
+ "lAThematicArea": {
+ "placeholder": "Themenbereich auswählen"
+ },
+ "storyboard": {
+ "controls": "Aktionen",
+ "reorder": "Verschieben",
+ "storyboardDialogRemoveSection": {
+ "deleteWarning": "Möchtest du diesen Abschnitt wirklich löschen?",
+ "title": "Wirklich löschen?"
+ }
+ }
+ },
+ "dialog": {
+ "dialogActivityEdit": {
+ "title": "Aktivität bearbeiten"
+ },
+ "formScheduleEntryList": {
+ "name": "Geplante Termine"
+ }
+ },
+ "menuCardlessContentNode": {
+ "deletingDisabled": "Muss leer sein zum Löschen"
+ },
+ "scheduleEntry": {
+ "backToContents": "Zurück zum Bearbeiten des Inhalts",
+ "changeLayout": "Layout ändern",
+ "clipboardInfoDialog": {
+ "allow": "Jetzt erlauben",
+ "denied": "Du hast den Zugriff auf die Zwischenablage verweigert. Kopierte Aktivitäten können daher nicht eingefügt werden.",
+ "description": "Damit du eine kopierte Aktivität einfügen kannst, musst du eCamp v3 erlauben, deine Zwischenablage zu lesen.",
+ "granted": "Du kannst nun kopierte Aktivitäten einfügen.",
+ "title": "Aktivität kopieren & einfügen"
+ },
+ "copyScheduleEntry": "Aktivität kopieren",
+ "deleteWarning": "Möchtest du diese Aktivität wirklich löschen? Der komplette Inhalt dieser Aktivität wird gelöscht."
+ },
+ "togglePaperSize": {
+ "switchToFullSize": "Zum Breitformat umschalten",
+ "switchToPaperSize": "Zum Papierformat umschalten"
+ }
+ },
+ "camp": {
+ "campInvitations": {
+ "title": "Einladen"
+ },
+ "campListItem": {
+ "public": "Öffentlich"
+ },
+ "campMembers": {
+ "title": "Mitglieder"
+ },
+ "footerSharedCamp": {
+ "outsider": "Du arbeitest in diesem Lager nicht mit:",
+ "outsiderDescription": "Du kannst keine Daten ändern, aber das Programm anschauen und kopieren.",
+ "shared": "Lager ist öffentlich freigegeben:",
+ "sharedDescription": "Fremde Personen können sämtliche Daten im Lager einsehen und kopieren."
+ }
+ },
+ "campAdmin": {
+ "campActivityProgressLabels": {
+ "create": "Status erstellen",
+ "deleteError": "Status konnte nicht gelöscht werden. Der Status wird noch verwendet.",
+ "deleteWarning": "Möchtest du dieses Status wirklich löschen?",
+ "exit": "Umsortieren beenden",
+ "moveDown": "Nach unten",
+ "moveUp": "Nach oben",
+ "reorder": "Umsortieren",
+ "title": "Block-Status"
+ },
+ "campAddress": {
+ "title": "Lageradresse"
+ },
+ "campCategories": {
+ "create": "Block-Kategorie erstellen",
+ "pasteCategory": "Kopierte Kategorie einfügen",
+ "title": "Block-Kategorien"
+ },
+ "campConditionalFields": {
+ "course": {
+ "title": "Kurse"
+ },
+ "title": "Einstellungen für J+S",
+ "ysCamp": {
+ "title": "Lager"
+ }
+ },
+ "campDangerZone": {
+ "deleteCamp": {
+ "description": "Nachdem ein Lager gelöscht ist, gibt es kein zurück mehr. Bitte fahre nur fort, wenn du sicher bist.",
+ "explanation": "Diese Aktion kann nicht rückgängig gemacht werden. Dadurch wird das Lager \"{campTitle}\" inklusive allen Aktivitäten, Materiallisten und Verantwortungszuweisungen dauerhaft gelöscht.",
+ "label": "Bitte gib zur Bestätigung \"{campTitle}\" ein.",
+ "title": "Lager löschen"
+ },
+ "title": "Gefahrenzone"
+ },
+ "campMaterialLists": {
+ "createMaterialList": "Materialliste erstellen",
+ "title": "Materiallisten"
+ },
+ "campPeriods": {
+ "createPeriod": "Lagerabschnitt erstellen",
+ "title": "Lagerabschnitt | Lagerabschnitte"
+ },
+ "campPeriodsListItem": {
+ "changePeriodDescription": "Bezeichnung ändern",
+ "deleteWarning": "Möchtest du diesen Lagerabschnitt wirklich löschen?",
+ "lastPeriodNotDeletable": "Der letzte Lagerabschnitt kann nicht gelöscht werden.",
+ "movePeriod": "Lagerabschnitt verschieben",
+ "periodChangeEnd": "Am Ende Tage hinzufügen/entfernen",
+ "periodChangeStart": "Zu Begin Tage hinzufügen/entfernen"
+ },
+ "campSettings": {
+ "title": "Beschreibung"
+ },
+ "campSharingSettings": {
+ "activate": "Freigabe aktivieren",
+ "deactivate": "Freigabe deaktivieren",
+ "implications": "Wenn du die Freigabe aktivierst, dann können fremde Personen das Programm, das Team, die Materiallisten und Verantwortlichkeiten und alles andere in diesem Lager ansehen und kopieren. Nur Personen im Team können Daten ändern.",
+ "notShared": {
+ "description": ": Nur Personen im Team können das Programm und die Daten in diesem Lager ansehen.",
+ "title": "Freigabe ist nicht aktiviert"
+ },
+ "publicCampUrl": "Sharing-Link zum Lager",
+ "shared": {
+ "description": ": Fremde Personen können das Programm, das Team, die Materiallisten und Verantwortlichkeiten und alles andere in diesem Lager ansehen und kopieren. Nur Personen im Team können Daten ändern.",
+ "title": "Lager ist öffentlich freigegeben"
+ },
+ "sharedSince": "Am {sharedSince} von {sharedBy}",
+ "title": "Lager teilen"
+ },
+ "createCampPeriods": {
+ "add": "Weiteren Lagerabschnitt hinzufügen"
+ },
+ "dialogActivityProgressLabelCreate": {
+ "title": "Block-Status erstellen"
+ },
+ "dialogActivityProgressLabelEdit": {
+ "title": "Status bearbeiten"
+ },
+ "dialogCategoryCreate": {
+ "clearClipboard": "Zwischenablage leeren",
+ "clipboard": "Zwischenablage",
+ "clipboardInfoDialog": {
+ "allow": "Jetzt erlauben",
+ "denied": "Du hast den Zugriff auf die Zwischenablage verweigert. Kopierte Kategorien können daher nicht eingefügt werden.",
+ "description": "Damit du eine kopierte Kategorie einfügen kannst, musst du eCamp v3 erlauben, deine Zwischenablage zu lesen.",
+ "granted": "Du kannst nun kopierte Kategorien einfügen.",
+ "title": "Kategorie kopieren & einfügen"
+ },
+ "copyCategoryOrActivity": "Kategorie oder Aktivität kopieren",
+ "copyContent": "Inhalt kopieren",
+ "copyPasteCategory": "Kategorie kopieren & einfügen",
+ "copySourceInfo": "Hier kannst du die URL einer Block-Kategorie oder einer Aktivität einfügen, um dessen Inhalte zu kopieren.",
+ "pasteCategory": "Kopierte Kategorie oder Aktivität einfügen",
+ "title": "Block-Kategorie erstellen"
+ },
+ "dialogMaterialListCreate": {
+ "title": "Materialliste erstellen"
+ },
+ "dialogMaterialListEdit": {
+ "deleteError": "Materialliste konnte nicht gelöscht werden. Überprüfe vor dem Löschen, dass die Liste leer ist.",
+ "title": "Materialliste bearbeiten"
+ },
+ "dialogPeriodCreate": {
+ "title": "Lagerabschnitt erstellen"
+ },
+ "dialogPeriodDateEdit": {
+ "movePeriod": "Lagerabschnitt verschieben",
+ "periodChangeEnd": "Am Ende vom Lagerabschnitt Tage hinzufügen / entfernen",
+ "periodChangeStart": "Am Anfang vom Lagerabschnitt Tage hinzufügen / entfernen",
+ "periodDuration": "Anzahl Tage"
+ },
+ "dialogPeriodEdit": {
+ "moveScheduleEntries": "Aktivitäten verschieben"
+ },
+ "errorExistingActivitiesList": {
+ "description": "Kann nicht gelöscht werden. Wird noch von folgenden Aktivitäten verwendet:"
+ }
+ },
+ "campCreate": {
+ "campCreate": {
+ "steps": {
+ "configurate": "Konfigurieren",
+ "infos": "Infos",
+ "template": "Vorlage"
+ }
+ },
+ "campCreateStep1": {
+ "submitTooltip": "Bitte fülle alle Pflichtfelder aus.",
+ "titlePlaceholder": "Klassenlager 2024 5. Klasse"
+ },
+ "campCreateStep2": {
+ "category": "Block-Kategorien",
+ "checklistItemCount": "Keine Einträge | Ein Eintrag | {count} Einträge",
+ "checklists": "Checklisten",
+ "clipboardInfoDialog": {
+ "allow": "Jetzt erlauben",
+ "denied": "Du hast den Zugriff auf die Zwischenablage verweigert. Bitte füge den Link zum Vorlage-Lager stattdessen von Hand ein.",
+ "description": "Damit du die Einstellungen eines kopierten Lagers einfügen kannst, musst du eCamp v3 erlauben, deine Zwischenablage zu lesen.",
+ "granted": "Du kannst nun kopierte Lagereinstellungen einfügen.",
+ "title": "Lagereinstellungen kopieren & einfügen"
+ },
+ "create": "Lager erstellen",
+ "materialLists": "Materiallisten",
+ "noContent": "Keine Inhalte",
+ "noPrototype": "Keine Vorlage",
+ "noPrototypeAlert": {
+ "description": "Du musst alle Einstellungen manuell vornehmen. Es sind keine Blockvorlagen & Layouts vorhanden. Dies ist nur für erfahrene Nutzende geeignet.",
+ "title": "Achtung: Du hast \"Keine Vorlage\" ausgewählt."
+ },
+ "otherPrototype": "Einstellungen von einem anderen Lager kopieren…",
+ "pasteCamp": "Kopierte Lagereinstellungen einfügen",
+ "preview": "Vorschau der Lagervorlage",
+ "progressLabels": "Blockstatus",
+ "prototypeCampUrl": "Link zum gewünschten Vorlage-Lager",
+ "prototypeHint": "Kopiere Kategorien, Blockvorlagen, Blockstatus, Material- und Checklisten von einer Lagervorlage",
+ "prototypeHintEmpty": "Keine Kategorien, Blockvorlagen, Blockstatus, Material- und Checklisten aus einer Lagervorlage kopieren",
+ "prototypeHintOther": "Kopiere Kategorien, Blockvorlagen, Blockstatus, unpersönliche Material- und Checklisten von diesem anderen Lager. Es werden keine Aktivitäten kopiert.",
+ "prototypeHintSelected": "Kopiere Kategorien, Blockvorlagen, Blockstatus und Materiallisten von dieser Lagervorlage",
+ "submitTooltipPrototype": "Du musst noch auswählen, ob und welche Lagervorlage du verwenden möchtest."
+ }
+ },
+ "category": {
+ "categoryTemplate": {
+ "contents": "Inhalte",
+ "createLayoutHelp": "Hier kannst du die Vorlage für neue {categoryShort}-Blöcke definieren.{br}Blockinhalt & Layout bereits erstellter {categoryShort}-Blöcke, werden nicht angepasst.",
+ "layout": "Layout",
+ "noTemplate": "Keine Vorlage"
+ }
+ },
+ "checklist": {
+ "checklistCreate": {
+ "title": "Checkliste erstellen"
+ },
+ "checklistDetail": {
+ "deleteError": "Checkliste konnte nicht gelöscht werden. Überprüfe vor dem Löschen, dass die Checkliste nicht mehr in Aktivitäten verwendet wird.",
+ "deleteWarning": "Möchtest du diese Checkliste wirklich löschen? Der komplette Inhalt dieser Checkliste wird gelöscht."
+ },
+ "checklistItemCreate": {
+ "add": "Checklisten-Eintrag erstellen",
+ "title": "Checklisten-Eintrag hinzufügen"
+ },
+ "checklistItemEdit": {
+ "delete": "Möchtest du diesen Eintrag wirklich löschen?",
+ "title": "Checklisten-Eintrag bearbeiten"
+ },
+ "sortableChecklist": {
+ "add": "Zu \"{parent}\" hinzufügen"
+ }
+ },
+ "collaborator": {
+ "collaboratorCreate": {
+ "invite": "Einladung verschicken",
+ "inviteCta": "Ins Team einladen",
+ "title": "Person einladen"
+ },
+ "collaboratorEdit": {
+ "cannotRemoveLastManager": "Du kannst die letzte Person mit administrativen Rechten nicht deaktivieren",
+ "deactivate": "Deaktivieren",
+ "delete": "Möchtest du die Person '{name}' wirklich entfernen?",
+ "inviteAgain": "Erneut einladen",
+ "leaveCamp": "Lager verlassen",
+ "resendEmail": "E-Mail erneut senden",
+ "resentEmail": "Einladung gesendet",
+ "title": "{user} bearbeiten"
+ },
+ "collaboratorForm": {
+ "overrideAvatar": "Avatar für dieses Lager überschreiben",
+ "roleHint": "Jedes Lager benötigt mindestens eine Person mit Administrationsrechten."
+ },
+ "promptCollaboratorDeactivate": {
+ "deactivate": "Deaktivieren",
+ "leaveCamp": "Lager verlassen",
+ "warningText": "Möchtest du '{name}' wirklich deaktivieren?",
+ "warningTextLeaveCamp": "Möchtest du das Lager '{camp}' wirklich verlassen?"
+ }
+ },
+ "dashboard": {
+ "selectFilter": {
+ "clear": "Zurücksetzen"
+ }
+ },
+ "dialog": {
+ "dialogEntityDelete": {
+ "title": "Wirklich löschen?"
+ }
+ },
+ "form": {
+ "base": {
+ "eColorField": {
+ "parseError": "Bitte gültige Farbe eingeben."
+ },
+ "eColorPicker": {
+ "closePicker": "Dialog schliessen",
+ "openPicker": "Dialog öffnen, um eine Farbe für {label} zu wählen"
+ },
+ "eDatePicker": {
+ "invalidFormat": "Ungültiges Format, bitte gib das Datum im Format DD.MM.YYYY ein",
+ "openPicker": "Dialog öffnen, um ein Datum für {label} zu wählen"
+ },
+ "eTimePicker": {
+ "invalidFormat": "Ungültiges Format, bitte gib die Zeit im Format HH:MM ein",
+ "openPicker": "Dialog öffnen, um eine Zeit für {label} zu wählen"
+ }
+ }
+ },
+ "generic": {
+ "lockButton": {
+ "clickToLock": "Klicken um zu sperren",
+ "clickToUnlock": "Klicken um zu entsperren",
+ "guestsCannotEdit": "Gäste können nicht bearbeiten"
+ }
+ },
+ "layout": {
+ "authContainer": {
+ "photoCredits": "Foto von Markus Rohner / Lotos"
+ }
+ },
+ "material": {
+ "dialogMaterialItemCreate": {
+ "title": "Materialeintrag erstellen"
+ },
+ "dialogMaterialItemEdit": {
+ "title": "Materialeintrag bearbeiten"
+ },
+ "materialCreateItem": {
+ "campSettingsButton": "Einstellungen",
+ "noMaterialListAvailable": "Für dieses Lager sind noch keine Materiallisten erfasst. Du kannst diese unter 'Admin' erfassen."
+ },
+ "materialLists": {
+ "materialsCount": "Keine Einträge | 1 Eintrag | {count} Einträge",
+ "overview": "Gesamtübersicht",
+ "unassigned": "Nicht zugewiesen"
+ },
+ "materialTable": {
+ "addNewItem": "Eintrag hinzufügen",
+ "noItems": "Keine Einträge gefunden",
+ "periodOnly": "Filter: Lagerabschnitt",
+ "reference": "Aktivität / Abschnitt"
+ },
+ "useMaterialViewHelper": {
+ "detail": "Materialliste",
+ "overview": "Gesamtübersicht"
+ }
+ },
+ "navigation": {
+ "userMeta": {
+ "admin": "Admin",
+ "invitations": "Einladungen",
+ "logOut": "Ausloggen",
+ "myCamps": "Meine Lager",
+ "profile": "Profil"
+ }
+ },
+ "personalInvitations": {
+ "dialogPersonalInvitationReject": {
+ "rejectInvitation": "Einladung ablehnen",
+ "warningText": "Möchtest du die Einladung zum Lager \"{campTitle}\" wirklich ablehnen?"
+ },
+ "personalInvitations": {
+ "accept": "Akzeptieren",
+ "noOpenInvitations": "Keine offene Einladungen. Wurde die Einladung an eine andere Adresse als \"{email}\" versendet?",
+ "reject": "Ablehnen"
+ }
+ },
+ "print": {
+ "config": {
+ "activityConfig": {
+ "activity": "Aktivität"
+ },
+ "dialogScheduleEntryFilter": {
+ "filterActive": "Filter: {filtered} von {total} Aktivitäten",
+ "filterActivities": "{total} Aktivitäten filtern",
+ "resultCount": "Resultat: {filtered} von {total} Aktivitäten",
+ "title": "Aktivitäten filtern"
+ },
+ "picassoConfig": {
+ "orientation": "Seitenlayout",
+ "landscape": "Querformat",
+ "periods": "Lagerabschnitt(e)",
+ "portrait": "Hochformat"
+ },
+ "programConfig": {
+ "dayOverview": "Tagesübersicht"
+ }
+ },
+ "documents": {
+ "campPrint": {
+ "filename": {
+ "activitiesOnly": "Aktivitäten {camp}",
+ "picassoOnly": "Grobprogramm {camp}"
+ }
+ }
+ },
+ "localPdfDownloadButton": {
+ "error": "Etwas hat nicht geklappt. Probiere es nochmals, oder lade die Seite neu."
+ },
+ "printClient": {
+ "downloadClientPdfButton": {
+ "label": "PDF herunterladen (Layout #2)"
+ },
+ "downloadClientPdfListItem": {
+ "label": "PDF herunterladen (Layout #2)"
+ },
+ "generatePdfMixin": {
+ "error": "Etwas hat nicht geklappt. Probiere es nochmals, oder lade die Seite neu.",
+ "progress": {
+ "loadingData": "Lade die aktuellsten Daten des Lagers…",
+ "setupTranslationsAndFonts": "Lade Schriftarten und Texte…",
+ "assembleContent": "Stelle {type} zusammen ({content} von {totalContents})…",
+ "layoutDocument": "Berechne Dokument-Layout…",
+ "layoutPage": "Berechne Layout von Seite {page}…",
+ "renderingPdfPage": "Rendere PDF-Seite {page} von {totalPages}…",
+ "downloadingPdf": "Lade die PDF-Datei herunter…",
+ "done": "Fertig",
+ "failed": "Abgebrochen"
+ }
+ },
+ "printPreviewClient": {
+ "previewError": "Etwas hat nicht geklappt. Probiere es nochmals mit anderen Druckeinstellungen, oder lade die Seite neu.",
+ "previewIframeTitle": "Druckvorschau"
+ }
+ },
+ "printConfigurator": {
+ "add": "Inhalt hinzufügen",
+ "config": {
+ "Activity": "Einzelne Aktivität",
+ "ActivityList": "Aktivitätenliste (Kurse)",
+ "Cover": "Titelseite",
+ "Picasso": "Grobprogramm",
+ "Program": "Detailprogramm",
+ "SafetyConsiderations": "Sicherheitsüberlegungen",
+ "Story": "Geschichte",
+ "Toc": "Inhaltsverzeichnis"
+ },
+ "options": "Einstellungen",
+ "pageNumbers": "Seitenzahlen"
+ },
+ "printNuxt": {
+ "downloadNuxtPdfButton": {
+ "label": "PDF herunterladen (Layout #1)"
+ },
+ "downloadNuxtPdfListItem": {
+ "label": "PDF herunterladen (Layout #1)"
+ },
+ "generatePdfMixin": {
+ "error": "Etwas hat nicht geklappt. Probiere es nochmals, oder lade die Seite neu.",
+ "queueFull": "Alle unsere PDF-Drucker sind im Moment beschäftigt. Bitte versuche es später nochmals."
+ },
+ "printPreviewNuxt": {
+ "openPreview": "Vorschau in neuem Fenster öffnen",
+ "previewError": "Etwas hat nicht geklappt. Probiere es nochmals mit anderen Druckeinstellungen, oder lade die Seite neu.",
+ "previewIframeTitle": "Druckvorschau"
+ }
+ }
+ },
+ "program": {
+ "dialogActivityCreate": {
+ "clearClipboard": "Zwischenablage leeren",
+ "clipboard": "Zwischenablage",
+ "clipboardInfoDialog": {
+ "allow": "Jetzt erlauben",
+ "denied": "Du hast den Zugriff auf die Zwischenablage verweigert. Kopierte Aktivitäten können daher nicht eingefügt werden.",
+ "description": "Damit du eine kopierte Aktivität einfügen kannst, musst du eCamp v3 erlauben, deine Zwischenablage zu lesen.",
+ "granted": "Du kannst nun kopierte Aktivitäten einfügen.",
+ "title": "Aktivität kopieren & einfügen"
+ },
+ "copyActivity": "Aktivität kopieren",
+ "copyActivityContent": "Inhalt von Aktivität kopieren",
+ "copyPasteActivity": "Aktivität kopieren & einfügen",
+ "copySourceInfo": "Hier kannst du die URL einer Aktivität einfügen, um dessen Inhalte zu kopieren.",
+ "pasteActivity": "Kopierte Aktivität einfügen"
+ },
+ "formScheduleEntryItem": {
+ "end": "Ende",
+ "start": "Start"
+ },
+ "formScheduleEntryList": {
+ "name": "Geplante Termine"
+ },
+ "periodSwitcher": {
+ "title": "Lagerabschnitt wählen"
+ },
+ "picasso": {
+ "picasso": {
+ "datetime": {
+ "fullDate": "dd, DD. MMMM YYYY",
+ "smallDate": "dd, D.MM. | dd, D. MMM | dd, D. MMM YY"
+ }
+ },
+ "picassoEntry": {
+ "clipboardInfoDialog": {
+ "allow": "Jetzt erlauben",
+ "denied": "Du hast den Zugriff auf die Zwischenablage verweigert. Kopierte Aktivitäten können daher nicht eingefügt werden.",
+ "description": "Damit du eine kopierte Aktivität einfügen kannst, musst du eCamp v3 erlauben, deine Zwischenablage zu lesen.",
+ "granted": "Du kannst nun kopierte Aktivitäten einfügen.",
+ "title": "Aktivität kopieren & einfügen"
+ },
+ "location": "Ort:",
+ "responsible": "Verantwortlich: "
+ }
+ },
+ "scheduleEntryFilters": {
+ "category": "Kategorie",
+ "clearFilters": "Filter entfernen",
+ "day": "Datum",
+ "dayLabel": "Tag {dayNumber} ({date})",
+ "onlyMyActivities": "Nur meine Aktivitäten",
+ "period": "Lagerabschnitt",
+ "progressLabel": "Status",
+ "progressLabelNone": "Kein Status",
+ "responsible": "Verantwortlich",
+ "responsibleNone": "Keine Verantwortlichen"
+ }
+ },
+ "prompt": {
+ "promptEntityDelete": {
+ "title": "Wirklich löschen?"
+ }
+ },
+ "story": {
+ "storyDay": {
+ "noStory": "Die Blöcke an diesem Tag enthalten keinen roten Faden."
+ }
+ },
+ "toast": {
+ "toasts": {
+ "multiLineToast": {
+ "generalError": "Ein Fehler ist aufgetreten."
+ }
+ }
+ },
+ "user": {
+ "dialogChangeMail": {
+ "error": "Leider ist ein Fehler aufgetreten.",
+ "message": "Zur Verifikation schicken wir dir eine Mail.",
+ "success": "Bevor du die neue E-Mail-Adresse verwenden kannst, muss du diese verifizieren. Prüfe jetzt deine Mailbox.",
+ "title": "E-Mail-Adresse ändern"
+ },
+ "dialogChangeMailRunning": {
+ "error": "Leider ist ein Fehler aufgetreten.",
+ "message": "E-Mail-Adresse wird geändert...",
+ "success": "E-Mail-Adresse erfolgreich geändert.",
+ "title": "E-Mail-Adresse wird geändert"
+ }
+ }
+ },
+ "global": {
+ "button": {
+ "add": "Hinzufügen",
+ "back": "Zurück",
+ "cancel": "Abbrechen",
+ "close": "Schliessen",
+ "content": "Inhalt",
+ "continue": "Weiter",
+ "copy": "Kopieren",
+ "create": "Erstellen",
+ "delete": "Löschen",
+ "discard": "Verwerfen",
+ "download": "Herunterladen",
+ "edit": "Bearbeiten",
+ "editable": "Bearbeitbar",
+ "filter": "Filtern",
+ "lock": "Sperren",
+ "login": "Anmelden",
+ "logout": "Ausloggen",
+ "move": "Verschieben",
+ "ok": "OK",
+ "open": "Öffnen",
+ "remove": "Entfernen",
+ "rename": "Umbenennen",
+ "reset": "Zurücksetzen",
+ "save": "Speichern",
+ "saving": "Speichert",
+ "search": "Suchen",
+ "share": "Freigeben",
+ "submit": "Abschicken",
+ "tryagain": "Erneut versuchen",
+ "unlock": "Entsperren",
+ "update": "Aktualisieren"
+ },
+ "changeLanguage": "Sprache ändern",
+ "collaborationAbilities": {
+ "guest": "Nur Leserecht",
+ "manager": "Vollständige Lageradministration, sowie Lese- & Schreibrecht",
+ "member": "Lese- & Schreibrecht"
+ },
+ "datetime": {
+ "vuetifyTimePickerFormat": "24hr"
+ },
+ "info": {
+ "offline": {
+ "description": "Daten speichern/laden nicht möglich.",
+ "title": "Du bist offline:"
+ }
+ },
+ "language": "Deutsch",
+ "loading": "Laden …",
+ "navigation": {
+ "admin": {
+ "title": "Admin"
+ },
+ "help": "Hilfe",
+ "news": "News"
+ },
+ "serverError": {
+ "409": "Uuuups... Diese Aktion hat zu einem Fehler auf dem Server geführt.",
+ "short": "Serverfehler"
+ },
+ "toast": {
+ "copied": "{source} kopiert"
+ },
+ "validation": {
+ "greaterThan": "{_field_} muss grösser sein als {min}",
+ "greaterThanOrEqual_date": "{_field_} darf nicht vor {min} liegen",
+ "greaterThan_time": "{_field_} muss später als {min} sein",
+ "lessThanOrEqual_date": "{_field_} darf nicht nach {max} liegen",
+ "oneEmojiOrTwoCharacters": "{_field_} darf nur 1 Emoji oder 2 Buchstaben/Zahlen haben"
+ },
+ "warning": {
+ "delete": "Möchtest du das wirklich löschen? | Möchtest du \"{entity}\" wirklich löschen?"
+ }
+ },
+ "views": {
+ "auth": {
+ "activate": {
+ "error": "Aktivierung fehlgeschlagen",
+ "success": "Account erfolgreich aktiviert",
+ "title": "Dein Account wird aktiviert"
+ },
+ "login": {
+ "acceptTermsOfServiceOnOAuthLogin": "Beim Login via einen dieser Services akzeptierst du die {termsOfServiceLink}.",
+ "accountless": "Hast du noch keinen Account?",
+ "email": "E-Mail",
+ "infoText": {
+ "dev": "Dies ist die Entwickler-Version von eCamp v3.{br}WICHTIG: Nur für Entwickler-Gebrauch geeignet. Alle Daten sind öffentlich und werden periodisch gelöscht!{br}Login: test(at)example.com / test"
+ },
+ "loginCallback": {
+ "loginInProgress": "Du wirst eingeloggt"
+ },
+ "notActivated": "Nicht aktiviert?",
+ "or": "oder mit",
+ "password": "Passwort",
+ "passwordForgotten": "Passwort vergessen?",
+ "provider": {
+ "cevidb": "CeviDB",
+ "ecamp": "Anmelden mit eCamp",
+ "google": "Google",
+ "jubladb": "JublaDB",
+ "midata": "MiData"
+ },
+ "registernow": "Jetzt registrieren",
+ "resendActivation": "Aktivierungsmail erneut senden",
+ "resetPassword": "Passwort zurücksetzen",
+ "termsOfServiceLink": "Nutzungsbedingungen"
+ },
+ "register": {
+ "acceptTermsOfService": "Nutzungsbedingungen akzeptieren",
+ "alreadyHaveAnAccount": "Hast du bereits ein Konto?",
+ "passwordConfirmation": "Passwort erneut eingeben",
+ "register": "Registrieren",
+ "requiredField": "Pflichtfelder",
+ "title": "Ein Konto erstellen"
+ },
+ "registerDone": {
+ "message": "Wir haben dir eine Mail geschickt. Bitte schliesse die Registrierung ab, in dem du den Aktivierungs-Link im Mail aufrufst.",
+ "success": "Erfolgreich registriert",
+ "title": "Konto erstellen"
+ },
+ "resendActivation": {
+ "errorMessage": "Erneutes Senden der Aktivierung fehlgeschlagen.",
+ "send": "Abschicken",
+ "successMessage": "Mail mit Link für 'Aktivierung' wird versendet.",
+ "title": "Aktivierungsmail erneut senden"
+ },
+ "resetPassword": {
+ "errorMessage": "Passwort konnte nicht geändert werden.",
+ "invalidRequest": "Ungültiger Link",
+ "password": "Neues Passwort",
+ "passwordConfirmation": "Neues Passwort wiederholen",
+ "send": "Passwort setzen",
+ "successMessage": "Neues Passwort erfolgreich gesetzt.",
+ "title": "Neues Passwort setzen"
+ },
+ "resetPasswordRequest": {
+ "errorMessage": "Passwort zurücksetzen nicht möglich.",
+ "send": "Abschicken",
+ "successMessage": "Mail mit Link für 'Passwort zurücksetzen' wird versendet.",
+ "title": "Passwort zurücksetzen"
+ }
+ },
+ "camp": {
+ "activity": {
+ "printPreview": "Druckvorschau öffnen",
+ "sideBarProgram": {
+ "title": "Tagesübersicht"
+ }
+ },
+ "admin": {
+ "activity": {
+ "title": "Aktivitätseinstellungen"
+ },
+ "adminMaterialLists": {
+ "title": "Materiallisten"
+ },
+ "collaborators": {
+ "email": "E-Mail-Adresse",
+ "inactiveCollaborators": "Inaktiv",
+ "members": "Mitarbeitende",
+ "openInvitations": "Offene Einladungen",
+ "title": "Team"
+ },
+ "info": {
+ "title": "Lagerinfos"
+ },
+ "print": {
+ "title": "Lager drucken"
+ },
+ "sideBarAdmin": {
+ "itemActivity": "Aktivitäten",
+ "itemActivitySubtitle": "Kategorie, Vorlagen & Status",
+ "itemCollaborators": "Team",
+ "itemInfos": "Lagerinfos",
+ "itemMaterialLists": "Materiallisten",
+ "itemPrint": "PDF / Drucken"
+ },
+ "title": "Admin"
+ },
+ "campProgram": {
+ "reminderLockedCreate": "Ziehen zum Erstellen ist nur im entsperrten Modus möglich.",
+ "reminderLockedMove": "Ziehen zum Verschieben ist nur im entsperrten Modus möglich.",
+ "title": "Grobprogramm"
+ },
+ "category": {
+ "category": {
+ "clipboardInfoDialog": {
+ "allow": "Jetzt erlauben",
+ "denied": "Du hast den Zugriff auf die Zwischenablage verweigert. Kopierte Kategorien können daher nicht eingefügt werden.",
+ "description": "Damit du eine kopierte Kategorie einfügen kannst, musst du eCamp v3 erlauben, deine Zwischenablage zu lesen.",
+ "granted": "Du kannst nun kopierte Kategorien einfügen.",
+ "title": "Kategorie kopieren & einfügen"
+ },
+ "copyCategory": "Kategorie kopieren",
+ "deleteCategory": "Kategorie löschen",
+ "properties": "Eigenschaften",
+ "template": "Vorlage für neue Blöcke"
+ },
+ "sideBarCategory": {
+ "title": "Kategorien"
+ }
+ },
+ "checklistOverview": {
+ "checklistLists": {
+ "title": "Checklisten-Übersicht"
+ }
+ },
+ "dashboard": {
+ "activities": "Aktivitäten",
+ "columns": {
+ "category": "Kategorie",
+ "number": "Nummer",
+ "responsible": "Verantwortlich",
+ "time": "Zeit",
+ "title": "Titel"
+ },
+ "noEntries": "Keine Aktivitäten gefunden. Versuche die Filter zu entfernen oder lade die Seite neu.",
+ "today": "Heute",
+ "welcome": "Willkommen in deinem neuen Lager. Es sind noch keine Aktivitäten erfasst. "
+ },
+ "invitation": {
+ "acceptCurrentAuth": "Einladung mit aktuellem Account akzeptieren",
+ "backToHome": "Zurück zur Startseite",
+ "error": "Ein unerwarteter Fehler ist aufgetreten",
+ "notFound": "Die Einladung wurde bereits angenommen oder gelöscht",
+ "openCamp": "Lager öffnen",
+ "register": "Registrieren",
+ "reject": "Einladung ablehnen",
+ "successfullyRejected": "Die Einladung wurde erfolgreich abgelehnt",
+ "title": "Einladung",
+ "useOtherAuth": "Ausloggen und einen anderen Account benutzen",
+ "userAlreadyInCamp": "Du arbeitest schon an diesem Lager mit"
+ },
+ "material": {
+ "materialLists": {
+ "title": "Materiallisten"
+ },
+ "materialOverview": {
+ "createNewList": "Materialliste erstellen",
+ "download": "Übersicht herunterladen",
+ "title": "Gesamtübersicht"
+ },
+ "materialUnassigned": {
+ "excelTitle": "nicht zugewiesen",
+ "title": "Nicht zugewiesenes Material"
+ },
+ "sideBarMaterialLists": {
+ "title": "Materiallisten"
+ }
+ },
+ "navigation": {
+ "desktop": {
+ "navTopbar": {
+ "campIsLoading": "Lager wird geladen",
+ "material": "Material",
+ "program": "Programm",
+ "story": "Geschichte"
+ }
+ },
+ "mobile": {
+ "navBottombar": {
+ "material": "Material",
+ "more": "Mehr",
+ "program": "Programm",
+ "story": "Geschichte"
+ },
+ "navSidebar": {
+ "itemActivity": "Aktivitäten",
+ "itemActivitySubtitle": "Kategorie, Vorlagen & Status",
+ "itemCamps": "Meine Lager",
+ "itemChecklists": "Checklisten-Übersicht",
+ "itemClose": "Menü schliessen",
+ "itemCollaborators": "Team",
+ "itemInfos": "Lagerinfos",
+ "itemMaterialLists": "Materiallisten",
+ "itemPrinting": "PDF / Drucken"
+ }
+ }
+ },
+ "story": {
+ "title": "Geschichte"
+ }
+ },
+ "campCreate": {
+ "title": "Lager erstellen"
+ },
+ "camps": {
+ "create": "Lager erstellen",
+ "pastCamps": "Alte Lager",
+ "prototypeCamps": "Vorlagen",
+ "title": "Meine Lager"
+ },
+ "invitations": {
+ "personalInvitations": "Einladungen"
+ },
+ "pageNotFound": {
+ "backToHome": "Zurück zur Startseite",
+ "detail": "Hoppla. Du bist vom Weg abgekommen…{br}Dieser Link funktioniert leider nicht."
+ },
+ "profile": {
+ "changeEmail": "Ändern",
+ "profile": "Profil"
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend-old/src/locales/en-CH-scout.json b/frontend-old/src/locales/en-CH-scout.json
new file mode 100644
index 0000000000..3c097e08c2
--- /dev/null
+++ b/frontend-old/src/locales/en-CH-scout.json
@@ -0,0 +1,12 @@
+{
+ "entity": {
+ "user": {
+ "fields": {
+ "nickname": "Scout Name"
+ }
+ }
+ },
+ "global": {
+ "language": "English (Scout)"
+ }
+}
\ No newline at end of file
diff --git a/frontend-old/src/locales/en.json b/frontend-old/src/locales/en.json
new file mode 100644
index 0000000000..57feb9c517
--- /dev/null
+++ b/frontend-old/src/locales/en.json
@@ -0,0 +1,868 @@
+{
+ "components": {
+ "activity": {
+ "content": {
+ "checklist": {
+ "title": "Edit checklist"
+ },
+ "columnLayout": {
+ "columnOperations": {
+ "addColumn": "Add a column",
+ "removeColumn": "Remove empty column"
+ }
+ },
+ "lAThematicArea": {
+ "placeholder": "Choose thematic area"
+ },
+ "storyboard": {
+ "controls": "Controls",
+ "reorder": "Reorder",
+ "storyboardDialogRemoveSection": {
+ "deleteWarning": "Do you really want to remove this section?",
+ "title": "Really delete?"
+ }
+ }
+ },
+ "dialog": {
+ "dialogActivityEdit": {
+ "title": "Edit activity"
+ },
+ "formScheduleEntryList": {
+ "name": "Scheduled"
+ }
+ },
+ "menuCardlessContentNode": {
+ "deletingDisabled": "Must be empty to delete"
+ },
+ "scheduleEntry": {
+ "backToContents": "Back to editing content",
+ "changeLayout": "Change layout",
+ "clipboardInfoDialog": {
+ "allow": "Allow now",
+ "denied": "You have denied access to your clipboard. Therefore, you cannot paste copied activities.",
+ "description": "In order to paste a copied activity, you must allow eCamp v3 to read your clipboard.",
+ "granted": "You can now paste copied activities.",
+ "title": "Copy & paste activity"
+ },
+ "copyScheduleEntry": "Copy activity",
+ "deleteWarning": "Do you really want to delete this activity? All content of this activity will be removed."
+ },
+ "togglePaperSize": {
+ "switchToFullSize": "Switch to full size",
+ "switchToPaperSize": "Switch to paper size"
+ }
+ },
+ "camp": {
+ "campInvitations": {
+ "title": "Invite"
+ },
+ "campListItem": {
+ "public": "public"
+ },
+ "campMembers": {
+ "title": "Members"
+ },
+ "footerSharedCamp": {
+ "outsider": "Not participating in this camp:",
+ "outsiderDescription": "You cannot change any data, but you may look at and copy the programme.",
+ "shared": "Camp is shared publicly:",
+ "sharedDescription": "External persons can see and copy all data in this camp."
+ }
+ },
+ "campAdmin": {
+ "campActivityProgressLabels": {
+ "create": "Create a state",
+ "deleteError": "State can't be deleted. It is still used.",
+ "deleteWarning": "Do you really want to delete this state?",
+ "exit": "Exit reordering",
+ "moveDown": "Move down",
+ "moveUp": "Move up",
+ "reorder": "Reorder",
+ "title": "Activity state"
+ },
+ "campAddress": {
+ "title": "Address"
+ },
+ "campCategories": {
+ "create": "Create activity category",
+ "pasteCategory": "Paste category",
+ "title": "Activity categories"
+ },
+ "campConditionalFields": {
+ "course": {
+ "title": "Courses"
+ },
+ "title": "Settings for Y+S",
+ "ysCamp": {
+ "title": "Camps"
+ }
+ },
+ "campDangerZone": {
+ "deleteCamp": {
+ "description": "Once you delete a camp, there is no going back. Please be certain.",
+ "explanation": "This action cannot be undone. This will permanently delete the \"{campTitle}\" camp, activities, material lists and remove all collaborator associations.",
+ "label": "Please type \"{campTitle}\" to confirm.",
+ "title": "Delete Camp"
+ },
+ "title": "Danger Zone"
+ },
+ "campMaterialLists": {
+ "createMaterialList": "Create material list",
+ "title": "Material lists"
+ },
+ "campPeriods": {
+ "createPeriod": "Create period",
+ "title": "Period | Periods"
+ },
+ "campPeriodsListItem": {
+ "changePeriodDescription": "Change description",
+ "deleteWarning": "Do you really want to delete this period?",
+ "lastPeriodNotDeletable": "Last period is not deletable.",
+ "movePeriod": "Move period",
+ "periodChangeEnd": "Add/remove days at end",
+ "periodChangeStart": "Add/remove days at start"
+ },
+ "campSettings": {
+ "title": "Settings"
+ },
+ "campSharingSettings": {
+ "activate": "Activate sharing",
+ "deactivate": "Deactivate sharing",
+ "implications": "If you activate sharing, external persons can view and copy the programme, team, equipment lists, responsibilities and everything else related to this camp. Only persons in the team can change data.",
+ "notShared": {
+ "description": ": If you enable sharing, external persons can view and copy the programme, the team, the material lists and responsibilities, and everything else in this camp. Only persons in the team can change data.",
+ "title": "Sharing is not active"
+ },
+ "publicCampUrl": "Public camp link",
+ "shared": {
+ "description": ": External persons can see and copy the programme, team, material lists, responsibilities and all other data in this camp. Only people in the team can change data.",
+ "title": "Camp is shared publicly"
+ },
+ "sharedSince": "Since {sharedSince} by {sharedBy}",
+ "title": "Sharing settings"
+ },
+ "createCampPeriods": {
+ "add": "Add a period"
+ },
+ "dialogActivityProgressLabelCreate": {
+ "title": "Create activity state"
+ },
+ "dialogActivityProgressLabelEdit": {
+ "title": "Edit activity state"
+ },
+ "dialogCategoryCreate": {
+ "clearClipboard": "Clear clipboard",
+ "clipboard": "Clipboard",
+ "clipboardInfoDialog": {
+ "allow": "Allow now",
+ "denied": "You have denied access to your clipboard. Therefore, you cannot paste copied categories.",
+ "description": "In order to paste a copied category, you must allow eCamp v3 to read your clipboard.",
+ "granted": "You can now paste copied categories.",
+ "title": "Copy & paste category"
+ },
+ "copyCategoryOrActivity": "Copy category or activity",
+ "copyContent": "Copy content",
+ "copyPasteCategory": "Copy & paste category",
+ "copySourceInfo": "Here you can paste the URL of a category or an activity to copy its contents.",
+ "pasteCategory": "paste category or activity",
+ "title": "Create activity category"
+ },
+ "dialogMaterialListCreate": {
+ "title": "Create material list"
+ },
+ "dialogMaterialListEdit": {
+ "deleteError": "Could not delete the material list. Check if the list is empty before deleting it.",
+ "title": "Edit material list"
+ },
+ "dialogPeriodCreate": {
+ "title": "Create period"
+ },
+ "dialogPeriodDateEdit": {
+ "movePeriod": "Move period",
+ "periodChangeEnd": "Add / remove days at end of this period",
+ "periodChangeStart": "Add / remove days at start of this period",
+ "periodDuration": "Number of days"
+ },
+ "dialogPeriodEdit": {
+ "moveScheduleEntries": "Move schedule entries"
+ },
+ "errorExistingActivitiesList": {
+ "description": "Cannot be deleted. Still used by the following activities:"
+ }
+ },
+ "campCreate": {
+ "campCreate": {
+ "steps": {
+ "configurate": "Configure",
+ "infos": "Info",
+ "template": "Template"
+ }
+ },
+ "campCreateStep1": {
+ "submitTooltip": "Please fill out all required fields.",
+ "titlePlaceholder": "Summer camp 2024"
+ },
+ "campCreateStep2": {
+ "category": "Activity categories",
+ "checklistItemCount": "No entries | One entry | {count} entries",
+ "checklists": "Checklists",
+ "clipboardInfoDialog": {
+ "allow": "Allow now",
+ "denied": "You have denied access to your clipboard. Therefore, you have to enter the link to the template camp manually.",
+ "description": "In order to paste the settings of a copied camp, you must allow eCamp v3 to read your clipboard.",
+ "granted": "You can now paste copied camp settings.",
+ "title": "Copy & paste camp settings"
+ },
+ "create": "Create the Camp",
+ "materialLists": "Materiallisten",
+ "noContent": "No Content",
+ "noPrototype": "No template",
+ "noPrototypeAlert": {
+ "description": "You must make all settings manually. There are no activity templates & layouts available. This is only suitable for experienced users.",
+ "title": "Attention: You chose \"No template\"."
+ },
+ "otherPrototype": "Copy the settings from another camp…",
+ "pasteCamp": "Paste copied camp settings",
+ "preview": "Preview of camp template",
+ "progressLabels": "Activity states",
+ "prototypeCampUrl": "Link of desired template camp",
+ "prototypeHint": "Copy categories, activity templates, states, material lists and checklists from a template.",
+ "prototypeHintEmpty": "Don't copy any categories, activity templates, states, material lists or checklists.",
+ "prototypeHintOther": "Copy categories, activity templates, states, unpersonal material lists and checklists from this other camp. No activities are copied.",
+ "prototypeHintSelected": "Copy categories, activity templates, states and material lists from this template.",
+ "submitTooltipPrototype": "You need to choose if and which template you want."
+ }
+ },
+ "category": {
+ "categoryTemplate": {
+ "contents": "Contents",
+ "createLayoutHelp": "Here you can define the template for new {categoryShort} activities.{br}The content & layout of already created {categoryShort} activities will not be adjusted.",
+ "layout": "Layout",
+ "noTemplate": "No template"
+ }
+ },
+ "checklist": {
+ "checklistCreate": {
+ "title": "Create checklist"
+ },
+ "checklistDetail": {
+ "deleteError": "Checklist couldn't be deleted. Ensure checklist is not used within activities before trying to delete it.",
+ "deleteWarning": "Do you really want to remove this checklist? All content of the checklist will be deleted."
+ },
+ "checklistItemCreate": {
+ "add": "Create checklist entry",
+ "title": "Add checklist entry"
+ },
+ "checklistItemEdit": {
+ "delete": "Do you really want to remove this item?",
+ "title": "Edit checklist entry"
+ },
+ "sortableChecklist": {
+ "add": "Add to \"{parent}\""
+ }
+ },
+ "collaborator": {
+ "collaboratorCreate": {
+ "invite": "Send invitation",
+ "inviteCta": "Invite to team",
+ "title": "Invite people"
+ },
+ "collaboratorEdit": {
+ "cannotRemoveLastManager": "You cannot remove the last person with administrative rights.",
+ "deactivate": "Deactivate",
+ "delete": "Do you really want to remove collaborator '{name}'?",
+ "inviteAgain": "Invite again",
+ "leaveCamp": "Leave camp",
+ "resendEmail": "Resend invite",
+ "resentEmail": "Invitation sent",
+ "title": "Edit {user}"
+ },
+ "collaboratorForm": {
+ "overrideAvatar": "Overwrite avatar for this camp",
+ "roleHint": "Each camp needs at least one person with administrative rights."
+ },
+ "promptCollaboratorDeactivate": {
+ "deactivate": "Deactivate",
+ "leaveCamp": "Leave camp",
+ "warningText": "Do you want to deactivate '{name}'?",
+ "warningTextLeaveCamp": "Do you want to leave the camp '{camp}'?"
+ }
+ },
+ "dashboard": {
+ "selectFilter": {
+ "clear": "Clear"
+ }
+ },
+ "dialog": {
+ "dialogEntityDelete": {
+ "title": "Really delete?"
+ }
+ },
+ "form": {
+ "base": {
+ "eColorField": {
+ "parseError": "Please enter a valid color."
+ },
+ "eColorPicker": {
+ "closePicker": "Close dialog",
+ "openPicker": "Open dialog to select a color for {label}"
+ },
+ "eDatePicker": {
+ "invalidFormat": "Invalid format, please enter the date in the format MM/DD/YYYY",
+ "openPicker": "Open dialog to select a date for {label}"
+ },
+ "eTimePicker": {
+ "invalidFormat": "Invalid format, please enter the time in the format HH:MM AM/PM",
+ "openPicker": "Open dialog to select a time for {label}"
+ }
+ }
+ },
+ "generic": {
+ "lockButton": {
+ "clickToLock": "Click to lock",
+ "clickToUnlock": "Click to unlock",
+ "guestsCannotEdit": "Guests have no permission to edit"
+ }
+ },
+ "layout": {
+ "authContainer": {
+ "photoCredits": "Photo by Markus Rohner / Lotos"
+ }
+ },
+ "material": {
+ "dialogMaterialItemCreate": {
+ "title": "Create material item"
+ },
+ "dialogMaterialItemEdit": {
+ "title": "Edit material item"
+ },
+ "materialCreateItem": {
+ "campSettingsButton": "Settings",
+ "noMaterialListAvailable": "There are no material lists for this camp yet. You can create them in 'Admin'."
+ },
+ "materialLists": {
+ "materialsCount": "No entries | 1 entry | {count} entries",
+ "overview": "General overview",
+ "unassigned": "Not assigned"
+ },
+ "materialTable": {
+ "addNewItem": "Add new item",
+ "noItems": "No material items found",
+ "periodOnly": "Filter: Period",
+ "reference": "Activity / Period"
+ },
+ "useMaterialViewHelper": {
+ "detail": "Material list",
+ "overview": "General overview"
+ }
+ },
+ "navigation": {
+ "userMeta": {
+ "admin": "Admin",
+ "invitations": "Invitations",
+ "logOut": "Log out",
+ "myCamps": "My camps",
+ "profile": "Profile"
+ }
+ },
+ "personalInvitations": {
+ "dialogPersonalInvitationReject": {
+ "rejectInvitation": "Reject invitation",
+ "warningText": "Are you sure you want to reject the initation to the camp \"{campTitle}\"?"
+ },
+ "personalInvitations": {
+ "accept": "Accept",
+ "noOpenInvitations": "No open invitations. Was the invitation sent to an address other than \"{email}\"?",
+ "reject": "Reject"
+ }
+ },
+ "print": {
+ "config": {
+ "activityConfig": {
+ "activity": "Activity"
+ },
+ "dialogScheduleEntryFilter": {
+ "filterActive": "Filter: {filtered} of {total} activities",
+ "filterActivities": "Filter {total} activities",
+ "resultCount": "Result: {filtered} of {total} activities",
+ "title": "Filter activities"
+ },
+ "picassoConfig": {
+ "orientation": "Page layout",
+ "landscape": "Landscape",
+ "periods": "Period(s)",
+ "portrait": "Portrait"
+ },
+ "programConfig": {
+ "dayOverview": "print day overview"
+ }
+ },
+ "documents": {
+ "campPrint": {
+ "filename": {
+ "activitiesOnly": "Activities {camp}",
+ "picassoOnly": "Picasso {camp}"
+ }
+ }
+ },
+ "localPdfDownloadButton": {
+ "error": "Something didn't work. Try again, or reload the page."
+ },
+ "printClient": {
+ "downloadClientPdfButton": {
+ "label": "Download PDF (layout #2)"
+ },
+ "downloadClientPdfListItem": {
+ "label": "Download PDF (layout #2)"
+ },
+ "generatePdfMixin": {
+ "error": "Something did not work. Please try again, or try reloading the page.",
+ "progress": {
+ "loadingData": "Loading the latest data of the camp…",
+ "setupTranslationsAndFonts": "Loading fonts and static texts…",
+ "assembleContent": "Assembling {type} ({content} of {totalContents})…",
+ "layoutDocument": "Calculating document layout…",
+ "layoutPage": "Calculating layout of page {page}…",
+ "renderingPdfPage": "Rendering pdf page {page} of {totalPages}…",
+ "downloadingPdf": "Downloading pdf file…",
+ "done": "Done",
+ "failed": "Failed"
+ }
+ },
+ "printPreviewClient": {
+ "previewError": "Something did not work. Please try again with different printing settings, or reload the page.",
+ "previewIframeTitle": "Print preview"
+ }
+ },
+ "printConfigurator": {
+ "add": "Add content",
+ "config": {
+ "Activity": "Single activity",
+ "ActivityList": "Activity list (courses)",
+ "Cover": "Cover page",
+ "Picasso": "Picasso",
+ "Program": "Program",
+ "SafetyConsiderations": "Safety considerations",
+ "Story": "Story",
+ "Toc": "Table of contents"
+ },
+ "options": "Einstellungen",
+ "pageNumbers": "Seitenzahlen"
+ },
+ "printNuxt": {
+ "downloadNuxtPdfButton": {
+ "label": "Download PDF (layout #1)"
+ },
+ "downloadNuxtPdfListItem": {
+ "label": "Download PDF (layout #1)"
+ },
+ "generatePdfMixin": {
+ "error": "Something did not work. Please try again, or try reloading the page.",
+ "queueFull": "All our PDF printers are busy at the moment. Please try again later."
+ },
+ "printPreviewNuxt": {
+ "openPreview": "Open preview in new window",
+ "previewError": "Something did not work. Please try again with different printing settings, or reload the page.",
+ "previewIframeTitle": "Print preview"
+ }
+ }
+ },
+ "program": {
+ "dialogActivityCreate": {
+ "clearClipboard": "Clear clipboard",
+ "clipboard": "Clipboard",
+ "clipboardInfoDialog": {
+ "allow": "Allow now",
+ "denied": "You have denied access to your clipboard. Therefore, you cannot paste copied activities.",
+ "description": "In order to paste a copied activity, you must allow eCamp v3 to read your clipboard.",
+ "granted": "You can now paste copied activities.",
+ "title": "Copy & paste activity"
+ },
+ "copyActivity": "Copy activity",
+ "copyActivityContent": "Copy content from activity",
+ "copyPasteActivity": "Copy & paste activity",
+ "copySourceInfo": "Here you can paste the URL of an activity to copy its contents.",
+ "pasteActivity": "paste activity"
+ },
+ "formScheduleEntryItem": {
+ "end": "End",
+ "start": "Start"
+ },
+ "formScheduleEntryList": {
+ "name": "Scheduled"
+ },
+ "periodSwitcher": {
+ "title": "Choose period"
+ },
+ "picasso": {
+ "picasso": {
+ "datetime": {
+ "fullDate": "ddd, DD. MMMM YYYY",
+ "smallDate": "dd, D.MM. | dd, D. MMM | ddd, D. MMM YY"
+ }
+ },
+ "picassoEntry": {
+ "clipboardInfoDialog": {
+ "allow": "Allow now",
+ "denied": "You have denied access to your clipboard. Therefore, you cannot paste copied activities.",
+ "description": "In order to paste a copied activity, you must allow eCamp v3 to read your clipboard.",
+ "granted": "You can now paste copied activities.",
+ "title": "Copy & paste activity"
+ },
+ "location": "Location:",
+ "responsible": "Responsible: "
+ }
+ },
+ "scheduleEntryFilters": {
+ "category": "Category",
+ "clearFilters": "Clear filters",
+ "day": "Date",
+ "dayLabel": "Day {dayNumber} ({date})",
+ "onlyMyActivities": "Only my activities",
+ "period": "Period",
+ "progressLabel": "State",
+ "progressLabelNone": "No state",
+ "responsible": "Responsible",
+ "responsibleNone": "No responsibles"
+ }
+ },
+ "prompt": {
+ "promptEntityDelete": {
+ "title": "Really delete?"
+ }
+ },
+ "story": {
+ "storyDay": {
+ "noStory": "The activities on this day do not contain any story content."
+ }
+ },
+ "toast": {
+ "toasts": {
+ "multiLineToast": {
+ "generalError": "An error has occurred."
+ }
+ }
+ },
+ "user": {
+ "dialogChangeMail": {
+ "error": "An error has occurred.",
+ "message": "You will receive an email for verification.",
+ "success": "Before you can use your new email address, you have to verify it. Check your mailbox now.",
+ "title": "Change email address"
+ },
+ "dialogChangeMailRunning": {
+ "error": "An error has occurred.",
+ "message": "Changing email address ...",
+ "success": "Email address successfully changed.",
+ "title": "Changing email address"
+ }
+ }
+ },
+ "global": {
+ "button": {
+ "add": "Add",
+ "back": "Back",
+ "cancel": "Cancel",
+ "close": "Close",
+ "content": "Content",
+ "continue": "Continue",
+ "copy": "Copy",
+ "create": "Create",
+ "delete": "Delete",
+ "discard": "Discard",
+ "download": "Download",
+ "edit": "Edit",
+ "editable": "Editable",
+ "filter": "Filter",
+ "lock": "Lock",
+ "login": "Log in",
+ "logout": "Logout",
+ "move": "Move",
+ "ok": "OK",
+ "open": "Open",
+ "remove": "Remove",
+ "rename": "Rename",
+ "reset": "Reset",
+ "save": "Save",
+ "saving": "Saving",
+ "search": "Search",
+ "share": "Share",
+ "submit": "Submit",
+ "tryagain": "Try again",
+ "unlock": "Unlock",
+ "update": "Update"
+ },
+ "changeLanguage": "Change language",
+ "collaborationAbilities": {
+ "guest": "Read-only",
+ "manager": "Read, write & camp management",
+ "member": "Read & write"
+ },
+ "datetime": {
+ "vuetifyTimePickerFormat": "ampm"
+ },
+ "info": {
+ "offline": {
+ "description": "Data saving/loading not possible.",
+ "title": "Your are offline:"
+ }
+ },
+ "language": "English",
+ "loading": "Loading …",
+ "navigation": {
+ "admin": {
+ "title": "Admin"
+ },
+ "help": "Help",
+ "news": "News"
+ },
+ "serverError": {
+ "409": "Uuuups... This action caused an server-side error.",
+ "short": "Server error"
+ },
+ "toast": {
+ "copied": "{source} copied"
+ },
+ "validation": {
+ "greaterThan": "{_field_} must be greater than {min}",
+ "greaterThanOrEqual_date": "{_field_} may not be earlier than {min}",
+ "greaterThan_time": "{_field_} must be later than {min}",
+ "lessThanOrEqual_date": "{_field_} may not be later than {max}",
+ "oneEmojiOrTwoCharacters": "{_field_} may only have 1 emoji or 2 letters/numbers"
+ },
+ "warning": {
+ "delete": "Do you really want to delete this? | Do you really want to delete \"{entity}\"?"
+ }
+ },
+ "views": {
+ "auth": {
+ "activate": {
+ "error": "Activation failed",
+ "success": "Account successfully activated",
+ "title": "Your account will be activated"
+ },
+ "login": {
+ "acceptTermsOfServiceOnOAuthLogin": "By logging in via one of these services, you accept the {termsOfServiceLink}.",
+ "accountless": "Don't have an account yet?",
+ "email": "Email",
+ "infoText": {
+ "dev": "This is the development version of eCamp v3.{br}IMPORTANT: This is meant for development usage only. All data is public and will be deleted periodically!{br}Login: test@example.com / test"
+ },
+ "loginCallback": {
+ "loginInProgress": "You will be logged in"
+ },
+ "notActivated": "Not activated?",
+ "or": "or with",
+ "password": "Password",
+ "passwordForgotten": "Forgotten your password?",
+ "provider": {
+ "cevidb": "CeviDB",
+ "ecamp": "Login with eCamp",
+ "google": "Google",
+ "jubladb": "JublaDB",
+ "midata": "MiData"
+ },
+ "registernow": "Register now",
+ "resendActivation": "Send activation email again",
+ "resetPassword": "Reset password",
+ "termsOfServiceLink": "terms of service"
+ },
+ "register": {
+ "acceptTermsOfService": "Accept the terms of service",
+ "alreadyHaveAnAccount": "Already have an account?",
+ "passwordConfirmation": "Enter password again",
+ "register": "Register",
+ "requiredField": "required field",
+ "title": "Create an account"
+ },
+ "registerDone": {
+ "message": "We sent you an email. Please complete your registration by clicking the activation-link in the email",
+ "success": "Successfully registered",
+ "title": "Create account"
+ },
+ "resendActivation": {
+ "errorMessage": "Sending the activation email again failed.",
+ "send": "Send",
+ "successMessage": "Mail with link to activate your account will be delivered.",
+ "title": "Send activation email again"
+ },
+ "resetPassword": {
+ "errorMessage": "Reset password failed.",
+ "invalidRequest": "Invalid link",
+ "password": "New password",
+ "passwordConfirmation": "New password again",
+ "send": "Set password",
+ "successMessage": "Successfully set new password.",
+ "title": "Reset password"
+ },
+ "resetPasswordRequest": {
+ "errorMessage": "Reset password failed.",
+ "send": "Send",
+ "successMessage": "Mail with Link to reset your password will be delivered.",
+ "title": "Reset password"
+ }
+ },
+ "camp": {
+ "activity": {
+ "printPreview": "Open print preview",
+ "sideBarProgram": {
+ "title": "Activities on this day"
+ }
+ },
+ "admin": {
+ "activity": {
+ "title": "Activity settings"
+ },
+ "adminMaterialLists": {
+ "title": "Material lists"
+ },
+ "collaborators": {
+ "email": "Email address",
+ "inactiveCollaborators": "Inactive",
+ "members": "Members",
+ "openInvitations": "Open invitations",
+ "title": "Team"
+ },
+ "info": {
+ "title": "Camp infos"
+ },
+ "print": {
+ "title": "Print camp"
+ },
+ "sideBarAdmin": {
+ "itemActivity": "Activities",
+ "itemActivitySubtitle": "Category, templates & status",
+ "itemCollaborators": "Team",
+ "itemInfos": "Camp infos",
+ "itemMaterialLists": "Material lists",
+ "itemPrint": "PDF / Print"
+ },
+ "title": "Admin"
+ },
+ "campProgram": {
+ "reminderLockedCreate": "Drag to create is only possible in unlocked mode.",
+ "reminderLockedMove": "Drag to move is only possible in unlocked mode.",
+ "title": "Picasso"
+ },
+ "category": {
+ "category": {
+ "clipboardInfoDialog": {
+ "allow": "Allow now",
+ "denied": "You have denied access to your clipboard. Therefore, you cannot paste copied categories.",
+ "description": "In order to paste a copied category, you must allow eCamp v3 to read your clipboard.",
+ "granted": "You can now paste copied categories.",
+ "title": "Copy & paste category"
+ },
+ "copyCategory": "Copy category",
+ "deleteCategory": "Delete category",
+ "properties": "Properties",
+ "template": "Template for new activities"
+ },
+ "sideBarCategory": {
+ "title": "Categories"
+ }
+ },
+ "checklistOverview": {
+ "checklistLists": {
+ "title": "Checklist Overview"
+ }
+ },
+ "dashboard": {
+ "activities": "Activities",
+ "columns": {
+ "category": "Category",
+ "number": "Number",
+ "responsible": "Responsible",
+ "time": "Time",
+ "title": "Title"
+ },
+ "noEntries": "No activities found. Try clearing the selection filters and/or reloading the page.",
+ "today": "Today",
+ "welcome": "Welcome to your new camp. There aren't any activities defined yet."
+ },
+ "invitation": {
+ "acceptCurrentAuth": "Accept invitation with current account",
+ "backToHome": "Back to home",
+ "error": "An unexpected error occurred",
+ "notFound": "This invitation was already accepted or deleted",
+ "openCamp": "Open camp",
+ "register": "Register",
+ "reject": "Deny invitation",
+ "successfullyRejected": "The invitation was successfully rejected",
+ "title": "Invitation",
+ "useOtherAuth": "Logout & use another account",
+ "userAlreadyInCamp": "You are already participating in this camp"
+ },
+ "material": {
+ "materialLists": {
+ "title": "Material lists"
+ },
+ "materialOverview": {
+ "createNewList": "Create material list",
+ "download": "Download overview",
+ "title": "General overview"
+ },
+ "materialUnassigned": {
+ "excelTitle": "unassigned",
+ "title": "Unassigned material"
+ },
+ "sideBarMaterialLists": {
+ "title": "Material lists"
+ }
+ },
+ "navigation": {
+ "desktop": {
+ "navTopbar": {
+ "campIsLoading": "Camp is loading",
+ "material": "Materials",
+ "program": "Program",
+ "story": "Story",
+ "checklist": "Checklist"
+ }
+ },
+ "mobile": {
+ "navBottombar": {
+ "material": "Materials",
+ "more": "More",
+ "program": "Program",
+ "story": "Story"
+ },
+ "navSidebar": {
+ "itemActivity": "Activities",
+ "itemActivitySubtitle": "Category, templates & status",
+ "itemCamps": "My Camps",
+ "itemChecklists": "Checklist Overview",
+ "itemClose": "Close menu",
+ "itemCollaborators": "Team",
+ "itemInfos": "Camp infos",
+ "itemMaterialLists": "Material lists",
+ "itemPrinting": "PDF / Print"
+ }
+ }
+ },
+ "story": {
+ "title": "Story"
+ }
+ },
+ "campCreate": {
+ "title": "Create a Camp"
+ },
+ "camps": {
+ "create": "Create a camp",
+ "pastCamps": "Past camps",
+ "prototypeCamps": "Prototypes",
+ "title": "My Camps"
+ },
+ "invitations": {
+ "personalInvitations": "Invitations"
+ },
+ "pageNotFound": {
+ "backToHome": "Back to home",
+ "detail": "Oops. You've lost your way…{br}Unfortunately, this link does not work."
+ },
+ "profile": {
+ "changeEmail": "Change",
+ "profile": "Profile"
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend-old/src/locales/fr-CH-scout.json b/frontend-old/src/locales/fr-CH-scout.json
new file mode 100644
index 0000000000..4893b0fa29
--- /dev/null
+++ b/frontend-old/src/locales/fr-CH-scout.json
@@ -0,0 +1,33 @@
+{
+ "components": {
+ "print": {
+ "printConfigurator": {
+ "config": {
+ "Story": "Fil rouge"
+ }
+ }
+ }
+ },
+ "contentNode": {
+ "storycontext": {
+ "name": "Fil rouge"
+ }
+ },
+ "entity": {
+ "user": {
+ "fields": {
+ "nickname": "Totem"
+ }
+ }
+ },
+ "global": {
+ "language": "Français (Scout)"
+ },
+ "views": {
+ "camp": {
+ "story": {
+ "title": "Fil rouge"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend-old/src/locales/fr.json b/frontend-old/src/locales/fr.json
new file mode 100644
index 0000000000..87d7686353
--- /dev/null
+++ b/frontend-old/src/locales/fr.json
@@ -0,0 +1,840 @@
+{
+ "components": {
+ "activity": {
+ "content": {
+ "checklist": {
+ "title": "Modifier la check-list"
+ },
+ "columnLayout": {
+ "columnOperations": {
+ "addColumn": "Ajouter une colonne",
+ "removeColumn": "Supprimer la colonne vide"
+ }
+ },
+ "lAThematicArea": {
+ "placeholder": "Choisir un domaine thématique"
+ },
+ "storyboard": {
+ "controls": "Actions",
+ "reorder": "Déplacer",
+ "storyboardDialogRemoveSection": {
+ "deleteWarning": "Veux-tu vraiment supprimer cette ligne ?",
+ "title": "Confirmer la suppression ?"
+ }
+ }
+ },
+ "dialog": {
+ "dialogActivityEdit": {
+ "title": "Modifier l'activité"
+ },
+ "formScheduleEntryList": {
+ "name": "Dates planifiées"
+ }
+ },
+ "menuCardlessContentNode": {
+ "deletingDisabled": "Doit être vide pour être supprimé"
+ },
+ "scheduleEntry": {
+ "backToContents": "Retour à l'édition du contenu",
+ "changeLayout": "Changer la mise en page",
+ "clipboardInfoDialog": {
+ "allow": "Autoriser maintenant",
+ "denied": "Vous avez refusé l'accès à votre presse-papiers. Par conséquent, vous ne pouvez pas coller les activités copiées.",
+ "description": "Pour coller une activité copiée, vous devez autoriser eCamp v3 à lire votre presse-papiers.",
+ "granted": "Vous pouvez maintenant coller les activités copiées.",
+ "title": "Copier et coller une activité"
+ },
+ "copyScheduleEntry": "Copier une activité",
+ "deleteWarning": "Veux-tu vraiment supprimer cette activité ? Tout le contenu de cette activité sera supprimé."
+ },
+ "togglePaperSize": {
+ "switchToFullSize": "Passer au format large",
+ "switchToPaperSize": "Passer au format papier"
+ }
+ },
+ "camp": {
+ "campInvitations": {
+ "title": "Inviter"
+ },
+ "campMembers": {
+ "title": "Membres"
+ },
+ "footerSharedCamp": {
+ "outsider": "Tu ne travailles pas dans ce camp :",
+ "outsiderDescription": "Tu ne peux pas modifier les données, mais tu peux consulter et copier le programme.",
+ "shared": "Le camp est ouvert au public :",
+ "sharedDescription": "Des personnes étrangères peuvent consulter et copier toutes les données du camp."
+ }
+ },
+ "campAdmin": {
+ "campActivityProgressLabels": {
+ "create": "Créer un statut",
+ "deleteError": "Le statut ne peut pas être supprimé. Il est toujours utilisé.",
+ "deleteWarning": "Veux-tu vraiment supprimer ce statut?",
+ "exit": "Fin du réagencement",
+ "moveDown": "Descendre",
+ "moveUp": "Monter",
+ "reorder": "Réordonner",
+ "title": "Statut des activités"
+ },
+ "campAddress": {
+ "title": "Adresse"
+ },
+ "campCategories": {
+ "create": "Créer une catégorie d'activité",
+ "pasteCategory": "Coller la catégorie",
+ "title": "Catégories d'activités"
+ },
+ "campConditionalFields": {
+ "course": {
+ "title": "Cours"
+ },
+ "title": "Paramètres pour J+S",
+ "ysCamp": {
+ "title": "Camps"
+ }
+ },
+ "campDangerZone": {
+ "deleteCamp": {
+ "description": "Une fois un camp supprimé, il est impossible de revenir en arrière. Ne continue que si tu es sûr.",
+ "explanation": "Cette action ne peut pas être annulée. Cette action supprimera définitivement le camp \"{campTitle}\", les activités, les listes de matériel et les personnes ajoutées.",
+ "label": "Veuillez saisir \"{campTitle}\" pour confirmer.",
+ "title": "Supprimer le camp"
+ },
+ "title": "Zone à risque"
+ },
+ "campMaterialLists": {
+ "createMaterialList": "Créer une liste de matériel",
+ "title": "Listes de matériel"
+ },
+ "campPeriods": {
+ "createPeriod": "Créer une période",
+ "title": "Période | Périodes"
+ },
+ "campPeriodsListItem": {
+ "changePeriodDescription": "Modifier la description",
+ "deleteWarning": "Veux-tu vraiment supprimer cette période ?",
+ "lastPeriodNotDeletable": "La dernière période ne peut pas être supprimée",
+ "movePeriod": "Déplacer la période",
+ "periodChangeEnd": "Ajouter/supprimer des jours à la fin",
+ "periodChangeStart": "Ajouter/supprimer des jours au début"
+ },
+ "campSettings": {
+ "title": "Paramètres"
+ },
+ "campSharingSettings": {
+ "activate": "Activer le partage",
+ "deactivate": "Désactiver le partage",
+ "implications": "Si tu actives le partage, les personnes extérieures peuvent consulter et copier le programme, la liste des participants, les listes de matériel, les responsabilités et tout autre élément relatif à ce camp. Seules les personnes faisant partie de l'équipe peuvent modifier les données.",
+ "notShared": {
+ "description": " : Seules les personnes faisant partie de l'équipe peuvent consulter le programme et les données relatives à ce camp.",
+ "title": "L'autorisation n'est pas activée"
+ },
+ "shared": {
+ "description": " : Les personnes extérieures peuvent consulter et copier le programme, la liste des participants, la liste du matériel, les responsabilités et tout autre élément relatif à ce camp. Seules les personnes faisant partie de l'équipe peuvent modifier les données.",
+ "title": "le camp est ouvert au public"
+ },
+ "sharedSince": "Le {sharedSince} par {sharedBy}",
+ "title": "Partager le camp"
+ },
+ "createCampPeriods": {
+ "add": "Ajouter une période"
+ },
+ "dialogActivityProgressLabelCreate": {
+ "title": "Créer un statut"
+ },
+ "dialogActivityProgressLabelEdit": {
+ "title": "Modifier le statut"
+ },
+ "dialogCategoryCreate": {
+ "clearClipboard": "Vider le presse-papiers",
+ "clipboard": "Presse-papiers",
+ "clipboardInfoDialog": {
+ "allow": "Autoriser maintenant",
+ "denied": "Tu as refusé l'accès à ton presse-papiers. Par conséquent, tu ne peux pas coller les catégories copiées.",
+ "description": "Pour coller une catégorie copiée, vous devez autoriser eCamp v3 à lire votre presse-papiers.",
+ "granted": "Tu peux maintenant coller les catégories copiées.",
+ "title": "Copier et coller la catégorie"
+ },
+ "copyCategoryOrActivity": "Copier la catégorie ou l'activité",
+ "copyContent": "Copier le contenu",
+ "copyPasteCategory": "Copier et coller la catégorie",
+ "copySourceInfo": "Tu peux coller ici l'URL d'une catégorie ou d'une activité pour copier son contenu.",
+ "pasteCategory": "Copier la catégorie ou l'activité",
+ "title": "Créer une catégorie d'activité"
+ },
+ "dialogMaterialListCreate": {
+ "title": "Créer une liste de matériel"
+ },
+ "dialogMaterialListEdit": {
+ "deleteError": "Impossible de supprimer la liste de matériel. Vérifie que la liste soit vide avant de la supprimer.",
+ "title": "Modifier la liste de matériel"
+ },
+ "dialogPeriodCreate": {
+ "title": "Créer une période"
+ },
+ "dialogPeriodDateEdit": {
+ "movePeriod": "Déplacer la période",
+ "periodChangeEnd": "Ajouter / supprimer des jours à la fin de cette période",
+ "periodChangeStart": "Ajouter / supprimer des jours au début de cette période",
+ "periodDuration": "Nombre de jours"
+ },
+ "dialogPeriodEdit": {
+ "moveScheduleEntries": "Déplacer les entrées du calendrier"
+ },
+ "errorExistingActivitiesList": {
+ "description": "Ne peut pas être supprimé. Toujours utilisé par les activités suivantes :"
+ }
+ },
+ "campCreate": {
+ "campCreate": {
+ "steps": {
+ "configurate": "Configuration",
+ "infos": "Info",
+ "template": "Modèle"
+ }
+ },
+ "campCreateStep1": {
+ "submitTooltip": "Remplis tous les champs obligatoires.",
+ "titlePlaceholder": "Camp d'été 2024"
+ },
+ "campCreateStep2": {
+ "category": "Catégories d'activités",
+ "checklistItemCount": "Aucune entrée | Une entrée | {count} entrées",
+ "checklists": "Check-lists",
+ "clipboardInfoDialog": {
+ "allow": "Autoriser maintenant",
+ "denied": "Tu as refusé l'accès à ton presse-papiers. Par conséquent, tu dois entrer le lien vers le modèle de camp manuellement.",
+ "description": "Pour coller les paramètres d'un camp copié, tu dois autoriser eCamp v3 à lire ton presse-papiers.",
+ "granted": "Tu peux maintenant coller les paramètres de camp copiés.",
+ "title": "Copier et coller les paramètres du camp"
+ },
+ "create": "Créer le camp",
+ "materialLists": "Listes de matériel",
+ "noContent": "Pas de contenu",
+ "noPrototype": "Pas de modèle",
+ "noPrototypeAlert": {
+ "description": "Tu dois effectuer tous les réglages manuellement. Il n'y a pas de modèles de blocs ni de mises en page. Ceci ne convient qu'aux utilisateurs expérimentés.",
+ "title": "Attention : Vous avez choisi \"Pas de modèle\"."
+ },
+ "otherPrototype": "Copier les paramètres d'un autre camp…",
+ "pasteCamp": "Coller les paramètres du camp copié",
+ "preview": "Aperçu du modèle du camp",
+ "progressLabels": "États d'activités",
+ "prototypeCampUrl": "Lien du modèle de camp désiré",
+ "prototypeHint": "Copie les catégories, les modèles de blocs et les états de blocs d'un modèle de camp.",
+ "prototypeHintEmpty": "Ne pas copier de catégories, de modèles de blocs et de statuts de blocs à partir d'un modèle de camp.",
+ "prototypeHintOther": "Copier les catégories, les modèles d'activités, les statuts, les listes de documents non personnels et les check-lists de cet autre camp. Aucune activité n'est copiée.",
+ "prototypeHintSelected": "Copie les catégories, les modèles de blocs et les statuts de blocs de ce modèle de camp.",
+ "submitTooltipPrototype": "Tu dois encore choisir si tu veux utiliser un modèle de champ et lequel."
+ }
+ },
+ "category": {
+ "categoryTemplate": {
+ "contents": "Contenus",
+ "createLayoutHelp": "Ici, vous pouvez définir le modèle pour de nouvelles activités {categoryShort}.{br}Le contenu et la mise en page des activités {categoryShort} déjà créées ne seront pas ajustés.",
+ "layout": "Layout",
+ "noTemplate": "Pas de modèle"
+ }
+ },
+ "checklist": {
+ "checklistCreate": {
+ "title": "Créer une check-list"
+ },
+ "checklistDetail": {
+ "deleteError": "La check-list n'a pas pu être supprimée. Assure-toi que la check-list n'est pas utilisée dans les activités avant d'essayer de la supprimer.",
+ "deleteWarning": "Veux-tu vraiment supprimer cette check-list ? Tout le contenu de cette check-list sera supprimé."
+ },
+ "checklistItemCreate": {
+ "add": "Créer un point de check-list",
+ "title": "Ajouter un point de check-list"
+ },
+ "checklistItemEdit": {
+ "delete": "Veux-tu vraiment supprimer cet élément ?",
+ "title": "Modifier le point de check-list"
+ },
+ "sortableChecklist": {
+ "add": "Ajouter à \"{parent}\""
+ }
+ },
+ "collaborator": {
+ "collaboratorCreate": {
+ "invite": "Envoyer l'invitation",
+ "inviteCta": "Inviter",
+ "title": "Inviter une personne"
+ },
+ "collaboratorEdit": {
+ "cannotRemoveLastManager": "Tu ne peux pas supprimer le dernier manager.",
+ "deactivate": "Désactiver",
+ "delete": "Veux-tu vraiment supprimer la personne '{name}' ?",
+ "inviteAgain": "Inviter de nouveau",
+ "resendEmail": "Renvoyer l'email",
+ "resentEmail": "Invitation envoyée",
+ "title": "Modifier {user}"
+ },
+ "collaboratorForm": {
+ "overrideAvatar": "Remplacer l'avatar pour ce camp",
+ "roleHint": "Chaque camp nécessite au moins une personne disposant de droits d'administrateur."
+ },
+ "promptCollaboratorDeactivate": {
+ "deactivate": "Désactiver",
+ "warningText": "Veux-tu désactiver '{name}' ?"
+ }
+ },
+ "dashboard": {
+ "selectFilter": {
+ "clear": "Vider"
+ }
+ },
+ "dialog": {
+ "dialogEntityDelete": {
+ "title": "Confirmation de suppression"
+ }
+ },
+ "form": {
+ "base": {
+ "eColorField": {
+ "parseError": "Merci de saisir une couleur valide."
+ },
+ "eColorPicker": {
+ "closePicker": "Fermer le dialogue",
+ "openPicker": "Ouvre un dialogue pour sélectionner une couleur pour {label}"
+ },
+ "eDatePicker": {
+ "invalidFormat": "Format non valide, veuillez saisir la date au format MM/JJ/AAAA.",
+ "openPicker": "Ouvre un dialogue pour sélectionner une date pour {label}"
+ },
+ "eTimePicker": {
+ "invalidFormat": "Format non valide, veuillez saisir l'heure au format HH:MM AM/PM",
+ "openPicker": "Ouvre un dialogue pour sélectionner une heure pour {label}"
+ }
+ }
+ },
+ "generic": {
+ "lockButton": {
+ "clickToLock": "Cliquer pour verrouiller",
+ "clickToUnlock": "Cliquer pour déverrouiller",
+ "guestsCannotEdit": "Les invités n'ont pas le droit de modifier"
+ }
+ },
+ "layout": {
+ "authContainer": {
+ "photoCredits": "Photo par Markus Rohner / Lotos"
+ }
+ },
+ "material": {
+ "dialogMaterialItemCreate": {
+ "title": "Ajouter du matériel"
+ },
+ "dialogMaterialItemEdit": {
+ "title": "Modifier l'entrée de matériel"
+ },
+ "materialCreateItem": {
+ "campSettingsButton": "Paramètres",
+ "noMaterialListAvailable": "Il n'y a pas encore de liste de matériel pour ce camp. Tu peux les créer dans 'Admin'."
+ },
+ "materialLists": {
+ "materialsCount": "Pas des éléments | Un élément | {count} éléments",
+ "overview": "Aperçu général"
+ },
+ "materialTable": {
+ "addNewItem": "Ajoute un nouvel élément",
+ "noItems": "Aucun élément matériel trouvé",
+ "periodOnly": "Filtre : Période",
+ "reference": "Activité / Période"
+ },
+ "useMaterialViewHelper": {
+ "detail": "Liste de matériel",
+ "overview": "Aperçu général"
+ }
+ },
+ "navigation": {
+ "userMeta": {
+ "admin": "Admin",
+ "invitations": "Invitations",
+ "logOut": "Se déconnecter",
+ "myCamps": "Mes camps",
+ "profile": "Profil"
+ }
+ },
+ "personalInvitations": {
+ "dialogPersonalInvitationReject": {
+ "rejectInvitation": "Refuser l'invitation",
+ "warningText": "Tu veux vraiment refuser l'invitation au camp \"{campTitle}\" ?"
+ },
+ "personalInvitations": {
+ "accept": "Accepter",
+ "noOpenInvitations": "Aucune invitation ouverte. L'invitation a-t-elle été envoyée à une autre adresse que \"{email}\" ?",
+ "reject": "Refuser"
+ }
+ },
+ "print": {
+ "config": {
+ "activityConfig": {
+ "activity": "Activité"
+ },
+ "dialogScheduleEntryFilter": {
+ "filterActive": "Filtre: {filtered} activités sur {total}",
+ "filterActivities": "Filtre: {total} activités",
+ "resultCount": "Résultat: {filtered} des activités sur {total}",
+ "title": "Filtrer les activités"
+ },
+ "picassoConfig": {
+ "orientation": "Mise en page",
+ "periods": "Période(s) du camp"
+ },
+ "programConfig": {
+ "dayOverview": "Aperçu des jours"
+ }
+ },
+ "documents": {
+ "campPrint": {
+ "filename": {
+ "activitiesOnly": "Activités {camp}",
+ "picassoOnly": "Programme général {camp}"
+ }
+ }
+ },
+ "localPdfDownloadButton": {
+ "error": "Une erreur est survenue. Essaie à nouveau, ou recharge la page."
+ },
+ "printClient": {
+ "downloadClientPdfButton": {
+ "label": "Télécharger le PDF (mise en page #2)"
+ },
+ "downloadClientPdfListItem": {
+ "label": "Télécharger le PDF (mise en page #2)"
+ },
+ "generatePdfMixin": {
+ "error": "Une erreur est survenue. Essaie à nouveau, ou recharge la page."
+ },
+ "printPreviewClient": {
+ "previewError": "Une erreur est survenue. Veuillez réessayer avec des paramètres d'impression différents, ou recharger la page.",
+ "previewIframeTitle": "Aperçu avant impression"
+ }
+ },
+ "printConfigurator": {
+ "add": "Ajouter du contenu",
+ "config": {
+ "Activity": "Activité unique",
+ "ActivityList": "Liste des blocs (cours)",
+ "Cover": "Page de couverture",
+ "Picasso": "Picasso",
+ "Program": "Programme",
+ "SafetyConsiderations": "Considérations sur la sécurité",
+ "Story": "Histoire",
+ "Toc": "Table des matières"
+ }
+ },
+ "printNuxt": {
+ "downloadNuxtPdfButton": {
+ "label": "Télécharger le PDF (mise en page #1)"
+ },
+ "downloadNuxtPdfListItem": {
+ "label": "Télécharger le PDF (mise en page #1)"
+ },
+ "generatePdfMixin": {
+ "error": "Une erreur est survenue. Essaie à nouveau, ou recharge la page.",
+ "queueFull": "Toutes nos imprimantes PDF sont occupées en ce moment. Veuillez réessayer plus tard."
+ },
+ "printPreviewNuxt": {
+ "openPreview": "Ouvrir l'aperçu dans une nouvelle fenêtre",
+ "previewError": "Une erreur est survenue. Veuillez réessayer avec des paramètres d'impression différents, ou recharger la page.",
+ "previewIframeTitle": "Aperçu avant impression"
+ }
+ }
+ },
+ "program": {
+ "dialogActivityCreate": {
+ "clearClipboard": "Vider le presse-papier",
+ "clipboard": "Presse-papier",
+ "clipboardInfoDialog": {
+ "allow": "Autoriser maintenant",
+ "denied": "Vous avez refusé l'accès à votre presse-papiers. Par conséquent, vous ne pouvez pas coller les activités copiées.",
+ "description": "Pour coller une activité copiée, vous devez autoriser eCamp v3 à lire votre presse-papiers.",
+ "granted": "Vous pouvez maintenant coller les activités copiées.",
+ "title": "Copier et coller une activité"
+ },
+ "copyActivity": "Copier une activité",
+ "copyActivityContent": "Copier le contenu depuis une activité",
+ "copyPasteActivity": "Copier et coller une activité",
+ "copySourceInfo": "Vous pouvez coller ici l'URL d'une activité pour copier son contenu.",
+ "pasteActivity": "coller l'activité"
+ },
+ "formScheduleEntryItem": {
+ "end": "Fin",
+ "start": "Début"
+ },
+ "formScheduleEntryList": {
+ "name": "Dates planifiées"
+ },
+ "periodSwitcher": {
+ "title": "Choisir la période"
+ },
+ "picasso": {
+ "picasso": {
+ "datetime": {
+ "fullDate": "dd DD. MMMM YYYY",
+ "smallDate": "dd D.MM. | dd D. MMM | dd D. MMM YY"
+ }
+ },
+ "picassoEntry": {
+ "clipboardInfoDialog": {
+ "allow": "Autoriser maintenant",
+ "denied": "Vous avez refusé l'accès à votre presse-papiers. Par conséquent, vous ne pouvez pas coller les activités copiées.",
+ "description": "Pour coller une activité copiée, vous devez autoriser eCamp v3 à lire votre presse-papiers.",
+ "granted": "Vous pouvez maintenant coller les activités copiées.",
+ "title": "Copier et coller une activité"
+ },
+ "location": "Lieu:",
+ "responsible": "Responsable:"
+ }
+ },
+ "scheduleEntryFilters": {
+ "category": "Catégorie",
+ "clearFilters": "Effacer les filtres",
+ "day": "Date",
+ "dayLabel": "Jour {dayNumber} ({date})",
+ "onlyMyActivities": "Seulement mes activités",
+ "period": "Période",
+ "progressLabel": "Statut",
+ "progressLabelNone": "Aucun statut",
+ "responsible": "Responsable",
+ "responsibleNone": "Aucun responsables"
+ }
+ },
+ "prompt": {
+ "promptEntityDelete": {
+ "title": "Confirmation de suppression"
+ }
+ },
+ "story": {
+ "storyDay": {
+ "noStory": "Aucun file rouge trouvé ce jour-là..."
+ }
+ },
+ "toast": {
+ "toasts": {
+ "multiLineToast": {
+ "generalError": "Une erreur est survenue."
+ }
+ }
+ },
+ "user": {
+ "dialogChangeMail": {
+ "error": "Une erreur est survenue.",
+ "message": "Un e-mail de vérification sera envoyé.",
+ "success": "Ta nouvelle adresse e-mail doit être vérifiée avant de pouvoir être utilisée. Vérifie ta boîte mail.",
+ "title": "Changer d'adresse e-mail"
+ },
+ "dialogChangeMailRunning": {
+ "error": "Une erreur est survenue.",
+ "message": "Changement d'adresse e-mail ...",
+ "success": "L'adresse e-mail a été modifiée avec succès.",
+ "title": "Changement d'adresse e-mail"
+ }
+ }
+ },
+ "global": {
+ "button": {
+ "add": "Ajouter",
+ "back": "Retour",
+ "cancel": "Annuler",
+ "close": "Fermer",
+ "content": "Contenu",
+ "continue": "Continuer",
+ "create": "Créer",
+ "delete": "Supprimer",
+ "discard": "Rejeter",
+ "download": "Télécharger",
+ "edit": "Éditer",
+ "editable": "Editable",
+ "filter": "Filtrer",
+ "lock": "Verrouiller",
+ "login": "Se connecter",
+ "logout": "Déconnexion",
+ "move": "Déplacer",
+ "ok": "OK",
+ "open": "Ouvrir",
+ "remove": "Retirer",
+ "rename": "Renommer",
+ "reset": "Réinitialiser",
+ "save": "Sauvegarder",
+ "saving": "En cours de sauvegarde",
+ "search": "Rechercher",
+ "share": "Partager",
+ "submit": "Envoyer",
+ "tryagain": "Réessayer",
+ "unlock": "Déverrouiller",
+ "update": "Mise à jour"
+ },
+ "changeLanguage": "Changer de langue",
+ "collaborationAbilities": {
+ "guest": "Lecture uniquement",
+ "manager": "Lecture, écriture & administration du camp",
+ "member": "Lecture & écriture"
+ },
+ "datetime": {
+ "vuetifyTimePickerFormat": "24hr"
+ },
+ "info": {
+ "offline": {
+ "description": "Impossible d'enregistrer / de charger des données.",
+ "title": "Tu es hors ligne :"
+ }
+ },
+ "language": "Français",
+ "loading": "Chargement ...",
+ "navigation": {
+ "admin": {
+ "title": "Admin"
+ },
+ "help": "Aide",
+ "news": "Actualités"
+ },
+ "serverError": {
+ "409": "Oooops... Cette action a provoqué une erreur côté serveur.",
+ "short": "erreur de serveur"
+ },
+ "toast": {
+ "copied": "{source} copié"
+ },
+ "validation": {
+ "greaterThan": "{_field_} doit être supérieur à {min}",
+ "greaterThanOrEqual_date": "{_field_} ne peut pas être antérieure à {min}",
+ "greaterThan_time": "{_field_} doit être postérieure à {min}",
+ "lessThanOrEqual_date": "{_field_} ne peut pas être postérieure à {max}",
+ "oneEmojiOrTwoCharacters": "{_field_} ne peut avoir que 1 emoji ou 2 lettres/chiffres"
+ },
+ "warning": {
+ "delete": "Veux-tu vraiment le supprimer ? | Veux-tu vraiment supprimer \"{entity}\" ?"
+ }
+ },
+ "views": {
+ "auth": {
+ "activate": {
+ "error": "L'activation a échoué",
+ "success": "Le compte a été activé avec succès",
+ "title": "Ton compte sera activé"
+ },
+ "login": {
+ "acceptTermsOfServiceOnOAuthLogin": "En te connectant via l'un de ces services, tu acceptes les {termsOfServiceLink}.",
+ "accountless": "Vous n'avez pas encore de compte?",
+ "email": "E-mail",
+ "infoText": {
+ "dev": "Ceci est la version de développement d'eCamp v3.{br}IMPORTANT : Ceci est destiné à un usage de développement uniquement. Toutes les données sont publiques et seront supprimées périodiquement !{br}Login : test@example.com / test"
+ },
+ "loginCallback": {
+ "loginInProgress": "Vous allez être connecté"
+ },
+ "notActivated": "Pas activité ?",
+ "or": "ou avec",
+ "password": "Mot de passe",
+ "passwordForgotten": "Tu as oublié ton mot de passe ?",
+ "provider": {
+ "cevidb": "CeviDB",
+ "ecamp": "Connectez-vous avec eCamp",
+ "google": "Google",
+ "jubladb": "Jubla",
+ "midata": "MiData"
+ },
+ "registernow": "Inscrivez-vous maintenant",
+ "resendActivation": "Envoyer à nouveau l'e-mail d'activation",
+ "resetPassword": "Réinitialiser le mot de passe",
+ "termsOfServiceLink": "conditions d'utilisation"
+ },
+ "register": {
+ "acceptTermsOfService": "Accepter les conditions d'utilisation",
+ "alreadyHaveAnAccount": "Vous avez déjà un compte ?",
+ "passwordConfirmation": "Entrez à nouveau votre mot de passe",
+ "register": "S'inscrire",
+ "requiredField": "champ obligatoire",
+ "title": "Créer un compte"
+ },
+ "registerDone": {
+ "message": "Nous t'avons envoyé un e-mail. Merci de compléter ton inscription en cliquant sur le lien d'activation dans l'email.",
+ "success": "Enregistré avec succès",
+ "title": "Créer un compte"
+ },
+ "resendActivation": {
+ "errorMessage": "L'envoi de l'e-mail d'activation a échoué à nouveau.",
+ "send": "Envoyer",
+ "successMessage": "Un e-mail avec le lien pour réinitialiser le mot de passe sera envoyé.",
+ "title": "Envoyer à nouveau l'e-mail d'activation"
+ },
+ "resetPassword": {
+ "errorMessage": "La réinitialisation du mot de passe a échoué.",
+ "invalidRequest": "Lien invalide",
+ "password": "Nouveau mot de passe",
+ "passwordConfirmation": "Confirmer le nouveau mot de passe",
+ "send": "Set mot de passe",
+ "successMessage": "Le nouveau mot de passe a été défini avec succès.",
+ "title": "Réinitialiser le mot de passe"
+ },
+ "resetPasswordRequest": {
+ "errorMessage": "La réinitialisation du mot de passe a échoué.",
+ "send": "Envoyer",
+ "successMessage": "Un e-mail avec le lien pour réinitialiser le mot de passe sera envoyé.",
+ "title": "Réinitialiser le mot de passe"
+ }
+ },
+ "camp": {
+ "activity": {
+ "printPreview": "Ouvrir l'aperçu avant impression",
+ "sideBarProgram": {
+ "title": "Activités du jour"
+ }
+ },
+ "admin": {
+ "activity": {
+ "title": "Paramètres d'activité"
+ },
+ "adminMaterialLists": {
+ "title": "Listes de matériel"
+ },
+ "collaborators": {
+ "email": "Adresse e-mail",
+ "inactiveCollaborators": "Inactif",
+ "members": "Membres",
+ "openInvitations": "Invitations ouvertes",
+ "title": "Equipe"
+ },
+ "info": {
+ "title": "Infos sur le camp"
+ },
+ "print": {
+ "title": "Imprimer le camp"
+ },
+ "sideBarAdmin": {
+ "itemActivity": "Activités",
+ "itemActivitySubtitle": "Catégorie, modèles et statut",
+ "itemCollaborators": "Equipe",
+ "itemInfos": "Infos sur le camp",
+ "itemMaterialLists": "Listes de matériel",
+ "itemPrint": "PDF / Imprimer"
+ },
+ "title": "Admin"
+ },
+ "campProgram": {
+ "reminderLockedCreate": "Glisser pour créer n'est possible qu'en mode déverrouillé.",
+ "reminderLockedMove": "Glisser pour déplacer n'est possible qu'en mode déverrouillé.",
+ "title": "Programme général"
+ },
+ "category": {
+ "category": {
+ "clipboardInfoDialog": {
+ "allow": "Autoriser maintenant",
+ "denied": "Tu as refusé l'accès à ton presse-papiers. Par conséquent, tu ne peux pas coller les catégories copiées.",
+ "description": "Pour coller une catégorie copiée, vous devez autoriser eCamp v3 à lire votre presse-papiers.",
+ "granted": "Tu peux maintenant coller les catégories copiées.",
+ "title": "Copier et coller la catégorie"
+ },
+ "copyCategory": "Copier la catégorie",
+ "deleteCategory": "Supprimer la catégorie",
+ "properties": "Propriétés",
+ "template": "Modèle pour les nouvelles activités"
+ },
+ "sideBarCategory": {
+ "title": "Catégories"
+ }
+ },
+ "checklistOverview": {
+ "checklistLists": {
+ "title": "Vue d'ensemble de la check-list"
+ }
+ },
+ "dashboard": {
+ "activities": "Activités",
+ "columns": {
+ "category": "Catégorie",
+ "number": "Numéro",
+ "responsible": "Responsable",
+ "time": "Temps",
+ "title": "Titre"
+ },
+ "noEntries": "Aucune activité trouvée. Essaie d'effacer les filtres de sélection et/ou de recharger la page.",
+ "today": "Aujourd'hui",
+ "welcome": "Bienvenue dans ton nouveau camp. Il n'y a pas encore d'activités définies."
+ },
+ "invitation": {
+ "acceptCurrentAuth": "Accepter l'invitation avec le compte courant",
+ "backToHome": "Retour à l'accueil",
+ "error": "Une erreur inattendue s'est produite",
+ "notFound": "Cette invitation est déjà acceptée ou supprimée",
+ "openCamp": "Camp ouvert",
+ "register": "S'enregistrer",
+ "reject": "Refuser l'invitation",
+ "successfullyRejected": "L'invitation a été rejetée avec succès",
+ "title": "Invitation",
+ "useOtherAuth": "Déconnecte-toi et utilise un autre compte",
+ "userAlreadyInCamp": "Tu participes déjà à ce camp"
+ },
+ "material": {
+ "materialLists": {
+ "title": "Listes de matériel"
+ },
+ "materialOverview": {
+ "createNewList": "Créer une liste de matériel",
+ "download": "Télécharger l'aperçu",
+ "title": "Aperçu général"
+ },
+ "sideBarMaterialLists": {
+ "title": "Listes de matériel"
+ }
+ },
+ "navigation": {
+ "desktop": {
+ "navTopbar": {
+ "campIsLoading": "Le camp est en cours de chargement",
+ "checklist": "Check-list",
+ "material": "Matériel",
+ "program": "Programme",
+ "story": "Histoire"
+ }
+ },
+ "mobile": {
+ "navBottombar": {
+ "material": "Matériel",
+ "more": "Plus",
+ "program": "Programme",
+ "story": "Histoire"
+ },
+ "navSidebar": {
+ "itemActivity": "Activités",
+ "itemActivitySubtitle": "Catégorie, modèle & statut",
+ "itemCamps": "Mes Camps",
+ "itemChecklists": "Vue d'ensemble de la check-list",
+ "itemClose": "Fermer le menu",
+ "itemCollaborators": "Team",
+ "itemInfos": "Infos sur le camp",
+ "itemMaterialLists": "Liste de matériel",
+ "itemPrinting": "PDF / Imprimer"
+ }
+ }
+ },
+ "story": {
+ "title": "Histoire"
+ }
+ },
+ "campCreate": {
+ "title": "Créer un camp"
+ },
+ "camps": {
+ "create": "Créer un camp",
+ "pastCamps": "Anciens camps",
+ "prototypeCamps": "Prototypes",
+ "title": "Mes camps"
+ },
+ "invitations": {
+ "personalInvitations": "Invitations"
+ },
+ "pageNotFound": {
+ "backToHome": "Retour à l'accueil",
+ "detail": "Hop là ! Tu t'es égaré...{br}Ce lien ne fonctionne malheureusement pas."
+ },
+ "profile": {
+ "changeEmail": "Changement",
+ "profile": "Profil"
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend-old/src/locales/it-CH-scout.json b/frontend-old/src/locales/it-CH-scout.json
new file mode 100644
index 0000000000..410997594a
--- /dev/null
+++ b/frontend-old/src/locales/it-CH-scout.json
@@ -0,0 +1,24 @@
+{
+ "contentNode": {
+ "storycontext": {
+ "name": "Filo rosso"
+ }
+ },
+ "entity": {
+ "user": {
+ "fields": {
+ "nickname": "Nome del Scout"
+ }
+ }
+ },
+ "global": {
+ "language": "Italiano (Scout)"
+ },
+ "views": {
+ "camp": {
+ "story": {
+ "title": "Filo rosso"
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend-old/src/locales/it.json b/frontend-old/src/locales/it.json
new file mode 100644
index 0000000000..ae3557ff78
--- /dev/null
+++ b/frontend-old/src/locales/it.json
@@ -0,0 +1,725 @@
+{
+ "components": {
+ "activity": {
+ "content": {
+ "columnLayout": {
+ "columnOperations": {
+ "addColumn": "Aggiungi una colonna",
+ "removeColumn": "Rimuovi la colonna vuota"
+ }
+ },
+ "lAThematicArea": {
+ "placeholder": "Selezionare l'area tematica"
+ },
+ "storyboard": {
+ "controls": "Azioni",
+ "reorder": "Spostare",
+ "storyboardDialogRemoveSection": {
+ "deleteWarning": "Vuoi davvero rimuovere questa sezione?",
+ "title": "Cancellare davvero?"
+ }
+ }
+ },
+ "dialog": {
+ "dialogActivityEdit": {
+ "title": "Modificare l'actività"
+ },
+ "formScheduleEntryList": {
+ "name": "Programmato"
+ }
+ },
+ "menuCardlessContentNode": {
+ "deletingDisabled": "Deve essere vuoto per essere eliminato"
+ },
+ "scheduleEntry": {
+ "backToContents": "Torna alla modifica dei contenuti",
+ "changeLayout": "Cambia il layout",
+ "clipboardInfoDialog": {
+ "allow": "Consenti ora"
+ },
+ "deleteWarning": "Vuoi davvero cancellare questa attività? Tutti i contenuti di questa attività saranno rimossi."
+ },
+ "togglePaperSize": {
+ "switchToFullSize": "Passare al formato largo",
+ "switchToPaperSize": "Passare al formato carta"
+ }
+ },
+ "camp": {
+ "campInvitations": {
+ "title": "Invitare"
+ },
+ "campMembers": {
+ "title": "Membri"
+ },
+ "footerSharedCamp": {
+ "outsider": "In questo campo non collabori:",
+ "outsiderDescription": "Non puoi modificare i dati, ma puoi visualizzare e copiare il programma.",
+ "shared": "Il campo è aperto al pubblico:",
+ "sharedDescription": "Persone estranee possono visionare e copiare tutti i dati presenti nel campo."
+ }
+ },
+ "campAdmin": {
+ "campActivityProgressLabels": {
+ "create": "Creare uno stato",
+ "deleteError": "Lo stato non può essere cancellato. Viene ancora utilizzato.",
+ "deleteWarning": "Vuoi davvero cancellare questo stato?",
+ "exit": "Terminare il riordino",
+ "moveDown": "Spostare in basso",
+ "moveUp": "Spostare in alto",
+ "reorder": "Riordinare",
+ "title": "Stati delle attività"
+ },
+ "campAddress": {
+ "title": "Indirizzo"
+ },
+ "campCategories": {
+ "create": "Creare categoria di attività",
+ "title": "Categorie di attività"
+ },
+ "campConditionalFields": {
+ "course": {
+ "title": "Corsi"
+ },
+ "title": "Impostazioni per G+S",
+ "ysCamp": {
+ "title": "Campi"
+ }
+ },
+ "campDangerZone": {
+ "deleteCamp": {
+ "description": "Una volta eliminato un campo, non è più possibile tornare indietro. Siate certi.",
+ "explanation": "Questa azione non può essere annullata. Questo eliminerà definitivamente il campo \"{campTitle}\", le attività, gli elenchi di materiali e rimuoverà tutte le associazioni di collaboratori.",
+ "label": "Digita \"{campTitle}\" per confermare.",
+ "title": "Elimina il campo"
+ },
+ "title": "Zona di pericolo"
+ },
+ "campMaterialLists": {
+ "createMaterialList": "Crea l'elenco dei materiali",
+ "title": "Elenchi di materiali"
+ },
+ "campPeriods": {
+ "createPeriod": "Creare il periodo",
+ "title": "Periodo | Periodi"
+ },
+ "campPeriodsListItem": {
+ "changePeriodDescription": "Cambia la descrizione",
+ "deleteWarning": "Volete davvero cancellarlo?",
+ "lastPeriodNotDeletable": "L'ultimo periodo non è cancellabile.",
+ "movePeriod": "Periodo di spostamento",
+ "periodChangeEnd": "Aggiungi/rimuovi giorni alla fine",
+ "periodChangeStart": "Aggiungi/rimuovi giorni all'inizio"
+ },
+ "campSettings": {
+ "title": "Impostazioni"
+ },
+ "campSharingSettings": {
+ "activate": "Attiva condivisione",
+ "deactivate": "Disattiva condivisione",
+ "implications": "Se attivi la condivisione, anche persone esterne potranno visualizzare e copiare il programma, la squadra, gli elenchi dei materiali, le responsabilità e tutto il resto relativo al campo. Solo i membri della squadra possono modificare i dati.",
+ "notShared": {
+ "description": ": Solo i membri del team possono visualizzare il programma e i dati relativi a questo campo.",
+ "title": "L'autorizzazione non è attivata"
+ },
+ "shared": {
+ "description": ": Le persone esterne possono visualizzare e copiare il programma, il team, gli elenchi dei materiali, le responsabilità e tutto il resto relativo a questo campo. Solo i membri del team possono modificare i dati.",
+ "title": "il campo è aperto al pubblico"
+ },
+ "sharedSince": "Il {sharedSince} da {sharedBy}",
+ "title": "Condividere il campo"
+ },
+ "createCampPeriods": {
+ "add": "Aggiungi un punto"
+ },
+ "dialogActivityProgressLabelCreate": {
+ "title": "Creare uno stato"
+ },
+ "dialogActivityProgressLabelEdit": {
+ "title": "Modificare il stato"
+ },
+ "dialogCategoryCreate": {
+ "clipboardInfoDialog": {
+ "allow": "Consenti ora"
+ },
+ "title": "Creare categoria di attività"
+ },
+ "dialogMaterialListCreate": {
+ "title": "Crea l'elenco dei materiali"
+ },
+ "dialogMaterialListEdit": {
+ "deleteError": "Impossibile eliminare l'elenco dei materiali. Controlla che l'elenco sia vuoto prima di eliminarlo.",
+ "title": "Modifica elenco materiali"
+ },
+ "dialogPeriodCreate": {
+ "title": "Creare il periodo"
+ },
+ "dialogPeriodDateEdit": {
+ "movePeriod": "Periodo di spostamento",
+ "periodChangeEnd": "Aggiungi/togli giorni alla fine di questo periodo",
+ "periodChangeStart": "Aggiungi/togli giorni all'inizio di questo periodo",
+ "periodDuration": "Numero di giorni"
+ },
+ "dialogPeriodEdit": {
+ "moveScheduleEntries": "Sposta le voci del programma"
+ },
+ "errorExistingActivitiesList": {
+ "description": "Non può essere eliminato. Ancora utilizzato dalle seguenti attività:"
+ }
+ },
+ "campCreate": {
+ "campCreate": {
+ "steps": {
+ "configurate": "Configurazione",
+ "infos": "Info",
+ "template": "Modello"
+ }
+ },
+ "campCreateStep1": {
+ "submitTooltip": "Compilare tutti i campi richiesti."
+ },
+ "campCreateStep2": {
+ "category": "Categorie di attività",
+ "create": "Creare il campo",
+ "noContent": "Nessun contenuto",
+ "noPrototype": "Nessun modello",
+ "noPrototypeAlert": {
+ "description": "È necessario effettuare tutte le impostazioni manualmente. Non sono disponibili modelli e layout di blocco. È adatto solo agli utenti esperti.",
+ "title": "Nota bene: avete scelto \"Nessun modello\"."
+ },
+ "preview": "Anteprima del modello di campo",
+ "progressLabels": "Stati di attività",
+ "prototypeHint": "Copiare le categorie, i modelli di blocco e stati di blocchi da un modello di campo.",
+ "prototypeHintEmpty": "Non copiare categorie, modelli di blocco e stati di blocco da un modello di campo.",
+ "prototypeHintSelected": "Copiate le categorie, i modelli di blocco e stati di blocchi da questo modello di campo.",
+ "submitTooltipPrototype": "È ancora necessario selezionare se e quale modello di campo si desidera utilizzare."
+ }
+ },
+ "category": {
+ "categoryTemplate": {
+ "contents": "Contenuti",
+ "createLayoutHelp": "Qui puoi definire il modello per le nuove attività {categoryShort}.{br}Il contenuto e il layout delle attività {categoryShort} già create non verranno modificati.",
+ "layout": "Layout",
+ "noTemplate": "Nessun modello"
+ }
+ },
+ "collaborator": {
+ "collaboratorCreate": {
+ "invite": "Inviare l'invito",
+ "inviteCta": "Invitare",
+ "title": "Invitare una persona"
+ },
+ "collaboratorEdit": {
+ "cannotRemoveLastManager": "Non è possibile rimuovere l'ultimo gestore.",
+ "deactivate": "Disattivare",
+ "delete": "Vuoi davvero rimuovere la persona '{name}'?",
+ "inviteAgain": "Invitare di nuovo",
+ "resendEmail": "Reinvia l'email",
+ "resentEmail": "Invito inviato",
+ "title": "Éditer {user}"
+ },
+ "collaboratorForm": {
+ "overrideAvatar": "Sovrascrivere l'avatar per questo campo",
+ "roleHint": "Ogni campo ha bisogno di almeno una persona con diritti di amministratore."
+ },
+ "promptCollaboratorDeactivate": {
+ "deactivate": "Disattivare",
+ "warningText": "Vuoi disattivare '{name}'?"
+ }
+ },
+ "dashboard": {
+ "selectFilter": {
+ "clear": "Libero"
+ }
+ },
+ "dialog": {
+ "dialogEntityDelete": {
+ "title": "Davvero cancellare?"
+ }
+ },
+ "form": {
+ "base": {
+ "eColorPicker": {
+ "closePicker": "Chiudere il dialogo",
+ "openPicker": "Apri una finestra di dialogo per selezionare un colore per {label}"
+ },
+ "eDatePicker": {
+ "invalidFormat": "Formato non valido, inserisci la data nel formato MM/GG/AAAA",
+ "openPicker": "Apri una finestra di dialogo per selezionare una data per {label}"
+ },
+ "eTimePicker": {
+ "invalidFormat": "Formato non valido, inserire l'ora nel formato HH:MM AM/PM",
+ "openPicker": "Apri una finestra di dialogo per selezionare un orario per {label}"
+ }
+ }
+ },
+ "generic": {
+ "lockButton": {
+ "clickToLock": "Clic per bloccare",
+ "clickToUnlock": "Clic per sbloccare",
+ "guestsCannotEdit": "Gli ospiti non hanno il permesso di modificare"
+ }
+ },
+ "layout": {
+ "authContainer": {
+ "photoCredits": "Foto di Markus Rohner / Lotos"
+ }
+ },
+ "material": {
+ "dialogMaterialItemCreate": {
+ "title": "Crea un oggetto materiale"
+ },
+ "dialogMaterialItemEdit": {
+ "title": "Modifica della voce materiale"
+ },
+ "materialCreateItem": {
+ "campSettingsButton": "Impostazioni",
+ "noMaterialListAvailable": "Non ci sono ancora liste di materiali per questo campo. Puoi crearle in \"Amministrazione\"."
+ },
+ "materialLists": {
+ "materialsCount": "Nessuna voce | 1 voce | {count} voci",
+ "overview": "Panoramica generale"
+ },
+ "materialTable": {
+ "addNewItem": "Aggiungi un nuovo elemento",
+ "noItems": "Nessun oggetto materiale trovato",
+ "periodOnly": "Filtro: Periodo",
+ "reference": "Attività / Periodo"
+ },
+ "useMaterialViewHelper": {
+ "detail": "Liste di materiali",
+ "overview": "Panoramica generale"
+ }
+ },
+ "navigation": {
+ "userMeta": {
+ "invitations": "Inviti",
+ "logOut": "Disconnettiti",
+ "myCamps": "I miei campi",
+ "profile": "Profilo"
+ }
+ },
+ "personalInvitations": {
+ "dialogPersonalInvitationReject": {
+ "rejectInvitation": "Rifiuta l'invito",
+ "warningText": "Volete davvero declinare l'invito al campo \"{campTitle}\"?"
+ },
+ "personalInvitations": {
+ "noOpenInvitations": "Nessun invito aperto. L'invito è stato inviato a un indirizzo diverso da \"{email}\"?"
+ }
+ },
+ "print": {
+ "config": {
+ "activityConfig": {
+ "activity": "Attività"
+ },
+ "picassoConfig": {
+ "orientation": "Layout della pagina"
+ },
+ "programConfig": {
+ "dayOverview": "Panoramica della giornata di stampa"
+ }
+ },
+ "documents": {
+ "campPrint": {
+ "filename": {
+ "activitiesOnly": "Attività {camp}",
+ "picassoOnly": "Picasso {camp}"
+ }
+ }
+ },
+ "localPdfDownloadButton": {
+ "error": "Qualcosa non ha funzionato. Riprova o ricarica la pagina."
+ },
+ "printClient": {
+ "downloadClientPdfButton": {
+ "label": "Scarica il PDF (layout #2)"
+ },
+ "downloadClientPdfListItem": {
+ "label": "Scarica il PDF (layout #2)"
+ },
+ "generatePdfMixin": {
+ "error": "Qualcosa non ha funzionato. Riprova o ricarica la pagina."
+ },
+ "printPreviewClient": {
+ "previewError": "Qualcosa non ha funzionato. Riprova con altre impostazioni di stampa o ricarica la pagina.",
+ "previewIframeTitle": "Anteprima di stampa"
+ }
+ },
+ "printConfigurator": {
+ "add": "Aggiungi contenuti",
+ "config": {
+ "Activity": "Attività singola",
+ "Cover": "Pagina di copertina",
+ "Picasso": "Picasso",
+ "Program": "Programma",
+ "SafetyConsiderations": "Considerazioni sulla sicurezza",
+ "Story": "Riassunto della storia",
+ "Toc": "Indice dei contenuti"
+ }
+ },
+ "printNuxt": {
+ "downloadNuxtPdfButton": {
+ "label": "Scarica il PDF (layout #1)"
+ },
+ "downloadNuxtPdfListItem": {
+ "label": "Scarica il PDF (layout #1)"
+ },
+ "generatePdfMixin": {
+ "error": "Qualcosa non ha funzionato. Riprova o ricarica la pagina.",
+ "queueFull": "Tutte le nostre stampanti PDF sono occupate al momento. Riprova più tardi."
+ },
+ "printPreviewNuxt": {
+ "openPreview": "Apri l'anteprima in una nuova finestra",
+ "previewError": "Qualcosa non ha funzionato. Riprova con altre impostazioni di stampa o ricarica la pagina.",
+ "previewIframeTitle": "Anteprima di stampa"
+ }
+ }
+ },
+ "program": {
+ "formScheduleEntryItem": {
+ "end": "Fine",
+ "start": "Inizio"
+ },
+ "formScheduleEntryList": {
+ "name": "Programmato"
+ },
+ "periodSwitcher": {
+ "title": "Scegli il periodo"
+ },
+ "picasso": {
+ "picasso": {
+ "datetime": {
+ "fullDate": "dd DD. MMMM YYYY",
+ "smallDate": "dd D.MM. | dd D. MMM | dd D. MMM YY"
+ }
+ },
+ "picassoEntry": {
+ "clipboardInfoDialog": {
+ "allow": "Consenti ora"
+ },
+ "location": "Luogo:",
+ "responsible": "Responsable:"
+ }
+ },
+ "scheduleEntryFilters": {
+ "category": "Categoria",
+ "clearFilters": "Cancella i filtri",
+ "onlyMyActivities": "Solo le mie attività",
+ "period": "Periodo",
+ "progressLabel": "Stati",
+ "progressLabelNone": "Nessuno stato",
+ "responsible": "Responsabile",
+ "responsibleNone": "Nessuna responsabile"
+ }
+ },
+ "prompt": {
+ "promptEntityDelete": {
+ "title": "Davvero cancellare?"
+ }
+ },
+ "story": {
+ "storyDay": {
+ "noStory": "Nessun contenuto di storia trovato in questo giorno..."
+ }
+ },
+ "toast": {
+ "toasts": {
+ "multiLineToast": {
+ "generalError": "Si è verificato un errore."
+ }
+ }
+ },
+ "user": {
+ "dialogChangeMail": {
+ "error": "Si è verificato un errore.",
+ "message": "Riceverai un'e-mail di verifica.",
+ "success": "Prima di poter utilizzare il tuo nuovo indirizzo e-mail, devi verificarlo. Controlla ora la tua casella di posta elettronica.",
+ "title": "Cambia l'indirizzo e-mail"
+ },
+ "dialogChangeMailRunning": {
+ "error": "Si è verificato un errore.",
+ "message": "Modifica dell'indirizzo e-mail ...",
+ "success": "L'indirizzo e-mail è stato modificato con successo.",
+ "title": "Modifica dell'indirizzo e-mail"
+ }
+ }
+ },
+ "global": {
+ "button": {
+ "add": "Aggiungere",
+ "back": "Indietro",
+ "cancel": "Annullare",
+ "close": "Chiudi",
+ "content": "Contenuto",
+ "continue": "Continuare",
+ "create": "Creare",
+ "delete": "Eliminare",
+ "discard": "Scartare",
+ "download": "Scaricare",
+ "edit": "Modificare",
+ "editable": "Modificabile",
+ "filter": "Filtro",
+ "lock": "Blocco",
+ "login": "Accedi",
+ "logout": "Disconnessione",
+ "move": "Spostamento",
+ "ok": "Bene",
+ "open": "Aprire",
+ "remove": "Rimuovere",
+ "rename": "Rinominare",
+ "reset": "Reset",
+ "save": "Salvare",
+ "saving": "Salvataggio",
+ "search": "Ricerca",
+ "share": "Condividi",
+ "submit": "Inviare",
+ "tryagain": "Riprovare",
+ "unlock": "Sblocca",
+ "update": "Aggiornare"
+ },
+ "changeLanguage": "Cambia lingua",
+ "collaborationAbilities": {
+ "guest": "Solo lettura",
+ "manager": "Lettura, scrittura e amministrazione del campo",
+ "member": "Lettura e scrittura"
+ },
+ "datetime": {
+ "vuetifyTimePickerFormat": "24hr"
+ },
+ "info": {
+ "offline": {
+ "description": "Impossibile salvare/caricare dati.",
+ "title": "Sei offline:"
+ }
+ },
+ "language": "Italiano",
+ "loading": "Caricamento ...",
+ "navigation": {
+ "admin": {
+ "title": "Admin"
+ },
+ "help": "Aiuto",
+ "news": "Notizie"
+ },
+ "serverError": {
+ "409": "Oooops... Questa azione ha causato un errore sul lato server.",
+ "short": "Errore del server"
+ },
+ "validation": {
+ "greaterThan": "{_field_} deve essere maggiore di {min}",
+ "greaterThanOrEqual_date": "{_field_} non può essere prima che {min}",
+ "greaterThan_time": "{_camp_} deve essere posteriore a {min}",
+ "lessThanOrEqual_date": "{_camp_} non può essere successivo a {max}",
+ "oneEmojiOrTwoCharacters": "{_field_} può contenere solo 1 emoji o 2 lettere/numeri"
+ },
+ "warning": {
+ "delete": "Volete davvero cancellarlo? | Volete davvero cancellarlo \"{entity}\"?"
+ }
+ },
+ "views": {
+ "auth": {
+ "activate": {
+ "error": "Attivazione fallita",
+ "success": "L'account è stato attivato con successo",
+ "title": "Il tuo account sarà attivato"
+ },
+ "login": {
+ "acceptTermsOfServiceOnOAuthLogin": "Effettuando l'accesso tramite uno di questi servizi, si accettano i {termsOfServiceLink}.",
+ "accountless": "Non hanno ancora un conto?",
+ "email": "Indirizzo e-mail",
+ "infoText": {
+ "dev": "Questa è la versione di sviluppo di eCamp v3.{br}IMPORTANTE: è destinata esclusivamente all'uso per lo sviluppo. Tutti i dati sono pubblici e verranno cancellati periodicamente!{br}Login: test@example.com / test"
+ },
+ "loginCallback": {
+ "loginInProgress": "Sarai loggato"
+ },
+ "or": "oppure con",
+ "password": "Parola d'ordine",
+ "passwordForgotten": "Hai dimenticato la password?",
+ "provider": {
+ "cevidb": "CeviDB",
+ "ecamp": "Accedi con eCamp",
+ "google": "Google",
+ "jubladb": "JublaDB",
+ "midata": "MiData"
+ },
+ "registernow": "Iscriviti ora",
+ "termsOfServiceLink": "condizioni di utilizzo"
+ },
+ "register": {
+ "acceptTermsOfService": "Accettare le condizioni di utilizzo",
+ "alreadyHaveAnAccount": "Hai già un conto?",
+ "passwordConfirmation": "Inserisci di nuovo la password",
+ "register": "Registrati",
+ "requiredField": "campo obbligatorio",
+ "title": "Crea un conto"
+ },
+ "registerDone": {
+ "message": "Ti abbiamo inviato un'e-mail. Completa la tua registrazione cliccando sul link di attivazione contenuto nell'e-mail.",
+ "success": "Registrato con successo",
+ "title": "Crea un conto"
+ },
+ "resetPassword": {
+ "errorMessage": "La reimpostazione della password è fallita.",
+ "invalidRequest": "Link non valido",
+ "password": "Nuova password",
+ "passwordConfirmation": "Nuova password",
+ "send": "Imposta la password",
+ "successMessage": "La nuova password è stata impostata con successo.",
+ "title": "Reimposta la password"
+ },
+ "resetPasswordRequest": {
+ "errorMessage": "La reimpostazione della password è fallita.",
+ "send": "Invia",
+ "successMessage": "Verrà recapitata una mail con il link per reimpostare la password.",
+ "title": "Reimposta la password"
+ }
+ },
+ "camp": {
+ "activity": {
+ "printPreview": "Apri l'anteprima di stampa",
+ "sideBarProgram": {
+ "title": "Attività in questo giorno"
+ }
+ },
+ "admin": {
+ "activity": {
+ "title": "Impostazioni dell'attività"
+ },
+ "adminMaterialLists": {
+ "title": "Liste di materiali"
+ },
+ "collaborators": {
+ "email": "Indirizzo e-mail",
+ "inactiveCollaborators": "Inattivo",
+ "members": "Membri",
+ "openInvitations": "Inviti aperti",
+ "title": "Team"
+ },
+ "info": {
+ "title": "Info sul campo"
+ },
+ "print": {
+ "title": "Stampa il campo"
+ },
+ "sideBarAdmin": {
+ "itemActivity": "Attività",
+ "itemActivitySubtitle": "Categoria, modello, stati",
+ "itemCollaborators": "Team",
+ "itemInfos": "Info sul campo",
+ "itemMaterialLists": "Liste di materiali",
+ "itemPrint": "PDF / Stampa"
+ },
+ "title": "Admin"
+ },
+ "campProgram": {
+ "reminderLockedCreate": "Trascinare per creare è possibile solo in modalità sbloccata.",
+ "reminderLockedMove": "Trascinare per spostarsi è possibile solo in modalità sbloccata.",
+ "title": "Picasso"
+ },
+ "category": {
+ "category": {
+ "clipboardInfoDialog": {
+ "allow": "Consenti ora"
+ },
+ "deleteCategory": "Elimina la categoria",
+ "properties": "Proprietà",
+ "template": "Modello per le nuove attività"
+ },
+ "sideBarCategory": {
+ "title": "Categorie"
+ }
+ },
+ "dashboard": {
+ "activities": "Attività",
+ "columns": {
+ "category": "Categoria",
+ "number": "Numero",
+ "responsible": "Responsabile",
+ "time": "Tempo",
+ "title": "Titolo"
+ },
+ "noEntries": "Non sono state trovate attività. Prova a cancellare i filtri di selezione e/o a ricaricare la pagina.",
+ "today": "Oggi",
+ "welcome": "Benvenuto nel tuo nuovo campo. Non ci sono ancora attività definite."
+ },
+ "invitation": {
+ "acceptCurrentAuth": "Accetta l'invito con un conto corrente",
+ "backToHome": "Torna a casa",
+ "error": "Si è verificato un errore inatteso",
+ "notFound": "Questo invito è già stato accettato o cancellato",
+ "openCamp": "Campo aperto",
+ "register": "Registro",
+ "reject": "Rifiuta l'invito",
+ "successfullyRejected": "L'invito è stato rifiutato con successo",
+ "title": "Invito",
+ "useOtherAuth": "Esci e usa un altro account",
+ "userAlreadyInCamp": "Stai già partecipando a questo campo"
+ },
+ "material": {
+ "materialLists": {
+ "title": "Liste di materiali"
+ },
+ "materialOverview": {
+ "createNewList": "Creare un elenco di materiali",
+ "download": "Scarica la panoramica",
+ "title": "Panoramica generale"
+ },
+ "sideBarMaterialLists": {
+ "title": "Liste di materiali"
+ }
+ },
+ "navigation": {
+ "desktop": {
+ "navTopbar": {
+ "campIsLoading": "Il campo sta caricando",
+ "material": "Material",
+ "program": "Programm",
+ "story": "Storia"
+ }
+ },
+ "mobile": {
+ "navBottombar": {
+ "material": "Materiale",
+ "more": "Più",
+ "program": "Programma",
+ "story": "Storia"
+ },
+ "navSidebar": {
+ "itemActivity": "Attività",
+ "itemActivitySubtitle": "Categoria, modello, stati",
+ "itemCamps": "I miei campi",
+ "itemClose": "Chiudere il menu",
+ "itemCollaborators": "Team",
+ "itemInfos": "Info sul campo",
+ "itemMaterialLists": "Liste di materiali",
+ "itemPrinting": "PDF / Stampa"
+ }
+ }
+ },
+ "story": {
+ "title": "Storia"
+ }
+ },
+ "campCreate": {
+ "title": "Creare il campo"
+ },
+ "camps": {
+ "create": "Creare il campo",
+ "pastCamps": "Campi passati",
+ "prototypeCamps": "Prototipi",
+ "title": "I miei campi"
+ },
+ "invitations": {
+ "personalInvitations": "Inviti"
+ },
+ "pageNotFound": {
+ "backToHome": "Torna a casa",
+ "detail": "Ops. Hai perso la strada…{br}Purtroppo questo link non funziona."
+ },
+ "profile": {
+ "changeEmail": "Cambiamento",
+ "profile": "Profilo"
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend-old/src/locales/rm-CH-scout.json b/frontend-old/src/locales/rm-CH-scout.json
new file mode 100644
index 0000000000..873a2203c2
--- /dev/null
+++ b/frontend-old/src/locales/rm-CH-scout.json
@@ -0,0 +1,24 @@
+{
+ "contentNode": {
+ "storycontext": {
+ "name": "Fil cotschen"
+ }
+ },
+ "entity": {
+ "user": {
+ "fields": {
+ "nickname": "Num da battasendas"
+ }
+ }
+ },
+ "global": {
+ "language": "Rumantsch (Battasendas)"
+ },
+ "views": {
+ "camp": {
+ "story": {
+ "title": "Fil cotschen"
+ }
+ }
+ }
+}
diff --git a/frontend-old/src/locales/rm.json b/frontend-old/src/locales/rm.json
new file mode 100644
index 0000000000..7d76764969
--- /dev/null
+++ b/frontend-old/src/locales/rm.json
@@ -0,0 +1,641 @@
+{
+ "components": {
+ "activity": {
+ "content": {
+ "columnLayout": {
+ "columnOperations": {
+ "addColumn": "Agiuntar ina colonna",
+ "removeColumn": "Stizzar la colonna vida"
+ }
+ },
+ "storyboard": {
+ "controls": "Acziuns",
+ "reorder": "Spustar",
+ "storyboardDialogRemoveSection": {
+ "deleteWarning": "Vuls ti propi stizzar questa part?",
+ "title": "Propi stizzar?"
+ }
+ }
+ },
+ "dialog": {
+ "dialogActivityEdit": {
+ "title": "Modifitgar l'activitad"
+ },
+ "formScheduleEntryList": {
+ "name": "Termins planisads"
+ }
+ },
+ "menuCardlessContentNode": {
+ "deletingDisabled": "Sto esser vid per stizzar"
+ },
+ "scheduleEntry": {
+ "backToContents": "Enavos per modifitgar il cuntegn",
+ "changeLayout": "Midar il layout",
+ "deleteWarning": "Vuls ti propi stizzar questa activitad? I vegn stizzà l'entir cuntegn da l'activitad."
+ }
+ },
+ "camp": {
+ "campInvitations": {
+ "title": "Invit"
+ },
+ "campMembers": {
+ "title": "Commembers"
+ }
+ },
+ "campAdmin": {
+ "campActivityProgressLabels": {
+ "create": "Crear in status",
+ "deleteError": "Impussibel da stizzar il status. Il status vegn anc utilisà.",
+ "deleteWarning": "Vuls ti propi stizzar quest status?",
+ "exit": "Terminar il reordinar",
+ "moveDown": "Engiu",
+ "moveUp": "Ensi",
+ "reorder": "Reordinatur",
+ "title": "Status da l'activitad"
+ },
+ "campAddress": {
+ "title": "Adressa dal champ"
+ },
+ "campCategories": {
+ "create": "Crear ina categoria dad activitad",
+ "title": "Categorias d'activitads"
+ },
+ "campConditionalFields": {
+ "course": {
+ "title": "Curs"
+ },
+ "title": "Parameters per G+S",
+ "ysCamp": {
+ "title": "Champs"
+ }
+ },
+ "campDangerZone": {
+ "deleteCamp": {
+ "description": "Sche ti stizzas in champ è quai definitiv. Cuntinuescha mo sche ti es segir.",
+ "explanation": "Questa acziun na po betg vegnir revocada. Uschia vegn il champ \"{campTitle}\" stizzà permanentamain cun tut sias activitads, glistas da material ed urdens da responsabladad.",
+ "label": "Endatescha \"{campTitle}\" per confermar.",
+ "title": "Stizzar il champ"
+ },
+ "title": "Zona da privel"
+ },
+ "campMaterialLists": {
+ "createMaterialList": "Crear ina glista da material",
+ "title": "Glistas da material"
+ },
+ "campPeriods": {
+ "createPeriod": "Crear ina part dal champ",
+ "title": "Part dal champ | Parts dal champ"
+ },
+ "campPeriodsListItem": {
+ "changePeriodDescription": "Midar la descripziun",
+ "deleteWarning": "Vuls ti propi stizzar questa part?",
+ "lastPeriodNotDeletable": "Impussibel da stizzar l'ultima part.",
+ "movePeriod": "Spustar questa part dal champ",
+ "periodChangeEnd": "Agiuntar/allontanar dis a la fin",
+ "periodChangeStart": "Agiuntar/allontanar dis a l'entschatta"
+ },
+ "campSettings": {
+ "title": "Descripziun"
+ },
+ "createCampPeriods": {
+ "add": "Agiuntar in ulteriura part dal champ"
+ },
+ "dialogActivityProgressLabelCreate": {
+ "title": "Crear in status"
+ },
+ "dialogActivityProgressLabelEdit": {
+ "title": "Modifitgar il status"
+ },
+ "dialogCategoryCreate": {
+ "title": "Crear ina categoria d'activitad"
+ },
+ "dialogMaterialListCreate": {
+ "title": "Crear ina glista da material"
+ },
+ "dialogMaterialListEdit": {
+ "deleteError": "Impussibel da stizzar la glista da material. Verifitgescha che la glista è vida avant che stizzar.",
+ "title": "Modifitgar la glista da material"
+ },
+ "dialogPeriodCreate": {
+ "title": "Craer ina part dal champ"
+ },
+ "dialogPeriodDateEdit": {
+ "movePeriod": "Spustar la part dal champ",
+ "periodChangeEnd": "Agiuntar/allontanar dis a la fin da la part dal champ",
+ "periodChangeStart": "Agiuntar/allontanar dis a l'entschatta da la part dal champ",
+ "periodDuration": "Dumber da dis"
+ },
+ "dialogPeriodEdit": {
+ "moveScheduleEntries": "Spustar las activitads"
+ },
+ "errorExistingActivitiesList": {
+ "description": "I na po betg vegnir extendì. Las suandantas activitads vegnan anc utilisadas:"
+ }
+ },
+ "category": {
+ "categoryTemplate": {
+ "contents": "Cuntegns",
+ "createLayoutHelp": "Qua pudais vus definir il project per novas activitads da {categoryShort}.{br}Il cuntegn ed il layout ch'èn gia vegnidas fatgas da {categoryShort} na vegnan betg adattads.",
+ "layout": "Layout",
+ "noTemplate": "Nagin model"
+ }
+ },
+ "collaborator": {
+ "collaboratorCreate": {
+ "invite": "Trametter l'invitaziun",
+ "inviteCta": "Envidar en il team",
+ "title": "Envidar ina persuna"
+ },
+ "collaboratorEdit": {
+ "cannotRemoveLastManager": "Ti na pos betg deactivar l'ultima persuna cun dretgs d'administraziun.",
+ "deactivate": "Deactivar",
+ "delete": "Vuls ti propi allontanar la persuna '{name}'?",
+ "inviteAgain": "Envidar anc ina giada",
+ "resendEmail": "Trametter anc ina giada l'e-mail",
+ "resentEmail": "Tramess l'invit",
+ "title": "Modifitgar {user}"
+ },
+ "collaboratorForm": {
+ "roleHint": "Mintga champ dovra almain ina persuna cun dretgs d'administraziun."
+ },
+ "promptCollaboratorDeactivate": {
+ "deactivate": "Deactivar",
+ "warningText": "Vuls ti propi deactivar '{name}'?"
+ }
+ },
+ "dashboard": {
+ "selectFilter": {
+ "clear": "Reinizialisar"
+ }
+ },
+ "dialog": {
+ "dialogEntityDelete": {
+ "title": "Propi stizzar?"
+ }
+ },
+ "form": {
+ "base": {
+ "eColorPicker": {
+ "closePicker": "Terminar il dialog",
+ "openPicker": "Avrir il dialog per tscherner ina colur per {label}"
+ },
+ "eDatePicker": {
+ "invalidFormat": "Format nunvalid, inditgescha per plaschair la data en il format DD-MM-YYYY",
+ "openPicker": "Avrir il dialog per tscherner ina data per {label}"
+ },
+ "eTimePicker": {
+ "invalidFormat": "Format nunvalid, inditgescha per plaschair il temp en il format HH:MM",
+ "openPicker": "Avrir il dialog per tscherner in temp per {label}"
+ }
+ }
+ },
+ "generic": {
+ "lockButton": {
+ "clickToLock": "Cliccar per bloccar",
+ "clickToUnlock": "Cliccar per debloccar",
+ "guestsCannotEdit": "Giasts na pon betg modifitgar"
+ }
+ },
+ "layout": {
+ "authContainer": {
+ "photoCredits": "Foto da Markus Rohner / Lotos"
+ }
+ },
+ "material": {
+ "dialogMaterialItemCreate": {
+ "title": "Crear ina endataziun da material"
+ },
+ "dialogMaterialItemEdit": {
+ "title": "Modifitgar l'endataziun da material"
+ },
+ "materialCreateItem": {
+ "campSettingsButton": "Parameters",
+ "noMaterialListAvailable": "Per quest champ n'èn anc endatadas naginas glistas da material. Ti las pos endatar sut 'Admin'."
+ },
+ "materialLists": {
+ "materialsCount": "Naginas endataziuns | 1 endataziun | {count} endataziuns",
+ "overview": "Survista"
+ },
+ "materialTable": {
+ "addNewItem": "Agiuntar ina endataziun",
+ "noItems": "Na chattà naginas endataziuns",
+ "periodOnly": "Filter: part dal champ",
+ "reference": "Activitad / part"
+ },
+ "useMaterialViewHelper": {
+ "detail": "Glista da material",
+ "overview": "Survista"
+ }
+ },
+ "navigation": {
+ "userMeta": {
+ "invitations": "Invitaziuns",
+ "logOut": "Sortir",
+ "myCamps": "Mes champ",
+ "profile": "Profil"
+ }
+ },
+ "personalInvitations": {
+ "dialogPersonalInvitationReject": {
+ "rejectInvitation": "Refusar l'invit",
+ "warningText": "Vulais Vus propi refusar l'invitaziun al champ \"{campTitle}\"?"
+ },
+ "personalInvitations": {
+ "noOpenInvitations": "Naginas invitaziuns avertas. È l'invitaziun vegnida tramessa ad in'autra adressa che \"{email}\"?"
+ }
+ },
+ "print": {
+ "config": {
+ "activityConfig": {
+ "activity": "Activitad"
+ },
+ "picassoConfig": {
+ "orientation": "Layout da la pagina"
+ },
+ "programConfig": {
+ "dayOverview": "Survista dal di"
+ }
+ },
+ "documents": {
+ "campPrint": {
+ "filename": {
+ "activitiesOnly": "Activitads {camp}",
+ "picassoOnly": "Program general {camp}"
+ }
+ }
+ },
+ "localPdfDownloadButton": {
+ "error": "Insatge n'ha betg funcziunà. Emprova anc ina giada u rechargia la pagina."
+ },
+ "printClient": {
+ "downloadClientPdfButton": {
+ "label": "Telechargiar il PDF (layout #2)"
+ },
+ "downloadClientPdfListItem": {
+ "label": "Telechargiar il PDF (layout #2)"
+ },
+ "generatePdfMixin": {
+ "error": "Insatge n'ha betg funcziunà. Emprova anc ina giada u rechargia la pagina."
+ },
+ "printPreviewClient": {
+ "previewError": "Insatge n'ha betg funcziunà. Emprova anc ina giada cun auters parameters da stampa u rechargia la pagina.",
+ "previewIframeTitle": "Prevista da stampa"
+ }
+ },
+ "printConfigurator": {
+ "add": "Agiuntar cuntegn",
+ "config": {
+ "Activity": "Singula activitad",
+ "Cover": "Pagina da titel",
+ "Picasso": "Program general",
+ "Program": "Program en detagl",
+ "Story": "Fil cotschen",
+ "Toc": "Tavla da cuntegn"
+ }
+ },
+ "printNuxt": {
+ "downloadNuxtPdfButton": {
+ "label": "Telechargiar il PDF (layout #1)"
+ },
+ "downloadNuxtPdfListItem": {
+ "label": "Telechargiar il PDF (layout #1)"
+ },
+ "generatePdfMixin": {
+ "error": "Insatge n'ha betg funcziunà. Emprova anc ina giada u rechargia la pagina.",
+ "queueFull": "Tut noss stampaders da PDF èn occupads per il mument. Emprova per plaschair pli tard anc ina giada."
+ },
+ "printPreviewNuxt": {
+ "openPreview": "Avrir la prevista en ina nova fanestra",
+ "previewError": "Insatge n'ha betg funcziunà. Emprova anc ina giada cun auters parameters da stampa u rechargia la pagina.",
+ "previewIframeTitle": "Prevista da stampa"
+ }
+ }
+ },
+ "program": {
+ "formScheduleEntryItem": {
+ "end": "Fin",
+ "start": "Entschatta"
+ },
+ "formScheduleEntryList": {
+ "name": "Termins planisads"
+ },
+ "periodSwitcher": {
+ "title": "Tscherner ina part dal champ"
+ },
+ "picasso": {
+ "picasso": {
+ "datetime": {
+ "fullDate": "dd, DD. MMMM YYYY",
+ "smallDate": "dd, D.MM. | dd, D. MMM | dd, D. MMM YY"
+ }
+ },
+ "picassoEntry": {
+ "location": "Lieu:",
+ "responsible": "Responsabel: "
+ }
+ },
+ "scheduleEntryFilters": {
+ "category": "Categoria",
+ "clearFilters": "Allontanar il filter",
+ "onlyMyActivities": "Mo mias activitads",
+ "period": "Part dal champ",
+ "progressLabel": "Status",
+ "progressLabelNone": "Nagin status",
+ "responsible": "Responsabel",
+ "responsibleNone": "Naginas persunas responsablas"
+ }
+ },
+ "prompt": {
+ "promptEntityDelete": {
+ "title": "Propi stizzar?"
+ }
+ },
+ "toast": {
+ "toasts": {
+ "multiLineToast": {
+ "generalError": "I ha dà in sbagl."
+ }
+ }
+ },
+ "user": {
+ "dialogChangeMail": {
+ "error": "Deplorablamain hai dà in sbagl.",
+ "message": "Per la verificaziun ta tramettain nus in e-mail.",
+ "success": "Avant che ti pos utilisar la nova adressa dad e-mail, stos ti la verifitgar. Controllescha tia posta entrada.",
+ "title": "Midar l'adressa dad e-mail"
+ },
+ "dialogChangeMailRunning": {
+ "error": "Deplorablamain hai dà in sbagl.",
+ "message": "L'adressa dad e-mail vegn midada...",
+ "success": "Midà cun success l'adressa dad e-mail.",
+ "title": "Midar l'adressa dad e-mail"
+ }
+ }
+ },
+ "global": {
+ "button": {
+ "add": "Agiuntar",
+ "back": "Enavos",
+ "cancel": "Interrumper",
+ "close": "Serrar",
+ "content": "Cuntegn",
+ "create": "Crear",
+ "delete": "Stizzar",
+ "discard": "Sbittar",
+ "download": "Telechargiar",
+ "edit": "Modifitgar",
+ "editable": "Modifitgabel",
+ "filter": "Filtrar",
+ "lock": "Bloccar",
+ "login": "S'annunziar",
+ "logout": "Sortir",
+ "move": "Spustar",
+ "ok": "OK",
+ "open": "Avrir",
+ "remove": "Allontanar",
+ "rename": "Renumnar",
+ "reset": "Reparter",
+ "save": "Memorisar",
+ "saving": "Memorisà",
+ "search": "Tschertgar",
+ "submit": "Trametter",
+ "tryagain": "Empruvar anc ina giada",
+ "unlock": "Debloccar",
+ "update": "Actualisar"
+ },
+ "changeLanguage": "Midar la lingua",
+ "collaborationAbilities": {
+ "guest": "Dretg mo per lectura",
+ "manager": "Administraziun cumpletta dal champ, sco era dretg da leger e scriver",
+ "member": "Dretg da leger e scriver"
+ },
+ "datetime": {
+ "vuetifyTimePickerFormat": "24 h"
+ },
+ "info": {
+ "offline": {
+ "description": "Salvament/chargiament da datas nu è pussaivel.",
+ "title": "Ti es offline:"
+ }
+ },
+ "language": "Rumantsch",
+ "loading": "Chargiar…",
+ "navigation": {
+ "admin": {
+ "title": "Admin"
+ },
+ "help": "Agid",
+ "news": "Noviteds"
+ },
+ "serverError": {
+ "409": "Uuuups... Questa acziun ha chaschunà ina errur sin il server."
+ },
+ "validation": {
+ "greaterThanOrEqual_date": "{_field_} na dastga betg esser avant {min}",
+ "greaterThan_time": "{_field_} sto esser suenter {min}",
+ "lessThanOrEqual_date": "{_field_} na dastga betg esser suenter {max}"
+ },
+ "warning": {
+ "delete": "Vuls ti propi stizzar quai? | Vuls ti propi stizzar \"{entity}\"?"
+ }
+ },
+ "views": {
+ "auth": {
+ "activate": {
+ "error": "L'activaziun n'è betg reussida",
+ "success": "Activà cun success il conto",
+ "title": "Tes conto vegn activà"
+ },
+ "login": {
+ "acceptTermsOfServiceOnOAuthLogin": "Cun s'annunziar via in da quests servetschs acceptas ti las {termsOfServiceLink}.",
+ "accountless": "N'has ti anc nagin conto?",
+ "email": "E-mail",
+ "infoText": {
+ "dev": "Quai è la versiun per sviluppaders dad eCamp v3.{br}IMPURTANT: Mo adattà per il diever da sviluppaders. Tut las datas èn publicas e vegnan stizzadas regularmain!{br}Annunzia: test@example.com / test"
+ },
+ "loginCallback": {
+ "loginInProgress": "Ti vegns annunzià"
+ },
+ "or": "u cun",
+ "password": "pled-clav",
+ "passwordForgotten": "Emblidà il pled-clav?",
+ "provider": {
+ "cevidb": "CeviDB",
+ "ecamp": "S'annunziar cun eCamp",
+ "google": "Google",
+ "jubladb": "JublaDB",
+ "midata": "MiData"
+ },
+ "registernow": "Ussa sa registrar",
+ "termsOfServiceLink": "cundiziuns d'utilisaziun"
+ },
+ "register": {
+ "acceptTermsOfService": "Acceptar las cundiziuns d'utilisaziun",
+ "alreadyHaveAnAccount": "Has ti gia in conto?",
+ "passwordConfirmation": "Endatar anc ina giada il pled-clav",
+ "register": "Registrar",
+ "requiredField": "Champs obligatorics",
+ "title": "Crear in conto"
+ },
+ "registerDone": {
+ "message": "Nus avain ta tramess in e-mail. Terminescha per plaschair la registraziun cun avrir la colliaziun d'activaziun en l'e-mail.",
+ "success": "Registrà cun success",
+ "title": "Crear in conto"
+ },
+ "resetPassword": {
+ "errorMessage": "Impussibel da midar il pled-clav.",
+ "invalidRequest": "Colliaziun nunvalida",
+ "password": "Nov pled-clav",
+ "passwordConfirmation": "Repeter il nov pled-clav",
+ "send": "Definir il pled-clav",
+ "successMessage": "Definì cun success il nov pled-clav.",
+ "title": "Definir in nov pled-clav"
+ },
+ "resetPasswordRequest": {
+ "errorMessage": "Impussibel da reinizialisar il pled-clav.",
+ "send": "Trametter",
+ "successMessage": "In e-mail cun la colliaziun per 'reinizialisar il pled-clav' vegn tramess.",
+ "title": "Reinizialisar il pled-clav"
+ }
+ },
+ "camp": {
+ "activity": {
+ "printPreview": "Avrir la prevista da stampa",
+ "sideBarProgram": {
+ "title": "Survista dal di"
+ }
+ },
+ "admin": {
+ "activity": {
+ "title": "Parameters dad activitad"
+ },
+ "adminMaterialLists": {
+ "title": "Glistas da material"
+ },
+ "collaborators": {
+ "email": "Adressa dad e-mail",
+ "inactiveCollaborators": "Inactiv",
+ "members": "Collavuratur*as",
+ "openInvitations": "Invits averts",
+ "title": "Team"
+ },
+ "info": {
+ "title": "Infus dal champ"
+ },
+ "print": {
+ "title": "Stampar il champ"
+ },
+ "sideBarAdmin": {
+ "itemActivity": "Activitads",
+ "itemActivitySubtitle": "Categoria, model e status",
+ "itemCollaborators": "Team",
+ "itemInfos": "Infus dal champ",
+ "itemMaterialLists": "Glistas da material",
+ "itemPrint": "PDF / stampar"
+ },
+ "title": "Admin"
+ },
+ "campProgram": {
+ "reminderLockedCreate": "Trair per crear è mo pussaivel en il modus debloccà.",
+ "reminderLockedMove": "Trair per spustar è mo pussaivel en il modus debloccà.",
+ "title": "Program general"
+ },
+ "category": {
+ "category": {
+ "deleteCategory": "Stizzar la categoria",
+ "properties": "Proprietad",
+ "template": "Model per novas activitads"
+ },
+ "sideBarCategory": {
+ "title": "Categorias"
+ }
+ },
+ "dashboard": {
+ "activities": "Activitads",
+ "columns": {
+ "category": "Categoria",
+ "number": "Numer",
+ "responsible": "Responsabel",
+ "time": "Temp",
+ "title": "Titel"
+ },
+ "noEntries": "Chattà naginas activitads. Emprova dad allontanar ils filters u rechargia la pagina.",
+ "welcome": "Bainvegni en tes nov champ. Anc n'èn vegnidas endatadas naginas activitads."
+ },
+ "invitation": {
+ "acceptCurrentAuth": "Acceptar l'invit cun il conto actual",
+ "backToHome": "Enavos a la pagina da partenza",
+ "error": "Ina errur nunspetgada è succedida",
+ "notFound": "L'invit è gia acceptà u stizzà",
+ "openCamp": "Avrir il champ",
+ "register": "Registrar",
+ "reject": "Refusar l'invit",
+ "successfullyRejected": "L'invit è vegnì refusà cun success",
+ "title": "Invit",
+ "useOtherAuth": "Sortir ed utilisar in auter conto",
+ "userAlreadyInCamp": "Ti collavureschas gia en quest champ"
+ },
+ "material": {
+ "materialOverview": {
+ "createNewList": "Crear ina glista da material",
+ "download": "Telechargiar la survista",
+ "title": "Survista"
+ },
+ "sideBarMaterialLists": {
+ "title": "Glistas da material"
+ }
+ },
+ "navigation": {
+ "desktop": {
+ "navTopbar": {
+ "campIsLoading": "Il champ vegn chargià",
+ "material": "Material",
+ "program": "Program",
+ "story": "Istorgia"
+ }
+ },
+ "mobile": {
+ "navBottombar": {
+ "material": "Material",
+ "more": "Dapli",
+ "program": "Program",
+ "story": "Istorgia"
+ },
+ "navSidebar": {
+ "itemActivity": "Activitads",
+ "itemActivitySubtitle": "Categoria, models e status",
+ "itemCamps": "Mes champs",
+ "itemClose": "Serrar il menu",
+ "itemCollaborators": "Team",
+ "itemInfos": "Infus dal champ",
+ "itemMaterialLists": "Glistas da material",
+ "itemPrinting": "PDF / stampar"
+ }
+ }
+ },
+ "story": {
+ "title": "Istorgia"
+ }
+ },
+ "campCreate": {
+ "title": "Crear in champ"
+ },
+ "camps": {
+ "create": "Crear il champ",
+ "pastCamps": "Champs vegls",
+ "prototypeCamps": "Models",
+ "title": "Mes champs"
+ },
+ "invitations": {
+ "personalInvitations": "Invitaziuns"
+ },
+ "pageNotFound": {
+ "backToHome": "Enavos a la pagina da partenza",
+ "detail": "Hopla. Ti has pers la via…{br}Questa colliaziun na funcziuna deplorablamain betg."
+ },
+ "profile": {
+ "changeEmail": "Midar",
+ "profile": "Profil"
+ }
+ }
+}
\ No newline at end of file
diff --git a/frontend-old/src/main.js b/frontend-old/src/main.js
new file mode 100644
index 0000000000..6f98317b3b
--- /dev/null
+++ b/frontend-old/src/main.js
@@ -0,0 +1,69 @@
+import Vue from 'vue'
+import App from './App.vue'
+import router from '@/router.js'
+import {
+ vuetifyLoader,
+ auth,
+ head,
+ unhead,
+ storeLoader,
+ formBaseComponents,
+ ignoreNativeBindingWarnMessages,
+ i18n,
+ dayjs,
+ color,
+ veeValidate,
+} from './plugins'
+import { store } from './plugins/store'
+import { vuetify } from './plugins/vuetify'
+import { getEnv } from '@/environment.js'
+import * as Sentry from '@sentry/vue'
+import Toast from 'vue-toastification'
+import 'vue-toastification/dist/index.css'
+
+import { ClickOutside, Resize } from 'vuetify/lib/directives'
+import ResizeObserver from 'v-resize-observer'
+import { initRefresh } from '@/plugins/auth.js'
+
+const env = getEnv()
+if (env && env.SENTRY_FRONTEND_DSN) {
+ const sentryEnvironment = env.SENTRY_ENVIRONMENT ?? 'local'
+ Sentry.init({
+ Vue,
+ dsn: env.SENTRY_FRONTEND_DSN,
+ environment: sentryEnvironment,
+ enableTracing: false,
+ autoSessionTracking: false,
+ logErrors: process.env.NODE_ENV !== 'production',
+ })
+}
+
+Vue.use(auth)
+Vue.use(head)
+Vue.use(formBaseComponents)
+Vue.use(ignoreNativeBindingWarnMessages)
+Vue.use(storeLoader)
+Vue.use(vuetifyLoader)
+Vue.use(dayjs)
+Vue.use(color)
+Vue.use(veeValidate)
+Vue.use(Toast, {
+ maxToasts: 2,
+})
+
+// manually importing necessary vuetify directives (there's no auomatic vuetify-loader for vitejs)
+Vue.directive('click-outside', ClickOutside)
+Vue.directive('resize', Resize)
+Vue.directive('resizeobserver', ResizeObserver.directive)
+
+new Vue({
+ router,
+ store,
+ vuetify,
+ i18n,
+ unhead,
+ render: (h) => h(App),
+}).$mount('#app')
+
+// noinspection JSIgnoredPromiseFromCall
+initRefresh()
diff --git a/frontend-old/src/mixins/apiPropsMixin.js b/frontend-old/src/mixins/apiPropsMixin.js
new file mode 100644
index 0000000000..8e056d1559
--- /dev/null
+++ b/frontend-old/src/mixins/apiPropsMixin.js
@@ -0,0 +1,55 @@
+export const apiPropsMixin = {
+ inheritAttrs: false,
+ inject: {
+ apiUri: { default: null },
+ },
+ props: {
+ /* value is not required; by default value is read directly from api */
+ value: { required: false, default: null },
+
+ /* field path and URI for saving back to API */
+ path: { type: String, required: true },
+
+ /* load default value from apiObject (via ApiForm injection) */
+ uri: {
+ type: String,
+ required: false,
+ default() {
+ if (this.apiUri === null) {
+ throw new Error(
+ 'ApiWrapper: `uri` not set on component; no ApiForm component found as parent for fallback'
+ )
+ }
+ return this.apiUri
+ },
+ },
+
+ /* overrideDirty=true will reset the input if 'value' changes, even if the input is dirty. Use with caution. */
+ overrideDirty: { type: Boolean, default: false, required: false },
+
+ /* enable/disable edit mode */
+ readonly: { type: Boolean, required: false, default: false }, // vuetify readonly: same look and feel as normal, but no changes possible
+ disabled: { type: Boolean, required: false, default: false }, // vuetify disabled: input greyed out, not focusable
+
+ /* enable/disable auto save */
+ autoSave: { type: Boolean, default: true, required: false },
+ autoSaveDelay: { type: Number, default: 800, required: false },
+
+ /* control style */
+ filled: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ outlined: {
+ type: Boolean,
+ default: true,
+ required: false,
+ },
+ dense: {
+ type: Boolean,
+ default: false,
+ required: false,
+ },
+ },
+}
diff --git a/frontend-old/src/mixins/campRoleMixin.js b/frontend-old/src/mixins/campRoleMixin.js
new file mode 100644
index 0000000000..83fbe7f4e9
--- /dev/null
+++ b/frontend-old/src/mixins/campRoleMixin.js
@@ -0,0 +1,41 @@
+export const campRoleMixin = {
+ computed: {
+ isContributor() {
+ return this.isMember || this.isManager
+ },
+ isGuest() {
+ return this.role === 'guest'
+ },
+ isManager() {
+ return this.role === 'manager'
+ },
+ isMember() {
+ return this.role === 'member'
+ },
+ isOutsider() {
+ return this.camp && typeof this._campCollaborations === 'function' && !this.role
+ },
+ role() {
+ const currentUserLink = this.$store.getters.getLoggedInUser?._meta.self
+ const result = this._campCollaborations
+ .filter((coll) => typeof coll.user === 'function')
+ .find((coll) => coll.user()._meta.self === currentUserLink)
+
+ if (result?._meta.loading) return null
+ return result?.role
+ },
+ _campCollaborations() {
+ if (!this.camp) return []
+ if (typeof this.camp.campCollaborations !== 'function') {
+ return []
+ }
+ return this._camp?.campCollaborations()?.items
+ },
+ _camp() {
+ if (typeof this.camp === 'function') {
+ return this.camp()
+ }
+ return this.camp
+ },
+ },
+}
diff --git a/frontend-old/src/mixins/contentNodeMixin.js b/frontend-old/src/mixins/contentNodeMixin.js
new file mode 100644
index 0000000000..6490727fb5
--- /dev/null
+++ b/frontend-old/src/mixins/contentNodeMixin.js
@@ -0,0 +1,14 @@
+export const contentNodeMixin = {
+ props: {
+ contentNode: { type: Object, required: true },
+ layoutMode: { type: Boolean, required: true },
+ draggable: { type: Boolean, default: false },
+ disabled: { type: Boolean, default: false },
+ },
+ inject: ['camp'],
+ computed: {
+ camp() {
+ return this.camp()
+ },
+ },
+}
diff --git a/frontend-old/src/mixins/dateHelperUTCFormatted.js b/frontend-old/src/mixins/dateHelperUTCFormatted.js
new file mode 100644
index 0000000000..10ddf660e0
--- /dev/null
+++ b/frontend-old/src/mixins/dateHelperUTCFormatted.js
@@ -0,0 +1,43 @@
+import {
+ dateShort,
+ dateLong,
+ hourShort,
+ hourLong,
+ timeDurationShort,
+ rangeTime,
+ rangeShort,
+ rangeLongEnd,
+ dateRange,
+} from '@/common/helpers/dateHelperUTCFormatted.js'
+
+export const dateHelperUTCFormatted = {
+ methods: {
+ dateShort(dateTimeString) {
+ return dateShort(dateTimeString, this.$tc.bind(this))
+ },
+ dateLong(dateTimeString) {
+ return dateLong(dateTimeString, this.$tc.bind(this))
+ },
+ hourShort(dateTimeString) {
+ return hourShort(dateTimeString, this.$tc.bind(this))
+ },
+ hourLong(dateTimeString) {
+ return hourLong(dateTimeString, this.$tc.bind(this))
+ },
+ timeDurationShort(start, end) {
+ return timeDurationShort(start, end, this.$tc.bind(this))
+ },
+ rangeTime(start, end) {
+ return rangeTime(start, end, this.$tc.bind(this))
+ },
+ rangeShort(start, end) {
+ return rangeShort(start, end, this.$tc.bind(this))
+ },
+ rangeLongEnd(start, end) {
+ return rangeLongEnd(start, end, this.$tc.bind(this))
+ },
+ dateRange(start, end) {
+ return dateRange(start, end, this.$tc.bind(this))
+ },
+ },
+}
diff --git a/frontend-old/src/mixins/formComponentMixin.js b/frontend-old/src/mixins/formComponentMixin.js
new file mode 100644
index 0000000000..0e1ba4bdf2
--- /dev/null
+++ b/frontend-old/src/mixins/formComponentMixin.js
@@ -0,0 +1,28 @@
+export const formComponentMixin = {
+ props: {
+ // ID for vee-validation
+ veeId: {
+ type: String,
+ required: false,
+ default: null,
+ },
+
+ // rules for vee-validation
+ veeRules: {
+ type: [String, Object],
+ required: false,
+ default: '',
+ },
+ },
+ computed: {
+ required() {
+ if ('object' === typeof this.veeRules) {
+ return Object.keys(this.veeRules).includes('required')
+ }
+ return this.veeRules
+ .split('|')
+ .map((rule) => rule.replace(/:.*/, ''))
+ .includes('required')
+ },
+ },
+}
diff --git a/frontend-old/src/mixins/formComponentPropsMixin.js b/frontend-old/src/mixins/formComponentPropsMixin.js
new file mode 100644
index 0000000000..ed2c5f3457
--- /dev/null
+++ b/frontend-old/src/mixins/formComponentPropsMixin.js
@@ -0,0 +1,86 @@
+export const formComponentPropsMixin = {
+ inject: {
+ entityName: { default: null },
+ },
+ props: {
+ id: {
+ type: String,
+ required: false,
+ default: null,
+ },
+
+ // vuetify property hideDetails
+ filled: {
+ type: Boolean,
+ default: true,
+ },
+
+ // vuetify property hideDetails
+ hideDetails: {
+ type: [String, Boolean],
+ default: 'auto',
+ },
+
+ // set classes on input
+ inputClass: {
+ type: String,
+ default: '',
+ required: false,
+ },
+
+ /**
+ * used as field path for validation
+ * and together with entityName as label (if no override label is provided)
+ */
+ path: {
+ type: String,
+ required: false,
+ default: null,
+ },
+
+ /**
+ * override the automatic entity field label
+ */
+ label: {
+ type: String,
+ required: false,
+ default: undefined,
+ },
+
+ /**
+ * override the automatic validation field name
+ */
+ validationLabelOverride: {
+ type: String,
+ required: false,
+ default: undefined,
+ },
+
+ // error messages from outside which should be displayed on the component
+ errorMessages: {
+ type: Array,
+ required: false,
+ default: () => [],
+ },
+ },
+ computed: {
+ labelOrEntityFieldLabel() {
+ if (this.label !== undefined) {
+ return this.label
+ }
+ if (!this.entityName || !this.path) {
+ return null
+ }
+ return this.$t(`entity.${this.entityName}.fields.${this.path}`)
+ },
+ validationLabel() {
+ if (this.validationLabelOverride !== undefined) {
+ return this.validationLabelOverride
+ }
+ if (this.label) {
+ return this.label
+ }
+ return this.$t(`entity.${this.entityName}.fields.${this.path}`)
+ },
+ },
+}
diff --git a/frontend-old/src/mixins/passwordStrengthMixin.js b/frontend-old/src/mixins/passwordStrengthMixin.js
new file mode 100644
index 0000000000..4643f90c06
--- /dev/null
+++ b/frontend-old/src/mixins/passwordStrengthMixin.js
@@ -0,0 +1,32 @@
+import { throttle } from 'lodash-es'
+
+export const passwordStrengthMixin = {
+ data: () => ({
+ passwordStrength: 0,
+ passwordStrengthColor: 'green',
+ }),
+ methods: {
+ async strength(password, lang = this.$store.state.lang.language.substring(0, 2)) {
+ if (password.length === 0) {
+ this.passwordStrength = 0
+ } else {
+ // Use dynamic import for large dependency which is only used on very few pages
+ const { passwordStrength } = await import('@/plugins/passwordStrength.js')
+ const strengthInfo = await passwordStrength(password, lang)
+ this.passwordStrength = (1 + strengthInfo.score) * 20
+ }
+ return this.passwordStrength
+ },
+ async strengthColor(password) {
+ const strength = await this.strength(password)
+ this.passwordStrengthColor = 'green'
+ if (strength <= 75) this.passwordStrengthColor = 'yellow'
+ if (strength <= 50) this.passwordStrengthColor = 'orange'
+ if (strength <= 25) this.passwordStrengthColor = 'red'
+ return this.passwordStrengthColor
+ },
+ debouncedPasswordStrengthCheck: throttle(function (password) {
+ this.strengthColor(password).then()
+ }, 250),
+ },
+}
diff --git a/frontend-old/src/pdf b/frontend-old/src/pdf
new file mode 120000
index 0000000000..c41d6753f8
--- /dev/null
+++ b/frontend-old/src/pdf
@@ -0,0 +1 @@
+../../pdf/dist
\ No newline at end of file
diff --git a/frontend-old/src/plugins/__tests__/auth.spec.js b/frontend-old/src/plugins/__tests__/auth.spec.js
new file mode 100644
index 0000000000..f384e6deda
--- /dev/null
+++ b/frontend-old/src/plugins/__tests__/auth.spec.js
@@ -0,0 +1,420 @@
+import { describe, beforeEach, afterEach, vi, expect, it } from 'vitest'
+import Vue from 'vue'
+import { auth } from '@/plugins/auth'
+import Cookies from 'js-cookie'
+import cloneDeep from 'lodash-es/cloneDeep'
+import { getEnv } from '@/environment'
+
+const storePlugin = await vi.importActual('@/plugins/store')
+const storeLoader = storePlugin.default
+
+Vue.use(storeLoader)
+
+const store = storePlugin.store
+const apiStore = storePlugin.apiStore
+
+// expired on 01-01-1970
+const expiredJWTPayload =
+ 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2MzMxMzM0MDksImV4cCI6MCwicm9sZXMiOlsiUk9MRV9VU0VSIl0sInVzZXJuYW1lIjoidGVzdC11c2VyIiwidXNlciI6Ii91c2Vycy8xYTJiM2M0ZCJ9'
+// {
+// "iat": 1633133409,
+// "exp": 0,
+// "roles": [
+// "ROLE_USER"
+// ],
+// "username": "test-user",
+// "user": "/users/1a2b3c4d"
+// }
+
+// expires on 01-01-3021, yes you read that right
+const validJWTPayload =
+ 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJpYXQiOjE2MzMxMzM0MDksImV4cCI6MzMxNjYzNjQ0MDAsInJvbGVzIjpbIlJPTEVfVVNFUiJdLCJ1c2VybmFtZSI6InRlc3QtdXNlciIsInVzZXIiOiIvdXNlcnMvMWEyYjNjNGQifQ'
+// {
+// "iat": 1633133409,
+// "exp": 33166364400,
+// "roles": [
+// "ROLE_USER"
+// ],
+// "username": "test-user",
+// "user": "/users/1a2b3c4d"
+// }
+
+const envBackup = cloneDeep(getEnv())
+
+expect.extend({
+ haveUri(actual, expectedUri) {
+ return {
+ pass: actual === expectedUri || actual._meta.self === expectedUri,
+ message: () => "expected to have the URI '" + expectedUri + "'",
+ }
+ },
+})
+
+vi.mock('@/router', async () => {
+ return {
+ default: {
+ push: () => Promise.resolve(),
+ resolve: () => ({
+ href: '/loginCallback',
+ }),
+ },
+ }
+})
+
+describe('authentication logic', () => {
+ afterEach(() => {
+ vi.restoreAllMocks()
+ Cookies.remove('localhost_jwt_hp')
+ window.environment = cloneDeep(envBackup)
+ })
+
+ describe('isLoggedIn()', () => {
+ it('returns true if JWT payload is not expired', () => {
+ // given
+ store.replaceState(createState())
+ Cookies.set('localhost_jwt_hp', validJWTPayload)
+
+ // when
+ const result = auth.isLoggedIn()
+
+ // then
+ expect(result).toBeTruthy()
+ })
+
+ it('returns false if JWT payload is expired', () => {
+ // given
+ store.replaceState(createState())
+ Cookies.set('localhost_jwt_hp', expiredJWTPayload)
+
+ // when
+ const result = auth.isLoggedIn()
+
+ // then
+ expect(result).toBeFalsy()
+ })
+
+ it('returns false if JWT cookie is missing', () => {
+ // given
+ store.replaceState(createState())
+ Cookies.set('localhost_jwt_hp', expiredJWTPayload)
+
+ // when
+ const result = auth.isLoggedIn()
+
+ // then
+ expect(result).toBeFalsy()
+ })
+ })
+
+ describe('register()', () => {
+ it('sends a POST request to the API', async () => {
+ // given
+ store.replaceState(createState())
+ vi.spyOn(apiStore, 'post').mockImplementation(async () => {})
+
+ // when
+ await auth.register({ email: 'bar', password: 'baz' })
+
+ // then
+ expect(apiStore.post).toHaveBeenCalledTimes(1)
+ expect(apiStore.post).toHaveBeenCalledWith('/users', {
+ email: 'bar',
+ password: 'baz',
+ })
+ })
+ })
+
+ describe('login()', () => {
+ it('resolves to true if the user successfully logs in', async () => {
+ // given
+ store.replaceState(createState())
+ vi.spyOn(apiStore, 'post').mockImplementation(async () => {
+ Cookies.set('localhost_jwt_hp', validJWTPayload)
+ })
+
+ // when
+ const result = await auth.login('foo', 'bar')
+
+ // then
+ expect(result).toBeTruthy()
+ expect(apiStore.post).toHaveBeenCalledTimes(1)
+ expect(apiStore.post).toHaveBeenCalledWith('/authentication_token', {
+ identifier: 'foo',
+ password: 'bar',
+ })
+ })
+
+ it('resolves to false if the login fails', async () => {
+ // given
+ vi.spyOn(apiStore, 'post').mockImplementation(async () => {
+ // login fails, no cookie added
+ })
+
+ // when
+ const result = await auth.login('foo', 'barrrr')
+
+ // then
+ expect(result).toBeFalsy()
+ expect(apiStore.post).toHaveBeenCalledTimes(1)
+ expect(apiStore.post).toHaveBeenCalledWith('/authentication_token', {
+ identifier: 'foo',
+ password: 'barrrr',
+ })
+ })
+ })
+
+ describe('loadUser()', () => {
+ it('resolves to null if not logged in', async () => {
+ // given
+ store.replaceState(createState())
+ vi.spyOn(apiStore, 'get')
+
+ // when
+ const result = await auth.loadUser()
+
+ // then
+ expect(result).toEqual(null)
+ expect(apiStore.get).toHaveBeenCalledTimes(0)
+ })
+
+ it('resolves to the user from the JWT token cookie', async () => {
+ // given
+ store.replaceState(createState())
+ Cookies.set('localhost_jwt_hp', validJWTPayload)
+ const rootEndpointGet = await apiStore.get()
+ vi.spyOn(rootEndpointGet, 'profiles')
+ vi.spyOn(apiStore, 'get').mockImplementation(() => rootEndpointGet)
+
+ // when
+ const result = await auth.loadUser()
+
+ // then
+ expect(result.id).toEqual('1a2b3c4d')
+ expect(rootEndpointGet.profiles).toHaveBeenCalledTimes(1)
+ expect(rootEndpointGet.profiles).toHaveBeenCalledWith({ user: '/users/1a2b3c4d' })
+ })
+
+ it.each([[401], [403], [404]])(
+ 'calls logout when fetching the user fails with status %s',
+ async (status) => {
+ // given
+ store.replaceState(createState())
+ Cookies.set('localhost_jwt_hp', validJWTPayload)
+
+ const rootEndpointGet = await apiStore.get()
+ vi.spyOn(rootEndpointGet, 'profiles').mockImplementation(() => ({
+ _meta: {
+ load: new Promise(() => {
+ const error = new Error('test error')
+ error.response = { status }
+ throw error
+ }),
+ },
+ }))
+ vi.spyOn(apiStore, 'get').mockImplementation(() => rootEndpointGet)
+ vi.spyOn(auth, 'logout')
+
+ // when
+ const result = await auth.loadUser()
+
+ // then
+ expect(result).toEqual(null)
+ expect(rootEndpointGet.profiles).toHaveBeenCalledTimes(1)
+ expect(rootEndpointGet.profiles).toHaveBeenCalledWith({ user: '/users/1a2b3c4d' })
+ expect(auth.logout).toHaveBeenCalledTimes(1)
+ }
+ )
+ })
+
+ describe('loginGoogle()', () => {
+ const { location } = window
+ beforeEach(() => {
+ delete window.location
+ window.location = {
+ origin: 'http://localhost',
+ href: 'http://localhost/login',
+ }
+ store.replaceState(createState())
+ })
+ afterEach(() => {
+ window.location = location
+ })
+
+ it('forwards to google authentication endpoint', async () => {
+ // when
+ await auth.loginGoogle()
+
+ // then
+ expect(window.location.href).toBe(
+ '/auth/google?callback=http%3A%2F%2Flocalhost%2FloginCallback'
+ )
+ })
+ })
+
+ describe('loginPbsMiData()', () => {
+ const { location } = window
+ beforeEach(() => {
+ delete window.location
+ window.location = {
+ origin: 'http://localhost',
+ href: 'http://localhost/login',
+ }
+ store.replaceState(createState())
+ })
+ afterEach(() => {
+ window.location = location
+ })
+
+ it('forwards to pbsmidata authentication endpoint', async () => {
+ // when
+ await auth.loginPbsMiData()
+
+ // then
+ expect(window.location.href).toBe(
+ '/auth/pbsmidata?callback=http%3A%2F%2Flocalhost%2FloginCallback'
+ )
+ })
+ })
+
+ describe('loginCeviDB()', () => {
+ const { location } = window
+ beforeEach(() => {
+ delete window.location
+ window.location = {
+ origin: 'http://localhost',
+ href: 'http://localhost/login',
+ }
+ store.replaceState(createState())
+ })
+ afterEach(() => {
+ window.location = location
+ })
+
+ it('forwards to cevidb authentication endpoint', async () => {
+ // when
+ await auth.loginCeviDB()
+
+ // then
+ expect(window.location.href).toBe(
+ '/auth/cevidb?callback=http%3A%2F%2Flocalhost%2FloginCallback'
+ )
+ })
+ })
+
+ describe('loginJublaDB()', () => {
+ const { location } = window
+ beforeEach(() => {
+ delete window.location
+ window.location = {
+ origin: 'http://localhost',
+ href: 'http://localhost/login',
+ }
+ store.replaceState(createState())
+ })
+ afterEach(() => {
+ window.location = location
+ })
+
+ it('forwards to jubladb authentication endpoint', async () => {
+ // when
+ await auth.loginJublaDB()
+
+ // then
+ expect(window.location.href).toBe(
+ '/auth/jubladb?callback=http%3A%2F%2Flocalhost%2FloginCallback'
+ )
+ })
+ })
+
+ describe('logout()', () => {
+ it('resolves to false if the user successfully logs out', async () => {
+ // given
+ Cookies.set('localhost_jwt_hp', validJWTPayload)
+
+ // when
+ const result = await auth.logout()
+
+ // then
+ expect(result).toBeFalsy()
+ })
+ })
+})
+
+function createState(authState = {}) {
+ return {
+ auth: {
+ user: null,
+ },
+ api: {
+ '': {
+ ...authState,
+ users: {
+ href: '/users',
+ },
+ login: {
+ href: '/authentication_token',
+ },
+ profiles: {
+ href: '/profiles{?user}',
+ templated: true,
+ },
+ oauthGoogle: {
+ href: '/auth/google{?callback}',
+ templated: true,
+ },
+ oauthPbsmidata: {
+ href: '/auth/pbsmidata{?callback}',
+ templated: true,
+ },
+ oauthCevidb: {
+ href: '/auth/cevidb{?callback}',
+ templated: true,
+ },
+ oauthJubladb: {
+ href: '/auth/jubladb{?callback}',
+ templated: true,
+ },
+ _meta: {
+ self: '',
+ },
+ },
+ '/users/1a2b3c4d': {
+ id: '1a2b3c4d',
+ profile: {
+ href: '/profile/5c6c7c8',
+ },
+ _meta: {
+ load: Promise.resolve({
+ id: '1a2b3c4d',
+ }),
+ },
+ },
+ '/profile/5c6c7c8': {
+ id: '5c6c7c8',
+ user: { href: '/users/1a2b3c4d' },
+ _meta: {
+ load: Promise.resolve({
+ id: '5c6c7c8',
+ user: { href: '/users/1a2b3c4d' },
+ }),
+ },
+ },
+ '/profiles?user=%2Fusers%2F1a2b3c4d': {
+ _meta: {
+ load: Promise.resolve({
+ items: [
+ {
+ href: '/profile/5c6c7c8',
+ },
+ ],
+ }),
+ },
+ items: [
+ {
+ href: '/profile/5c6c7c8',
+ },
+ ],
+ },
+ },
+ }
+}
diff --git a/frontend-old/src/plugins/__tests__/preferences.spec.js b/frontend-old/src/plugins/__tests__/preferences.spec.js
new file mode 100644
index 0000000000..607ef8253d
--- /dev/null
+++ b/frontend-old/src/plugins/__tests__/preferences.spec.js
@@ -0,0 +1,450 @@
+import { describe, beforeEach, afterEach, expect, it } from 'vitest'
+import { getters, loadFromLocalStorage, mutations } from '@/plugins/store/preferences'
+
+const CAMP_URI = '/camps/1a2b3c4d'
+
+let originalLocalStorage
+beforeEach(() => {
+ originalLocalStorage = window.localStorage
+ window.localStorage = (() => {
+ let store = {}
+
+ return {
+ getItem: (key) => store[key] ?? null,
+ setItem: (key, value) => {
+ store[key] = value?.toString() ?? 'undefined'
+ },
+ removeItem: (key) => {
+ delete store[key]
+ },
+ clear: () => {
+ store = {}
+ },
+ key: () => '',
+ length: Object.keys(store).length,
+ }
+ })()
+})
+
+afterEach(() => {
+ window.localStorage = originalLocalStorage
+})
+
+describe('reading state', () => {
+ it('loads saved picasso edit mode true', async () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:picassoEditMode`]: 'true',
+ })
+
+ // when
+ const result = getters.getPicassoEditMode(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeTruthy()
+ })
+
+ it('loads saved picasso edit mode false', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:picassoEditMode`]: 'false',
+ })
+
+ // when
+ const result = getters.getPicassoEditMode(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeFalsy()
+ })
+
+ it('falls back to false for picasso edit mode', () => {
+ // given
+ const state = loadFromLocalStorage({})
+
+ // when
+ const result = getters.getPicassoEditMode(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeFalsy()
+ })
+
+ it('handles invalid data for picasso edit mode', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:picassoEditMode`]: 'invalid json',
+ })
+
+ // when
+ const result = getters.getPicassoEditMode(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeFalsy()
+ })
+
+ it('loads saved story context edit mode true', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:storyContextEditMode`]: 'true',
+ })
+
+ // when
+ const result = getters.getStoryContextEditMode(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeTruthy()
+ })
+
+ it('loads saved story context edit mode false', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:storyContextEditMode`]: 'false',
+ })
+
+ // when
+ const result = getters.getStoryContextEditMode(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeFalsy()
+ })
+
+ it('falls back to false for story context edit mode', () => {
+ // given
+ const state = loadFromLocalStorage({})
+
+ // when
+ const result = getters.getStoryContextEditMode(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeFalsy()
+ })
+
+ it('handles invalid data for story context edit mode', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:storyContextEditMode`]: 'invalid json',
+ })
+
+ // when
+ const result = getters.getStoryContextEditMode(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeFalsy()
+ })
+
+ it('loads saved paper display size true', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:paperDisplaySize`]: 'true',
+ })
+
+ // when
+ const result = getters.getPaperDisplaySize(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeTruthy()
+ })
+
+ it('loads saved paper display size false', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:paperDisplaySize`]: 'false',
+ })
+
+ // when
+ const result = getters.getPaperDisplaySize(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeFalsy()
+ })
+
+ it('falls back to true for paper display size', () => {
+ // given
+ const state = loadFromLocalStorage({})
+
+ // when
+ const result = getters.getPaperDisplaySize(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeTruthy()
+ })
+
+ it('handles invalid data for paper display size', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:paperDisplaySize`]: 'invalid json',
+ })
+
+ // when
+ const result = getters.getPaperDisplaySize(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toBeTruthy()
+ })
+
+ it('loads saved print config undefined', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:lastPrintConfig`]: 'undefined',
+ })
+
+ // when
+ const result = getters.getLastPrintConfig(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toEqual({})
+ })
+
+ it('loads saved print config empty object', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:lastPrintConfig`]: '{}',
+ })
+
+ // when
+ const result = getters.getLastPrintConfig(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toEqual({})
+ })
+
+ it('loads saved print config with contents', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:lastPrintConfig`]: '{"lang":"de"}',
+ })
+
+ // when
+ const result = getters.getLastPrintConfig(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toEqual({
+ lang: 'de',
+ })
+ })
+
+ it('handles invalid data for print config', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:lastPrintConfig`]: 'invalid json',
+ })
+
+ // when
+ const result = getters.getLastPrintConfig(state)('/camps/1a2b3c4d')
+
+ // then
+ expect(result).toEqual({})
+ })
+})
+
+describe('writing state', () => {
+ it('saves picasso edit mode true', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:picassoEditMode`]: 'false',
+ })
+
+ // when
+ mutations.setPicassoEditMode(state, { campUri: '/camps/1a2b3c4d', editMode: true })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].picassoEditMode).toBeTruthy()
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:picassoEditMode`)
+ ).toEqual('true')
+ })
+
+ it('saves picasso edit mode false', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:picassoEditMode`]: 'true',
+ })
+
+ // when
+ mutations.setPicassoEditMode(state, { campUri: '/camps/1a2b3c4d', editMode: false })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].picassoEditMode).toBeFalsy()
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:picassoEditMode`)
+ ).toEqual('false')
+ })
+
+ it('saves picasso edit mode when previously not saved', () => {
+ // given
+ const state = loadFromLocalStorage({})
+
+ // when
+ mutations.setPicassoEditMode(state, { campUri: '/camps/1a2b3c4d', editMode: true })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].picassoEditMode).toBeTruthy()
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:picassoEditMode`)
+ ).toEqual('true')
+ })
+
+ it('saves story context edit mode true', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:storyContextEditMode`]: 'false',
+ })
+
+ // when
+ mutations.setStoryContextEditMode(state, {
+ campUri: '/camps/1a2b3c4d',
+ editMode: true,
+ })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].storyContextEditMode).toBeTruthy()
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:storyContextEditMode`)
+ ).toEqual('true')
+ })
+
+ it('saves story context edit mode false', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:storyContextEditMode`]: 'true',
+ })
+
+ // when
+ mutations.setStoryContextEditMode(state, {
+ campUri: '/camps/1a2b3c4d',
+ editMode: false,
+ })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].storyContextEditMode).toBeFalsy()
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:storyContextEditMode`)
+ ).toEqual('false')
+ })
+
+ it('saves story context edit mode when previously not saved', () => {
+ // given
+ const state = loadFromLocalStorage({})
+
+ // when
+ mutations.setStoryContextEditMode(state, {
+ campUri: '/camps/1a2b3c4d',
+ editMode: true,
+ })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].storyContextEditMode).toBeTruthy()
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:storyContextEditMode`)
+ ).toEqual('true')
+ })
+
+ it('saves paper display size false', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:paperDisplaySize`]: 'true',
+ })
+
+ // when
+ mutations.setPaperDisplaySize(state, {
+ campUri: '/camps/1a2b3c4d',
+ paperDisplaySize: false,
+ })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].paperDisplaySize).toBeFalsy()
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:paperDisplaySize`)
+ ).toEqual('false')
+ })
+
+ it('saves paper display size true', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:paperDisplaySize`]: 'false',
+ })
+
+ // when
+ mutations.setPaperDisplaySize(state, {
+ campUri: '/camps/1a2b3c4d',
+ paperDisplaySize: true,
+ })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].paperDisplaySize).toBeTruthy()
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:paperDisplaySize`)
+ ).toEqual('true')
+ })
+
+ it('saves paper display size when previously not saved', () => {
+ // given
+ const state = loadFromLocalStorage({})
+
+ // when
+ mutations.setPaperDisplaySize(state, {
+ campUri: '/camps/1a2b3c4d',
+ paperDisplaySize: false,
+ })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].paperDisplaySize).toBeFalsy()
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:paperDisplaySize`)
+ ).toEqual('false')
+ })
+
+ it('saves print config with content', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:lastPrintConfig`]: '{}',
+ })
+
+ // when
+ mutations.setLastPrintConfig(state, {
+ campUri: '/camps/1a2b3c4d',
+ printConfig: { lang: 'de' },
+ })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].lastPrintConfig).toEqual({ lang: 'de' })
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:lastPrintConfig`)
+ ).toEqual('{"lang":"de"}')
+ })
+
+ it('saves print config undefined', () => {
+ // given
+ const state = loadFromLocalStorage({
+ [`preferences:${CAMP_URI}:lastPrintConfig`]: '{}',
+ })
+
+ // when
+ mutations.setLastPrintConfig(state, {
+ campUri: '/camps/1a2b3c4d',
+ printConfig: undefined,
+ })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].lastPrintConfig).toEqual(undefined)
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:lastPrintConfig`)
+ ).toEqual('undefined')
+ })
+
+ it('saves print config when previously not saved', () => {
+ // given
+ const state = loadFromLocalStorage({})
+
+ // when
+ mutations.setLastPrintConfig(state, {
+ campUri: '/camps/1a2b3c4d',
+ printConfig: { lang: 'de' },
+ })
+
+ // then
+ expect(state.preferences['/camps/1a2b3c4d'].lastPrintConfig).toEqual({ lang: 'de' })
+ expect(
+ window.localStorage.getItem(`preferences:${CAMP_URI}:lastPrintConfig`)
+ ).toEqual('{"lang":"de"}')
+ })
+})
diff --git a/frontend-old/src/plugins/auth.js b/frontend-old/src/plugins/auth.js
new file mode 100644
index 0000000000..71129888a4
--- /dev/null
+++ b/frontend-old/src/plugins/auth.js
@@ -0,0 +1,243 @@
+import axios from 'axios'
+import { apiStore, store } from '@/plugins/store'
+import { hasLoggedOutFromLocalStorage } from '@/plugins/store/auth.js'
+import router from '@/router'
+import Cookies from 'js-cookie'
+import { getEnv } from '@/environment.js'
+
+axios.interceptors.response.use(null, (error) => {
+ if (error.status === 401) {
+ logout().then(() => {})
+ }
+ return Promise.reject(error)
+})
+
+let scheduledRefresh = null
+
+export async function initRefresh() {
+ // Cookies.get was not reliable to detect if the cookie was present.
+ if (hasLoggedOutFromLocalStorage()) {
+ return
+ }
+ let originalTarget = `${window.location.pathname}`
+ if (window.location.search) {
+ originalTarget += `?${window.location.search}`
+ }
+ let refreshedSuccessfully = false
+ if (!isLoggedIn()) {
+ try {
+ await refresh()
+ } catch {
+ /* empty */
+ }
+ if (!isLoggedIn()) {
+ return
+ }
+ refreshedSuccessfully = true
+ }
+ rescheduleRefresh()
+ if (refreshedSuccessfully) {
+ await router.replace(originalTarget)
+ }
+}
+
+function rescheduleRefresh() {
+ if (scheduledRefresh != null) {
+ clearTimeout(scheduledRefresh)
+ }
+ const timeout = (getJWTExpirationTimestamp() - Date.now()) / 2
+ const realTimeout = Math.max(Math.min(timeout, 30 * 60 * 1000), 2 * 60 * 1000)
+ scheduledRefresh = setTimeout(refreshAndSchedule, realTimeout)
+}
+
+async function refreshAndSchedule() {
+ await refresh()
+ rescheduleRefresh()
+}
+
+async function refresh() {
+ const url = await apiStore.href(apiStore.get(), 'refreshToken')
+ return apiStore.post(url)
+}
+
+function getJWTPayloadFromCookie() {
+ const jwtHeaderAndPayload = Cookies.get(headerAndPayloadCookieName())
+ if (!jwtHeaderAndPayload) return ''
+
+ return jwtHeaderAndPayload.split('.')[1]
+}
+
+function parseJWTPayload(payload) {
+ if (!payload) return {}
+ const base64 = payload.replace(/-/g, '+').replace(/_/g, '/')
+ const jsonPayload = decodeURIComponent(
+ atob(base64)
+ .split('')
+ .map(function (c) {
+ return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
+ })
+ .join('')
+ )
+
+ return JSON.parse(jsonPayload)
+}
+
+function getJWTExpirationTimestamp() {
+ return (parseJWTPayload(getJWTPayloadFromCookie()).exp ?? 0) * 1000
+}
+
+export function isLoggedIn() {
+ const isLoggedIn = Date.now() < getJWTExpirationTimestamp()
+
+ if (isLoggedIn) {
+ loadUser()
+ }
+
+ return isLoggedIn
+}
+
+export function isAdmin() {
+ if (!isLoggedIn()) {
+ return false
+ }
+
+ return parseJWTPayload(getJWTPayloadFromCookie()).roles.includes('ROLE_ADMIN')
+}
+
+async function login(email, password) {
+ const url = await apiStore.href(apiStore.get(), 'login')
+ return apiStore.post(url, { identifier: email, password: password }).then(() => {
+ rescheduleRefresh()
+ return isLoggedIn()
+ })
+}
+
+async function resetPasswordRequest(email, recaptchaToken) {
+ const url = await apiStore.href(apiStore.get(), 'resetPassword')
+ return apiStore.post(url, { email: email, recaptchaToken: recaptchaToken })
+}
+
+async function resetPassword(id, password, recaptchaToken) {
+ const url = await apiStore.href(apiStore.get(), 'resetPassword', { id: id })
+ return apiStore.patch(url, { password: password, recaptchaToken: recaptchaToken })
+}
+
+async function resendUserActivation(email, recaptchaToken) {
+ const url = await apiStore.href(apiStore.get(), 'resendActivation')
+ return apiStore.post(url, { email: email, recaptchaToken: recaptchaToken })
+}
+
+async function loadUser() {
+ if (!getJWTPayloadFromCookie()) {
+ store.commit('logout')
+ return null
+ }
+
+ try {
+ const profiles = await apiStore
+ .get()
+ .profiles({ user: parseJWTPayload(getJWTPayloadFromCookie()).user })._meta.load
+ store.commit('login', profiles.items[0].user())
+ return profiles.items[0].user()
+ } catch (e) {
+ if (e.response && [401, 403, 404].includes(e.response.status)) {
+ // 401 means no complete token was submitted, so we may be missing the JWT signature cookie
+ // 403 means we can theoretically interact in some way with the user, but apparently not read it
+ // 404 means the user doesn't exist or we don't have access to it
+ // Either way, we aren't allowed to access the user from the token, so it's best to ask the user
+ // to log in again.
+ auth.logout()
+ return null
+ }
+
+ throw e
+ }
+}
+
+async function register(data) {
+ const url = await apiStore.href(apiStore.get(), 'users')
+ return apiStore.post(url, data)
+}
+
+async function redirectToOAuthLogin(provider) {
+ let returnUrl = window.location.origin + router.resolve({ name: 'loginCallback' }).href
+
+ const params = new URLSearchParams(window.location.search)
+ if (params.has('redirect')) {
+ returnUrl += '?redirect=' + params.get('redirect')
+ }
+
+ return apiStore
+ .href(apiStore.get(), provider, { callback: encodeURI(returnUrl) })
+ .then((url) => {
+ window.location.href = url
+ })
+}
+
+async function loginGoogle() {
+ return redirectToOAuthLogin('oauthGoogle')
+}
+
+async function loginPbsMiData() {
+ return redirectToOAuthLogin('oauthPbsmidata')
+}
+
+async function loginCeviDB() {
+ return redirectToOAuthLogin('oauthCevidb')
+}
+
+async function loginJublaDB() {
+ return redirectToOAuthLogin('oauthJubladb')
+}
+
+export async function logout() {
+ if (scheduledRefresh != null) {
+ clearTimeout(scheduledRefresh)
+ }
+ Cookies.remove(headerAndPayloadCookieName())
+ store.commit('logout')
+ return router
+ .push({ name: 'login' })
+ .catch(() => {}) // prevents throwing NavigationDuplicated is already on /login
+ .then(() => apiStore.purgeAll())
+ .then(() => isLoggedIn())
+}
+
+function headerAndPayloadCookieName() {
+ return `${cookiePrefix()}jwt_hp`
+}
+
+function cookiePrefix() {
+ return getEnv().COOKIE_PREFIX || ''
+}
+
+export const auth = {
+ initRefresh,
+ isLoggedIn,
+ isAdmin,
+ login,
+ register,
+ loginGoogle,
+ loginPbsMiData,
+ loginCeviDB,
+ loginJublaDB,
+ logout,
+ loadUser,
+ resetPasswordRequest,
+ resetPassword,
+ resendUserActivation,
+}
+
+class AuthPlugin {
+ install(Vue) {
+ Object.defineProperties(Vue.prototype, {
+ $auth: {
+ get() {
+ return auth
+ },
+ },
+ })
+ }
+}
+
+export default new AuthPlugin()
diff --git a/frontend-old/src/plugins/color.js b/frontend-old/src/plugins/color.js
new file mode 100644
index 0000000000..6633e6ba8d
--- /dev/null
+++ b/frontend-old/src/plugins/color.js
@@ -0,0 +1,7 @@
+import { ColorSpace, sRGB } from 'colorjs.io/fn'
+
+export default {
+ install: () => {
+ ColorSpace.register(sRGB)
+ },
+}
diff --git a/frontend-old/src/plugins/dayjs.js b/frontend-old/src/plugins/dayjs.js
new file mode 100644
index 0000000000..975ad2011a
--- /dev/null
+++ b/frontend-old/src/plugins/dayjs.js
@@ -0,0 +1,14 @@
+import dayjs from '@/common/helpers/dayjs.js'
+
+export default {
+ install: (Vue) => {
+ Object.defineProperties(Vue.prototype, {
+ $date: {
+ get() {
+ return dayjs
+ },
+ },
+ })
+ Vue.dayjs = dayjs
+ },
+}
diff --git a/frontend-old/src/plugins/formBaseComponents.js b/frontend-old/src/plugins/formBaseComponents.js
new file mode 100644
index 0000000000..b2514f6f55
--- /dev/null
+++ b/frontend-old/src/plugins/formBaseComponents.js
@@ -0,0 +1,35 @@
+// Global registration of all Vue components in folder components/form/base
+
+import ECheckbox from '@/components/form/base/ECheckbox.vue'
+import EColorPicker from '@/components/form/base/EColorPicker.vue'
+import EColorField from '@/components/form/base/EColorField.vue'
+import EDatePicker from '@/components/form/base/EDatePicker.vue'
+import EForm from '@/components/form/base/EForm.vue'
+import ENumberField from '@/components/form/base/ENumberField.vue'
+import EParseField from '@/components/form/base/EParseField.vue'
+import ERichtext from '@/components/form/base/ERichtext.vue'
+import ESelect from '@/components/form/base/ESelect.vue'
+import ESwitch from '@/components/form/base/ESwitch.vue'
+import ETextarea from '@/components/form/base/ETextarea.vue'
+import ETextField from '@/components/form/base/ETextField.vue'
+import ETimePicker from '@/components/form/base/ETimePicker.vue'
+
+class FormBaseComponentsPlugin {
+ install(Vue) {
+ Vue.component('ECheckbox', ECheckbox)
+ Vue.component('EColorField', EColorField)
+ Vue.component('EColorPicker', EColorPicker)
+ Vue.component('EDatePicker', EDatePicker)
+ Vue.component('EForm', EForm)
+ Vue.component('ENumberField', ENumberField)
+ Vue.component('EParseField', EParseField)
+ Vue.component('ERichtext', ERichtext)
+ Vue.component('ESelect', ESelect)
+ Vue.component('ESwitch', ESwitch)
+ Vue.component('ETextarea', ETextarea)
+ Vue.component('ETextField', ETextField)
+ Vue.component('ETimePicker', ETimePicker)
+ }
+}
+
+export default new FormBaseComponentsPlugin()
diff --git a/frontend-old/src/plugins/head.js b/frontend-old/src/plugins/head.js
new file mode 100644
index 0000000000..87bdb48db6
--- /dev/null
+++ b/frontend-old/src/plugins/head.js
@@ -0,0 +1,24 @@
+import { createHead, useHead } from '@unhead/vue'
+export { UnheadPlugin as head } from '@unhead/vue/vue2'
+import { getEnv } from '@/environment.js'
+
+const env = getEnv().SENTRY_ENVIRONMENT.split('.')[0]
+const environment =
+ env === 'app' || env === ''
+ ? null
+ : env.match('^pr[0-9]+')
+ ? `[${env.toUpperCase()}]`
+ : `[${env.substring(0, 1).toUpperCase() + env.substring(1)}]`
+
+export const unhead = createHead()
+
+useHead({
+ title: null,
+ templateParams: {
+ site: 'eCamp v3',
+ separator: '·',
+ environment,
+ section: null,
+ },
+ titleTemplate: '%environment %s %separator %section %separator %site',
+})
diff --git a/frontend-old/src/plugins/i18n/__tests__/apiFallbackLocalesFor.spec.js b/frontend-old/src/plugins/i18n/__tests__/apiFallbackLocalesFor.spec.js
new file mode 100644
index 0000000000..0db9f116c0
--- /dev/null
+++ b/frontend-old/src/plugins/i18n/__tests__/apiFallbackLocalesFor.spec.js
@@ -0,0 +1,36 @@
+import { describe, expect, it } from 'vitest'
+import { fallbackLocales } from '@/plugins/i18n'
+import fallbackLocalesFor from '@/plugins/i18n/apiFallbackLocalesFor'
+
+const fallbackLocale = fallbackLocales.default
+
+describe('apiFallbackLocales', () => {
+ ;[null, undefined, 1, [], {}].forEach((param) => {
+ it(`returns fallbackLocale if input is not a string, but ${param}`, () => {
+ expect([...fallbackLocalesFor(param)]).toStrictEqual([fallbackLocale])
+ })
+
+ it('returns fallbackLocale if string does not contain dashes', () => {
+ expect([...fallbackLocalesFor('de')]).toStrictEqual([fallbackLocale])
+ })
+
+ Object.entries({
+ de_CH_scout: ['de_CH', 'de', fallbackLocale],
+ de_CH: ['de', fallbackLocale],
+ fr_CH_scout: ['fr_CH', 'fr', fallbackLocale],
+ fr_CH: ['fr', fallbackLocale],
+ it_CH_scout: ['it_CH', 'it', fallbackLocale],
+ it_CH: ['it', fallbackLocale],
+ en_CH_scout: ['en_CH', 'en', fallbackLocale],
+ en_CH: ['en', fallbackLocale],
+ rm_CH: ['rm', 'de', fallbackLocale],
+ rm_CH_scout: ['rm_CH', 'rm', 'de', fallbackLocale],
+ }).forEach((entry) => {
+ const locale = entry[0]
+ const result = entry[1]
+ it(`returns correct fallbacks [${result}] for locale ${locale}`, () => {
+ expect([...fallbackLocalesFor(locale)]).toStrictEqual(result)
+ })
+ })
+ })
+})
diff --git a/frontend-old/src/plugins/i18n/apiFallbackLocalesFor.js b/frontend-old/src/plugins/i18n/apiFallbackLocalesFor.js
new file mode 100644
index 0000000000..20767a0c5f
--- /dev/null
+++ b/frontend-old/src/plugins/i18n/apiFallbackLocalesFor.js
@@ -0,0 +1,24 @@
+import { fallbackLocales } from '@/plugins/i18n/index'
+
+export default function* (locale) {
+ if (typeof locale !== 'string') {
+ yield fallbackLocales.default
+ return
+ }
+ if (fallbackLocales[locale]) {
+ for (const fallback of fallbackLocales[locale]) {
+ yield fallback
+ }
+ }
+ const parts = locale.split('_')
+ for (let i = parts.length - 1; i > 0; i--) {
+ const implicitFallback = parts.slice(0, i).join('_')
+ yield implicitFallback
+ if (fallbackLocales[implicitFallback]) {
+ for (const fallback of fallbackLocales[implicitFallback]) {
+ yield fallback
+ }
+ }
+ }
+ yield fallbackLocales.default
+}
diff --git a/frontend-old/src/plugins/i18n/index.js b/frontend-old/src/plugins/i18n/index.js
new file mode 100644
index 0000000000..a6b8a7a11a
--- /dev/null
+++ b/frontend-old/src/plugins/i18n/index.js
@@ -0,0 +1,130 @@
+import Vue from 'vue'
+import VueI18n from 'vue-i18n'
+import deepmerge from 'deepmerge'
+
+import itCommon from '@/common/locales/it.json'
+import itCHScoutCommon from '@/common/locales/it-CH-scout.json'
+import frCommon from '@/common/locales/fr.json'
+import frCHScoutCommon from '@/common/locales/fr-CH-scout.json'
+import enCommon from '@/common/locales/en.json'
+import enCHScoutCommon from '@/common/locales/en-CH-scout.json'
+import deCommon from '@/common/locales/de.json'
+import deCHScoutCommon from '@/common/locales/de-CH-scout.json'
+import rmCommon from '@/common/locales/rm.json'
+import rmCHScoutCommon from '@/common/locales/rm-CH-scout.json'
+
+import it from '@/locales/it.json'
+import itCHScout from '@/locales/it-CH-scout.json'
+import fr from '@/locales/fr.json'
+import frCHScout from '@/locales/fr-CH-scout.json'
+import en from '@/locales/en.json'
+import enCHScout from '@/locales/en-CH-scout.json'
+import de from '@/locales/de.json'
+import deCHScout from '@/locales/de-CH-scout.json'
+import rm from '@/locales/rm.json'
+import rmCHScout from '@/locales/rm-CH-scout.json'
+
+import validationIt from 'vee-validate/dist/locale/it.json'
+import validationFr from 'vee-validate/dist/locale/fr.json'
+import validationEn from 'vee-validate/dist/locale/en.json'
+import validationDe from 'vee-validate/dist/locale/de.json'
+
+import vuetifyEn from 'vuetify/lib/locale/en'
+import vuetifyDe from 'vuetify/lib/locale/de'
+import vuetifyFr from 'vuetify/lib/locale/fr'
+import vuetifyIt from 'vuetify/lib/locale/it'
+
+Vue.use(VueI18n)
+
+const fallbackLocales = {
+ rm: ['de'],
+ default: 'en',
+}
+
+const i18n = new VueI18n({
+ locale: 'de',
+ fallbackLocale: fallbackLocales,
+ messages: deepmerge.all([
+ // vee-validate locales
+ {
+ it: {
+ global: {
+ validation: validationIt.messages,
+ },
+ },
+ fr: {
+ global: {
+ validation: validationFr.messages,
+ },
+ },
+ en: {
+ global: {
+ validation: validationEn.messages,
+ },
+ },
+ de: {
+ global: {
+ validation: validationDe.messages,
+ },
+ },
+ },
+
+ // vuetify locales
+ {
+ en: { $vuetify: vuetifyEn },
+ de: { $vuetify: vuetifyDe },
+ fr: { $vuetify: vuetifyFr },
+ it: { $vuetify: vuetifyIt },
+ },
+
+ // eCamp common locales
+ {
+ it: itCommon,
+ 'it-CH-scout': itCHScoutCommon,
+ fr: frCommon,
+ 'fr-CH-scout': frCHScoutCommon,
+ en: enCommon,
+ 'en-CH-scout': enCHScoutCommon,
+ de: deCommon,
+ 'de-CH-scout': deCHScoutCommon,
+ rm: rmCommon,
+ 'rm-CH-scout': rmCHScoutCommon,
+ },
+
+ // eCamp frontend only locales
+ {
+ it,
+ 'it-CH-scout': itCHScout,
+ fr,
+ 'fr-CH-scout': frCHScout,
+ en,
+ 'en-CH-scout': enCHScout,
+ de,
+ 'de-CH-scout': deCHScout,
+ rm,
+ 'rm-CH-scout': rmCHScout,
+ },
+ ]),
+ silentTranslationWarn: true,
+})
+
+Object.defineProperty(i18n, 'browserPreferredLocale', {
+ get: function () {
+ const languages = navigator.languages || [navigator.language]
+ for (const language of languages) {
+ if (this.availableLocales.includes(language)) {
+ return language
+ }
+
+ const languageFallback = language.substring(0, 2)
+ if (this.availableLocales.includes(languageFallback)) {
+ return languageFallback
+ }
+ }
+ return fallbackLocales.default
+ },
+})
+
+export default i18n
+
+export { i18n, fallbackLocales }
diff --git a/frontend-old/src/plugins/ignoreNativeBindingWarnMessages.js b/frontend-old/src/plugins/ignoreNativeBindingWarnMessages.js
new file mode 100644
index 0000000000..825b9eba01
--- /dev/null
+++ b/frontend-old/src/plugins/ignoreNativeBindingWarnMessages.js
@@ -0,0 +1,24 @@
+/**
+ * Disables display of console warn messages related to .native event bindings
+ *
+ * See also here https://github.com/vuejs/vue/issues/10939
+ * and here https://github.com/vuetifyjs/vuetify/issues/9999
+ */
+class IgnoreNativeBindingWarnMessagesPlugin {
+ install(Vue) {
+ const ignoreWarnMessage =
+ 'The .native modifier for v-on is only valid on components but it was used on'
+ Vue.config.warnHandler = function (msg, vm, trace) {
+ // `trace` is the component hierarchy trace
+ if (msg.startsWith(ignoreWarnMessage)) {
+ msg = null
+ vm = null
+ trace = null
+ } else {
+ console.error(`[Vue warn]: ${msg}${trace}`)
+ }
+ }
+ }
+}
+
+export default new IgnoreNativeBindingWarnMessagesPlugin()
diff --git a/frontend-old/src/plugins/index.js b/frontend-old/src/plugins/index.js
new file mode 100644
index 0000000000..69e6fe5b20
--- /dev/null
+++ b/frontend-old/src/plugins/index.js
@@ -0,0 +1,10 @@
+export { default as vuetifyLoader } from './vuetify'
+export { default as storeLoader } from './store'
+export { default as auth } from './auth'
+export { default as formBaseComponents } from './formBaseComponents'
+export { default as ignoreNativeBindingWarnMessages } from './ignoreNativeBindingWarnMessages'
+export { default as i18n } from './i18n'
+export { default as veeValidate } from './veeValidate'
+export { default as dayjs } from './dayjs'
+export { default as color } from './color'
+export { head, unhead } from './head'
diff --git a/frontend-old/src/plugins/passwordStrength.js b/frontend-old/src/plugins/passwordStrength.js
new file mode 100644
index 0000000000..8fc0efbd11
--- /dev/null
+++ b/frontend-old/src/plugins/passwordStrength.js
@@ -0,0 +1,25 @@
+import { zxcvbnAsync, zxcvbnOptions } from '@zxcvbn-ts/core'
+import { adjacencyGraphs, dictionary } from '@zxcvbn-ts/language-common'
+import * as en from '@zxcvbn-ts/language-en'
+import * as de from '@zxcvbn-ts/language-de'
+import * as fr from '@zxcvbn-ts/language-fr'
+import * as it from '@zxcvbn-ts/language-it'
+
+const languages = { en, de, fr, it }
+
+const options = function (lang) {
+ const baseLanguage = lang.split('-', 2)[0]
+ return {
+ translations: languages[baseLanguage].translations,
+ graphs: adjacencyGraphs,
+ dictionary: {
+ ...dictionary,
+ ...languages[baseLanguage].dictionary,
+ },
+ }
+}
+
+export const passwordStrength = async function (password, lang) {
+ zxcvbnOptions.setOptions(options(lang))
+ return await zxcvbnAsync(password)
+}
diff --git a/frontend-old/src/plugins/slugify.js b/frontend-old/src/plugins/slugify.js
new file mode 100644
index 0000000000..944c485905
--- /dev/null
+++ b/frontend-old/src/plugins/slugify.js
@@ -0,0 +1,9 @@
+import slugify from 'slugify'
+
+slugify.extend({
+ '@': '(at)',
+ '.': ' ',
+ ':': ' ',
+})
+
+export { slugify }
diff --git a/frontend-old/src/plugins/store/auth.js b/frontend-old/src/plugins/store/auth.js
new file mode 100644
index 0000000000..7608f81398
--- /dev/null
+++ b/frontend-old/src/plugins/store/auth.js
@@ -0,0 +1,43 @@
+import { apiStore } from '@/plugins/store/index'
+
+/**
+ * Because we cannot differentiate between a expired cookie and a deleted cookie,
+ * we use localStorage to track if a user has logged out and does not want
+ * to refresh the access token.
+ */
+const HAS_LOGGED_OUT = 'hasLoggedOut'
+
+export const state = {
+ user: null,
+}
+
+export const mutations = {
+ login(state, user) {
+ state.user = user
+ window.localStorage.setItem(HAS_LOGGED_OUT, 'false')
+ },
+
+ logout(state) {
+ state.user = null
+ window.localStorage.setItem(HAS_LOGGED_OUT, 'true')
+ },
+}
+export const getters = {
+ /**
+ * Since store.auth.user isn't always up to date - uses the logged-in user URI and returns the latest data for that user
+ * @returns {*} the Logged-in user with the latest fetched api data
+ */
+ getLoggedInUser: (authState) => {
+ return authState.user ? apiStore.get(authState.user._meta.self) : authState.user
+ },
+}
+
+export function hasLoggedOutFromLocalStorage() {
+ return window.localStorage.getItem(HAS_LOGGED_OUT) === 'true'
+}
+
+export default {
+ state,
+ mutations,
+ getters,
+}
diff --git a/frontend-old/src/plugins/store/index.js b/frontend-old/src/plugins/store/index.js
new file mode 100644
index 0000000000..cc7c66358d
--- /dev/null
+++ b/frontend-old/src/plugins/store/index.js
@@ -0,0 +1,47 @@
+import Vuex from 'vuex'
+import axios from 'axios'
+import VueAxios from 'vue-axios/dist/vue-axios.common.min'
+import HalJsonVuex from 'hal-json-vuex'
+import lang from './lang'
+import auth from './auth'
+import preferences from './preferences'
+import { getEnv } from '@/environment.js'
+
+class StorePlugin {
+ install(Vue) {
+ Vue.use(Vuex)
+
+ store = new Vuex.Store({
+ modules: {
+ lang,
+ auth,
+ preferences,
+ },
+ strict: process.env.NODE_ENV !== 'production',
+ })
+
+ axios.defaults.withCredentials = true
+ axios.defaults.baseURL = getEnv().API_ROOT_URL
+ axios.defaults.headers.common.Accept = 'application/hal+json'
+ axios.interceptors.request.use(function (config) {
+ if (config.method === 'patch') {
+ config.headers['Content-Type'] = 'application/merge-patch+json'
+ }
+ return config
+ })
+
+ Vue.use(VueAxios, axios)
+
+ let halJsonVuex = HalJsonVuex
+ if (typeof halJsonVuex !== 'function') {
+ halJsonVuex = HalJsonVuex.default
+ }
+ apiStore = halJsonVuex(store, axios, { forceRequestedSelfLink: true })
+ Vue.use(apiStore)
+ }
+}
+
+export let apiStore
+export let store
+
+export default new StorePlugin()
diff --git a/frontend-old/src/plugins/store/lang.js b/frontend-old/src/plugins/store/lang.js
new file mode 100644
index 0000000000..7dee770d4a
--- /dev/null
+++ b/frontend-old/src/plugins/store/lang.js
@@ -0,0 +1,37 @@
+import Vue from 'vue'
+import axios from 'axios'
+import VueI18n from '@/plugins/i18n'
+import { localeChanged } from 'vee-validate'
+import { toDayjsLocale } from '@/common/helpers/dayjs.js'
+
+const LANG_KEY = 'language'
+
+export const state = {
+ language: window.localStorage.getItem(LANG_KEY) || window.navigator.language || 'en',
+}
+
+export const mutations = {
+ /**
+ * Changes language
+ * @param state Vuex state
+ * @param lang Language string
+ */
+ setLanguage(state, lang) {
+ if (!lang) {
+ return
+ }
+
+ state.language = lang
+ VueI18n.locale = lang
+ Vue.dayjs.locale(toDayjsLocale(lang))
+ localeChanged()
+ axios.defaults.headers.common['Accept-Language'] = lang
+ document.querySelector('html').setAttribute('lang', lang)
+ window.localStorage.setItem(LANG_KEY, lang)
+ },
+}
+
+export default {
+ state,
+ mutations,
+}
diff --git a/frontend-old/src/plugins/store/preferences.js b/frontend-old/src/plugins/store/preferences.js
new file mode 100644
index 0000000000..f9559d9a80
--- /dev/null
+++ b/frontend-old/src/plugins/store/preferences.js
@@ -0,0 +1,96 @@
+import Vue from 'vue'
+
+const LOCAL_STORAGE_PREFIX = 'preferences:'
+
+export function loadFromLocalStorage(localStorage = window.localStorage) {
+ const values = {}
+ Object.keys(localStorage)
+ .filter((key) => key.startsWith(LOCAL_STORAGE_PREFIX))
+ .forEach((key) => {
+ try {
+ const [, uri, identifier] = key.match(
+ new RegExp(`^${LOCAL_STORAGE_PREFIX}(.*):(.*)$`)
+ )
+ values[uri] ||= {}
+ values[uri][identifier] = JSON.parse(localStorage[key])
+ } catch {
+ // Just ignore this key and continue with the others
+ }
+ })
+ return {
+ preferences: values,
+ }
+}
+
+export const state = loadFromLocalStorage()
+
+export const getters = {
+ getPicassoEditMode: (state) => (campUri) => {
+ return !!(state.preferences[campUri]?.picassoEditMode ?? false)
+ },
+ getStoryContextEditMode: (state) => (campUri) => {
+ return !!(state.preferences[campUri]?.storyContextEditMode ?? false)
+ },
+ getPaperDisplaySize: (state) => (campUri) => {
+ return !!(state.preferences[campUri]?.paperDisplaySize ?? true)
+ },
+ getLastPrintConfig:
+ (state) =>
+ (campUri, fallback = {}) => {
+ return state.preferences[campUri]?.lastPrintConfig ?? fallback
+ },
+}
+
+function setPreferenceValue(state, campUri, identifier, value) {
+ if (!(campUri in state.preferences)) Vue.set(state.preferences, campUri, {})
+ Vue.set(state.preferences[campUri], identifier, value)
+ window.localStorage.setItem(
+ `${LOCAL_STORAGE_PREFIX}${campUri}:${identifier}`,
+ JSON.stringify(value)
+ )
+}
+
+export const mutations = {
+ /**
+ * Changes the edit mode of the picasso (locked or unlocked)
+ * @param state Vuex state
+ * @param campUri The URI of the camp to which this preference belongs
+ * @param editMode boolean value, true meaning unlocked, false meaning locked
+ */
+ setPicassoEditMode(state, { campUri, editMode }) {
+ setPreferenceValue(state, campUri, 'picassoEditMode', editMode)
+ },
+ /**
+ * Changes the edit mode of the story context overview (locked or unlocked)
+ * @param state Vuex state
+ * @param campUri The URI of the camp to which this preference belongs
+ * @param editMode boolean value, true meaning unlocked, false meaning locked
+ */
+ setStoryContextEditMode(state, { campUri, editMode }) {
+ setPreferenceValue(state, campUri, 'storyContextEditMode', editMode)
+ },
+ /**
+ * Changes the width of the displayed activity details between paper size and full-screen width
+ * @param state Vuex state
+ * @param campUri The URI of the camp to which this preference belongs
+ * @param paperDisplaySize boolean value, true meaning unlocked, false meaning locked
+ */
+ setPaperDisplaySize(state, { campUri, paperDisplaySize }) {
+ setPreferenceValue(state, campUri, 'paperDisplaySize', paperDisplaySize)
+ },
+ /**
+ * Remembers the last used PDF print settings
+ * @param state Vuex state
+ * @param campUri The URI of the camp to which this preference belongs
+ * @param printConfig an object describing the last used print configuration
+ */
+ setLastPrintConfig(state, { campUri, printConfig }) {
+ setPreferenceValue(state, campUri, 'lastPrintConfig', printConfig)
+ },
+}
+
+export default {
+ state,
+ getters,
+ mutations,
+}
diff --git a/frontend-old/src/plugins/veeValidate.js b/frontend-old/src/plugins/veeValidate.js
new file mode 100644
index 0000000000..ce79aa52aa
--- /dev/null
+++ b/frontend-old/src/plugins/veeValidate.js
@@ -0,0 +1,49 @@
+import { extend, configure, setInteractionMode } from 'vee-validate'
+import * as rules from 'vee-validate/dist/rules'
+import i18n from '@/plugins/i18n'
+import greaterThan from '@/plugins/veeValidate/greaterThan'
+import greaterThan_time from './veeValidate/greaterThan_time.js'
+import greaterThanOrEqual_date from './veeValidate/greaterThanOrEqual_date.js'
+import lessThanOrEqual_date from './veeValidate/lessThanOrEqual_date.js'
+import oneEmojiOrTwoCharacters from '@/plugins/veeValidate/oneEmojiOrTwoCharacters.js'
+
+class VeeValidatePlugin {
+ install(Vue) {
+ // Eager = Lazy at the beginning, Agressive once the field is invalid (https://vee-validate.logaretm.com/v3/guide/interaction-and-ux.html#interaction-modes)
+ setInteractionMode('eager')
+
+ // translate default error messages
+ configure({
+ // this will be used to generate messages.
+ defaultMessage: (field, values) => {
+ return i18n.tc(`global.validation.${values._rule_}`, 0, values)
+ },
+ })
+
+ // install all default rules
+ Object.keys(rules).forEach((rule) => {
+ extend(rule, {
+ ...rules[rule], // copies rule configuration
+ })
+ })
+
+ /**
+ * define custom rules
+ */
+
+ extend('greaterThan', greaterThan(i18n))
+
+ extend('greaterThan_time', greaterThan_time(Vue.dayjs, i18n))
+
+ // check if date (value) is equal or larger than another date (min)
+ extend('greaterThanOrEqual_date', greaterThanOrEqual_date(Vue.dayjs, i18n))
+
+ // check if date (value) is equal or less than another date (max)
+ extend('lessThanOrEqual_date', lessThanOrEqual_date(Vue.dayjs, i18n))
+
+ // check if date (value) is equal or less than another date (max)
+ extend('oneEmojiOrTwoCharacters', oneEmojiOrTwoCharacters(i18n))
+ }
+}
+
+export default new VeeValidatePlugin()
diff --git a/frontend-old/src/plugins/veeValidate/__tests__/greaterThan.spec.js b/frontend-old/src/plugins/veeValidate/__tests__/greaterThan.spec.js
new file mode 100644
index 0000000000..142fa136da
--- /dev/null
+++ b/frontend-old/src/plugins/veeValidate/__tests__/greaterThan.spec.js
@@ -0,0 +1,32 @@
+import { describe, expect, it } from 'vitest'
+import greaterThan from '@/plugins/veeValidate/greaterThan'
+
+const mockI18n = {
+ $tc: (key) => key,
+}
+
+describe('greaterThan validation', () => {
+ it.each([
+ [[1, { min: 0 }], true],
+ [['1', { min: 0 }], true],
+ [[0, { min: 0 }], false],
+ [[0.0001, { min: 0 }], true],
+ [['0.0001', { min: 0 }], true],
+ [[-0.0001, { min: 0 }], false],
+ [['-0.0001', { min: 0 }], false],
+ [[-0, { min: 0 }], false],
+ [[-1, { min: 0 }], false],
+ [[1e-10, { min: 0 }], true],
+ [[-1e-10, { min: 0 }], false],
+ [['not a number', { min: 0 }], false],
+ ])('validates %p as %p', (input, expected) => {
+ // given
+ const rule = greaterThan(mockI18n)
+
+ // when
+ const result = rule.validate(...input)
+
+ // then
+ expect(result).toBe(expected)
+ })
+})
diff --git a/frontend-old/src/plugins/veeValidate/__tests__/greaterThanOrEqual_date.spec.js b/frontend-old/src/plugins/veeValidate/__tests__/greaterThanOrEqual_date.spec.js
new file mode 100644
index 0000000000..0a8fdd9595
--- /dev/null
+++ b/frontend-old/src/plugins/veeValidate/__tests__/greaterThanOrEqual_date.spec.js
@@ -0,0 +1,58 @@
+import { describe, beforeEach, expect, it } from 'vitest'
+import greaterThanOrEqual_date from '../greaterThanOrEqual_date.js'
+import dayjs from '@/common/helpers/dayjs.js'
+
+const mockI18n = {
+ $tc: (key) => key,
+}
+
+describe('greaterThanOrEqual_date validation', () => {
+ describe('german', () => {
+ beforeEach(() => {
+ dayjs.locale('de')
+ })
+
+ it.each([
+ [['20.01.2020', { min: '19.01.2020' }], true],
+ [['19.01.2020', { min: '19.01.2020' }], true],
+ [['18.01.2020', { min: '19.01.2020' }], false],
+ [['', { min: '19.01.2020' }], false],
+ [['2.1.2020', { min: '02.01.2020' }], false], // invalid date
+ [['1.1.2020', { min: '02.01.2020' }], false], // invalid date
+ [['today', { min: '02.01.2020' }], false], // invalid date
+ ])('validates %p as %p', (input, expected) => {
+ // given
+ const rule = greaterThanOrEqual_date(dayjs, mockI18n)
+
+ // when
+ const result = rule.validate(...input)
+
+ // then
+ expect(result).toBe(expected)
+ })
+ })
+ describe('english', () => {
+ beforeEach(() => {
+ dayjs.locale('en')
+ })
+
+ it.each([
+ [['01/20/2020', { min: '01/19/2020' }], true],
+ [['01/19/2020', { min: '01/19/2020' }], true],
+ [['01/18/2020', { min: '01/19/2020' }], false],
+ [['', { min: '01/19/2020' }], false],
+ [['1/2/2020', { min: '01/02/2020' }], false], // invalid date
+ [['1/1/2020', { min: '01/02/2020' }], false], // invalid date
+ [['today', { min: '01/02/2020' }], false], // invalid date
+ ])('validates %p as %p', (input, expected) => {
+ // given
+ const rule = greaterThanOrEqual_date(dayjs, mockI18n)
+
+ // when
+ const result = rule.validate(...input)
+
+ // then
+ expect(result).toBe(expected)
+ })
+ })
+})
diff --git a/frontend-old/src/plugins/veeValidate/__tests__/greaterThan_time.spec.js b/frontend-old/src/plugins/veeValidate/__tests__/greaterThan_time.spec.js
new file mode 100644
index 0000000000..2d71b887ee
--- /dev/null
+++ b/frontend-old/src/plugins/veeValidate/__tests__/greaterThan_time.spec.js
@@ -0,0 +1,74 @@
+import { describe, beforeEach, expect, it } from 'vitest'
+import greaterThan_time from '../greaterThan_time.js'
+import dayjs from '@/common/helpers/dayjs.js'
+
+const mockI18n = {
+ $tc: (key) => key,
+}
+
+function convertTimeStringToDayjsObject(timeString) {
+ // The date should be actively ignored by the validator, so we can use any random date
+ return dayjs.utc('2022-09-03 ' + timeString, 'YYYY-MM-DD LT')
+}
+
+describe('greaterThan_time validation', () => {
+ const testcases = {
+ de: [
+ [['09:31', { min: '09:30' }], true],
+ [['09:30', { min: '09:30' }], false],
+ [['09:29', { min: '09:30' }], false],
+ [['', { min: '09:30' }], false],
+ [['9:31 AM', { min: '09:30' }], true], // dayjs parser is somewhat forgiving here
+ [['9:31', { min: '09:30' }], true],
+ [['9:30', { min: '09:30' }], false],
+ [['9:29', { min: '09:30' }], false],
+ [['now', { min: '09:30' }], false], // invalid date
+ ],
+ en: [
+ [['09:31 AM', { min: '09:30 AM' }], true],
+ [['09:30 AM', { min: '09:30 AM' }], false],
+ [['09:29 AM', { min: '09:30 AM' }], false],
+ [['', { min: '09:30 AM' }], false],
+ [['09:30', { min: '09:30 AM' }], false], // wrong format
+ [['9:31', { min: '09:30 AM' }], false], // wrong format
+ [['now', { min: '09:30 AM' }], false], // invalid date
+ ],
+ }
+
+ describe.each(Object.entries(testcases))('%s', (language, cases) => {
+ beforeEach(() => {
+ dayjs.locale(language)
+ })
+
+ describe('when min is a string', () => {
+ it.each(cases)('validates %p as %p', (input, expected) => {
+ // given
+ const rule = greaterThan_time(dayjs, mockI18n)
+
+ // when
+ const result = rule.validate(...input)
+
+ // then
+ expect(result).toBe(expected)
+ })
+ })
+
+ describe('when min is a dayjs object', () => {
+ const casesWithDayjsObjectsAsMin = cases.map(([[input, { min }], expected]) => {
+ dayjs.locale(language)
+ return [[input, { min: convertTimeStringToDayjsObject(min) }], expected]
+ })
+
+ it.each(casesWithDayjsObjectsAsMin)('validates %p as %p', (input, expected) => {
+ // given
+ const rule = greaterThan_time(dayjs, mockI18n)
+
+ // when
+ const result = rule.validate(...input)
+
+ // then
+ expect(result).toBe(expected)
+ })
+ })
+ })
+})
diff --git a/frontend-old/src/plugins/veeValidate/__tests__/lessThanOrEqual_date.spec.js b/frontend-old/src/plugins/veeValidate/__tests__/lessThanOrEqual_date.spec.js
new file mode 100644
index 0000000000..86ab2dc90d
--- /dev/null
+++ b/frontend-old/src/plugins/veeValidate/__tests__/lessThanOrEqual_date.spec.js
@@ -0,0 +1,58 @@
+import { describe, beforeEach, expect, it } from 'vitest'
+import lessThanOrEqual_date from '../lessThanOrEqual_date.js'
+import dayjs from '@/common/helpers/dayjs.js'
+
+const mockI18n = {
+ $tc: (key) => key,
+}
+
+describe('lessThanOrEqual_date validation', () => {
+ describe('german', () => {
+ beforeEach(() => {
+ dayjs.locale('de')
+ })
+
+ it.each([
+ [['18.01.2020', { max: '19.01.2020' }], true],
+ [['19.01.2020', { max: '19.01.2020' }], true],
+ [['20.01.2020', { max: '19.01.2020' }], false],
+ [['', { max: '19.01.2020' }], false],
+ [['2.1.2020', { max: '02.01.2020' }], false], // invalid date
+ [['3.1.2020', { max: '02.01.2020' }], false], // invalid date
+ [['today', { max: '02.01.2020' }], false], // invalid date
+ ])('validates %p as %p', (input, expected) => {
+ // given
+ const rule = lessThanOrEqual_date(dayjs, mockI18n)
+
+ // when
+ const result = rule.validate(...input)
+
+ // then
+ expect(result).toBe(expected)
+ })
+ })
+ describe('english', () => {
+ beforeEach(() => {
+ dayjs.locale('en')
+ })
+
+ it.each([
+ [['01/18/2020', { max: '01/19/2020' }], true],
+ [['01/19/2020', { max: '01/19/2020' }], true],
+ [['01/20/2020', { max: '01/19/2020' }], false],
+ [['', { max: '01/19/2020' }], false],
+ [['1/2/2020', { max: '01/02/2020' }], false], // invalid date
+ [['1/3/2020', { max: '01/02/2020' }], false], // invalid date
+ [['today', { max: '01/02/2020' }], false], // invalid date
+ ])('validates %p as %p', (input, expected) => {
+ // given
+ const rule = lessThanOrEqual_date(dayjs, mockI18n)
+
+ // when
+ const result = rule.validate(...input)
+
+ // then
+ expect(result).toBe(expected)
+ })
+ })
+})
diff --git a/frontend-old/src/plugins/veeValidate/__tests__/oneEmojiOrTwoCharacters.spec.js b/frontend-old/src/plugins/veeValidate/__tests__/oneEmojiOrTwoCharacters.spec.js
new file mode 100644
index 0000000000..bfdb516776
--- /dev/null
+++ b/frontend-old/src/plugins/veeValidate/__tests__/oneEmojiOrTwoCharacters.spec.js
@@ -0,0 +1,30 @@
+import { describe, expect, it } from 'vitest'
+import oneEmojiOrTwoCharacters from '../oneEmojiOrTwoCharacters.js'
+
+const mockI18n = {
+ $tc: (key) => key,
+}
+
+describe('oneEmojiOrTwoCharacters validation', () => {
+ it.each([
+ ['1', true],
+ ['12', true],
+ ['123', false],
+ ['🧑🏼🔧', true],
+ ['🧑🏼🔧😊', false],
+ ['😊', true],
+ ['😊😊', false],
+ ['a😊', false],
+ ['', true],
+ ['😊😊😊😊', false],
+ ])('validates %s as %s', (input, expected) => {
+ // given
+ const rule = oneEmojiOrTwoCharacters(mockI18n)
+
+ // when
+ const result = rule.validate(input)
+
+ // then
+ expect(result).toBe(expected)
+ })
+})
diff --git a/frontend-old/src/plugins/veeValidate/greaterThan.js b/frontend-old/src/plugins/veeValidate/greaterThan.js
new file mode 100644
index 0000000000..854cf95ebc
--- /dev/null
+++ b/frontend-old/src/plugins/veeValidate/greaterThan.js
@@ -0,0 +1,17 @@
+export default (i18n) => ({
+ params: ['min'],
+ /**
+ *
+ * @param {string} value value of a float number
+ * @param number min Comparison value in string format 'HH:mm'
+ * @returns {boolean} validation result
+ */
+ validate: (value, { min }) => {
+ return parseFloat(value) > min
+ },
+ message: (field, { min }) => {
+ return i18n.tc('global.validation.greaterThan', 0, {
+ min: min,
+ })
+ },
+})
diff --git a/frontend-old/src/plugins/veeValidate/greaterThanOrEqual_date.js b/frontend-old/src/plugins/veeValidate/greaterThanOrEqual_date.js
new file mode 100644
index 0000000000..99bbbc9cdb
--- /dev/null
+++ b/frontend-old/src/plugins/veeValidate/greaterThanOrEqual_date.js
@@ -0,0 +1,15 @@
+export default (dayjs, i18n) => ({
+ params: ['min'],
+ /**
+ * @param {string} value Dater value in local string format
+ * @param {string} min comparison valye in local string format
+ * @returns {bool} validation result
+ */
+ validate: (value, { min }) => {
+ const minDate = dayjs.utc(min, 'L')
+ const valueDate = dayjs.utc(value, 'L')
+ return valueDate.diff(minDate, 'day') >= 0
+ },
+ message: (field, values) =>
+ i18n.tc('global.validation.greaterThanOrEqual_date', 0, values),
+})
diff --git a/frontend-old/src/plugins/veeValidate/greaterThan_time.js b/frontend-old/src/plugins/veeValidate/greaterThan_time.js
new file mode 100644
index 0000000000..bfe874b359
--- /dev/null
+++ b/frontend-old/src/plugins/veeValidate/greaterThan_time.js
@@ -0,0 +1,25 @@
+function normalizeMin(min, dayjs) {
+ return typeof min === 'string'
+ ? dayjs.utc('1970-01-01 ' + min, 'YYYY-MM-DD LT')
+ : min.set('date', 1).set('month', 0).set('year', 1970)
+}
+
+export default (dayjs, i18n) => ({
+ params: ['min'],
+ /**
+ *
+ * @param {string} value Time value in string format 'HH:mm'
+ * @param {string} min Comparison value in string format 'HH:mm'
+ * @returns {bool} validation result
+ */
+ validate: (value, { min }) => {
+ const valueDate = dayjs.utc('1970-01-01 ' + value, 'YYYY-MM-DD LT')
+ const minDate = normalizeMin(min, dayjs)
+ return valueDate.unix() > minDate.unix()
+ },
+ message: (field, values) => {
+ return i18n.tc('global.validation.greaterThan_time', 0, {
+ min: normalizeMin(values.min, dayjs).format('LT'),
+ })
+ },
+})
diff --git a/frontend-old/src/plugins/veeValidate/lessThanOrEqual_date.js b/frontend-old/src/plugins/veeValidate/lessThanOrEqual_date.js
new file mode 100644
index 0000000000..cfb79abf78
--- /dev/null
+++ b/frontend-old/src/plugins/veeValidate/lessThanOrEqual_date.js
@@ -0,0 +1,15 @@
+export default (dayjs, i18n) => ({
+ params: ['max'],
+ /**
+ * @param {string} value Dater value in local string format
+ * @param {string} max comparison valye in local string format
+ * @returns {bool} validation result
+ */
+ validate: (value, { max }) => {
+ const maxDate = dayjs.utc(max, 'L')
+ const valueDate = dayjs.utc(value, 'L')
+ return valueDate.diff(maxDate, 'day') <= 0
+ },
+ message: (field, values) =>
+ i18n.tc('global.validation.lessThanOrEqual_date', 0, values),
+})
diff --git a/frontend-old/src/plugins/veeValidate/oneEmojiOrTwoCharacters.js b/frontend-old/src/plugins/veeValidate/oneEmojiOrTwoCharacters.js
new file mode 100644
index 0000000000..f563ec69bc
--- /dev/null
+++ b/frontend-old/src/plugins/veeValidate/oneEmojiOrTwoCharacters.js
@@ -0,0 +1,9 @@
+import { size } from 'lodash-es'
+
+export default (i18n) => ({
+ validate: (value) => {
+ return /\p{Extended_Pictographic}/u.test(value) ? size(value) <= 1 : value.length <= 2
+ },
+ message: (field, values) =>
+ i18n.tc('global.validation.oneEmojiOrTwoCharacters', 0, values),
+})
diff --git a/frontend-old/src/plugins/vuetify.js b/frontend-old/src/plugins/vuetify.js
new file mode 100644
index 0000000000..2edfc73ade
--- /dev/null
+++ b/frontend-old/src/plugins/vuetify.js
@@ -0,0 +1,59 @@
+// You still need to register Vuetify itself
+// src/plugins/vuetify.js
+
+import Vuetify from 'vuetify/lib'
+import PbsLogo from '@/assets/PbsLogo.svg'
+import GoogleLogo from '@/assets/GoogleLogo.svg'
+import eCampLogo from '@/assets/eCampLogo.svg'
+import CeviLogo from '@/assets/CeviLogo.svg'
+import JublaLogo from '@/assets/JublaLogo.svg'
+import JSLogo from '@/common/assets/logos/JSLogo.svg'
+import GSLogo from '@/common/assets/logos/GSLogo.svg'
+import TentDay from '@/assets/tents/TentDay.svg'
+import PaperSize from '@/assets/icons/PaperSize.svg'
+import BigScreen from '@/assets/icons/BigScreen.svg'
+import ResponsiveLayout from '@/assets/icons/ResponsiveLayout.svg'
+import ColumnLayout from '@/assets/icons/ColumnLayout.svg'
+import i18n from '@/plugins/i18n'
+import colors from 'vuetify/lib/util/colors'
+
+class VuetifyLoaderPlugin {
+ install(Vue) {
+ Vue.use(Vuetify)
+
+ const opts = {
+ lang: {
+ t: (key, ...params) => i18n.tc(key, 0, params),
+ },
+ icons: {
+ values: {
+ pbs: { component: PbsLogo },
+ google: { component: GoogleLogo },
+ ecamp: { component: eCampLogo },
+ cevi: { component: CeviLogo },
+ jubla: { component: JublaLogo },
+ js: { component: JSLogo },
+ gs: { component: GSLogo },
+ tentDay: { component: TentDay },
+ paperSize: { component: PaperSize },
+ bigScreen: { component: BigScreen },
+ columnLayout: { component: ColumnLayout },
+ responsiveLayout: { component: ResponsiveLayout },
+ },
+ },
+ theme: {
+ themes: {
+ light: {
+ error: colors.red.darken2,
+ },
+ },
+ },
+ }
+
+ vuetify = new Vuetify(opts)
+ }
+}
+
+export let vuetify
+
+export default new VuetifyLoaderPlugin()
diff --git a/frontend-old/src/router.js b/frontend-old/src/router.js
new file mode 100644
index 0000000000..214a71090c
--- /dev/null
+++ b/frontend-old/src/router.js
@@ -0,0 +1,1050 @@
+import Vue from 'vue'
+import Router from 'vue-router'
+import { slugify } from '@/plugins/slugify.js'
+import { isLoggedIn, isAdmin } from '@/plugins/auth'
+import { apiStore } from '@/plugins/store'
+import { campShortTitle } from '@/common/helpers/campShortTitle'
+import { getEnv } from '@/environment.js'
+
+Vue.use(Router)
+
+const NavigationAuth = () => import('./views/auth/NavigationAuth.vue')
+const NavigationDefault = () => import('./views/NavigationDefault.vue')
+const NavigationCamp = () => import('./views/camp/navigation/NavigationCamp.vue')
+const GenericPage = () => import('./components/generic/GenericPage.vue')
+
+/* istanbul ignore next */
+export default new Router({
+ mode: 'history',
+ base: '/old',
+ routes: [
+ ...(getEnv().FEATURE_DEVELOPER
+ ? [
+ // Dev-Pages:
+ {
+ path: '/controls',
+ name: 'controls',
+ components: {
+ default: () => import('./views/dev/Controls.vue'),
+ },
+ beforeEnter: requireAuth,
+ },
+ {
+ path: '/performance',
+ name: 'performance',
+ components: {
+ default: () => import('./views/dev/Performance.vue'),
+ },
+ beforeEnter: requireAuth,
+ },
+ ]
+ : []),
+
+ // Prod-Pages:
+ {
+ path: '/debug',
+ name: 'debug',
+ components: {
+ default: () => import('./views/admin/Debug.vue'),
+ },
+ },
+ {
+ path: '/admin',
+ beforeEnter: all([requireAuth, requireAdmin]),
+ components: {
+ navigation: NavigationDefault,
+ default: GenericPage,
+ aside: () => import('./views/admin/SideBarAdmin.vue'),
+ },
+ children: [
+ {
+ path: '',
+ redirect: 'debug',
+ },
+ {
+ path: 'debug',
+ name: 'admin/debug',
+ components: {
+ default: () => import('./views/admin/Debug.vue'),
+ },
+ },
+ ...(getEnv().FEATURE_CHECKLIST
+ ? [
+ {
+ path: 'checklists',
+ name: 'admin/checklists',
+ components: {
+ default: () => import('./views/admin/Checklists.vue'),
+ },
+ },
+ {
+ path: 'checklist/:checklistId/:checklistName?',
+ name: 'admin/checklists/checklist',
+ components: {
+ default: () => import('./views/admin/Checklist.vue'),
+ },
+ props: {
+ default: (route) => ({
+ checklist: checklistFromRoute(route),
+ }),
+ },
+ },
+ ]
+ : []),
+ ],
+ },
+
+ {
+ path: '/register',
+ name: 'register',
+ components: {
+ navigation: NavigationAuth,
+ default: () => import('./views/auth/Register.vue'),
+ },
+ },
+ {
+ path: '/register-done',
+ name: 'register-done',
+ components: {
+ navigation: NavigationAuth,
+ default: () => import('./views/auth/RegisterDone.vue'),
+ },
+ },
+ {
+ path: '/resend-activation',
+ name: 'resendActivation',
+ components: {
+ navigation: NavigationAuth,
+ default: () => import('./views/auth/ResendActivation.vue'),
+ },
+ },
+ {
+ path: '/reset-password',
+ name: 'resetPasswordRequest',
+ components: {
+ navigation: NavigationAuth,
+ default: () => import('./views/auth/ResetPasswordRequest.vue'),
+ },
+ },
+ {
+ path: '/reset-password/:id',
+ name: 'resetPassword',
+ components: {
+ navigation: NavigationAuth,
+ default: () => import('./views/auth/ResetPassword.vue'),
+ },
+ props: {
+ default: (route) => {
+ return {
+ id: route.params.id,
+ }
+ },
+ },
+ },
+ {
+ path: '/activate/:userId/:activationKey',
+ name: 'activate',
+ components: {
+ navigation: NavigationAuth,
+ default: () => import('./views/auth/Activate.vue'),
+ },
+ props: {
+ default: (route) => {
+ return {
+ userId: route.params.userId,
+ activationKey: route.params.activationKey,
+ }
+ },
+ },
+ },
+ {
+ path: '/login',
+ name: 'login',
+ components: {
+ navigation: NavigationAuth,
+ default: () => import('./views/auth/Login.vue'),
+ },
+ },
+ {
+ path: '/loginCallback',
+ name: 'loginCallback',
+ components: {
+ navigation: NavigationAuth,
+ default: () => import('./views/auth/LoginCallback.vue'),
+ },
+ },
+ {
+ path: '/profile',
+ name: 'profile',
+ components: {
+ navigation: NavigationDefault,
+ default: () => import('./views/Profile.vue'),
+ },
+ beforeEnter: requireAuth,
+ },
+ {
+ path: '/profile/verify-mail/:emailVerificationKey',
+ name: 'profileVerifyEmail',
+ components: {
+ navigation: NavigationDefault,
+ default: () => import('./views/Profile.vue'),
+ },
+ props: {
+ default: (route) => {
+ return { emailVerificationKey: route.params.emailVerificationKey }
+ },
+ },
+ beforeEnter: requireAuth,
+ },
+ {
+ path: '/camps',
+ name: 'camps',
+ components: {
+ navigation: NavigationDefault,
+ default: () => import('./views/Camps.vue'),
+ },
+ beforeEnter: requireAuth,
+ },
+ {
+ path: '/invitations',
+ name: 'invitations',
+ components: {
+ navigation: NavigationDefault,
+ default: () => import('./views/Invitations.vue'),
+ },
+ beforeEnter: requireAuth,
+ },
+ {
+ path: '/camps/create',
+ name: 'camps/create',
+ components: {
+ navigation: NavigationDefault,
+ default: () => import('./views/CampCreate.vue'),
+ },
+ beforeEnter: requireAuth,
+ },
+ {
+ path: '/camps/invitation/rejected',
+ name: 'invitationRejected',
+ components: {
+ navigation: NavigationDefault,
+ default: () => import('./views/camp/Invitation.vue'),
+ },
+ props: {
+ default: () => ({
+ variant: 'rejected',
+ }),
+ },
+ },
+ {
+ path: '/camps/invitation/updateError',
+ name: 'invitationUpdateError',
+ components: {
+ navigation: NavigationDefault,
+ default: () => import('./views/camp/Invitation.vue'),
+ },
+ props: {
+ default: () => ({
+ variant: 'error',
+ }),
+ },
+ },
+ {
+ path: '/camps/invitation/:inviteKey',
+ name: 'campInvitation',
+ components: {
+ navigation: NavigationDefault,
+ default: () => import('./views/camp/Invitation.vue'),
+ },
+ props: {
+ default: (route) => ({
+ variant: 'default',
+ invitation: invitationFromInviteKey(route.params.inviteKey),
+ }),
+ },
+ },
+ {
+ path: '/camps/:campId/:campShortTitle?',
+ components: {
+ navigation: NavigationCamp,
+ default: GenericPage,
+ },
+ beforeEnter: all([requireAuth, requireCamp]),
+ props: {
+ navigation: (route) => ({ camp: campFromRoute(route) }),
+ default: (route) => ({
+ camp: campFromRoute(route),
+ period: periodFromRoute(route),
+ layout: getContentLayout(route),
+ }),
+ },
+ children: [
+ {
+ path: 'program/period/:periodId/:periodTitle?',
+ name: 'camp/period/program',
+ component: () => import('./views/camp/CampProgram.vue'),
+ beforeEnter: requirePeriod,
+ },
+ {
+ path: 'program',
+ name: 'camp/program',
+ async beforeEnter(to, from, next) {
+ redirectToPeriod(to, from, next, 'camp/period/program')
+ },
+ },
+ {
+ path: 'overview/checklists',
+ name: 'camp/overview/checklists',
+ component: () => import('./views/camp/checklistOverview/ChecklistLists.vue'),
+ },
+ {
+ path: 'story/period/:periodId/:periodTitle?',
+ name: 'camp/period/story',
+ component: () => import('./views/camp/Story.vue'),
+ beforeEnter: requirePeriod,
+ },
+ {
+ path: 'story',
+ name: 'camp/story',
+ async beforeEnter(to, from, next) {
+ redirectToPeriod(to, from, next, 'camp/period/story')
+ },
+ },
+ {
+ path: 'dashboard',
+ name: 'camp/dashboard',
+ component: () => import('./views/camp/Dashboard.vue'),
+ },
+ {
+ path: '',
+ name: 'camp/home',
+ redirect: { name: 'camp/dashboard' },
+ },
+ ],
+ },
+ {
+ name: 'camp/material/all',
+ path: '/camps/:campId/:campShortTitle?/material/all',
+ components: {
+ navigation: NavigationCamp,
+ default: () => import('./views/camp/material/MaterialOverview.vue'),
+ aside: () => import('./views/camp/material/SideBarMaterialLists.vue'),
+ },
+ beforeEnter: all([requireAuth, requireCamp]),
+ props: {
+ navigation: (route) => ({ camp: campFromRoute(route) }),
+ aside: (route) => ({ camp: campFromRoute(route) }),
+ default: (route) => ({
+ camp: campFromRoute(route),
+ }),
+ },
+ },
+ {
+ name: 'camp/material/unassigned',
+ path: '/camps/:campId/:campShortTitle?/material/unassigned',
+ components: {
+ navigation: NavigationCamp,
+ default: () => import('./views/camp/material/MaterialUnassigned.vue'),
+ aside: () => import('./views/camp/material/SideBarMaterialLists.vue'),
+ },
+ beforeEnter: all([requireAuth, requireCamp]),
+ props: {
+ navigation: (route) => ({ camp: campFromRoute(route) }),
+ aside: (route) => ({ camp: campFromRoute(route) }),
+ default: (route) => ({
+ camp: campFromRoute(route),
+ }),
+ },
+ },
+ {
+ name: 'camp/overview/checklists/checklist',
+ path: '/camps/:campId/:campShortTitle?/overview/checklists/:checklistId/:checklistName?',
+ components: {
+ navigation: NavigationCamp,
+ default: () => import('./views/camp/checklistOverview/ChecklistOverview.vue'),
+ aside: () =>
+ import('./views/camp/checklistOverview/SideBarChecklistOverview.vue'),
+ },
+ beforeEnter: all([requireAuth, requireCamp]),
+ props: {
+ navigation: (route) => ({ camp: campFromRoute(route) }),
+ aside: (route) => ({ camp: campFromRoute(route) }),
+ default: (route) => ({
+ camp: campFromRoute(route),
+ checklist: checklistFromRoute(route),
+ }),
+ },
+ },
+ {
+ name: 'camp/material/lists', // Only used on mobile
+ path: '/camps/:campId/:campShortTitle?/material/lists',
+ components: {
+ navigation: NavigationCamp,
+ default: () => import('./views/camp/material/MaterialLists.vue'),
+ },
+ beforeEnter: all([requireAuth, requireCamp]),
+ props: {
+ navigation: (route) => ({ camp: campFromRoute(route) }),
+ default: (route) => ({
+ camp: campFromRoute(route),
+ }),
+ },
+ },
+ {
+ name: 'camp/material/detail',
+ path: '/camps/:campId/:campShortTitle?/material/:materialId/:materialName?',
+ components: {
+ navigation: NavigationCamp,
+ default: () => import('./views/camp/material/MaterialDetail.vue'),
+ aside: () => import('./views/camp/material/SideBarMaterialLists.vue'),
+ },
+ beforeEnter: all([requireAuth, requireCamp, requireMaterialList]),
+ props: {
+ navigation: (route) => ({ camp: campFromRoute(route) }),
+ aside: (route) => ({ camp: campFromRoute(route) }),
+ default: (route) => ({
+ camp: campFromRoute(route),
+ materialList: materialListFromRoute(route),
+ }),
+ },
+ },
+ {
+ name: 'camp/admin/activity/category',
+ path: '/camps/:campId/:campShortTitle?/category/:categoryId/:categoryName?',
+ components: {
+ navigation: NavigationCamp,
+ default: () => import('./views/camp/category/Category.vue'),
+ aside: () => import('./views/camp/category/SideBarCategory.vue'),
+ },
+ beforeEnter: all([requireAuth, requireCamp, requireCategory]),
+ props: {
+ navigation: (route) => ({ camp: campFromRoute(route) }),
+ aside: (route) => ({ camp: campFromRoute(route) }),
+ default: (route) => ({
+ camp: campFromRoute(route),
+ category: categoryFromRoute(route),
+ }),
+ },
+ },
+ ...(getEnv().FEATURE_CHECKLIST
+ ? [
+ // Checklist-Pages:
+ {
+ name: 'camp/admin/checklists/checklist',
+ path: '/camps/:campId/:campTitle?/admin/checklist/:checklistId/:checklistName?',
+ components: {
+ navigation: NavigationCamp,
+ default: () => import('./views/camp/checklist/Checklist.vue'),
+ aside: () => import('./views/camp/checklist/SideBarChecklist.vue'),
+ },
+ beforeEnter: all([requireAuth, requireCamp, requireChecklist]),
+ props: {
+ navigation: (route) => ({ camp: campFromRoute(route) }),
+ aside: (route) => ({ camp: campFromRoute(route) }),
+ default: (route) => ({
+ camp: campFromRoute(route),
+ checklist: checklistFromRoute(route),
+ }),
+ },
+ },
+ ]
+ : []),
+ {
+ path: '/camps/:campId/:campShortTitle?/admin',
+ components: {
+ navigation: NavigationCamp,
+ default: GenericPage,
+ aside: () => import('./views/camp/admin/SideBarAdmin.vue'),
+ },
+ beforeEnter: all([requireAuth, requireCamp]),
+ props: {
+ navigation: (route) => ({ camp: campFromRoute(route) }),
+ default: (route) => ({
+ camp: campFromRoute(route),
+ layout: getContentLayout(route),
+ }),
+ aside: (route) => ({ camp: campFromRoute(route) }),
+ },
+ children: [
+ {
+ path: 'info',
+ name: 'camp/admin/info',
+ component: () => import('./views/camp/admin/Info.vue'),
+ props: (route) => ({ camp: campFromRoute(route) }),
+ },
+ {
+ path: 'activity',
+ name: 'camp/admin/activity',
+ component: () => import('./views/camp/admin/Activity.vue'),
+ props: (route) => ({ camp: campFromRoute(route) }),
+ },
+ {
+ path: 'collaborators',
+ name: 'camp/admin/collaborators',
+ component: () => import('./views/camp/admin/Collaborators.vue'),
+ props: () => ({ layout: 'normal' }),
+ },
+ {
+ path: 'material',
+ name: 'camp/admin/material',
+ component: () => import('./views/camp/admin/AdminMaterialLists.vue'),
+ },
+ {
+ path: 'print',
+ name: 'camp/admin/print',
+ component: () => import('./views/camp/admin/Print.vue'),
+ props: (route) => ({ camp: campFromRoute(route) }),
+ },
+ ...(getEnv().FEATURE_CHECKLIST
+ ? [
+ // Checklist-Pages:
+ {
+ path: 'checklists',
+ name: 'camp/admin/checklists',
+ component: () => import('./views/camp/admin/Checklists.vue'),
+ props: (route) => ({ camp: campFromRoute(route) }),
+ },
+ ]
+ : []),
+ {
+ path: 'materiallists',
+ name: 'camp/material',
+ redirect: { name: 'camp/admin/material' },
+ },
+ {
+ path: '',
+ name: 'camp/admin',
+ redirect: { name: 'camp/admin/info' },
+ },
+ ],
+ },
+ {
+ path: '/camps/:campId/:campShortTitle/program/activity/:activityId/:scheduleEntryId?/:activityName?',
+ name: 'camp/activity',
+ components: {
+ navigation: NavigationCamp,
+ default: () => import('./views/camp/activity/Activity.vue'),
+ aside: () => import('./views/camp/activity/SideBarProgram.vue'),
+ },
+ beforeEnter: all([requireAuth, requireCamp, requireActivityScheduleEntry]),
+ props: {
+ navigation: (route) => ({ camp: campFromRoute(route) }),
+ default: (route) => ({
+ activityId: route.params.activityId,
+ scheduleEntryId: route.params.scheduleEntryId,
+ }),
+ aside: (route) => ({
+ camp: campFromRoute(route),
+ activityId: route.params.activityId,
+ scheduleEntryId: route.params.scheduleEntryId,
+ }),
+ },
+ },
+ {
+ path: '/camps/:campId/:campShortTitle/program/activities/:scheduleEntryId/:activityName?',
+ redirect:
+ '/camps/:campId/:campShortTitle/program/activity/0/:scheduleEntryId?/:activityName?',
+ },
+ {
+ path: '/',
+ name: 'home',
+ redirect: { name: 'camps' },
+ },
+ {
+ path: '**',
+ name: 'PageNotFound',
+ components: {
+ navigation: NavigationDefault,
+ default: () => import('./views/PageNotFound.vue'),
+ },
+ },
+ ],
+})
+
+function evaluateGuards(guards, to, from, next) {
+ const guardsLeft = guards.slice(0)
+ const nextGuard = guardsLeft.shift()
+
+ if (nextGuard === undefined) {
+ next()
+ return
+ }
+
+ nextGuard(to, from, (nextArg) => {
+ if (nextArg === undefined) {
+ evaluateGuards(guardsLeft, to, from, next)
+ return
+ }
+ next(nextArg)
+ })
+}
+
+function all(guards) {
+ return (to, from, next) => evaluateGuards(guards, to, from, next)
+}
+
+function requireAuth(to, from, next) {
+ if (isLoggedIn()) {
+ next()
+ } else {
+ next({ name: 'login', query: to.path === '/' ? {} : { redirect: to.fullPath } })
+ }
+}
+
+function requireAdmin(to, from, next) {
+ if (isAdmin()) {
+ next()
+ } else {
+ next({
+ name: 'PageNotFound',
+ params: [to.fullPath, ''],
+ replace: true,
+ })
+ }
+}
+
+async function requireCamp(to, from, next) {
+ const camp = await campFromRoute(to)
+ if (camp === undefined) {
+ next({
+ name: 'PageNotFound',
+ params: [to.fullPath, ''],
+ replace: true,
+ })
+ } else {
+ await camp._meta.load
+ .then(() => {
+ next()
+ })
+ .catch(() => {
+ next({
+ name: 'PageNotFound',
+ params: [to.fullPath, ''],
+ replace: true,
+ })
+ })
+ }
+}
+
+async function requireActivityScheduleEntry(to, from, next) {
+ await apiStore
+ .get()
+ .activities({ id: to.params.activityId })
+ .$reload()
+ .then(async (activity) => {
+ // activity exists
+ const scheduleEntry = to.params.scheduleEntryId
+ ? activity
+ .scheduleEntries()
+ .items.find((entry) => entry.id === to.params.scheduleEntryId)
+ : null
+
+ if (scheduleEntry) {
+ // activity and scheduleEntry exist
+ next()
+ } else {
+ // scheduleEntry is not found, use first activity scheduleEntry
+ to.params.scheduleEntryId = (await firstActivityScheduleEntry(activity)).id
+ next(to)
+ }
+ })
+ .catch(() => {
+ // activityId does not exist, check if scheduleEntryId exists
+ if (to.params.scheduleEntryId) {
+ apiStore
+ .get()
+ .scheduleEntries({ id: to.params.scheduleEntryId })
+ ._meta.load.then(async (scheduleEntry) => {
+ to.params.activityId = scheduleEntry.activity().id
+ next(to)
+ })
+ .catch(async () => {
+ // scheduleEntry and activity are not found, fallback to camp program
+ next({
+ ...to,
+ name: 'camp/program',
+ })
+ })
+ }
+ })
+}
+
+async function requirePeriod(to, from, next) {
+ const period = await periodFromRoute(to)
+ if (period === undefined) {
+ next({
+ name: 'PageNotFound',
+ params: [to.fullPath, ''],
+ replace: true,
+ })
+ } else {
+ await period._meta.load
+ .then(() => {
+ next()
+ })
+ .catch(() => {
+ next(campRoute(campFromRoute(to)))
+ })
+ }
+}
+
+async function requireCategory(to, from, next) {
+ const category = await categoryFromRoute(to)
+ if (category === undefined) {
+ next({
+ name: 'PageNotFound',
+ params: [to.fullPath, ''],
+ replace: true,
+ })
+ } else {
+ await category._meta.load
+ .then(() => {
+ next()
+ })
+ .catch(() => {
+ next({
+ name: 'PageNotFound',
+ params: [to.fullPath, ''],
+ replace: true,
+ })
+ })
+ }
+}
+
+async function requireMaterialList(to, from, next) {
+ const materialList = await materialListFromRoute(to)
+ if (materialList === undefined) {
+ next({
+ name: 'PageNotFound',
+ params: [to.fullPath, ''],
+ replace: true,
+ })
+ } else {
+ await materialList._meta.load
+ .then(() => {
+ next()
+ })
+ .catch(() => {
+ next(campRoute(campFromRoute(to)))
+ })
+ }
+}
+
+async function requireChecklist(to, from, next) {
+ const checklist = await checklistFromRoute(to)
+ if (checklist === undefined) {
+ next({
+ name: 'PageNotFound',
+ params: [to.fullPath, ''],
+ replace: true,
+ })
+ } else {
+ await checklist._meta.load
+ .then(() => {
+ next()
+ })
+ .catch(() => {
+ next(campRoute(campFromRoute(to)))
+ })
+ }
+}
+
+export function campFromRoute(route) {
+ if (!route.params.campId) {
+ return undefined
+ }
+ return apiStore.get().camps({ id: route.params.campId })
+}
+
+export function invitationFromInviteKey(inviteKey) {
+ return apiStore.get().invitations({ action: 'find', id: inviteKey })
+}
+
+export function periodFromRoute(route) {
+ if (!route.params.periodId) {
+ return undefined
+ }
+ return apiStore.get().periods({ id: route.params.periodId })
+}
+
+function categoryFromRoute(route) {
+ if (!route.params.categoryId) {
+ return undefined
+ }
+ return campFromRoute(route)
+ .categories()
+ .allItems.find((c) => c.id === route.params.categoryId)
+}
+
+export function materialListFromRoute(route) {
+ if (!route.params.materialId) {
+ return undefined
+ }
+ return apiStore.get().materialLists({ id: route.params.materialId })
+}
+
+export function checklistFromRoute(route) {
+ if (!route.params.checklistId) {
+ return undefined
+ }
+ return campFromRoute(route)
+ .checklists()
+ .allItems.find((c) => c.id === route.params.checklistId)
+}
+
+function getContentLayout(route) {
+ switch (route.name) {
+ case 'camp/checklist':
+ case 'camp/period/program':
+ case 'camp/admin/print':
+ case 'camp/admin/activity/category':
+ return 'full'
+ case 'camp/print':
+ case 'camp/material':
+ case 'camp/admin/info':
+ case 'camp/admin/activity':
+ return 'wide'
+ case 'camp/admin/collaborators':
+ case 'camp/admin/material':
+ default:
+ return 'normal'
+ }
+}
+
+/**
+ * @param camp
+ * @param subroute {'admin' | 'dashboard' | 'program' | 'material' | 'story' | 'home' | 'print' }
+ * @param query
+ */
+export function campRoute(camp, subroute = 'dashboard', query = {}) {
+ if (camp._meta.loading) return {}
+ return {
+ name: 'camp/' + subroute,
+ params: { campId: camp.id, campShortTitle: slugify(campShortTitle(camp)) },
+ query,
+ }
+}
+
+/**
+ * @param camp
+ * @param materialListOrRoute { '/all', '/lists', object }
+ * @param query
+ */
+export function materialListRoute(camp, materialListOrRoute = '/all', query = {}) {
+ if (camp._meta.loading) return {}
+ if (typeof materialListOrRoute === 'string') {
+ return {
+ name: `camp/material${materialListOrRoute}`,
+ params: { campId: camp.id, campShortTitle: slugify(campShortTitle(camp)) },
+ query,
+ }
+ }
+ if (!materialListOrRoute?._meta || materialListOrRoute.meta?.loading) return {}
+ return {
+ name: 'camp/material/detail',
+ params: {
+ campId: camp.id,
+ campShortTitle: slugify(campShortTitle(camp)),
+ materialId: materialListOrRoute.id,
+ materialName: slugify(materialListOrRoute.name),
+ },
+ query,
+ }
+}
+
+/**
+ * @param camp
+ * @param subroute {'info' | 'activity' | 'collaborators' | 'material' | 'print'}
+ * @param query
+ */
+export function adminRoute(camp, subroute = 'info', query = {}) {
+ if (camp._meta.loading) return {}
+ return {
+ name: 'camp/admin/' + subroute,
+ params: { campId: camp.id, campShortTitle: slugify(campShortTitle(camp)) },
+ query,
+ }
+}
+
+export function loginRoute(redirectTo) {
+ return { path: '/login', query: { redirect: redirectTo } }
+}
+
+export function periodRoute(period, routeName = 'camp/period/program', query = {}) {
+ const camp = period.camp()
+ if (camp._meta.loading || period._meta.loading) return {}
+ return {
+ name: routeName,
+ params: {
+ campId: camp.id,
+ campShortTitle: slugify(campShortTitle(camp)),
+ periodId: period.id,
+ periodTitle: slugify(period.description),
+ },
+ query,
+ }
+}
+
+export function scheduleEntryRoute(scheduleEntry, query = {}) {
+ if (scheduleEntry?._meta.loading || scheduleEntry?.activity()._meta.loading) return {}
+
+ const activity = scheduleEntry.activity()
+ const camp = activity.camp()
+
+ // if (camp._meta.loading) return {}
+
+ return {
+ name: 'camp/activity',
+ params: {
+ campId: camp.id,
+ campShortTitle: slugify(campShortTitle(camp)),
+ scheduleEntryId: scheduleEntry.id,
+ activityId: activity.id,
+ activityName: slugify(activity.title),
+ },
+ query,
+ }
+}
+
+export async function firstActivityScheduleEntryRoute(activity, query = {}) {
+ if (typeof activity === 'string') {
+ activity = await apiStore.get().activities({ id: activity })._meta.load
+ }
+ const camp = activity.camp()
+ apiStore.reload(activity.scheduleEntries())
+ const scheduleEntry = await firstActivityScheduleEntry(activity)
+
+ return {
+ name: 'camp/activity',
+ params: {
+ campId: camp.id,
+ campShortTitle: slugify(campShortTitle(camp)),
+ scheduleEntryId: scheduleEntry.id,
+ activityId: activity.id,
+ activityName: slugify(activity.title),
+ },
+ query,
+ }
+}
+
+export function categoryRoute(camp, category, query = {}) {
+ if (camp._meta.loading || category._meta.loading) return {}
+ return {
+ name: 'camp/admin/activity/category',
+ params: {
+ campId: camp.id,
+ campShortTitle: slugify(campShortTitle(camp)),
+ categoryId: category.id,
+ categoryName: slugify(category.name),
+ },
+ query,
+ }
+}
+
+export function checklistRoute(camp, checklist, query = {}) {
+ if (camp?._meta.loading || checklist?._meta.loading) return {}
+
+ if (!camp) {
+ if (!checklist) {
+ return {
+ name: 'admin/checklists',
+ query,
+ }
+ }
+ return {
+ name: 'admin/checklists/checklist',
+ params: {
+ checklistId: checklist.id,
+ checklistName: slugify(checklist.name),
+ },
+ query,
+ }
+ }
+
+ if (!checklist) {
+ return {
+ name: 'camp/admin/checklists',
+ params: {
+ campId: camp.id,
+ campTitle: slugify(camp.title),
+ },
+ query,
+ }
+ }
+ return {
+ name: 'camp/admin/checklists/checklist',
+ params: {
+ campId: camp.id,
+ campTitle: slugify(camp.title),
+ checklistId: checklist.id,
+ checklistName: slugify(checklist.name),
+ },
+ query,
+ }
+}
+
+export function checklistOverviewRoute(camp, checklist, query = {}) {
+ if (camp?._meta.loading || checklist._meta.loading) return {}
+
+ if (!checklist) {
+ return {
+ name: 'camp/overview/checklists',
+ params: {
+ campId: camp.id,
+ campTitle: slugify(camp.title),
+ },
+ query,
+ }
+ }
+
+ return {
+ name: 'camp/overview/checklists/checklist',
+ params: {
+ campId: camp.id,
+ campTitle: slugify(camp.title),
+ checklistId: checklist.id,
+ checklistName: slugify(checklist.name),
+ },
+ query,
+ }
+}
+
+async function firstFuturePeriod(route) {
+ if (!route.params.campId) {
+ return undefined
+ }
+ const periods = await apiStore.get().camps({ id: route.params.campId }).periods()._meta
+ .load
+ // Return the first period that hasn't ended, or if no such period exists, return the first period
+ return (
+ periods.items.find((period) => new Date(period.end) >= new Date()) ||
+ periods.items.find((_) => true)
+ )
+}
+
+export async function firstActivityScheduleEntry(activity) {
+ if (typeof activity === 'string') {
+ activity = await apiStore.get().activities({ id: activity })._meta.load
+ }
+ await activity.scheduleEntries()._meta.load
+ return activity
+ .scheduleEntries()
+ .items.reduce(
+ (result, current) =>
+ result === null || new Date(result.start) > new Date(current.start)
+ ? current
+ : result,
+ null
+ )
+}
+
+async function redirectToPeriod(to, from, next, routeName) {
+ const period = await firstFuturePeriod(to)
+ if (period) {
+ await period.camp()._meta.load
+ next(periodRoute(period, routeName, to.query))
+ } else {
+ const camp = await apiStore.get().camps({ id: to.params.campId })
+ next(campRoute(camp, 'camp/admin', to.query))
+ }
+}
diff --git a/frontend-old/src/scss/global.scss b/frontend-old/src/scss/global.scss
new file mode 100644
index 0000000000..3333db56c6
--- /dev/null
+++ b/frontend-old/src/scss/global.scss
@@ -0,0 +1,258 @@
+@use '~inter-ui/default' as inter-ui with (
+ $inter-font-path: '~inter-ui/Inter (web hinted latin)'
+);
+@include inter-ui.weight-200;
+@include inter-ui.weight-400;
+@include inter-ui.weight-500;
+@include inter-ui.weight-600;
+body {
+ font-feature-settings: 'ss03', 'calt', 'rlig', 'kern', 'cv05', 'cv07', 'cv08';
+}
+.text-uppercase {
+ font-feature-settings: 'ss03', 'calt', 'rlig', 'kern', 'cv05', 'cv07';
+}
+.v-toolbar__title {
+ font-weight: 500;
+}
+.theme--light.v-application {
+ background: #546e7a !important;
+}
+.v-calendar-daily,
+.v-skeleton-loader__actions,
+.v-skeleton-loader__article,
+.v-skeleton-loader__card-heading,
+.v-skeleton-loader__card-text,
+.v-skeleton-loader__date-picker,
+.v-skeleton-loader__list-item,
+.v-skeleton-loader__list-item-avatar,
+.v-skeleton-loader__list-item-text,
+.v-skeleton-loader__list-item-two-line,
+.v-skeleton-loader__list-item-avatar-two-line,
+.v-skeleton-loader__list-item-three-line,
+.v-skeleton-loader__list-item-avatar-three-line,
+.v-skeleton-loader__table-heading,
+.v-skeleton-loader__table-thead,
+.v-skeleton-loader__table-tbody,
+.v-skeleton-loader__table-tfoot {
+ background: none !important;
+}
+.v-skeleton-loader--inherit-size .v-skeleton-loader__bone {
+ height: inherit !important;
+ width: inherit !important;
+}
+.v-skeleton-loader--no-margin .v-skeleton-loader__bone {
+ margin-bottom: 0 !important;
+}
+.v-btn--outlined:before {
+ opacity: 0.08 !important;
+}
+.theme--dark.v-btn.v-btn--outlined:hover::before {
+ opacity: 0.16 !important;
+}
+.w-100 {
+ width: 100%;
+}
+.h-full {
+ height: 100%;
+}
+.v-application {
+ ul.v-list {
+ padding-left: 0;
+ }
+
+ .min-h-0 {
+ min-height: 0;
+ }
+
+ .e-title-link {
+ color: inherit;
+ text-underline-offset: 0.1em;
+ text-decoration-color: #1976d2;
+ outline-offset: 0.2em;
+
+ &:focus {
+ text-decoration-line: none;
+ }
+
+ &:hover {
+ text-decoration-thickness: 2px;
+ }
+ }
+
+ .v-alert.warning--text {
+ .v-alert__icon {
+ color: #d25f00 !important;
+ }
+ .v-alert__content {
+ color: #972e00 !important;
+ }
+ border-color: #ffe0b2 !important;
+ }
+
+ .elevation-4--light {
+ box-shadow:
+ 0 2px 4px -1px rgba(0, 0, 0, 0.1),
+ 0 4px 5px 0px rgba(0, 0, 0, 0.07),
+ 0 1px 10px 0px rgba(0, 0, 0, 0.06) !important;
+ }
+}
+.tabular-nums {
+ font-variant-numeric: tabular-nums;
+}
+
+.v-list-item--link:before {
+ border-radius: inherit;
+}
+
+.e-form-container + .e-form-container {
+ margin-top: 12px;
+}
+
+.basis-auto {
+ flex-basis: auto !important;
+}
+
+.select-left .v-select__selections {
+ justify-content: flex-start !important;
+}
+
+.flex-full {
+ flex: 1 1 0;
+}
+
+.v-sheet > .v-expansion-panels,
+.v-card > .v-expansion-panels {
+ border-top: 1px solid #eee;
+ border-bottom: 1px solid #eee;
+
+ > .v-expansion-panel + .v-expansion-panel {
+ border-top: 1px solid #eee;
+ }
+}
+
+.v-text-field--outlined.v-input--is-readonly:not(.v-input--is-focused):not(
+ .v-input--has-state
+ )
+ > .v-input__control
+ > .v-input__slot
+ fieldset {
+ color: rgb(0, 0, 0, 0.18);
+}
+
+.v-text-field.v-input--is-readonly > .v-input__control > .v-input__slot,
+.v-text-field.v-input--is-readonly > .v-input__control > .v-input__slot:before,
+.v-text-field.v-input--is-readonly > .v-input__control > .v-input__slot:after {
+ border-color: transparent !important;
+ background-color: transparent !important;
+}
+
+.v-text-field--outlined.v-input--is-readonly
+ > .v-input__control
+ > .v-input__slot
+ fieldset {
+ border: none !important;
+}
+
+.v-list-item__title {
+ font-size: 16px;
+}
+.v-list-item__content {
+ align-content: flex-start;
+}
+
+.dragging {
+ cursor: move;
+}
+
+.vertical-baseline {
+ vertical-align: baseline !important;
+}
+
+.pb-safe {
+ padding-bottom: env(safe-area-inset-bottom);
+}
+
+.mb-safe {
+ margin-bottom: env(safe-area-inset-bottom);
+}
+
+.d-flow-root {
+ display: flow-root;
+}
+.v-application {
+ .d-contents {
+ display: contents !important;
+ }
+
+ @media #{map-get($display-breakpoints, 'sm-and-up')} {
+ .d-sm-contents {
+ display: contents !important;
+ }
+ }
+}
+
+.d-grid {
+ display: grid;
+}
+
+.d-inline-grid {
+ display: inline-grid;
+}
+
+.pointer-events-none {
+ pointer-events: none;
+}
+
+.user-select-none {
+ user-select: none;
+}
+
+.opacity-60 {
+ opacity: 0.6;
+}
+
+.grid-cols-2 {
+ grid-template-columns: 1fr 1fr;
+}
+
+.grid-cols-fill {
+ grid-template-columns: repeat(auto-fill, 1fr);
+}
+
+.currentColor--text {
+ color: currentColor !important;
+}
+
+.e-button-container {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1rem;
+}
+
+.relative {
+ position: relative;
+}
+
+.absolute {
+ position: absolute;
+}
+
+.bottom-0 {
+ bottom: 0;
+}
+
+.bottom-n100 {
+ bottom: -100%;
+}
+
+.right-0 {
+ right: 0;
+}
+
+.top-0 {
+ top: 0;
+}
+
+.justify-items-center {
+ justify-items: center;
+}
diff --git a/frontend-old/src/scss/tailwind.scss b/frontend-old/src/scss/tailwind.scss
new file mode 100644
index 0000000000..3a2d23bc71
--- /dev/null
+++ b/frontend-old/src/scss/tailwind.scss
@@ -0,0 +1,53 @@
+// This will eventually be replaced by tailwind
+
+.whitespace-normal {
+ white-space: normal;
+}
+
+.w-0 {
+ width: 0;
+}
+
+.gap-1 {
+ gap: 4px;
+}
+
+.gap-x-1 {
+ column-gap: 4px;
+}
+
+.gap-2 {
+ gap: 8px;
+}
+
+.gap-x-2 {
+ column-gap: 8px;
+}
+
+.gap-4 {
+ gap: 16px;
+}
+
+.gap-x-4 {
+ column-gap: 16px;
+}
+
+.flex-wrap-reverse {
+ flex-wrap: wrap-reverse;
+}
+
+.justify-items-start {
+ justify-items: start;
+}
+
+.justify-content-between {
+ justify-content: space-between;
+}
+
+.opacity-60 {
+ opacity: 0.6;
+}
+
+.italic {
+ font-style: italic;
+}
diff --git a/frontend-old/src/scss/variables.scss b/frontend-old/src/scss/variables.scss
new file mode 100644
index 0000000000..65fec7cdc3
--- /dev/null
+++ b/frontend-old/src/scss/variables.scss
@@ -0,0 +1,103 @@
+$body-font-family: 'Inter', sans-serif, emoji;
+$heading-font-family: $body-font-family;
+$body-font-weight: 400;
+$font-weights: (
+ 'light': 200,
+ 'regular': 400,
+ 'bold': 600,
+);
+$headings: (
+ 'h1': (
+ 'size': 6rem,
+ 'weight': 200,
+ 'line-height': 1.428571429,
+ 'letter-spacing': -0.022em,
+ 'font-family': $heading-font-family,
+ ),
+ 'h2': (
+ 'size': 3.75rem,
+ 'weight': 200,
+ 'line-height': 1.428571429,
+ 'letter-spacing': -0.022em,
+ 'font-family': $heading-font-family,
+ ),
+ 'h3': (
+ 'size': 3rem,
+ 'weight': 400,
+ 'line-height': 1.428571429,
+ 'letter-spacing': -0.0215em,
+ 'font-family': $heading-font-family,
+ ),
+ 'h4': (
+ 'size': 2.125rem,
+ 'weight': 400,
+ 'line-height': 1.428571429,
+ 'letter-spacing': -0.021em,
+ 'font-family': $heading-font-family,
+ ),
+ 'h5': (
+ 'size': 1.5rem,
+ 'weight': 400,
+ 'line-height': 1.428571429,
+ 'letter-spacing': -0.019em,
+ 'font-family': $heading-font-family,
+ ),
+ 'h6': (
+ 'size': 1.25rem,
+ 'weight': 600,
+ 'line-height': 1.428571429,
+ 'letter-spacing': -0.0125em,
+ 'font-family': $heading-font-family,
+ ),
+ 'subtitle-1': (
+ 'size': 1.125rem,
+ 'weight': 400,
+ 'line-height': 1.428571429,
+ 'letter-spacing': -0.017em,
+ 'font-family': $body-font-family,
+ ),
+ 'subtitle-2': (
+ 'size': 0.875rem,
+ 'weight': 600,
+ 'line-height': 1.375rem,
+ 'letter-spacing': -0.006em,
+ 'font-family': $body-font-family,
+ ),
+ 'body-2': (
+ 'size': 0.875rem,
+ 'weight': 400,
+ 'line-height': 1.428571429,
+ 'letter-spacing': -0.006em,
+ 'font-family': $body-font-family,
+ ),
+ 'body-1': (
+ 'size': 1rem,
+ 'weight': 400,
+ 'line-height': 1.428571429,
+ 'letter-spacing': -0.011em,
+ 'font-family': $body-font-family,
+ ),
+ 'caption': (
+ 'size': 0.75rem,
+ 'weight': 400,
+ 'line-height': 1.428571429,
+ 'letter-spacing': 0em,
+ 'font-family': $body-font-family,
+ ),
+ 'overline': (
+ 'size': 0.625rem,
+ 'weight': 400,
+ 'line-height': 1.428571429,
+ 'letter-spacing': 0.01em,
+ 'font-family': $body-font-family,
+ ),
+);
+$btn-letter-spacing: 0.25px;
+$btn-text-transform: inherit;
+$bottom-nav-btn-min-width: 60px;
+$list-item-action-icon-margin: 20px;
+$menu-content-border-radius: 0 0 4px 4px;
+$primary-transition: 0.1s ease-in-out !default;
+$card-actions-padding: 16px;
+$expansion-panel-header-padding: 16px;
+$expansion-panel-content-padding: 0 16px 16px;
diff --git a/frontend-old/src/test/mockEventClass.js b/frontend-old/src/test/mockEventClass.js
new file mode 100644
index 0000000000..aaa3f538d1
--- /dev/null
+++ b/frontend-old/src/test/mockEventClass.js
@@ -0,0 +1,27 @@
+import { beforeEach, afterEach } from 'vitest'
+/**
+ * @param { "ClipboardEvent" | "DragEvent" } eventName
+ */
+export function mockEventClass(eventName) {
+ const eventBackup = window[eventName]
+ const eventClass = class extends Event {
+ constructor(type, eventInitDict) {
+ // noinspection JSCheckFunctionSignatures
+ super(type, eventInitDict)
+ }
+ }
+
+ beforeEach(() => {
+ if (!eventBackup) {
+ window[eventName] = eventClass
+ } else {
+ console.error(
+ `you don't need to mock ${eventName} anymore, the test runner supports it now natively`
+ )
+ }
+ })
+
+ afterEach(() => {
+ window[eventName] = eventBackup
+ })
+}
diff --git a/frontend-old/src/test/renderWithVuetify.js b/frontend-old/src/test/renderWithVuetify.js
new file mode 100644
index 0000000000..f72d330685
--- /dev/null
+++ b/frontend-old/src/test/renderWithVuetify.js
@@ -0,0 +1,65 @@
+import { render as testingLibraryRender } from '@testing-library/vue'
+import i18n from '@/plugins/i18n/index.js'
+import dayjs from '@/plugins/dayjs.js'
+import Vue from 'vue'
+import Vuetify from 'vuetify'
+import VueI18n from '../plugins/i18n/index.js'
+import formBaseComponents from '@/plugins/formBaseComponents'
+import { Wrapper } from '@vue/test-utils'
+import { localize } from 'vee-validate'
+import Vuex from 'vuex'
+
+Vue.use(Vuetify)
+Vue.use(formBaseComponents)
+Vue.use(dayjs)
+Vue.use(Vuex)
+
+export const render = (component, options, callback) => {
+ const root = document.createElement('div')
+ root.setAttribute('data-app', 'true')
+
+ return testingLibraryRender(
+ component,
+ {
+ container: document.body.appendChild(root),
+ vuetify: new Vuetify(),
+ i18n,
+ ...options,
+ },
+ callback
+ )
+}
+
+/**
+ * Set up a Vuex Store
+ * @param {import('vuex').StoreOptions} storeOptions will be in the components this.$store
+ * @return store the Store object (pass this into the options on the render function)
+ */
+export const createVuexStore = (storeOptions) => {
+ return new Vuex.Store(storeOptions)
+}
+
+/**
+ * This function emulates the setLanguage mutation on the Vuex store,
+ * for use in tests which need to set the application language.
+ */
+export const setTestLocale = (locale) => {
+ const language = locale.substring(0, 2)
+ VueI18n.locale = locale
+ Vue.dayjs.locale(locale)
+ localize(language)
+}
+
+/**
+ * Wraps DOM nodes (such as the container object returned from a vue testing library render)
+ * in a vue-test-utils wrapper object, so it is processed by
+ */
+export const snapshotOf = (testingLibraryContainer) => {
+ if (!(testingLibraryContainer instanceof Element)) {
+ throw new Error(
+ 'snapshotOf expects a DOM element as argument, but received ' +
+ testingLibraryContainer.toString()
+ )
+ }
+ return new Wrapper(testingLibraryContainer)
+}
diff --git a/frontend-old/src/test/util.js b/frontend-old/src/test/util.js
new file mode 100644
index 0000000000..8980e4ec33
--- /dev/null
+++ b/frontend-old/src/test/util.js
@@ -0,0 +1,20 @@
+export function touch(element) {
+ const createTrigger = (eventName) => (clientX, clientY) => {
+ const touches = [{ clientX, clientY }]
+ const event = new Event(eventName)
+
+ event.touches = touches
+ event.changedTouches = touches
+ element.element.dispatchEvent(event)
+
+ return touch(element)
+ }
+
+ return {
+ start: createTrigger('touchstart'),
+ move: createTrigger('touchmove'),
+ end: createTrigger('touchend'),
+ }
+}
+
+export const waitForDebounce = () => new Promise((resolve) => setTimeout(resolve, 110))
diff --git a/frontend-old/src/views/CampCreate.vue b/frontend-old/src/views/CampCreate.vue
new file mode 100644
index 0000000000..c4a0cab638
--- /dev/null
+++ b/frontend-old/src/views/CampCreate.vue
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/Camps.vue b/frontend-old/src/views/Camps.vue
new file mode 100644
index 0000000000..55ce2a7863
--- /dev/null
+++ b/frontend-old/src/views/Camps.vue
@@ -0,0 +1,175 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $tc('views.camps.create') }}
+
+
+
+
+
+
+
+
+ {{ $tc('views.camps.prototypeCamps') }}
+
+
+
+
+
+
+
+
+
+
+
+ {{ $tc('views.camps.pastCamps') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/Invitations.vue b/frontend-old/src/views/Invitations.vue
new file mode 100644
index 0000000000..0fa795a5c5
--- /dev/null
+++ b/frontend-old/src/views/Invitations.vue
@@ -0,0 +1,55 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/NavigationDefault.vue b/frontend-old/src/views/NavigationDefault.vue
new file mode 100644
index 0000000000..20fa38c2da
--- /dev/null
+++ b/frontend-old/src/views/NavigationDefault.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ {{ $tc('global.navigation.help') }}
+ mdi-open-in-new
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/PageNotFound.vue b/frontend-old/src/views/PageNotFound.vue
new file mode 100644
index 0000000000..021a19ebae
--- /dev/null
+++ b/frontend-old/src/views/PageNotFound.vue
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mdi-tent
+ {{ $tc('views.pageNotFound.backToHome') }}
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/Profile.vue b/frontend-old/src/views/Profile.vue
new file mode 100644
index 0000000000..beabc2683e
--- /dev/null
+++ b/frontend-old/src/views/Profile.vue
@@ -0,0 +1,138 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $tc('views.profile.changeEmail') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $tc('global.button.logout') }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/admin/Checklist.vue b/frontend-old/src/views/admin/Checklist.vue
new file mode 100644
index 0000000000..c95ca66f55
--- /dev/null
+++ b/frontend-old/src/views/admin/Checklist.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
diff --git a/frontend-old/src/views/admin/Checklists.vue b/frontend-old/src/views/admin/Checklists.vue
new file mode 100644
index 0000000000..b55d55ed6a
--- /dev/null
+++ b/frontend-old/src/views/admin/Checklists.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
diff --git a/frontend-old/src/views/admin/Debug.vue b/frontend-old/src/views/admin/Debug.vue
new file mode 100644
index 0000000000..453a601059
--- /dev/null
+++ b/frontend-old/src/views/admin/Debug.vue
@@ -0,0 +1,113 @@
+
+
+
+
+ Debug
+
+
+
+
+
+
+ Version
+
+ {{ version }}
+
+ {{ deploymentTime }}
+
+
+
+
+
+ {{ key }} |
+
+ {{ value }}
+ |
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/admin/SideBarAdmin.vue b/frontend-old/src/views/admin/SideBarAdmin.vue
new file mode 100644
index 0000000000..7f1f9fb370
--- /dev/null
+++ b/frontend-old/src/views/admin/SideBarAdmin.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/auth/Activate.vue b/frontend-old/src/views/auth/Activate.vue
new file mode 100644
index 0000000000..8965d87b68
--- /dev/null
+++ b/frontend-old/src/views/auth/Activate.vue
@@ -0,0 +1,83 @@
+
+
+ {{ $tc('views.auth.activate.title') }}
+
+
+
+
+
+
+
+ {{ $tc('views.auth.activate.success') }}
+
+
+
+ {{ $tc('global.button.login') }}
+
+
+
+
+
+ {{ $tc('views.auth.activate.error') }}
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/auth/Login.vue b/frontend-old/src/views/auth/Login.vue
new file mode 100644
index 0000000000..6cf6c33486
--- /dev/null
+++ b/frontend-old/src/views/auth/Login.vue
@@ -0,0 +1,303 @@
+
+
+
+ $vuetify.icons.ecamp
+
+
+
+ {{ $tc('global.button.login') }}
+
+
+
+
+
+
+
+ {
+ email = event.shiftKey ? 'admin@example.com' : 'test@example.com'
+ password = 'test'
+ login()
+ }
+ "
+ >
+ Login
+ mdi-auto-fix
+
+
+
+
+ {{ error }}
+ {{ $tc('views.auth.login.passwordForgotten') }}
+
+ {{ $tc('views.auth.login.resetPassword') }}
+
+
+ {{ $tc('views.auth.login.notActivated') }}
+
+ {{ $tc('views.auth.login.resendActivation') }}
+
+
+
+
+
+
+
+
+
+ {{ $tc('views.auth.login.passwordForgotten') }}
+
+
+
+
+
+ $vuetify.icons.ecamp
+
+ {{ $tc('views.auth.login.provider.ecamp') }}
+
+
+
+
+
+
+
+ {{ $tc('views.auth.login.accountless') }}
+
+ {{ $tc('views.auth.login.registernow') }}
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/auth/LoginCallback.vue b/frontend-old/src/views/auth/LoginCallback.vue
new file mode 100644
index 0000000000..01361020d5
--- /dev/null
+++ b/frontend-old/src/views/auth/LoginCallback.vue
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+ {{$tc('views.auth.loginCallback.loginInProgress')
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/auth/NavigationAuth.vue b/frontend-old/src/views/auth/NavigationAuth.vue
new file mode 100644
index 0000000000..8e2941bcd9
--- /dev/null
+++ b/frontend-old/src/views/auth/NavigationAuth.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+ mdi-script-text-outline
+ {{ $tc('global.navigation.news') }}
+
+
+
+ mdi-help
+ {{ $tc('global.navigation.help') }}
+
+
+
+ mdi-help
+ {{ $tc('global.navigation.help') }}
+
+
+
+
+
+
diff --git a/frontend-old/src/views/auth/Register.vue b/frontend-old/src/views/auth/Register.vue
new file mode 100644
index 0000000000..d67d7823d1
--- /dev/null
+++ b/frontend-old/src/views/auth/Register.vue
@@ -0,0 +1,256 @@
+
+
+ {{ $tc('views.auth.register.title') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $tc('views.auth.register.acceptTermsOfService') }}
+
+
+
+
+ mdi-open-in-new
+
+
+
+
+
+
+ *
+ {{ $tc('views.auth.register.requiredField') }}
+
+
+
+
+
+
+ {{ $tc('views.auth.register.register') }}
+
+
+
+
+
+
+
+
+ {{ $tc('views.auth.register.alreadyHaveAnAccount') }}
+
+ {{ $tc('global.button.login') }}
+
+
+
+
+
+
diff --git a/frontend-old/src/views/auth/RegisterDone.vue b/frontend-old/src/views/auth/RegisterDone.vue
new file mode 100644
index 0000000000..21182d0534
--- /dev/null
+++ b/frontend-old/src/views/auth/RegisterDone.vue
@@ -0,0 +1,28 @@
+
+
+ {{ $tc('views.auth.registerDone.title') }}
+
+
+ {{ $tc('views.auth.registerDone.success') }}
+
+
+ {{ $tc('views.auth.registerDone.message') }}
+
+
+
+
+ {{ $tc('global.button.login') }}
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/auth/ResendActivation.vue b/frontend-old/src/views/auth/ResendActivation.vue
new file mode 100644
index 0000000000..447fcd9c1c
--- /dev/null
+++ b/frontend-old/src/views/auth/ResendActivation.vue
@@ -0,0 +1,104 @@
+
+
+
+ {{ $tc('views.auth.resendActivation.title') }}
+
+
+
+ {{ $tc('views.auth.resendActivation.successMessage') }}
+
+
+
+ {{ $tc('views.auth.resendActivation.errorMessage') }}
+
+
+
+
+
+
+ $vuetify.icons.ecamp
+
+ {{ $tc('views.auth.resendActivation.send') }}
+
+
+
+
+
+
+ {{ $tc('global.button.login') }}
+
+
+
+
+
+
diff --git a/frontend-old/src/views/auth/ResetPassword.vue b/frontend-old/src/views/auth/ResetPassword.vue
new file mode 100644
index 0000000000..9430a38944
--- /dev/null
+++ b/frontend-old/src/views/auth/ResetPassword.vue
@@ -0,0 +1,181 @@
+
+
+
+ {{ $tc('views.auth.resetPassword.title') }}
+
+
+
+
+
+
+
+ {{ $tc('views.auth.resetPassword.invalidRequest') }}
+
+
+
+ {{ $tc('views.auth.resetPassword.successMessage') }}
+
+
+
+ {{ $tc('views.auth.resetPassword.errorMessage') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ $vuetify.icons.ecamp
+
+ {{ $tc('views.auth.resetPassword.send') }}
+
+
+
+
+
+
+
+
+ {{ $tc('global.button.login') }}
+
+
+
+
+
+
diff --git a/frontend-old/src/views/auth/ResetPasswordRequest.vue b/frontend-old/src/views/auth/ResetPasswordRequest.vue
new file mode 100644
index 0000000000..ddc62405a8
--- /dev/null
+++ b/frontend-old/src/views/auth/ResetPasswordRequest.vue
@@ -0,0 +1,107 @@
+
+
+
+ {{ $tc('views.auth.resetPasswordRequest.title') }}
+
+
+
+ {{ $tc('views.auth.resetPasswordRequest.successMessage') }}
+
+
+
+ {{ $tc('views.auth.resetPasswordRequest.errorMessage') }}
+
+
+
+
+
+
+ $vuetify.icons.ecamp
+
+ {{ $tc('views.auth.resetPasswordRequest.send') }}
+
+
+
+
+
+
+ {{ $tc('global.button.login') }}
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/CampProgram.vue b/frontend-old/src/views/camp/CampProgram.vue
new file mode 100644
index 0000000000..28b80f728d
--- /dev/null
+++ b/frontend-old/src/views/camp/CampProgram.vue
@@ -0,0 +1,313 @@
+
+
+
+
+
+
+
+
+
+
+ mdi-filter
+ {{ filteredPropertiesCount }}
+
+
+
+ mdi-filter
+
+
+
+
+
+
+
+ mdi-dots-horizontal
+
+ mdi-dots-horizontal
+
+
+
+
+
+
+ mdi-filter
+
+
+ Filter
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ mdi-lock
+ {{ reminderText }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/Dashboard.vue b/frontend-old/src/views/camp/Dashboard.vue
new file mode 100644
index 0000000000..be2763a177
--- /dev/null
+++ b/frontend-old/src/views/camp/Dashboard.vue
@@ -0,0 +1,350 @@
+
+
+
+
+
+ mdi-calendar-today
+ {{ $tc('views.camp.dashboard.today') }}
+
+
+
+
+
+
+
+
+
+ {{ periods[uri].description }}
+
+
+
+
+
+ {{ $tc('views.camp.dashboard.columns.number') }}
+ |
+
+ {{ $tc('views.camp.dashboard.columns.category') }}
+ |
+
+ {{ $tc('views.camp.dashboard.columns.time') }}
+ |
+
+ {{ $tc('views.camp.dashboard.columns.title') }}
+ |
+
+ {{ $tc('views.camp.dashboard.columns.responsible') }}
+ |
+
+
+
+
+
+
+
+
+
+
+ {{ $tc('views.camp.dashboard.noEntries') }}
+
+
+ {{ $tc('views.camp.dashboard.welcome') }}
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/Invitation.vue b/frontend-old/src/views/camp/Invitation.vue
new file mode 100644
index 0000000000..c43ce6e9cb
--- /dev/null
+++ b/frontend-old/src/views/camp/Invitation.vue
@@ -0,0 +1,449 @@
+
+
+
+
$vuetify.icons.tentDay
+
+
+
+
+
+
+
+
+
+
+
+ {{
+ $tc('views.camp.invitation.title')
+ }}
+ {{ invite?.campTitle }}
+
+ {{
+ $tc('views.camp.invitation.successfullyRejected')
+ }}
+ {{
+ $tc('views.camp.invitation.error')
+ }}
+ {{ $tc('views.camp.invitation.notFound') }}
+
+
+
+
+ {{ $tc('views.camp.invitation.userAlreadyInCamp') }}
+
+
+ {{ $tc('views.camp.invitation.openCamp') }}
+
+
+ {{ $tc('views.camp.invitation.useOtherAuth') }}
+
+
+
+
+
+ {{ $tc('views.camp.invitation.acceptCurrentAuth') }}
+
+
+ {{ $tc('views.camp.invitation.useOtherAuth') }}
+
+
+
+
+
+ {{ $tc('global.button.login') }}
+
+
+ {{ $tc('views.camp.invitation.register') }}
+
+
+
+ {{ $tc('views.camp.invitation.reject') }}
+
+
+
+
+ mdi-tent
+ {{ $tc('views.camp.invitation.backToHome') }}
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/Story.vue b/frontend-old/src/views/camp/Story.vue
new file mode 100644
index 0000000000..69d738b3cc
--- /dev/null
+++ b/frontend-old/src/views/camp/Story.vue
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+ mdi-dots-vertical
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/activity/Activity.vue b/frontend-old/src/views/camp/activity/Activity.vue
new file mode 100644
index 0000000000..4105672b79
--- /dev/null
+++ b/frontend-old/src/views/camp/activity/Activity.vue
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/activity/SideBarProgram.vue b/frontend-old/src/views/camp/activity/SideBarProgram.vue
new file mode 100644
index 0000000000..de03c6ac20
--- /dev/null
+++ b/frontend-old/src/views/camp/activity/SideBarProgram.vue
@@ -0,0 +1,99 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/admin/Activity.vue b/frontend-old/src/views/camp/admin/Activity.vue
new file mode 100644
index 0000000000..8ff85b0382
--- /dev/null
+++ b/frontend-old/src/views/camp/admin/Activity.vue
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/admin/AdminMaterialLists.vue b/frontend-old/src/views/camp/admin/AdminMaterialLists.vue
new file mode 100644
index 0000000000..e50967586a
--- /dev/null
+++ b/frontend-old/src/views/camp/admin/AdminMaterialLists.vue
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+ {{ $tc('global.button.create') }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/admin/Checklists.vue b/frontend-old/src/views/camp/admin/Checklists.vue
new file mode 100644
index 0000000000..245ab688d8
--- /dev/null
+++ b/frontend-old/src/views/camp/admin/Checklists.vue
@@ -0,0 +1,27 @@
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/admin/Collaborators.vue b/frontend-old/src/views/camp/admin/Collaborators.vue
new file mode 100644
index 0000000000..461fe84998
--- /dev/null
+++ b/frontend-old/src/views/camp/admin/Collaborators.vue
@@ -0,0 +1,84 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/admin/Info.vue b/frontend-old/src/views/camp/admin/Info.vue
new file mode 100644
index 0000000000..21ac80db8b
--- /dev/null
+++ b/frontend-old/src/views/camp/admin/Info.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/admin/Print.vue b/frontend-old/src/views/camp/admin/Print.vue
new file mode 100644
index 0000000000..15d2d02582
--- /dev/null
+++ b/frontend-old/src/views/camp/admin/Print.vue
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+ mdi-file-restore-outline
+ {{ $tc('global.button.reset') }}
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/admin/SideBarAdmin.vue b/frontend-old/src/views/camp/admin/SideBarAdmin.vue
new file mode 100644
index 0000000000..222afbdb14
--- /dev/null
+++ b/frontend-old/src/views/camp/admin/SideBarAdmin.vue
@@ -0,0 +1,66 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/admin/__tests__/Admin.spec.js b/frontend-old/src/views/camp/admin/__tests__/Admin.spec.js
new file mode 100644
index 0000000000..655e5acc97
--- /dev/null
+++ b/frontend-old/src/views/camp/admin/__tests__/Admin.spec.js
@@ -0,0 +1,140 @@
+import { describe, expect, it } from 'vitest'
+import Vue from 'vue'
+import { fireEvent } from '@testing-library/vue'
+import { render } from '@/test/renderWithVuetify.js'
+import Admin from '../Info.vue'
+import flushPromises from 'flush-promises'
+
+describe('Admin view', () => {
+ it('shows the danger zone when the user has a manager role', async () => {
+ const camp = createCampWithRole('manager')
+ const { getByText } = render(Admin, {
+ props: {
+ camp,
+ },
+ routes: [],
+ store: createStoreObject(),
+ mocks: {
+ api: createApiMock(camp),
+ },
+ stubs: createStubs(),
+ })
+
+ await flushPromises()
+
+ const dangerZone = getByText('Gefahrenzone')
+
+ expect(dangerZone).toBeInTheDocument()
+ await fireEvent.click(dangerZone)
+ await Vue.nextTick()
+ expect(getByText('Lager löschen')).toBeInTheDocument()
+ })
+
+ it("doesn't show the danger zone when the user has a member role", async () => {
+ const camp = createCampWithRole('member')
+ const { queryByText } = render(Admin, {
+ props: {
+ camp,
+ },
+ routes: [],
+ store: createStoreObject(),
+ mocks: {
+ api: createApiMock(camp),
+ },
+ stubs: createStubs(),
+ })
+
+ await flushPromises()
+
+ expect(queryByText('Gefahrenzone')).not.toBeInTheDocument()
+ expect(queryByText('Lager löschen')).not.toBeInTheDocument()
+ })
+
+ it("doesn't show the danger zone when the user has the guest role", async () => {
+ const camp = createCampWithRole('guest')
+ const { queryByText } = render(Admin, {
+ props: {
+ camp,
+ },
+ routes: [],
+ store: createStoreObject(),
+ mocks: {
+ api: createApiMock(camp),
+ },
+ stubs: createStubs(),
+ })
+
+ await flushPromises()
+
+ expect(queryByText('Gefahrenzone')).not.toBeInTheDocument()
+ expect(queryByText('Lager löschen')).not.toBeInTheDocument()
+ })
+})
+
+const USER_URL = '/users/17d341a80579'
+
+const USER = {
+ _meta: {
+ self: USER_URL,
+ },
+}
+
+function createStoreObject() {
+ return {
+ state: {
+ auth: {
+ user: USER,
+ },
+ },
+ getters: {
+ getLoggedInUser: (state) => {
+ return state.auth.user
+ },
+ },
+ }
+}
+
+function createCampWithRole(role) {
+ return {
+ campCollaborations: () => ({
+ items: [
+ {
+ role: role,
+ user: () => USER,
+ _meta: {
+ loading: false,
+ },
+ },
+ ],
+ }),
+ materialLists: () => {},
+ progressLabels: () => {},
+ _meta: { load: Promise.resolve() },
+ }
+}
+
+function createApiMock(camp) {
+ const halJsonResponse = {
+ ...camp,
+ _meta: {
+ loading: false,
+ load: Promise.resolve(camp),
+ },
+ }
+ return {
+ reload: () => Promise.resolve(),
+ get: () => halJsonResponse,
+ }
+}
+
+function createStubs() {
+ return [
+ 'CampSettings',
+ 'CampAddress',
+ 'CampPeriods',
+ 'CampCategories',
+ 'CampActivityProgressLabels',
+ 'CampMaterialLists',
+ 'CampConditionalFields',
+ ]
+}
diff --git a/frontend-old/src/views/camp/category/Category.vue b/frontend-old/src/views/camp/category/Category.vue
new file mode 100644
index 0000000000..44f82c170e
--- /dev/null
+++ b/frontend-old/src/views/camp/category/Category.vue
@@ -0,0 +1,235 @@
+
+
+
+
+
+
+ {{ category.name }}
+
+
+
+
+
+
+
+
+ mdi-dots-vertical
+
+
+
+
+
+ mdi-content-copy
+
+
+ {{ $tc('views.camp.category.category.copyCategory') }}
+
+
+
+
+
+
+
+ mdi-delete
+
+
+ {{ $tc('views.camp.category.category.deleteCategory') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $tc('views.camp.category.category.properties') }}
+
+
+
+
+
+
+
+
+
+
+ {{ $tc('views.camp.category.category.template') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/category/SideBarCategory.vue b/frontend-old/src/views/camp/category/SideBarCategory.vue
new file mode 100644
index 0000000000..4c12f4922c
--- /dev/null
+++ b/frontend-old/src/views/camp/category/SideBarCategory.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+ {{ category.name }}
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/checklist/Checklist.vue b/frontend-old/src/views/camp/checklist/Checklist.vue
new file mode 100644
index 0000000000..3cb1b5e70b
--- /dev/null
+++ b/frontend-old/src/views/camp/checklist/Checklist.vue
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/checklist/SideBarChecklist.vue b/frontend-old/src/views/camp/checklist/SideBarChecklist.vue
new file mode 100644
index 0000000000..76c1bdd2a4
--- /dev/null
+++ b/frontend-old/src/views/camp/checklist/SideBarChecklist.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+ {{ checklist.name }}
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/checklistOverview/ChecklistLists.vue b/frontend-old/src/views/camp/checklistOverview/ChecklistLists.vue
new file mode 100644
index 0000000000..81982316e1
--- /dev/null
+++ b/frontend-old/src/views/camp/checklistOverview/ChecklistLists.vue
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+ {{ checklist.name }}
+
+
+ mdi-chevron-right
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/checklistOverview/ChecklistOverview.vue b/frontend-old/src/views/camp/checklistOverview/ChecklistOverview.vue
new file mode 100644
index 0000000000..48c0446978
--- /dev/null
+++ b/frontend-old/src/views/camp/checklistOverview/ChecklistOverview.vue
@@ -0,0 +1,115 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/checklistOverview/SideBarChecklistOverview.vue b/frontend-old/src/views/camp/checklistOverview/SideBarChecklistOverview.vue
new file mode 100644
index 0000000000..2f268840dc
--- /dev/null
+++ b/frontend-old/src/views/camp/checklistOverview/SideBarChecklistOverview.vue
@@ -0,0 +1,29 @@
+
+
+
+
+
+ {{ checklist.name }}
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/material/MaterialDetail.vue b/frontend-old/src/views/camp/material/MaterialDetail.vue
new file mode 100644
index 0000000000..efd8690d21
--- /dev/null
+++ b/frontend-old/src/views/camp/material/MaterialDetail.vue
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+ mdi-dots-vertical
+
+
+
+
+
+
+
+ mdi-pencil
+
+ {{
+ $tc('global.button.edit')
+ }}
+
+
+
+
+
+ mdi-microsoft-excel
+
+ {{
+ $tc('global.button.download')
+ }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/material/MaterialLists.vue b/frontend-old/src/views/camp/material/MaterialLists.vue
new file mode 100644
index 0000000000..0178ef2882
--- /dev/null
+++ b/frontend-old/src/views/camp/material/MaterialLists.vue
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+ {{ $tc('global.button.create') }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/material/MaterialOverview.vue b/frontend-old/src/views/camp/material/MaterialOverview.vue
new file mode 100644
index 0000000000..6bd7c3c049
--- /dev/null
+++ b/frontend-old/src/views/camp/material/MaterialOverview.vue
@@ -0,0 +1,92 @@
+
+
+
+
+
+
+
+ mdi-dots-vertical
+
+
+
+
+
+
+
+ mdi-plus
+
+ {{ $tc('views.camp.material.materialOverview.createNewList') }}
+
+
+
+
+
+
+ mdi-microsoft-excel
+
+ {{ $tc('views.camp.material.materialOverview.download') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/material/MaterialUnassigned.vue b/frontend-old/src/views/camp/material/MaterialUnassigned.vue
new file mode 100644
index 0000000000..246170405e
--- /dev/null
+++ b/frontend-old/src/views/camp/material/MaterialUnassigned.vue
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+ mdi-dots-vertical
+
+
+
+
+
+ mdi-microsoft-excel
+
+ {{ $tc('global.button.download') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/material/SideBarMaterialLists.vue b/frontend-old/src/views/camp/material/SideBarMaterialLists.vue
new file mode 100644
index 0000000000..3e267d30d4
--- /dev/null
+++ b/frontend-old/src/views/camp/material/SideBarMaterialLists.vue
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/navigation/NavigationCamp.vue b/frontend-old/src/views/camp/navigation/NavigationCamp.vue
new file mode 100644
index 0000000000..c2d0cbf8a6
--- /dev/null
+++ b/frontend-old/src/views/camp/navigation/NavigationCamp.vue
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/navigation/desktop/NavTopbar.vue b/frontend-old/src/views/camp/navigation/desktop/NavTopbar.vue
new file mode 100644
index 0000000000..99dc8b3183
--- /dev/null
+++ b/frontend-old/src/views/camp/navigation/desktop/NavTopbar.vue
@@ -0,0 +1,95 @@
+
+
+
+
+
+
+ mdi-tent
+ {{ campShortTitle(camp) }}
+
+
+ mdi-view-dashboard
+ {{
+ $tc('views.camp.navigation.desktop.navTopbar.program')
+ }}
+
+
+ mdi-clipboard-list-outline
+ {{
+ $tc('views.camp.navigation.desktop.navTopbar.checklist')
+ }}
+
+
+ mdi-book-open-variant
+ {{
+ $tc('views.camp.navigation.desktop.navTopbar.story')
+ }}
+
+
+ mdi-package-variant
+ {{
+ $tc('views.camp.navigation.desktop.navTopbar.material')
+ }}
+
+
+ mdi-cogs
+ {{
+ $tc('global.navigation.admin.title')
+ }}
+
+
+
+
+
+ {{ $tc('global.navigation.help') }}
+ mdi-open-in-new
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/navigation/mobile/NavBottombar.vue b/frontend-old/src/views/camp/navigation/mobile/NavBottombar.vue
new file mode 100644
index 0000000000..a6299683a2
--- /dev/null
+++ b/frontend-old/src/views/camp/navigation/mobile/NavBottombar.vue
@@ -0,0 +1,80 @@
+
+
+
+ {{ $tc('views.camp.navigation.mobile.navBottombar.program') }}
+ mdi-view-dashboard
+
+
+ {{ $tc('views.camp.navigation.mobile.navBottombar.story') }}
+ mdi-book-open-variant
+
+
+ {{ campShortTitle(camp) }}
+ mdi-tent
+
+
+ {{ $tc('views.camp.navigation.mobile.navBottombar.material') }}
+ mdi-package-variant
+
+
+ {{ $tc('views.camp.navigation.mobile.navBottombar.more') }}
+ mdi-menu
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/camp/navigation/mobile/NavSidebar.vue b/frontend-old/src/views/camp/navigation/mobile/NavSidebar.vue
new file mode 100644
index 0000000000..94b0acb073
--- /dev/null
+++ b/frontend-old/src/views/camp/navigation/mobile/NavSidebar.vue
@@ -0,0 +1,175 @@
+
+
+
+
+ $vuetify.icons.ecamp
+
eCamp
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $tc('views.camp.navigation.mobile.navSidebar.itemClose') }}
+ mdi-close
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/dev/Controls.vue b/frontend-old/src/views/dev/Controls.vue
new file mode 100644
index 0000000000..138b2ee516
--- /dev/null
+++ b/frontend-old/src/views/dev/Controls.vue
@@ -0,0 +1,332 @@
+
+
+
+
+
+
+
+
+ Input Table
+
+
+ {{ item.id }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend-old/src/views/dev/Performance.vue b/frontend-old/src/views/dev/Performance.vue
new file mode 100644
index 0000000000..159aad36aa
--- /dev/null
+++ b/frontend-old/src/views/dev/Performance.vue
@@ -0,0 +1,93 @@
+
+
+
+
+ {{ numberformat.format(item.collection) }} ms
+ {{ numberformat.format(item.entity) }} ms
+
+
+
+
+
+
+
+
diff --git a/frontend-old/tests/globalSetup.js b/frontend-old/tests/globalSetup.js
new file mode 100644
index 0000000000..4768a356ff
--- /dev/null
+++ b/frontend-old/tests/globalSetup.js
@@ -0,0 +1,3 @@
+export function setup() {
+ process.env.TZ = 'Pacific/Tongatapu'
+}
diff --git a/frontend-old/tests/infrastructure/package-lock.spec.js b/frontend-old/tests/infrastructure/package-lock.spec.js
new file mode 100644
index 0000000000..e6463eaeb8
--- /dev/null
+++ b/frontend-old/tests/infrastructure/package-lock.spec.js
@@ -0,0 +1,8 @@
+import { describe, test, expect } from 'vitest'
+import { lockfileVersion } from '../../package-lock.json'
+
+describe('The package-lock.json', () => {
+ test('uses lockfileVersion 3', () => {
+ expect(lockfileVersion).toBe(3)
+ })
+})
diff --git a/frontend-old/tests/setup.js b/frontend-old/tests/setup.js
new file mode 100644
index 0000000000..09a5fed2ad
--- /dev/null
+++ b/frontend-old/tests/setup.js
@@ -0,0 +1,12 @@
+import { expect, afterEach } from 'vitest'
+import { cleanup } from '@testing-library/vue'
+import '@testing-library/jest-dom/vitest'
+import snapshotSerializer from 'jest-serializer-vue-tjw'
+import 'vitest-canvas-mock'
+
+// runs a cleanup after each test case (e.g. clearing jsdom)
+afterEach(() => {
+ cleanup()
+})
+
+expect.addSnapshotSerializer(snapshotSerializer)
diff --git a/frontend-old/vite.config.js b/frontend-old/vite.config.js
new file mode 100644
index 0000000000..1ee9448a3a
--- /dev/null
+++ b/frontend-old/vite.config.js
@@ -0,0 +1,184 @@
+import { defineConfig } from 'vite'
+import vue from '@vitejs/plugin-vue2'
+import { createSvgPlugin } from 'vite-plugin-vue2-svg'
+import { comlink } from 'vite-plugin-comlink'
+import * as path from 'path'
+import { VuetifyResolver } from 'unplugin-vue-components/resolvers'
+import Components from 'unplugin-vue-components/vite'
+import { sentryVitePlugin } from '@sentry/vite-plugin'
+import { configDefaults } from 'vitest/config'
+
+const plugins = [
+ comlink(), // must be first
+ vue(),
+ Components({
+ resolvers: [
+ // Vuetify
+ VuetifyResolver(),
+ ],
+ }),
+ createSvgPlugin(),
+]
+const sentryAuthToken = process.env.SENTRY_AUTH_TOKEN
+if (sentryAuthToken) {
+ plugins.push(
+ sentryVitePlugin({
+ authToken: sentryAuthToken,
+ org: process.env.SENTRY_ORG || 'ecamp',
+ project: process.env.SENTRY_FRONTEND_PROJECT || 'ecamp3-frontend',
+ telemetry: false,
+ sourcemaps: {
+ assets: ['./dist/**/*'],
+ },
+ release: {
+ name: process.env.SENTRY_RELEASE_NAME || 'development',
+ },
+ })
+ )
+}
+
+export default defineConfig(({ mode }) => ({
+ base: '/old',
+ server: {
+ port: 3000,
+ allowedHosts: ['frontend-old', 'localhost:3000'],
+ },
+ plugins,
+ worker: {
+ plugins: () => [comlink()],
+ },
+ optimizeDeps: {
+ include: [
+ '@intlify/core',
+ '@react-pdf/font',
+ '@react-pdf/layout',
+ '@react-pdf/pdfkit',
+ '@react-pdf/primitives',
+ '@react-pdf/render',
+ '@sentry/browser',
+ '@sentry/vue',
+ '@tiptap/pm/state',
+ '@tiptap/pm/view',
+ '@zxcvbn-ts/core',
+ '@zxcvbn-ts/language-common',
+ '@zxcvbn-ts/language-de',
+ '@zxcvbn-ts/language-en',
+ '@zxcvbn-ts/language-fr',
+ '@zxcvbn-ts/language-it',
+ 'colorjs.io',
+ 'comlink',
+ 'core-js/modules/es.array.concat.js',
+ 'core-js/modules/es.array.find.js',
+ 'core-js/modules/es.array.push.js',
+ 'core-js/modules/es.array.slice.js',
+ 'core-js/modules/es.array.splice.js',
+ 'core-js/modules/es.function.name.js',
+ 'core-js/modules/es.object.to-string.js',
+ 'core-js/modules/es.regexp.exec.js',
+ 'core-js/modules/es.regexp.test.js',
+ 'core-js/modules/es.symbol.description.js',
+ 'core-js/modules/es.symbol.js',
+ 'dayjs',
+ 'dayjs/locale/de',
+ 'dayjs/locale/de-ch',
+ 'dayjs/locale/fr',
+ 'dayjs/locale/it',
+ 'dayjs/plugin/customParseFormat',
+ 'dayjs/plugin/isBetween',
+ 'dayjs/plugin/isSameOrBefore',
+ 'dayjs/plugin/isSameOrAfter',
+ 'dayjs/plugin/localizedFormat',
+ 'dayjs/plugin/timezone',
+ 'dayjs/plugin/utc',
+ 'file-saver',
+ 'linkify-it',
+ 'lodash/camelCase.js',
+ 'lodash/cloneDeep.js',
+ 'lodash/groupBy.js',
+ 'lodash/keyBy.js',
+ 'lodash/sortBy.js',
+ 'lodash/minBy.js',
+ 'lodash/maxBy.js',
+ 'lodash/size.js',
+ 'runes',
+ 'vee-validate',
+ 'vite-plugin-comlink/symbol',
+ 'vue',
+ 'vuedraggable',
+ 'vue-toastification',
+ 'vuetify/es5/components/VCalendar/modes/column.js',
+ 'vuetify/es5/components/VCalendar/util/events.js',
+ ],
+ },
+ build: {
+ sourcemap: true,
+ minify: mode === 'development' ? false : 'esbuild',
+ },
+ resolve: {
+ alias: [
+ // webpack alias @ (import in Vue files)
+ {
+ find: '~@',
+ replacement: path.resolve(__dirname, 'src'),
+ },
+ {
+ find: '@',
+ replacement: path.resolve(__dirname, 'src'),
+ },
+
+ // individual webpack aliases for ~ (node modules)
+ {
+ find: '~@mdi',
+ replacement: path.resolve(__dirname, 'node_modules', '@mdi'),
+ },
+ {
+ find: '~inter-ui',
+ replacement: path.resolve(__dirname, 'node_modules', 'inter-ui'),
+ },
+
+ // find dayjs from commons
+ {
+ find: 'dayjs',
+ replacement: path.resolve(__dirname, 'node_modules', 'dayjs'),
+ },
+ ],
+ preserveSymlinks: true,
+ },
+ css: {
+ preprocessorOptions: {
+ scss: {
+ // support for legacy api will be removed in vite 7. https://vite.dev/guide/migration.html#sass-now-uses-modern-api-by-default
+ api: 'legacy',
+ additionalData: '@import "./node_modules/vuetify/src/styles/styles.sass";\n', // original default variables from vuetify
+ silenceDeprecations: ['slash-div', 'mixed-decls'],
+ },
+ sass: {
+ // support for legacy api will be removed in vite 7. https://vite.dev/guide/migration.html#sass-now-uses-modern-api-by-default
+ api: 'legacy',
+ additionalData: '@import "./src/scss/variables.scss"\n', // vuetify variable overrides
+ silenceDeprecations: ['slash-div', 'mixed-decls'],
+ },
+ },
+ },
+ test: {
+ environment: 'jsdom',
+ alias: [{ find: /^vue$/, replacement: 'vue/dist/vue.runtime.common.js' }],
+ globalSetup: './tests/globalSetup.js',
+ setupFiles: './tests/setup.js',
+ maxWorkers: 1,
+ minWorkers: 1,
+ coverage: {
+ all: true,
+ exclude: [...configDefaults.coverage.exclude, '**/src/pdf/**'],
+ reporter: ['text', 'lcov', 'html'],
+ reportsDirectory: './data/coverage',
+ },
+ deps: {
+ optimizer: {
+ web: {
+ exclude: ['vue'],
+ },
+ },
+ },
+ },
+}))
diff --git a/frontend-old/vue.config.js b/frontend-old/vue.config.js
new file mode 100644
index 0000000000..2c74438f9d
--- /dev/null
+++ b/frontend-old/vue.config.js
@@ -0,0 +1,26 @@
+export const pluginOptions = {
+ jestSerializer: {
+ attributesToClear: ['id', 'for'],
+ formatting: {
+ indent_char: ' ',
+ indent_inner_html: true,
+ indent_size: 5,
+ inline: [],
+ sep: '\n',
+ wrap_attributes: 'force-aligned',
+ },
+ },
+}
+
+export default {
+ devServer: {
+ useLocalIp: false,
+ allowedHosts: ['ecamp3', 'localhost', '127.0.0.1'],
+ },
+
+ transpileDependencies: ['vuetify'],
+ pluginOptions,
+ css: {
+ sourceMap: true,
+ },
+}
diff --git a/frontend/eslint.config.mjs b/frontend/eslint.config.mjs
index 8c2987bc7b..8ef281e555 100644
--- a/frontend/eslint.config.mjs
+++ b/frontend/eslint.config.mjs
@@ -13,7 +13,6 @@ const __filename = fileURLToPath(import.meta.url)
const __dirname = path.dirname(__filename)
const gitignorePath = path.resolve(__dirname, '.gitignore')
export default [
- ...vueEslint.configs['flat/vue2-recommended'],
...vueEslint.configs['flat/recommended'],
...vueScopedCssEslint.configs['flat/recommended'],
js.configs.recommended,
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 7cfb69e309..f4c29c8c2c 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -7,6 +7,7 @@
"name": "@ecamp3/frontend",
"hasInstallScript": true,
"dependencies": {
+ "@date-io/dayjs": "^3.2.0",
"@intlify/core": "11.1.12",
"@leeoniya/ufuzzy": "1.0.19",
"@mdi/font": "7.4.47",
@@ -17,24 +18,26 @@
"@react-pdf/render": "4.3.1",
"@sentry/browser": "9.46.0",
"@sentry/vue": "9.46.0",
- "@tiptap/extension-bold": "2.26.2",
- "@tiptap/extension-bubble-menu": "2.26.2",
- "@tiptap/extension-bullet-list": "2.26.2",
- "@tiptap/extension-document": "2.26.2",
- "@tiptap/extension-hard-break": "2.26.2",
- "@tiptap/extension-heading": "2.26.2",
- "@tiptap/extension-history": "2.26.2",
- "@tiptap/extension-italic": "2.26.2",
- "@tiptap/extension-list-item": "2.26.2",
- "@tiptap/extension-ordered-list": "2.26.2",
- "@tiptap/extension-paragraph": "2.26.2",
- "@tiptap/extension-placeholder": "2.26.2",
- "@tiptap/extension-strike": "2.26.2",
- "@tiptap/extension-text": "2.26.2",
- "@tiptap/extension-underline": "2.26.2",
- "@tiptap/pm": "2.26.2",
- "@tiptap/vue-2": "2.26.2",
- "@unhead/vue": "1.11.20",
+ "@tiptap/extension-bold": "3.0.9",
+ "@tiptap/extension-bubble-menu": "3.0.9",
+ "@tiptap/extension-bullet-list": "3.0.9",
+ "@tiptap/extension-document": "3.0.9",
+ "@tiptap/extension-hard-break": "3.0.9",
+ "@tiptap/extension-heading": "3.0.9",
+ "@tiptap/extension-history": "3.0.9",
+ "@tiptap/extension-italic": "3.0.9",
+ "@tiptap/extension-list-item": "3.0.9",
+ "@tiptap/extension-ordered-list": "3.0.9",
+ "@tiptap/extension-paragraph": "3.0.9",
+ "@tiptap/extension-placeholder": "3.0.9",
+ "@tiptap/extension-strike": "3.0.9",
+ "@tiptap/extension-text": "3.0.9",
+ "@tiptap/extension-underline": "3.0.9",
+ "@tiptap/vue-3": "3.0.9",
+ "@unhead/vue": "2.0.14",
+ "@vee-validate/i18n": "4.15.1",
+ "@vee-validate/rules": "4.15.1",
+ "@vue/compat": "3.5.21",
"@zxcvbn-ts/core": "3.0.4",
"@zxcvbn-ts/language-common": "3.0.4",
"@zxcvbn-ts/language-de": "3.0.2",
@@ -59,31 +62,36 @@
"url-template": "3.1.1",
"util": "0.12.5",
"v-resize-observer": "2.1.0",
- "vee-validate": "3.4.15",
- "vue": "2.7.15",
+ "vee-validate": "4.15.1",
+ "vue": "3.5.21",
"vue-axios": "3.5.2",
- "vue-i18n": "8.28.2",
- "vue-recaptcha-v3": "1.9.0",
- "vue-router": "3.6.5",
- "vue-toastification": "1.7.14",
- "vuedraggable": "2.24.3",
- "vuetify": "2.7.2",
- "vuex": "3.6.2",
+ "vue-i18n": "11.1.11",
+ "vue-recaptcha-v3": "2.0.1",
+ "vue-router": "4.5.1",
+ "vue-toastification": "2.0.0-rc.5",
+ "vuedraggable": "4.1.0",
+ "vuetify": "3.10.4",
+ "vuex": "4.0.2",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz"
},
"devDependencies": {
"@babel/eslint-parser": "7.28.4",
"@eslint/compat": "1.4.0",
"@eslint/js": "9.36.0",
- "@sentry/vite-plugin": "3.6.1",
+ "@sentry/vite-plugin": "4.0.2",
"@testing-library/jest-dom": "6.8.0",
"@testing-library/user-event": "14.6.1",
- "@testing-library/vue": "5.9.0",
- "@vitejs/plugin-vue2": "2.3.3",
+ "@testing-library/vue": "8.1.0",
+ "@vitejs/plugin-vue": "6.0.1",
"@vitest/coverage-v8": "3.2.4",
"@vue/babel-preset-app": "5.0.9",
+ "@vue/compiler-dom": "3.5.21",
+ "@vue/compiler-sfc": "3.5.21",
"@vue/eslint-config-prettier": "10.2.0",
- "@vue/test-utils": "1.3.6",
+ "@vue/runtime-dom": "3.5.21",
+ "@vue/server-renderer": "3.5.21",
+ "@vue/shared": "3.5.21",
+ "@vue/test-utils": "2.4.6",
"autoprefixer": "10.4.21",
"babel-plugin-require-context-hook": "1.0.0",
"eslint": "9.36.0",
@@ -96,17 +104,17 @@
"eslint-plugin-vue-scoped-css": "2.12.0",
"flush-promises": "1.0.2",
"globals": "16.4.0",
- "jest-serializer-vue-tjw": "3.20.0",
+ "jest-serializer-vue-tjw": "4.0.0",
"jsdom": "26.1.0",
"lint-staged": "16.2.1",
"patch-package": "8.0.0",
"prettier": "3.6.2",
- "sass": "1.78.0",
- "source-map": "0.7.6",
+ "sass-embedded": "1.89.2",
"unplugin-vue-components": "29.1.0",
"vite": "6.3.6",
"vite-plugin-comlink": "5.3.0",
- "vite-plugin-vue2-svg": "0.4.0",
+ "vite-plugin-vuetify": "2.1.2",
+ "vite-svg-loader": "5.1.0",
"vitest": "3.2.4",
"vitest-canvas-mock": "0.3.3",
"vue-template-compiler": "2.7.15"
@@ -1804,6 +1812,13 @@
"node": ">=18"
}
},
+ "node_modules/@bufbuild/protobuf": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.9.0.tgz",
+ "integrity": "sha512-rnJenoStJ8nvmt9Gzye8nkYd6V22xUAnu4086ER7h1zJ508vStko4pMvDeQ446ilDTFpV5wnoc5YS7XvMwwMqA==",
+ "dev": true,
+ "license": "(Apache-2.0 AND BSD-3-Clause)"
+ },
"node_modules/@csstools/color-helpers": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
@@ -1919,6 +1934,29 @@
"node": ">=18"
}
},
+ "node_modules/@date-io/core": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@date-io/core/-/core-3.2.0.tgz",
+ "integrity": "sha512-hqwXvY8/YBsT9RwQITG868ZNb1MVFFkF7W1Ecv4P472j/ZWa7EFcgSmxy8PUElNVZfvhdvfv+a8j6NWJqOX5mA==",
+ "license": "MIT"
+ },
+ "node_modules/@date-io/dayjs": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/@date-io/dayjs/-/dayjs-3.2.0.tgz",
+ "integrity": "sha512-+3LV+3N+cpQbEtmrFo8odg07k02AFY7diHgbi2EKYYANOOCPkDYUjDr2ENiHuYNidTs3tZwzDKckZoVNN4NXxg==",
+ "license": "MIT",
+ "dependencies": {
+ "@date-io/core": "^3.2.0"
+ },
+ "peerDependencies": {
+ "dayjs": "^1.8.17"
+ },
+ "peerDependenciesMeta": {
+ "dayjs": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.25.10",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz",
@@ -2549,6 +2587,31 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz",
+ "integrity": "sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.4",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.4.tgz",
+ "integrity": "sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.3",
+ "@floating-ui/utils": "^0.2.10"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.10",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
+ "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
+ "license": "MIT"
+ },
"node_modules/@humanfs/core": {
"version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -2781,7 +2844,6 @@
"version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
@@ -2848,16 +2910,6 @@
"url": "https://opencollective.com/pkgr"
}
},
- "node_modules/@popperjs/core": {
- "version": "2.11.8",
- "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
- "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==",
- "license": "MIT",
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/popperjs"
- }
- },
"node_modules/@react-pdf/fns": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@react-pdf/fns/-/fns-3.1.2.tgz",
@@ -2993,12 +3045,20 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-3.0.0.tgz",
"integrity": "sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==",
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.29",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.29.tgz",
+ "integrity": "sha512-NIJgOsMjbxAXvoGq/X0gD7VPMQ8j9g0BiDaNjVNVjvl+iKXxL3Jre0v31RmBYeLEmkbj2s02v8vFTbUXi5XS2Q==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@rollup/rollup-android-arm-eabi": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.3.tgz",
- "integrity": "sha512-h6cqHGZ6VdnwliFG1NXvMPTy/9PS3h8oLh7ImwR+kl+oYnQizgjxsONmmPSb2C66RksfkfIxEVtDSEcJiO0tqw==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz",
+ "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==",
"cpu": [
"arm"
],
@@ -3010,9 +3070,9 @@
]
},
"node_modules/@rollup/rollup-android-arm64": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.3.tgz",
- "integrity": "sha512-wd+u7SLT/u6knklV/ifG7gr5Qy4GUbH2hMWcDauPFJzmCZUAJ8L2bTkVXC2niOIxp8lk3iH/QX8kSrUxVZrOVw==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz",
+ "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==",
"cpu": [
"arm64"
],
@@ -3024,9 +3084,9 @@
]
},
"node_modules/@rollup/rollup-darwin-arm64": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.3.tgz",
- "integrity": "sha512-lj9ViATR1SsqycwFkJCtYfQTheBdvlWJqzqxwc9f2qrcVrQaF/gCuBRTiTolkRWS6KvNxSk4KHZWG7tDktLgjg==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz",
+ "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==",
"cpu": [
"arm64"
],
@@ -3038,9 +3098,9 @@
]
},
"node_modules/@rollup/rollup-darwin-x64": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.3.tgz",
- "integrity": "sha512-+Dyo7O1KUmIsbzx1l+4V4tvEVnVQqMOIYtrxK7ncLSknl1xnMHLgn7gddJVrYPNZfEB8CIi3hK8gq8bDhb3h5A==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz",
+ "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==",
"cpu": [
"x64"
],
@@ -3052,9 +3112,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-arm64": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.3.tgz",
- "integrity": "sha512-u9Xg2FavYbD30g3DSfNhxgNrxhi6xVG4Y6i9Ur1C7xUuGDW3banRbXj+qgnIrwRN4KeJ396jchwy9bCIzbyBEQ==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz",
+ "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==",
"cpu": [
"arm64"
],
@@ -3066,9 +3126,9 @@
]
},
"node_modules/@rollup/rollup-freebsd-x64": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.3.tgz",
- "integrity": "sha512-5M8kyi/OX96wtD5qJR89a/3x5x8x5inXBZO04JWhkQb2JWavOWfjgkdvUqibGJeNNaz1/Z1PPza5/tAPXICI6A==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz",
+ "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==",
"cpu": [
"x64"
],
@@ -3080,9 +3140,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-gnueabihf": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.3.tgz",
- "integrity": "sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz",
+ "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==",
"cpu": [
"arm"
],
@@ -3094,9 +3154,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm-musleabihf": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.3.tgz",
- "integrity": "sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz",
+ "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==",
"cpu": [
"arm"
],
@@ -3108,9 +3168,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-gnu": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.3.tgz",
- "integrity": "sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz",
+ "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==",
"cpu": [
"arm64"
],
@@ -3122,9 +3182,9 @@
]
},
"node_modules/@rollup/rollup-linux-arm64-musl": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.3.tgz",
- "integrity": "sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz",
+ "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==",
"cpu": [
"arm64"
],
@@ -3136,9 +3196,9 @@
]
},
"node_modules/@rollup/rollup-linux-loong64-gnu": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.3.tgz",
- "integrity": "sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz",
+ "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==",
"cpu": [
"loong64"
],
@@ -3150,9 +3210,9 @@
]
},
"node_modules/@rollup/rollup-linux-ppc64-gnu": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.3.tgz",
- "integrity": "sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz",
+ "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==",
"cpu": [
"ppc64"
],
@@ -3164,9 +3224,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-gnu": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.3.tgz",
- "integrity": "sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz",
+ "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==",
"cpu": [
"riscv64"
],
@@ -3178,9 +3238,9 @@
]
},
"node_modules/@rollup/rollup-linux-riscv64-musl": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.3.tgz",
- "integrity": "sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz",
+ "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==",
"cpu": [
"riscv64"
],
@@ -3192,9 +3252,9 @@
]
},
"node_modules/@rollup/rollup-linux-s390x-gnu": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.3.tgz",
- "integrity": "sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz",
+ "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==",
"cpu": [
"s390x"
],
@@ -3206,9 +3266,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-gnu": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.3.tgz",
- "integrity": "sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz",
+ "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==",
"cpu": [
"x64"
],
@@ -3220,9 +3280,9 @@
]
},
"node_modules/@rollup/rollup-linux-x64-musl": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.3.tgz",
- "integrity": "sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz",
+ "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==",
"cpu": [
"x64"
],
@@ -3234,9 +3294,9 @@
]
},
"node_modules/@rollup/rollup-openharmony-arm64": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.3.tgz",
- "integrity": "sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz",
+ "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==",
"cpu": [
"arm64"
],
@@ -3248,9 +3308,9 @@
]
},
"node_modules/@rollup/rollup-win32-arm64-msvc": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.3.tgz",
- "integrity": "sha512-+zteHZdoUYLkyYKObGHieibUFLbttX2r+58l27XZauq0tcWYYuKUwY2wjeCN9oK1Um2YgH2ibd6cnX/wFD7DuA==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz",
+ "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==",
"cpu": [
"arm64"
],
@@ -3262,9 +3322,9 @@
]
},
"node_modules/@rollup/rollup-win32-ia32-msvc": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.3.tgz",
- "integrity": "sha512-of1iHkTQSo3kr6dTIRX6t81uj/c/b15HXVsPcEElN5sS859qHrOepM5p9G41Hah+CTqSh2r8Bm56dL2z9UQQ7g==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz",
+ "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==",
"cpu": [
"ia32"
],
@@ -3276,9 +3336,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-gnu": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.3.tgz",
- "integrity": "sha512-s0hybmlHb56mWVZQj8ra9048/WZTPLILKxcvcq+8awSZmyiSUZjjem1AhU3Tf4ZKpYhK4mg36HtHDOe8QJS5PQ==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz",
+ "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==",
"cpu": [
"x64"
],
@@ -3290,9 +3350,9 @@
]
},
"node_modules/@rollup/rollup-win32-x64-msvc": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.3.tgz",
- "integrity": "sha512-zGIbEVVXVtauFgl3MRwGWEN36P5ZGenHRMgNw88X5wEhEBpq0XrMEZwOn07+ICrwM17XO5xfMZqh0OldCH5VTA==",
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz",
+ "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==",
"cpu": [
"x64"
],
@@ -3354,9 +3414,9 @@
}
},
"node_modules/@sentry/babel-plugin-component-annotate": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-3.6.1.tgz",
- "integrity": "sha512-zmvUa4RpzDG3LQJFpGCE8lniz8Rk1Wa6ZvvK+yEH+snZeaHHRbSnAQBMR607GOClP+euGHNO2YtaY4UAdNTYbg==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-4.0.2.tgz",
+ "integrity": "sha512-Nr/VamvpQs6w642EI5t+qaCUGnVEro0qqk+S8XO1gc8qSdpc8kkZJFnUk7ozAr+ljYWGfVgWXrxI9lLiriLsRA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -3380,14 +3440,14 @@
}
},
"node_modules/@sentry/bundler-plugin-core": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-3.6.1.tgz",
- "integrity": "sha512-/ubWjPwgLep84sUPzHfKL2Ns9mK9aQrEX4aBFztru7ygiJidKJTxYGtvjh4dL2M1aZ0WRQYp+7PF6+VKwdZXcQ==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-4.0.2.tgz",
+ "integrity": "sha512-LeARs8qHhEw19tk+KZd9DDV+Rh/UeapIH0+C09fTmff9p8Y82Cj89pEQ2a1rdUiF/oYIjQX45vnZscB7ra42yw==",
"dev": true,
"license": "MIT",
"dependencies": {
"@babel/core": "^7.18.5",
- "@sentry/babel-plugin-component-annotate": "3.6.1",
+ "@sentry/babel-plugin-component-annotate": "4.0.2",
"@sentry/cli": "^2.49.0",
"dotenv": "^16.3.1",
"find-up": "^5.0.0",
@@ -3583,13 +3643,13 @@
}
},
"node_modules/@sentry/vite-plugin": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/@sentry/vite-plugin/-/vite-plugin-3.6.1.tgz",
- "integrity": "sha512-x8WMdv2K2HcGS2ezEUIEZXpT/fNeWQ9rsEeF0K9DfKXK8Z9lzRmCr6TVA6I9+yW39Is+1/0cv1Rsu0LhO7lHzg==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@sentry/vite-plugin/-/vite-plugin-4.0.2.tgz",
+ "integrity": "sha512-rStdvDGj0VcJBsl2NANE3OQF5m13VCJ+2Ovs5pb2LAqOW72/1GLmef9Mo3b9BwzCjWOQIcgtllwOfSV5Pse03w==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@sentry/bundler-plugin-core": "3.6.1",
+ "@sentry/bundler-plugin-core": "4.0.2",
"unplugin": "1.0.1"
},
"engines": {
@@ -3690,22 +3750,27 @@
}
},
"node_modules/@testing-library/vue": {
- "version": "5.9.0",
- "resolved": "https://registry.npmjs.org/@testing-library/vue/-/vue-5.9.0.tgz",
- "integrity": "sha512-HWvI4s6FayFLmiqGcEMAMfTSO1SV12NukdoyllYMBobFqfO0TalQmfofMtiO+eRz+Amej8Z26dx4/WYIROzfVw==",
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/@testing-library/vue/-/vue-8.1.0.tgz",
+ "integrity": "sha512-ls4RiHO1ta4mxqqajWRh8158uFObVrrtAPoxk7cIp4HrnQUj/ScKzqz53HxYpG3X6Zb7H2v+0eTGLSoy8HQ2nA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.21.0",
- "@testing-library/dom": "^9.0.0",
- "@vue/test-utils": "^1.3.0"
+ "@babel/runtime": "^7.23.2",
+ "@testing-library/dom": "^9.3.3",
+ "@vue/test-utils": "^2.4.1"
},
"engines": {
"node": ">=14"
},
"peerDependencies": {
- "vue": "^2.6.10",
- "vue-template-compiler": "^2.6.10"
+ "@vue/compiler-sfc": ">= 3",
+ "vue": ">= 3"
+ },
+ "peerDependenciesMeta": {
+ "@vue/compiler-sfc": {
+ "optional": true
+ }
}
},
"node_modules/@testing-library/vue/node_modules/@testing-library/dom": {
@@ -3739,9 +3804,9 @@
}
},
"node_modules/@tiptap/core": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.26.2.tgz",
- "integrity": "sha512-cr30QWJECl5j7qUUG4Z4BDitHgJIBWipbC3JbjoDtumgZLedGa5SV+JiGa4GUhNt9E34Pw1BH0gBDL4adGHiLg==",
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.6.5.tgz",
+ "integrity": "sha512-CgXuhevQbBcPfxaXzGZgIY9+aVMSAd68Q21g3EONz1iZBw026QgiaLhGK6jgGTErZL4GoNL/P+gC5nFCvN7+cA==",
"license": "MIT",
"peer": true,
"funding": {
@@ -3749,232 +3814,260 @@
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/pm": "^3.6.5"
}
},
"node_modules/@tiptap/extension-bold": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.26.2.tgz",
- "integrity": "sha512-kNjbHZhLyDu2ZBZmJINzXg3MAW7+05KqGkcwxudC1X/DQM5V5FpW7u6TOlC+nf1I9wABgayxURyU8FsIaXDxqA==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.0.9.tgz",
+ "integrity": "sha512-rVULIFt9ZO+fO5ty9zuC3HwY3knxUw7q9JBztpKPfQQCuIJ+iQnOfB8NtI3L8hxVSxhIR1pqr8B3S/8vlpXbVg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "^3.0.9"
}
},
"node_modules/@tiptap/extension-bubble-menu": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.26.2.tgz",
- "integrity": "sha512-kB7/bGTUnC7ZCBH/fkigpfId925nwGOn+Nq1hf199NYMu2ffWbKk75ckLwyqlETprQYzzHfViIqcwyxJzo04Sg==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.0.9.tgz",
+ "integrity": "sha512-fZQfdSbKJl3J+Yi+s8NrcLBgXHOaGVD4g+vn+orTPUlZdG9FWvEoon8DexOdK9OvYnW6QMM7kS8whOgpogVyUQ==",
"license": "MIT",
"dependencies": {
- "tippy.js": "^6.3.7"
+ "@floating-ui/dom": "^1.0.0"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/core": "^3.0.9",
+ "@tiptap/pm": "^3.0.9"
}
},
"node_modules/@tiptap/extension-bullet-list": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.26.2.tgz",
- "integrity": "sha512-L0qxUa5vzUciLEVtr1nY6HG8gH8432GtuX807MM/5wKiYYdbSii3I22456ZnboiozoqXrjjvYUHeB++HhOSPgQ==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.0.9.tgz",
+ "integrity": "sha512-Aob5TVfrtoEzfTm3wl7lognmWia6EEilOxLihSGISCvI4FTndJg+mwhumduQeYCLWkF9i/DR87m2/3EbjR3R4Q==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/extension-list": "^3.0.9"
}
},
"node_modules/@tiptap/extension-document": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.26.2.tgz",
- "integrity": "sha512-s0/P3A8zxWL/h3e20xWMTT/rcwD0+57I6mT9JgNBPtvhPePy8d698G6/qFK+x+GdIyjJylfsq2BrSE9H+QhIBg==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.0.9.tgz",
+ "integrity": "sha512-DB/R5e6QvuGhY8EhCkfNjR2xQfz/TOWoxfQGhDuy5U+oK3WBwCcHq9t5+nbSCMHtKfi/i49aHKDvv7TQCpuP0w==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "^3.0.9"
}
},
"node_modules/@tiptap/extension-floating-menu": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.26.2.tgz",
- "integrity": "sha512-AILrhwKAGU4Z6GcjNXJAsN0LHlL26bE7VRrYIqUwDv44ImiQf5vu9jEnncBOeHWzMe8SgjrrJWGNNu+dceACpw==",
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.6.5.tgz",
+ "integrity": "sha512-ASKb5vHkYyB9g3vOAr2E2U+b6MbHk4Ff4PqngafGlWRAmOAmFxTcw9fLa3HKnj4pokSsYAEvYGOso99/W3GzhA==",
"license": "MIT",
- "dependencies": {
- "tippy.js": "^6.3.7"
- },
+ "optional": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@floating-ui/dom": "^1.0.0",
+ "@tiptap/core": "^3.6.5",
+ "@tiptap/pm": "^3.6.5"
}
},
"node_modules/@tiptap/extension-hard-break": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.26.2.tgz",
- "integrity": "sha512-OLpeTey7p3ChyEsABLPvNv7rD/8E4k1JTt+H+MUjyL0dnrZuIWluckUJCJKnV8PhR9Mifngk1MTFUilpooiv1g==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.0.9.tgz",
+ "integrity": "sha512-PWNYsUwVsMWt/R5/OWjfGb+7DQT0DvH+1owBimRq0pWZepg8qkz1jdPGgsRmUFyERRsXeEpgj3VaQfrgbyUfrA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "^3.0.9"
}
},
"node_modules/@tiptap/extension-heading": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.26.2.tgz",
- "integrity": "sha512-0VAr1l1QKFJ0B2l4D4wV0LRlyFYeJt0S09mz+HPF2TqKF4twmPjaGD6o5zzXWl8c4cQj1CmM8P+9an3SKRjOaA==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.0.9.tgz",
+ "integrity": "sha512-LRLCIt87fvDZ5CdkinzhkCwRz5ax6FlsjJzG32MJ3wXyvVslqeLXBvH28JFUZEyzgcd/SnYmYxnef5+yvAX61g==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "^3.0.9"
}
},
"node_modules/@tiptap/extension-history": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.26.2.tgz",
- "integrity": "sha512-X/cu79AV5D2Z1QtuvKo/4/Rgl/Uti/n5V3QgCxFLQRCKTxHOCis+RlBCjBfOPztJX4T9QUE6lq20KqB47rsNwQ==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-3.0.9.tgz",
+ "integrity": "sha512-wTSNy85Kla77y9zSUrNEGUVkqCWK9fU+Dsq8wH1A7Xrgi7vBPGkzhy8jpA8+CYtd6xI6c9IiuZrX+x44vjBlSw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/extensions": "^3.0.9"
}
},
"node_modules/@tiptap/extension-italic": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.26.2.tgz",
- "integrity": "sha512-/4AiE2JWtjY9yW+MifMP8EOOwOSDKDUxCyqtGT6e4xqqFUNLZJA0o4P/MYjcKVwsa1/IsDRsOaFRlAiMmAXVXw==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.0.9.tgz",
+ "integrity": "sha512-Gt4FbMtZerzKpit8+FvIjIQ3CBD559/FFC+kOT9y8JHlINeqWyh/bgHuaA/9/XtHphOQiA7NDwOiuPh4KIKpqA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.0.9"
+ }
+ },
+ "node_modules/@tiptap/extension-list": {
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.6.5.tgz",
+ "integrity": "sha512-2S6wNeaGvvYzJygBhHRLP0YubJAzY00WxQSO3NvHFeLFRFvilCnmh0JGMAqsNU+Owpz0iVrWY0YZskN5gPeR9w==",
"license": "MIT",
+ "peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "^3.6.5",
+ "@tiptap/pm": "^3.6.5"
}
},
"node_modules/@tiptap/extension-list-item": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.26.2.tgz",
- "integrity": "sha512-T1dFfx1JjRRX0iyStSTwMNajMyT+OE7XEggn+DON1g+zbgA+4cJ11WQpfrfA9VM2H5QWYyKGfHFigoFcJ8rjog==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.0.9.tgz",
+ "integrity": "sha512-K+ogk1BH/eYhsK9nSTXNdIXlxQcXzty6h1QFiZNr9XmaLk+q4NZFHR5FVz3EJ7QXyw+Gv/2FQn+T2Q/GpbMxZQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/extension-list": "^3.0.9"
}
},
"node_modules/@tiptap/extension-ordered-list": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.26.2.tgz",
- "integrity": "sha512-UVGYyWKB5wWWvrvdN/WrPXPHJoP/UD1TNyeoE75M6nq4oD4l+Nc9Y5MIPsngrv/TimbomLNilR4ZRHibEriG9w==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.0.9.tgz",
+ "integrity": "sha512-ACubdGc/y/rKPEgHTO7hDSg547wRRA+Es7c/rQgjrkpI///LBJQfixyUvNg2UNNPttNsavF/CUwhshCeo9MeBA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/extension-list": "^3.0.9"
}
},
"node_modules/@tiptap/extension-paragraph": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.26.2.tgz",
- "integrity": "sha512-dccyffm95nNT9arjxGOyv4/cOPF4XS5Osylccp9KYLrmiSTXEuzVIYtMXhXbc0UUdABGvbFQWi88tRxgeDTkgA==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.0.9.tgz",
+ "integrity": "sha512-K5zGg4zLxxqAG0BgtRpLvKclYSGoSSuU1Fza0M5MwUgrFA0S2q4JnLB1czQ77S4pfb3hpScIe50fwJzZmIUEQw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "^3.0.9"
}
},
"node_modules/@tiptap/extension-placeholder": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.26.2.tgz",
- "integrity": "sha512-XBTDcpEo7Zo/1+RhGnRxA2TF0elQW7EayUcV+lJ3f7HQ5lrb5NTnakYc1ydeZ8Ih6vUqbK2CQUsESe3UWHHgHg==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-3.0.9.tgz",
+ "integrity": "sha512-OgDVijDrNFDJpe/1/yMx6VFEmGBt0vE6ZWw5kGkM4NVfOxhRvv6mSZXio269dc9oBSjmyTISKaI1JAYVCfyJIw==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0"
+ "@tiptap/extensions": "^3.0.9"
}
},
"node_modules/@tiptap/extension-strike": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.26.2.tgz",
- "integrity": "sha512-gY8/P8ycvICiZsa9OeTpOnSL0o+PAYH1QpBomaBhdZZ2tcsziMYN9BZto6uQARi9tdxeOYRePyZ+Junk4xsyFg==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.0.9.tgz",
+ "integrity": "sha512-2TBQ9P/FGe+/34ckfwP+eCdb4vbxDVZ5qD0piDIR9Ws5QI5IdtW90pNO4roxiPeRdVFrhTbFPEIuL0tg4NQRmg==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "^3.0.9"
}
},
"node_modules/@tiptap/extension-text": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.26.2.tgz",
- "integrity": "sha512-Rb8Le/Li+EixQNc/pGkEJpLjozTjWYP9glaYfnjPtRVw4tHcd7khVm5mer0TQjonbBUjVC1zSuXv9gifXOv6DQ==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.0.9.tgz",
+ "integrity": "sha512-yWdz4aW1nu5YGcinxfu3FXiwMnP/7jp+s7dFXhq9m/6zhDUD2+qyUwhJfIU4Tcz+BGdVHqoNgOA3QXLMA6jyFA==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "^3.0.9"
}
},
"node_modules/@tiptap/extension-underline": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.26.2.tgz",
- "integrity": "sha512-kpKJSfsn1+b8l96YPg2GRn3aaN78pLqSeyzfA5FYVbN0lyptbqRVOrNM8p3n6l0LbAkiEjc/TgOMwNNEL93kyA==",
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.0.9.tgz",
+ "integrity": "sha512-xLR5NbnxlEJmvfb4Aj8wCbTmh/ycnPsSDeP8+TAsdAYxypSA6BP6G0t4d4NWreqAq+tq6QV6Eh0+YDN0G1VZxw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/ueberdosis"
+ },
+ "peerDependencies": {
+ "@tiptap/core": "^3.0.9"
+ }
+ },
+ "node_modules/@tiptap/extensions": {
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.6.5.tgz",
+ "integrity": "sha512-7aadEaRjSbFAIp3WGYR1LXrvtVprmBNxw3FakEUMJ+XKmGNErDJgDMZh+siAYw5MWwCCGa5kKu8Qi/i+DU+ILg==",
"license": "MIT",
+ "peer": true,
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
"peerDependencies": {
- "@tiptap/core": "^2.7.0"
+ "@tiptap/core": "^3.6.5",
+ "@tiptap/pm": "^3.6.5"
}
},
"node_modules/@tiptap/pm": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.26.2.tgz",
- "integrity": "sha512-H2kJHckC9idlYPu/PNdu5XR3Rdu7gbNb+Qrdt2gBnaDyHgAcs+14wak6x19vy27GV9FFzg9722Eb7LErooo28w==",
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.6.5.tgz",
+ "integrity": "sha512-S+j6MPgUXRIQd5/mdaLjaJnOt4ptFwjqGjGMUfBbf9a3uKpXUXaCCzfuC6ZikwaUtoVh4KN9BU3HCYDtgtENPA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-changeset": "^2.3.0",
"prosemirror-collab": "^1.3.1",
@@ -3986,38 +4079,38 @@
"prosemirror-keymap": "^1.2.2",
"prosemirror-markdown": "^1.13.1",
"prosemirror-menu": "^1.2.4",
- "prosemirror-model": "^1.23.0",
+ "prosemirror-model": "^1.24.1",
"prosemirror-schema-basic": "^1.2.3",
- "prosemirror-schema-list": "^1.4.1",
+ "prosemirror-schema-list": "^1.5.0",
"prosemirror-state": "^1.4.3",
"prosemirror-tables": "^1.6.4",
"prosemirror-trailing-node": "^3.0.0",
"prosemirror-transform": "^1.10.2",
- "prosemirror-view": "^1.37.0"
+ "prosemirror-view": "^1.38.1"
},
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
}
},
- "node_modules/@tiptap/vue-2": {
- "version": "2.26.2",
- "resolved": "https://registry.npmjs.org/@tiptap/vue-2/-/vue-2-2.26.2.tgz",
- "integrity": "sha512-DM5mBVGmsN8l5UavfQvvTARmSJBSGodIyWGOher3Xps5TjVbXaDLvY2Jh5lhh9/toMw/eH4AVMjIENSaJJPJvg==",
+ "node_modules/@tiptap/vue-3": {
+ "version": "3.0.9",
+ "resolved": "https://registry.npmjs.org/@tiptap/vue-3/-/vue-3-3.0.9.tgz",
+ "integrity": "sha512-IEhddTn2M0LbatFGkBPxfdbs+kL+xVC2GgYWWxvR/44ptXpKV4axtlH42nqh7sLj4DSBfP9jRlU6U0n6zdeMGg==",
"license": "MIT",
- "dependencies": {
- "@tiptap/extension-bubble-menu": "^2.26.2",
- "@tiptap/extension-floating-menu": "^2.26.2",
- "vue-ts-types": "1.6.2"
- },
"funding": {
"type": "github",
"url": "https://github.com/sponsors/ueberdosis"
},
+ "optionalDependencies": {
+ "@tiptap/extension-bubble-menu": "^3.0.9",
+ "@tiptap/extension-floating-menu": "^3.0.9"
+ },
"peerDependencies": {
- "@tiptap/core": "^2.7.0",
- "@tiptap/pm": "^2.7.0",
- "vue": "^2.6.0"
+ "@floating-ui/dom": "^1.0.0",
+ "@tiptap/core": "^3.0.9",
+ "@tiptap/pm": "^3.0.9",
+ "vue": "^3.0.0"
}
},
"node_modules/@trysound/sax": {
@@ -4058,7 +4151,7 @@
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/@types/json-schema": {
@@ -4072,13 +4165,15 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz",
"integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/@types/markdown-it": {
"version": "14.1.2",
"resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz",
"integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/linkify-it": "^5",
"@types/mdurl": "^2"
@@ -4088,77 +4183,55 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==",
- "license": "MIT"
- },
- "node_modules/@unhead/dom": {
- "version": "1.11.20",
- "resolved": "https://registry.npmjs.org/@unhead/dom/-/dom-1.11.20.tgz",
- "integrity": "sha512-jgfGYdOH+xHJF/j8gudjsYu3oIjFyXhCWcgKaw3vQnT616gSqyqnGQGOItL+BQtQZACKNISwIfx5PuOtztMKLA==",
"license": "MIT",
- "dependencies": {
- "@unhead/schema": "1.11.20",
- "@unhead/shared": "1.11.20"
- },
- "funding": {
- "url": "https://github.com/sponsors/harlan-zw"
- }
+ "peer": true
},
- "node_modules/@unhead/schema": {
- "version": "1.11.20",
- "resolved": "https://registry.npmjs.org/@unhead/schema/-/schema-1.11.20.tgz",
- "integrity": "sha512-0zWykKAaJdm+/Y7yi/Yds20PrUK7XabLe9c3IRcjnwYmSWY6z0Cr19VIs3ozCj8P+GhR+/TI2mwtGlueCEYouA==",
+ "node_modules/@unhead/vue": {
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-2.0.14.tgz",
+ "integrity": "sha512-Ym9f+Kd2Afqek2FtUHvYvK+j2uZ2vbZ6Rr9NCnNGGBMdmafAuiZpT117YGyh0ARcueL6Znia0U8ySqPsnHOZIg==",
"license": "MIT",
"dependencies": {
"hookable": "^5.5.3",
- "zhead": "^2.2.4"
+ "unhead": "2.0.14"
},
"funding": {
"url": "https://github.com/sponsors/harlan-zw"
- }
- },
- "node_modules/@unhead/shared": {
- "version": "1.11.20",
- "resolved": "https://registry.npmjs.org/@unhead/shared/-/shared-1.11.20.tgz",
- "integrity": "sha512-1MOrBkGgkUXS+sOKz/DBh4U20DNoITlJwpmvSInxEUNhghSNb56S0RnaHRq0iHkhrO/cDgz2zvfdlRpoPLGI3w==",
- "license": "MIT",
- "dependencies": {
- "@unhead/schema": "1.11.20",
- "packrup": "^0.1.2"
},
- "funding": {
- "url": "https://github.com/sponsors/harlan-zw"
+ "peerDependencies": {
+ "vue": ">=3.5.18"
}
},
- "node_modules/@unhead/vue": {
- "version": "1.11.20",
- "resolved": "https://registry.npmjs.org/@unhead/vue/-/vue-1.11.20.tgz",
- "integrity": "sha512-sqQaLbwqY9TvLEGeq8Fd7+F2TIuV3nZ5ihVISHjWpAM3y7DwNWRU7NmT9+yYT+2/jw1Vjwdkv5/HvDnvCLrgmg==",
+ "node_modules/@vee-validate/i18n": {
+ "version": "4.15.1",
+ "resolved": "https://registry.npmjs.org/@vee-validate/i18n/-/i18n-4.15.1.tgz",
+ "integrity": "sha512-6ogEyxkoTr7IZk4EAUT0rD3pr34Nw/YEToez/c0DSlI1/cBeOpunSmGmoYPh32pmd3imxFPEawCB7IyFvRJSAw==",
+ "license": "MIT"
+ },
+ "node_modules/@vee-validate/rules": {
+ "version": "4.15.1",
+ "resolved": "https://registry.npmjs.org/@vee-validate/rules/-/rules-4.15.1.tgz",
+ "integrity": "sha512-2TGXq2MYt21nT8ysuxyFY/LBVQDcMPKn1hY0w3uIDmG333vzBkOtXAylmvrS93AsJ7N5coHMJjcCGG4JxCO2hQ==",
"license": "MIT",
"dependencies": {
- "@unhead/schema": "1.11.20",
- "@unhead/shared": "1.11.20",
- "hookable": "^5.5.3",
- "unhead": "1.11.20"
- },
- "funding": {
- "url": "https://github.com/sponsors/harlan-zw"
- },
- "peerDependencies": {
- "vue": ">=2.7 || >=3"
+ "vee-validate": "4.15.1"
}
},
- "node_modules/@vitejs/plugin-vue2": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue2/-/plugin-vue2-2.3.3.tgz",
- "integrity": "sha512-qexY6+bbwY8h0AZerzUuGywNTi0cLOkbiSbggr0R3WEW95iB2hblQFyv4MAkkc2vm4gZN1cO5kzT1Kp6xlVzZw==",
+ "node_modules/@vitejs/plugin-vue": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-vue/-/plugin-vue-6.0.1.tgz",
+ "integrity": "sha512-+MaE752hU0wfPFJEUAIxqw18+20euHHdxVtMvbFcOEpjEyfqXH/5DCoTHiVJ0J29EhTJdoTkjEv5YBKU9dnoTw==",
"dev": true,
"license": "MIT",
+ "dependencies": {
+ "@rolldown/pluginutils": "1.0.0-beta.29"
+ },
"engines": {
- "node": "^14.18.0 || >= 16.0.0"
+ "node": "^20.19.0 || >=22.12.0"
},
"peerDependencies": {
- "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0",
- "vue": "^2.7.0-0"
+ "vite": "^5.0.0 || ^6.0.0 || ^7.0.0",
+ "vue": "^3.2.25"
}
},
"node_modules/@vitest/coverage-v8": {
@@ -4590,45 +4663,56 @@
"@babel/core": "^7.0.0-0"
}
},
+ "node_modules/@vue/compat": {
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/@vue/compat/-/compat-3.5.21.tgz",
+ "integrity": "sha512-uakoBcdx+7qb1dv+rYJ4Bgex7fz7guWrr5Iv0gHCqdl7ijyNXyDJ0BqpZrvxDej82O+113i5LGZmIxfVMyPy/Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.28.3",
+ "estree-walker": "^2.0.2",
+ "source-map-js": "^1.2.1"
+ },
+ "peerDependencies": {
+ "vue": "3.5.21"
+ }
+ },
"node_modules/@vue/compiler-core": {
- "version": "3.5.22",
- "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.22.tgz",
- "integrity": "sha512-jQ0pFPmZwTEiRNSb+i9Ow/I/cHv2tXYqsnHKKyCQ08irI2kdF5qmYedmF8si8mA7zepUFmJ2hqzS8CQmNOWOkQ==",
- "dev": true,
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-core/-/compiler-core-3.5.21.tgz",
+ "integrity": "sha512-8i+LZ0vf6ZgII5Z9XmUvrCyEzocvWT+TeR2VBUVlzIH6Tyv57E20mPZ1bCS+tbejgUgmjrEh7q/0F0bibskAmw==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.4",
- "@vue/shared": "3.5.22",
+ "@babel/parser": "^7.28.3",
+ "@vue/shared": "3.5.21",
"entities": "^4.5.0",
"estree-walker": "^2.0.2",
"source-map-js": "^1.2.1"
}
},
"node_modules/@vue/compiler-dom": {
- "version": "3.5.22",
- "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.22.tgz",
- "integrity": "sha512-W8RknzUM1BLkypvdz10OVsGxnMAuSIZs9Wdx1vzA3mL5fNMN15rhrSCLiTm6blWeACwUwizzPVqGJgOGBEN/hA==",
- "dev": true,
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-dom/-/compiler-dom-3.5.21.tgz",
+ "integrity": "sha512-jNtbu/u97wiyEBJlJ9kmdw7tAr5Vy0Aj5CgQmo+6pxWNQhXZDPsRr1UWPN4v3Zf82s2H3kF51IbzZ4jMWAgPlQ==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-core": "3.5.22",
- "@vue/shared": "3.5.22"
+ "@vue/compiler-core": "3.5.21",
+ "@vue/shared": "3.5.21"
}
},
"node_modules/@vue/compiler-sfc": {
- "version": "3.5.22",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.22.tgz",
- "integrity": "sha512-tbTR1zKGce4Lj+JLzFXDq36K4vcSZbJ1RBu8FxcDv1IGRz//Dh2EBqksyGVypz3kXpshIfWKGOCcqpSbyGWRJQ==",
- "dev": true,
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-3.5.21.tgz",
+ "integrity": "sha512-SXlyk6I5eUGBd2v8Ie7tF6ADHE9kCR6mBEuPyH1nUZ0h6Xx6nZI29i12sJKQmzbDyr2tUHMhhTt51Z6blbkTTQ==",
"license": "MIT",
"dependencies": {
- "@babel/parser": "^7.28.4",
- "@vue/compiler-core": "3.5.22",
- "@vue/compiler-dom": "3.5.22",
- "@vue/compiler-ssr": "3.5.22",
- "@vue/shared": "3.5.22",
+ "@babel/parser": "^7.28.3",
+ "@vue/compiler-core": "3.5.21",
+ "@vue/compiler-dom": "3.5.21",
+ "@vue/compiler-ssr": "3.5.21",
+ "@vue/shared": "3.5.21",
"estree-walker": "^2.0.2",
- "magic-string": "^0.30.19",
+ "magic-string": "^0.30.18",
"postcss": "^8.5.6",
"source-map-js": "^1.2.1"
}
@@ -4637,113 +4721,54 @@
"version": "0.30.19",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
"integrity": "sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
"node_modules/@vue/compiler-ssr": {
- "version": "3.5.22",
- "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.22.tgz",
- "integrity": "sha512-GdgyLvg4R+7T8Nk2Mlighx7XGxq/fJf9jaVofc3IL0EPesTE86cP/8DD1lT3h1JeZr2ySBvyqKQJgbS54IX1Ww==",
- "dev": true,
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/@vue/compiler-ssr/-/compiler-ssr-3.5.21.tgz",
+ "integrity": "sha512-vKQ5olH5edFZdf5ZrlEgSO1j1DMA4u23TVK5XR1uMhvwnYvVdDF0nHXJUblL/GvzlShQbjhZZ2uvYmDlAbgo9w==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-dom": "3.5.22",
- "@vue/shared": "3.5.22"
+ "@vue/compiler-dom": "3.5.21",
+ "@vue/shared": "3.5.21"
}
},
- "node_modules/@vue/component-compiler-utils": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/@vue/component-compiler-utils/-/component-compiler-utils-3.3.0.tgz",
- "integrity": "sha512-97sfH2mYNU+2PzGrmK2haqffDpVASuib9/w2/noxiFi31Z54hW+q3izKQXXQZSNhtiUpAI36uSuYepeBe4wpHQ==",
- "dev": true,
+ "node_modules/@vue/devtools-api": {
+ "version": "7.7.7",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.7.tgz",
+ "integrity": "sha512-lwOnNBH2e7x1fIIbVT7yF5D+YWhqELm55/4ZKf45R9T8r9dE2AIOy8HKjfqzGsoTHFbWbr337O4E0A0QADnjBg==",
"license": "MIT",
"dependencies": {
- "consolidate": "^0.15.1",
- "hash-sum": "^1.0.2",
- "lru-cache": "^4.1.2",
- "merge-source-map": "^1.1.0",
- "postcss": "^7.0.36",
- "postcss-selector-parser": "^6.0.2",
- "source-map": "~0.6.1",
- "vue-template-es2015-compiler": "^1.9.0"
- },
- "optionalDependencies": {
- "prettier": "^1.18.2 || ^2.0.0"
- }
- },
- "node_modules/@vue/component-compiler-utils/node_modules/lru-cache": {
- "version": "4.1.5",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz",
- "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "pseudomap": "^1.0.2",
- "yallist": "^2.1.2"
+ "@vue/devtools-kit": "^7.7.7"
}
},
- "node_modules/@vue/component-compiler-utils/node_modules/picocolors": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz",
- "integrity": "sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/@vue/component-compiler-utils/node_modules/postcss": {
- "version": "7.0.39",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz",
- "integrity": "sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA==",
- "dev": true,
+ "node_modules/@vue/devtools-kit": {
+ "version": "7.7.7",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.7.tgz",
+ "integrity": "sha512-wgoZtxcTta65cnZ1Q6MbAfePVFxfM+gq0saaeytoph7nEa7yMXoi6sCPy4ufO111B9msnw0VOWjPEFCXuAKRHA==",
"license": "MIT",
"dependencies": {
- "picocolors": "^0.2.1",
- "source-map": "^0.6.1"
- },
- "engines": {
- "node": ">=6.0.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
+ "@vue/devtools-shared": "^7.7.7",
+ "birpc": "^2.3.0",
+ "hookable": "^5.5.3",
+ "mitt": "^3.0.1",
+ "perfect-debounce": "^1.0.0",
+ "speakingurl": "^14.0.1",
+ "superjson": "^2.2.2"
}
},
- "node_modules/@vue/component-compiler-utils/node_modules/prettier": {
- "version": "2.8.8",
- "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz",
- "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==",
- "dev": true,
+ "node_modules/@vue/devtools-shared": {
+ "version": "7.7.7",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.7.tgz",
+ "integrity": "sha512-+udSj47aRl5aKb0memBvcUG9koarqnxNM5yjuREvqwK6T3ap4mn3Zqqc17QrBFTqSMjr3HK1cvStEZpMDpfdyw==",
"license": "MIT",
- "optional": true,
- "bin": {
- "prettier": "bin-prettier.js"
- },
- "engines": {
- "node": ">=10.13.0"
- },
- "funding": {
- "url": "https://github.com/prettier/prettier?sponsor=1"
- }
- },
- "node_modules/@vue/component-compiler-utils/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
+ "dependencies": {
+ "rfdc": "^1.4.1"
}
},
- "node_modules/@vue/component-compiler-utils/node_modules/yallist": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
- "integrity": "sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/@vue/eslint-config-prettier": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/@vue/eslint-config-prettier/-/eslint-config-prettier-10.2.0.tgz",
@@ -4759,27 +4784,79 @@
"prettier": ">= 3.0.0"
}
},
+ "node_modules/@vue/reactivity": {
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/@vue/reactivity/-/reactivity-3.5.21.tgz",
+ "integrity": "sha512-3ah7sa+Cwr9iiYEERt9JfZKPw4A2UlbY8RbbnH2mGCE8NwHkhmlZt2VsH0oDA3P08X3jJd29ohBDtX+TbD9AsA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/shared": "3.5.21"
+ }
+ },
+ "node_modules/@vue/runtime-core": {
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-core/-/runtime-core-3.5.21.tgz",
+ "integrity": "sha512-+DplQlRS4MXfIf9gfD1BOJpk5RSyGgGXD/R+cumhe8jdjUcq/qlxDawQlSI8hCKupBlvM+3eS1se5xW+SuNAwA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.21",
+ "@vue/shared": "3.5.21"
+ }
+ },
+ "node_modules/@vue/runtime-dom": {
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/@vue/runtime-dom/-/runtime-dom-3.5.21.tgz",
+ "integrity": "sha512-3M2DZsOFwM5qI15wrMmNF5RJe1+ARijt2HM3TbzBbPSuBHOQpoidE+Pa+XEaVN+czbHf81ETRoG1ltztP2em8w==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/reactivity": "3.5.21",
+ "@vue/runtime-core": "3.5.21",
+ "@vue/shared": "3.5.21",
+ "csstype": "^3.1.3"
+ }
+ },
+ "node_modules/@vue/server-renderer": {
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/@vue/server-renderer/-/server-renderer-3.5.21.tgz",
+ "integrity": "sha512-qr8AqgD3DJPJcGvLcJKQo2tAc8OnXRcfxhOJCPF+fcfn5bBGz7VCcO7t+qETOPxpWK1mgysXvVT/j+xWaHeMWA==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/compiler-ssr": "3.5.21",
+ "@vue/shared": "3.5.21"
+ },
+ "peerDependencies": {
+ "vue": "3.5.21"
+ }
+ },
"node_modules/@vue/shared": {
- "version": "3.5.22",
- "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.22.tgz",
- "integrity": "sha512-F4yc6palwq3TT0u+FYf0Ns4Tfl9GRFURDN2gWG7L1ecIaS/4fCIuFOjMTnCyjsu/OK6vaDKLCrGAa+KvvH+h4w==",
- "dev": true,
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/@vue/shared/-/shared-3.5.21.tgz",
+ "integrity": "sha512-+2k1EQpnYuVuu3N7atWyG3/xoFWIVJZq4Mz8XNOdScFI0etES75fbny/oU4lKWk/577P1zmg0ioYvpGEDZ3DLw==",
"license": "MIT"
},
"node_modules/@vue/test-utils": {
- "version": "1.3.6",
- "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-1.3.6.tgz",
- "integrity": "sha512-udMmmF1ts3zwxUJEIAj5ziioR900reDrt6C9H3XpWPsLBx2lpHKoA4BTdd9HNIYbkGltWw+JjWJ+5O6QBwiyEw==",
+ "version": "2.4.6",
+ "resolved": "https://registry.npmjs.org/@vue/test-utils/-/test-utils-2.4.6.tgz",
+ "integrity": "sha512-FMxEjOpYNYiFe0GkaHsnJPXFHxQ6m4t8vI/ElPGpMWxZKpmRvQ33OIrvRXemy6yha03RxhOlQuy+gZMC3CQSow==",
"dev": true,
"license": "MIT",
"dependencies": {
- "dom-event-types": "^1.0.0",
- "lodash": "^4.17.15",
- "pretty": "^2.0.0"
+ "js-beautify": "^1.14.9",
+ "vue-component-type-helpers": "^2.0.0"
+ }
+ },
+ "node_modules/@vuetify/loader-shared": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/@vuetify/loader-shared/-/loader-shared-2.1.1.tgz",
+ "integrity": "sha512-jSZTzTYaoiv8iwonFCVZQ0YYX/M+Uyl4ng+C4egMJT0Hcmh9gIxJL89qfZICDeo3g0IhqrvipW2FFKKRDMtVcA==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "upath": "^2.0.1"
},
"peerDependencies": {
- "vue": "2.x",
- "vue-template-compiler": "^2.x"
+ "vue": "^3.0.0",
+ "vuetify": "^3.0.0"
}
},
"node_modules/@yarnpkg/lockfile": {
@@ -5218,9 +5295,9 @@
"license": "MIT"
},
"node_modules/baseline-browser-mapping": {
- "version": "2.8.10",
- "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.10.tgz",
- "integrity": "sha512-uLfgBi+7IBNay8ECBO2mVMGZAc1VgZWEChxm4lv+TobGdG82LnXMjuNGo/BSSZZL4UmkWhxEHP2f5ziLNwGWMA==",
+ "version": "2.8.11",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.8.11.tgz",
+ "integrity": "sha512-i+sRXGhz4+QW8aACZ3+r1GAKMt0wlFpeA8M5rOQd0HEYw9zhDrlx9Wc8uQ0IdXakjJRthzglEwfB/yqIjO6iDg==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -5249,12 +5326,14 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/bluebird": {
- "version": "3.7.2",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
- "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
- "dev": true,
- "license": "MIT"
+ "node_modules/birpc": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.6.1.tgz",
+ "integrity": "sha512-LPnFhlDpdSH6FJhJyn4M0kFO7vtQ5iPw24FnG0y21q09xC7e8+1LeR31S1MAIrDAHp4m7aas4bEkTDTvMAtebQ==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/antfu"
+ }
},
"node_modules/boolbase": {
"version": "1.0.0",
@@ -5339,6 +5418,13 @@
"node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
}
},
+ "node_modules/buffer-builder": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/buffer-builder/-/buffer-builder-0.2.0.tgz",
+ "integrity": "sha512-7VPMEPuYznPSoR21NE1zvd2Xna6c/CloiZCfcMXR1Jny6PjX0N4Nsa38zcBFo/FMK+BlA+FLKbJCQ0i2yxp+Xg==",
+ "dev": true,
+ "license": "MIT/X11"
+ },
"node_modules/cac": {
"version": "6.7.14",
"resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz",
@@ -5417,9 +5503,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001746",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001746.tgz",
- "integrity": "sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==",
+ "version": "1.0.30001747",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001747.tgz",
+ "integrity": "sha512-mzFa2DGIhuc5490Nd/G31xN1pnBnYMadtkyTjefPI7wzypqgCEpeWu9bJr0OnDsyKrW75zA9ZAt7pbQFmwLsQg==",
"dev": true,
"funding": [
{
@@ -5558,44 +5644,6 @@
"entities": "^6.0.0"
}
},
- "node_modules/chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/chokidar/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/ci-info": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
@@ -5731,21 +5779,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/condense-newlines": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/condense-newlines/-/condense-newlines-0.2.1.tgz",
- "integrity": "sha512-P7X+QL9Hb9B/c8HI5BFFKmjgBu2XpQuF98WZ9XkO+dBGgk5XgwiQz7o1SmpglNWId3581UcS0SFAWfoIhMHPfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "extend-shallow": "^2.0.1",
- "is-whitespace": "^0.3.0",
- "kind-of": "^3.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/confbox": {
"version": "0.2.2",
"resolved": "https://registry.npmjs.org/confbox/-/confbox-0.2.2.tgz",
@@ -5764,27 +5797,28 @@
"proto-list": "~1.2.1"
}
},
- "node_modules/consolidate": {
- "version": "0.15.1",
- "resolved": "https://registry.npmjs.org/consolidate/-/consolidate-0.15.1.tgz",
- "integrity": "sha512-DW46nrsMJgy9kqAbPt5rKaCr7uFtpo4mSUvLHIUbJEjm0vo+aY5QLwBUq3FK4tRnJr/X0Psc0C4jf/h+HtXSMw==",
- "deprecated": "Please upgrade to consolidate v1.0.0+ as it has been modernized with several long-awaited fixes implemented. Maintenance is supported by Forward Email at https://forwardemail.net ; follow/watch https://github.com/ladjs/consolidate for updates and release changelog",
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
"dev": true,
- "license": "MIT",
+ "license": "MIT"
+ },
+ "node_modules/copy-anything": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz",
+ "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==",
+ "license": "MIT",
"dependencies": {
- "bluebird": "^3.1.1"
+ "is-what": "^4.1.8"
},
"engines": {
- "node": ">= 0.10.0"
+ "node": ">=12.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
}
},
- "node_modules/convert-source-map": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
- "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/core-js": {
"version": "3.45.1",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.45.1.tgz",
@@ -5815,7 +5849,8 @@
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz",
"integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/cross-spawn": {
"version": "7.0.6",
@@ -6018,7 +6053,7 @@
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
"integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -6174,13 +6209,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/dom-event-types": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/dom-event-types/-/dom-event-types-1.1.0.tgz",
- "integrity": "sha512-jNCX+uNJ3v38BKvPbpki6j5ItVlnSqVV6vDWGS6rExzCMjsc39frLjm1n91o6YaKK6AZl0wLloItW6C6mr61BQ==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
@@ -6333,9 +6361,9 @@
}
},
"node_modules/electron-to-chromium": {
- "version": "1.5.228",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.228.tgz",
- "integrity": "sha512-nxkiyuqAn4MJ1QbobwqJILiDtu/jk14hEAWaMiJmNPh1Z+jqoFlBFZjdXwLWGeVSeu9hGLg6+2G9yJaW8rBIFA==",
+ "version": "1.5.230",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.230.tgz",
+ "integrity": "sha512-A6A6Fd3+gMdaed9wX83CvHYJb4UuapPD5X5SLq72VZJzxHSY0/LUweGXRWmQlh2ln7KV7iw7jnwXK7dlPoOnHQ==",
"dev": true,
"license": "ISC"
},
@@ -6481,7 +6509,7 @@
"version": "0.25.10",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.10.tgz",
"integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==",
- "dev": true,
+ "devOptional": true,
"hasInstallScript": true,
"license": "MIT",
"bin": {
@@ -7062,7 +7090,6 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==",
- "dev": true,
"license": "MIT"
},
"node_modules/esutils": {
@@ -7099,19 +7126,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/extend-shallow": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
- "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-extendable": "^0.1.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -7693,13 +7707,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/hash-sum": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/hash-sum/-/hash-sum-1.0.2.tgz",
- "integrity": "sha512-fUs4B4L+mlt8/XAtSOGMUO1TXmAelItBPtJG7CyHJfYTdDjwisntGO2JQz7oUsatOY9o68+57eziUVNw/mRHmA==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/hasown": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@@ -7861,9 +7868,9 @@
}
},
"node_modules/immutable": {
- "version": "4.3.7",
- "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.7.tgz",
- "integrity": "sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==",
+ "version": "5.1.3",
+ "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.3.tgz",
+ "integrity": "sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==",
"dev": true,
"license": "MIT"
},
@@ -8036,13 +8043,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-buffer": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
- "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/is-callable": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
@@ -8104,16 +8104,6 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/is-extendable": {
- "version": "0.1.1",
- "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
- "integrity": "sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -8368,14 +8358,16 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-whitespace": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/is-whitespace/-/is-whitespace-0.3.0.tgz",
- "integrity": "sha512-RydPhl4S6JwAyj0JJjshWJEFG6hNye3pZFBRZaTUfZFwGHxzppNaNOVgQuS/E/SlhrApuMXrpnK1EEIXfdo3Dg==",
- "dev": true,
+ "node_modules/is-what": {
+ "version": "4.1.16",
+ "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz",
+ "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==",
"license": "MIT",
"engines": {
- "node": ">=0.10.0"
+ "node": ">=12.13"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/mesqueeb"
}
},
"node_modules/is-wsl": {
@@ -8496,10 +8488,11 @@
}
},
"node_modules/jest-serializer-vue-tjw": {
- "version": "3.20.0",
- "resolved": "https://registry.npmjs.org/jest-serializer-vue-tjw/-/jest-serializer-vue-tjw-3.20.0.tgz",
- "integrity": "sha512-N49cGTHbc1Fd06rZt1DxwWMksJmMrY5CMQPwJbTPC13htAYBKhFDSJ0lzWHdyX1EfWClYhN1gX+aHkTOvhRTqg==",
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jest-serializer-vue-tjw/-/jest-serializer-vue-tjw-4.0.0.tgz",
+ "integrity": "sha512-VZv8shuNyPbXx96PkKVOAHGPlim86Tl8ie2ehdsjJT/dfQT0fsPl0UtBJdphK6DUAGBSIfltgPlQEYXDwW2EmQ==",
"dev": true,
+ "hasInstallScript": true,
"license": "MIT",
"dependencies": {
"cheerio": "^1.0.0-rc.3",
@@ -8794,19 +8787,6 @@
"json-buffer": "3.0.1"
}
},
- "node_modules/kind-of": {
- "version": "3.2.2",
- "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
- "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-buffer": "^1.1.5"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/klaw-sync": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/klaw-sync/-/klaw-sync-6.0.0.tgz",
@@ -9100,6 +9080,7 @@
"resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz",
"integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"argparse": "^2.0.1",
"entities": "^4.4.0",
@@ -9132,7 +9113,8 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz",
"integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/media-engine": {
"version": "1.0.3",
@@ -9140,26 +9122,6 @@
"integrity": "sha512-aa5tG6sDoK+k70B9iEX1NeyfT8ObCKhNDs6lJVpwF6r8vhUfuKMslIcirq6HIUYuuUYLefcEQOn9bSBOvawtwg==",
"license": "MIT"
},
- "node_modules/merge-source-map": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/merge-source-map/-/merge-source-map-1.1.0.tgz",
- "integrity": "sha512-Qkcp7P2ygktpMPh2mCQZaf3jhN6D3Z/qVZHSdWvQ+2Ef5HgRAPBO57A77+ENm0CPx2+1Ce/MYKi3ymqdfuqibw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "source-map": "^0.6.1"
- }
- },
- "node_modules/merge-source-map/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "dev": true,
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/micromatch": {
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
@@ -9251,6 +9213,12 @@
"node": ">=8"
}
},
+ "node_modules/mitt": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz",
+ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==",
+ "license": "MIT"
+ },
"node_modules/mlly": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.8.0.tgz",
@@ -9297,7 +9265,7 @@
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT"
},
"node_modules/nano-spawn": {
@@ -9385,9 +9353,9 @@
}
},
"node_modules/node-releases": {
- "version": "2.0.21",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.21.tgz",
- "integrity": "sha512-5b0pgg78U3hwXkCM8Z9b2FJdPZlr9Psr9V2gQPESdGHqbntyFJKFW4r5TeWGFzafGY3hzs1JC62VEQMbl1JFkw==",
+ "version": "2.0.23",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.23.tgz",
+ "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==",
"dev": true,
"license": "MIT"
},
@@ -9579,7 +9547,8 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz",
"integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/os-tmpdir": {
"version": "1.0.2",
@@ -9630,15 +9599,6 @@
"dev": true,
"license": "BlueOak-1.0.0"
},
- "node_modules/packrup": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/packrup/-/packrup-0.1.2.tgz",
- "integrity": "sha512-ZcKU7zrr5GlonoS9cxxrb5HVswGnyj6jQvwFBa6p5VFw7G71VAHcUKL5wyZSU/ECtPM/9gacWxy2KFQKt1gMNA==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/harlan-zw"
- }
- },
"node_modules/pako": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
@@ -9849,6 +9809,12 @@
"node": ">= 14.16"
}
},
+ "node_modules/perfect-debounce": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz",
+ "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==",
+ "license": "MIT"
+ },
"node_modules/picocolors": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
@@ -10053,21 +10019,6 @@
"node": ">=6.0.0"
}
},
- "node_modules/pretty": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/pretty/-/pretty-2.0.0.tgz",
- "integrity": "sha512-G9xUchgTEiNpormdYBl+Pha50gOUovT18IvAe7EYMZ1/f9W/WWMPRn+xI68yXNMUk3QXHDwo/1wV/4NejVNe1w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "condense-newlines": "^0.2.1",
- "extend-shallow": "^2.0.1",
- "js-beautify": "^1.6.12"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
@@ -10111,6 +10062,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.3.1.tgz",
"integrity": "sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-transform": "^1.0.0"
}
@@ -10120,6 +10072,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz",
"integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-state": "^1.0.0"
}
@@ -10129,6 +10082,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz",
"integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-model": "^1.0.0",
"prosemirror-state": "^1.0.0",
@@ -10140,6 +10094,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz",
"integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-state": "^1.0.0",
"prosemirror-transform": "^1.1.0",
@@ -10151,6 +10106,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz",
"integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-keymap": "^1.0.0",
"prosemirror-model": "^1.0.0",
@@ -10163,6 +10119,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.1.tgz",
"integrity": "sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-state": "^1.2.2",
"prosemirror-transform": "^1.0.0",
@@ -10175,6 +10132,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.5.0.tgz",
"integrity": "sha512-K0xJRCmt+uSw7xesnHmcn72yBGTbY45vm8gXI4LZXbx2Z0jwh5aF9xrGQgrVPu0WbyFVFF3E/o9VhJYz6SQWnA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-state": "^1.0.0",
"prosemirror-transform": "^1.0.0"
@@ -10185,6 +10143,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz",
"integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-state": "^1.0.0",
"w3c-keyname": "^2.2.0"
@@ -10195,6 +10154,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.13.2.tgz",
"integrity": "sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@types/markdown-it": "^14.0.0",
"markdown-it": "^14.0.0",
@@ -10206,6 +10166,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.5.tgz",
"integrity": "sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"crelt": "^1.0.0",
"prosemirror-commands": "^1.0.0",
@@ -10218,6 +10179,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.3.tgz",
"integrity": "sha512-dY2HdaNXlARknJbrManZ1WyUtos+AP97AmvqdOQtWtrrC5g4mohVX5DTi9rXNFSk09eczLq9GuNTtq3EfMeMGA==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"orderedmap": "^2.0.0"
}
@@ -10227,6 +10189,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.4.tgz",
"integrity": "sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-model": "^1.25.0"
}
@@ -10236,6 +10199,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz",
"integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-model": "^1.0.0",
"prosemirror-state": "^1.0.0",
@@ -10247,6 +10211,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz",
"integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-model": "^1.0.0",
"prosemirror-transform": "^1.0.0",
@@ -10258,6 +10223,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.1.tgz",
"integrity": "sha512-DAgDoUYHCcc6tOGpLVPSU1k84kCUWTWnfWX3UDy2Delv4ryH0KqTD6RBI6k4yi9j9I8gl3j8MkPpRD/vWPZbug==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-keymap": "^1.2.2",
"prosemirror-model": "^1.25.0",
@@ -10271,6 +10237,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-3.0.0.tgz",
"integrity": "sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"@remirror/core-constants": "3.0.0",
"escape-string-regexp": "^4.0.0"
@@ -10286,6 +10253,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.10.4.tgz",
"integrity": "sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-model": "^1.21.0"
}
@@ -10295,6 +10263,7 @@
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.2.tgz",
"integrity": "sha512-PGS/jETmh+Qjmre/6vcG7SNHAKiGc4vKOJmHMPRmvcUl7ISuVtrtHmH06UDUwaim4NDJfZfVMl7U7JkMMETa6g==",
"license": "MIT",
+ "peer": true,
"dependencies": {
"prosemirror-model": "^1.20.0",
"prosemirror-state": "^1.0.0",
@@ -10314,13 +10283,6 @@
"integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==",
"license": "MIT"
},
- "node_modules/pseudomap": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
- "integrity": "sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -10336,6 +10298,7 @@
"resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz",
"integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==",
"license": "MIT",
+ "peer": true,
"engines": {
"node": ">=6"
}
@@ -10373,19 +10336,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "picomatch": "^2.2.1"
- },
- "engines": {
- "node": ">=8.10.0"
- }
- },
"node_modules/recaptcha-v3": {
"version": "1.11.3",
"resolved": "https://registry.npmjs.org/recaptcha-v3/-/recaptcha-v3-1.11.3.tgz",
@@ -10568,7 +10518,6 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
- "dev": true,
"license": "MIT"
},
"node_modules/rimraf": {
@@ -10608,10 +10557,10 @@
}
},
"node_modules/rollup": {
- "version": "4.52.3",
- "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.3.tgz",
- "integrity": "sha512-RIDh866U8agLgiIcdpB+COKnlCreHJLfIhWC3LVflku5YHfpnsIKigRZeFfMfCc4dVcqNVfQQ5gO/afOck064A==",
- "dev": true,
+ "version": "4.52.4",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.52.4.tgz",
+ "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==",
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"@types/estree": "1.0.8"
@@ -10624,28 +10573,28 @@
"npm": ">=8.0.0"
},
"optionalDependencies": {
- "@rollup/rollup-android-arm-eabi": "4.52.3",
- "@rollup/rollup-android-arm64": "4.52.3",
- "@rollup/rollup-darwin-arm64": "4.52.3",
- "@rollup/rollup-darwin-x64": "4.52.3",
- "@rollup/rollup-freebsd-arm64": "4.52.3",
- "@rollup/rollup-freebsd-x64": "4.52.3",
- "@rollup/rollup-linux-arm-gnueabihf": "4.52.3",
- "@rollup/rollup-linux-arm-musleabihf": "4.52.3",
- "@rollup/rollup-linux-arm64-gnu": "4.52.3",
- "@rollup/rollup-linux-arm64-musl": "4.52.3",
- "@rollup/rollup-linux-loong64-gnu": "4.52.3",
- "@rollup/rollup-linux-ppc64-gnu": "4.52.3",
- "@rollup/rollup-linux-riscv64-gnu": "4.52.3",
- "@rollup/rollup-linux-riscv64-musl": "4.52.3",
- "@rollup/rollup-linux-s390x-gnu": "4.52.3",
- "@rollup/rollup-linux-x64-gnu": "4.52.3",
- "@rollup/rollup-linux-x64-musl": "4.52.3",
- "@rollup/rollup-openharmony-arm64": "4.52.3",
- "@rollup/rollup-win32-arm64-msvc": "4.52.3",
- "@rollup/rollup-win32-ia32-msvc": "4.52.3",
- "@rollup/rollup-win32-x64-gnu": "4.52.3",
- "@rollup/rollup-win32-x64-msvc": "4.52.3",
+ "@rollup/rollup-android-arm-eabi": "4.52.4",
+ "@rollup/rollup-android-arm64": "4.52.4",
+ "@rollup/rollup-darwin-arm64": "4.52.4",
+ "@rollup/rollup-darwin-x64": "4.52.4",
+ "@rollup/rollup-freebsd-arm64": "4.52.4",
+ "@rollup/rollup-freebsd-x64": "4.52.4",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.52.4",
+ "@rollup/rollup-linux-arm-musleabihf": "4.52.4",
+ "@rollup/rollup-linux-arm64-gnu": "4.52.4",
+ "@rollup/rollup-linux-arm64-musl": "4.52.4",
+ "@rollup/rollup-linux-loong64-gnu": "4.52.4",
+ "@rollup/rollup-linux-ppc64-gnu": "4.52.4",
+ "@rollup/rollup-linux-riscv64-gnu": "4.52.4",
+ "@rollup/rollup-linux-riscv64-musl": "4.52.4",
+ "@rollup/rollup-linux-s390x-gnu": "4.52.4",
+ "@rollup/rollup-linux-x64-gnu": "4.52.4",
+ "@rollup/rollup-linux-x64-musl": "4.52.4",
+ "@rollup/rollup-openharmony-arm64": "4.52.4",
+ "@rollup/rollup-win32-arm64-msvc": "4.52.4",
+ "@rollup/rollup-win32-ia32-msvc": "4.52.4",
+ "@rollup/rollup-win32-x64-gnu": "4.52.4",
+ "@rollup/rollup-win32-x64-msvc": "4.52.4",
"fsevents": "~2.3.2"
}
},
@@ -10653,7 +10602,8 @@
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz",
"integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/rrweb-cssom": {
"version": "0.8.0",
@@ -10671,6 +10621,16 @@
"node": ">=4.0.0"
}
},
+ "node_modules/rxjs": {
+ "version": "7.8.2",
+ "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.2.tgz",
+ "integrity": "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.1.0"
+ }
+ },
"node_modules/safe-buffer": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
@@ -10715,103 +10675,414 @@
"dev": true,
"license": "MIT"
},
- "node_modules/sass": {
- "version": "1.78.0",
- "resolved": "https://registry.npmjs.org/sass/-/sass-1.78.0.tgz",
- "integrity": "sha512-AaIqGSrjo5lA2Yg7RvFZrlXDBCp3nV4XP73GrLGvdRWWwk+8H3l0SDvq/5bA4eF+0RFPLuWUk3E+P1U/YqnpsQ==",
+ "node_modules/sass-embedded": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded/-/sass-embedded-1.89.2.tgz",
+ "integrity": "sha512-Ack2K8rc57kCFcYlf3HXpZEJFNUX8xd8DILldksREmYXQkRHI879yy8q4mRDJgrojkySMZqmmmW1NxrFxMsYaA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "chokidar": ">=3.0.0 <4.0.0",
- "immutable": "^4.0.0",
- "source-map-js": ">=0.6.2 <2.0.0"
+ "@bufbuild/protobuf": "^2.5.0",
+ "buffer-builder": "^0.2.0",
+ "colorjs.io": "^0.5.0",
+ "immutable": "^5.0.2",
+ "rxjs": "^7.4.0",
+ "supports-color": "^8.1.1",
+ "sync-child-process": "^1.0.2",
+ "varint": "^6.0.0"
},
"bin": {
- "sass": "sass.js"
+ "sass": "dist/bin/sass.js"
+ },
+ "engines": {
+ "node": ">=16.0.0"
},
+ "optionalDependencies": {
+ "sass-embedded-android-arm": "1.89.2",
+ "sass-embedded-android-arm64": "1.89.2",
+ "sass-embedded-android-riscv64": "1.89.2",
+ "sass-embedded-android-x64": "1.89.2",
+ "sass-embedded-darwin-arm64": "1.89.2",
+ "sass-embedded-darwin-x64": "1.89.2",
+ "sass-embedded-linux-arm": "1.89.2",
+ "sass-embedded-linux-arm64": "1.89.2",
+ "sass-embedded-linux-musl-arm": "1.89.2",
+ "sass-embedded-linux-musl-arm64": "1.89.2",
+ "sass-embedded-linux-musl-riscv64": "1.89.2",
+ "sass-embedded-linux-musl-x64": "1.89.2",
+ "sass-embedded-linux-riscv64": "1.89.2",
+ "sass-embedded-linux-x64": "1.89.2",
+ "sass-embedded-win32-arm64": "1.89.2",
+ "sass-embedded-win32-x64": "1.89.2"
+ }
+ },
+ "node_modules/sass-embedded-android-arm": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-arm/-/sass-embedded-android-arm-1.89.2.tgz",
+ "integrity": "sha512-oHAPTboBHRZlDBhyRB6dvDKh4KvFs+DZibDHXbkSI6dBZxMTT+Yb2ivocHnctVGucKTLQeT7+OM5DjWHyynL/A==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
"engines": {
"node": ">=14.0.0"
}
},
- "node_modules/sax": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
- "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/saxes": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
- "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "node_modules/sass-embedded-android-arm64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-arm64/-/sass-embedded-android-arm64-1.89.2.tgz",
+ "integrity": "sha512-+pq7a7AUpItNyPu61sRlP6G2A8pSPpyazASb+8AK2pVlFayCSPAEgpwpCE9A2/Xj86xJZeMizzKUHxM2CBCUxA==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "license": "ISC",
- "dependencies": {
- "xmlchars": "^2.2.0"
- },
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
"engines": {
- "node": ">=v12.22.7"
+ "node": ">=14.0.0"
}
},
- "node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "node_modules/sass-embedded-android-riscv64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-riscv64/-/sass-embedded-android-riscv64-1.89.2.tgz",
+ "integrity": "sha512-HfJJWp/S6XSYvlGAqNdakeEMPOdhBkj2s2lN6SHnON54rahKem+z9pUbCriUJfM65Z90lakdGuOfidY61R9TYg==",
+ "cpu": [
+ "riscv64"
+ ],
"dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/set-function-length": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
- "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
"license": "MIT",
- "dependencies": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "function-bind": "^1.1.2",
- "get-intrinsic": "^1.2.4",
- "gopd": "^1.0.1",
- "has-property-descriptors": "^1.0.2"
- },
+ "optional": true,
+ "os": [
+ "android"
+ ],
"engines": {
- "node": ">= 0.4"
+ "node": ">=14.0.0"
}
},
- "node_modules/set-function-name": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
- "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "node_modules/sass-embedded-android-x64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-android-x64/-/sass-embedded-android-x64-1.89.2.tgz",
+ "integrity": "sha512-BGPzq53VH5z5HN8de6jfMqJjnRe1E6sfnCWFd4pK+CAiuM7iw5Fx6BQZu3ikfI1l2GY0y6pRXzsVLdp/j4EKEA==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "define-data-property": "^1.1.4",
- "es-errors": "^1.3.0",
- "functions-have-names": "^1.2.3",
- "has-property-descriptors": "^1.0.2"
- },
+ "optional": true,
+ "os": [
+ "android"
+ ],
"engines": {
- "node": ">= 0.4"
+ "node": ">=14.0.0"
}
},
- "node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "node_modules/sass-embedded-darwin-arm64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-darwin-arm64/-/sass-embedded-darwin-arm64-1.89.2.tgz",
+ "integrity": "sha512-UCm3RL/tzMpG7DsubARsvGUNXC5pgfQvP+RRFJo9XPIi6elopY5B6H4m9dRYDpHA+scjVthdiDwkPYr9+S/KGw==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
"engines": {
- "node": ">=8"
+ "node": ">=14.0.0"
}
},
- "node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "node_modules/sass-embedded-darwin-x64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-darwin-x64/-/sass-embedded-darwin-x64-1.89.2.tgz",
+ "integrity": "sha512-D9WxtDY5VYtMApXRuhQK9VkPHB8R79NIIR6xxVlN2MIdEid/TZWi1MHNweieETXhWGrKhRKglwnHxxyKdJYMnA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-arm": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm/-/sass-embedded-linux-arm-1.89.2.tgz",
+ "integrity": "sha512-leP0t5U4r95dc90o8TCWfxNXwMAsQhpWxTkdtySDpngoqtTy3miMd7EYNYd1znI0FN1CBaUvbdCMbnbPwygDlA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-arm64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-arm64/-/sass-embedded-linux-arm64-1.89.2.tgz",
+ "integrity": "sha512-2N4WW5LLsbtrWUJ7iTpjvhajGIbmDR18ZzYRywHdMLpfdPApuHPMDF5CYzHbS+LLx2UAx7CFKBnj5LLjY6eFgQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-arm": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm/-/sass-embedded-linux-musl-arm-1.89.2.tgz",
+ "integrity": "sha512-Z6gG2FiVEEdxYHRi2sS5VIYBmp17351bWtOCUZ/thBM66+e70yiN6Eyqjz80DjL8haRUegNQgy9ZJqsLAAmr9g==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-arm64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-arm64/-/sass-embedded-linux-musl-arm64-1.89.2.tgz",
+ "integrity": "sha512-nTyuaBX6U1A/cG7WJh0pKD1gY8hbg1m2SnzsyoFG+exQ0lBX/lwTLHq3nyhF+0atv7YYhYKbmfz+sjPP8CZ9lw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-riscv64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-riscv64/-/sass-embedded-linux-musl-riscv64-1.89.2.tgz",
+ "integrity": "sha512-N6oul+qALO0SwGY8JW7H/Vs0oZIMrRMBM4GqX3AjM/6y8JsJRxkAwnfd0fDyK+aICMFarDqQonQNIx99gdTZqw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-musl-x64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-musl-x64/-/sass-embedded-linux-musl-x64-1.89.2.tgz",
+ "integrity": "sha512-K+FmWcdj/uyP8GiG9foxOCPfb5OAZG0uSVq80DKgVSC0U44AdGjvAvVZkrgFEcZ6cCqlNC2JfYmslB5iqdL7tg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-riscv64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-riscv64/-/sass-embedded-linux-riscv64-1.89.2.tgz",
+ "integrity": "sha512-g9nTbnD/3yhOaskeqeBQETbtfDQWRgsjHok6bn7DdAuwBsyrR3JlSFyqKc46pn9Xxd9SQQZU8AzM4IR+sY0A0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-linux-x64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-linux-x64/-/sass-embedded-linux-x64-1.89.2.tgz",
+ "integrity": "sha512-Ax7dKvzncyQzIl4r7012KCMBvJzOz4uwSNoyoM5IV6y5I1f5hEwI25+U4WfuTqdkv42taCMgpjZbh9ERr6JVMQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-win32-arm64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-win32-arm64/-/sass-embedded-win32-arm64-1.89.2.tgz",
+ "integrity": "sha512-j96iJni50ZUsfD6tRxDQE2QSYQ2WrfHxeiyAXf41Kw0V4w5KYR/Sf6rCZQLMTUOHnD16qTMVpQi20LQSqf4WGg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded-win32-x64": {
+ "version": "1.89.2",
+ "resolved": "https://registry.npmjs.org/sass-embedded-win32-x64/-/sass-embedded-win32-x64-1.89.2.tgz",
+ "integrity": "sha512-cS2j5ljdkQsb4PaORiClaVYynE9OAPZG/XjbOMxpQmjRIf7UroY4PEIH+Waf+y47PfXFX9SyxhYuw2NIKGbEng==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/sass-embedded/node_modules/supports-color": {
+ "version": "8.1.1",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+ "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/supports-color?sponsor=1"
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
+ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/saxes": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz",
+ "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "xmlchars": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=v12.22.7"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/set-function-length": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz",
+ "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==",
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2",
+ "get-intrinsic": "^1.2.4",
+ "gopd": "^1.0.1",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/set-function-name": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz",
+ "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "define-data-property": "^1.1.4",
+ "es-errors": "^1.3.0",
+ "functions-have-names": "^1.2.3",
+ "has-property-descriptors": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true,
"license": "MIT",
@@ -10974,9 +11245,9 @@
}
},
"node_modules/sortablejs": {
- "version": "1.10.2",
- "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.10.2.tgz",
- "integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A==",
+ "version": "1.14.0",
+ "resolved": "https://registry.npmjs.org/sortablejs/-/sortablejs-1.14.0.tgz",
+ "integrity": "sha512-pBXvQCs5/33fdN1/39pPL0NZF20LeRbLQ5jtnheIPN9JQAaufGjKdWduZn4U7wCtVuzKhmRkI0DFYHYRbB2H1w==",
"license": "MIT"
},
"node_modules/source-map": {
@@ -11010,6 +11281,15 @@
"decode-uri-component": "^0.2.0"
}
},
+ "node_modules/speakingurl": {
+ "version": "14.0.1",
+ "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz",
+ "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
"node_modules/stackback": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz",
@@ -11252,6 +11532,18 @@
"url": "https://github.com/sponsors/isaacs"
}
},
+ "node_modules/superjson": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz",
+ "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "copy-anything": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=16"
+ }
+ },
"node_modules/supports-color": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -11333,6 +11625,29 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/sync-child-process": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/sync-child-process/-/sync-child-process-1.0.2.tgz",
+ "integrity": "sha512-8lD+t2KrrScJ/7KXCSyfhT3/hRq78rC0wBFqNJXv3mZyn6hW2ypM05JmlSvtqRbeq6jqA94oHbxAr2vYsJ8vDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "sync-message-port": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
+ "node_modules/sync-message-port": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/sync-message-port/-/sync-message-port-1.1.3.tgz",
+ "integrity": "sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.0.0"
+ }
+ },
"node_modules/synckit": {
"version": "0.11.11",
"resolved": "https://registry.npmjs.org/synckit/-/synckit-0.11.11.tgz",
@@ -11350,9 +11665,9 @@
}
},
"node_modules/tapable": {
- "version": "2.2.3",
- "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.3.tgz",
- "integrity": "sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==",
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -11459,7 +11774,7 @@
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.5.0",
@@ -11476,7 +11791,7 @@
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
@@ -11494,7 +11809,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -11533,15 +11848,6 @@
"node": ">=14.0.0"
}
},
- "node_modules/tippy.js": {
- "version": "6.3.7",
- "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz",
- "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==",
- "license": "MIT",
- "dependencies": {
- "@popperjs/core": "^2.9.0"
- }
- },
"node_modules/tldts": {
"version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
@@ -11669,11 +11975,23 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/type-fest": {
+ "version": "4.41.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz",
+ "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==",
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/typescript": {
"version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
"integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
- "dev": true,
+ "devOptional": true,
"license": "Apache-2.0",
"peer": true,
"bin": {
@@ -11708,14 +12026,11 @@
}
},
"node_modules/unhead": {
- "version": "1.11.20",
- "resolved": "https://registry.npmjs.org/unhead/-/unhead-1.11.20.tgz",
- "integrity": "sha512-3AsNQC0pjwlLqEYHLjtichGWankK8yqmocReITecmpB1H0aOabeESueyy+8X1gyJx4ftZVwo9hqQ4O3fPWffCA==",
+ "version": "2.0.14",
+ "resolved": "https://registry.npmjs.org/unhead/-/unhead-2.0.14.tgz",
+ "integrity": "sha512-dRP6OCqtShhMVZQe1F4wdt/WsYl2MskxKK+cvfSo0lQnrPJ4oAUQEkxRg7pPP+vJENabhlir31HwAyHUv7wfMg==",
"license": "MIT",
"dependencies": {
- "@unhead/dom": "1.11.20",
- "@unhead/schema": "1.11.20",
- "@unhead/shared": "1.11.20",
"hookable": "^5.5.3"
},
"funding": {
@@ -11881,6 +12196,44 @@
}
}
},
+ "node_modules/unplugin-vue-components/node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/unplugin-vue-components/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
"node_modules/unplugin-vue-components/node_modules/magic-string": {
"version": "0.30.19",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.19.tgz",
@@ -11891,17 +12244,17 @@
"@jridgewell/sourcemap-codec": "^1.5.5"
}
},
- "node_modules/unplugin-vue-components/node_modules/picomatch": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
- "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "node_modules/unplugin-vue-components/node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
"dev": true,
"license": "MIT",
- "engines": {
- "node": ">=12"
+ "dependencies": {
+ "picomatch": "^2.2.1"
},
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
+ "engines": {
+ "node": ">=8.10.0"
}
},
"node_modules/unplugin-vue-components/node_modules/unplugin": {
@@ -11920,6 +12273,19 @@
"node": ">=18.12.0"
}
},
+ "node_modules/unplugin-vue-components/node_modules/unplugin/node_modules/picomatch": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
"node_modules/unplugin-vue-components/node_modules/webpack-virtual-modules": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",
@@ -11927,6 +12293,68 @@
"dev": true,
"license": "MIT"
},
+ "node_modules/unplugin/node_modules/chokidar": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+ "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "anymatch": "~3.1.2",
+ "braces": "~3.0.2",
+ "glob-parent": "~5.1.2",
+ "is-binary-path": "~2.1.0",
+ "is-glob": "~4.0.1",
+ "normalize-path": "~3.0.0",
+ "readdirp": "~3.6.0"
+ },
+ "engines": {
+ "node": ">= 8.10.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/unplugin/node_modules/glob-parent": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+ "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "is-glob": "^4.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/unplugin/node_modules/readdirp": {
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+ "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "picomatch": "^2.2.1"
+ },
+ "engines": {
+ "node": ">=8.10.0"
+ }
+ },
+ "node_modules/upath": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/upath/-/upath-2.0.1.tgz",
+ "integrity": "sha512-1uEe95xksV1O0CYKXo8vQvN1JEbtJp7lb7C5U9HMsIp6IVwntkH/oNUzyVNQSd4S1sYk2FpSSW44FqMc8qee5w==",
+ "devOptional": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4",
+ "yarn": "*"
+ }
+ },
"node_modules/update-browserslist-db": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@@ -12039,20 +12467,31 @@
}
}
},
+ "node_modules/varint": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/varint/-/varint-6.0.0.tgz",
+ "integrity": "sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/vee-validate": {
- "version": "3.4.15",
- "resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-3.4.15.tgz",
- "integrity": "sha512-qEtvq9X2N7l5pjBe/6YGrrIBHxJA4KTrb3QrER3qzM7fzkH730m8LyVWMIzM47l//618nlUa7VvTdMFTRKr8tQ==",
+ "version": "4.15.1",
+ "resolved": "https://registry.npmjs.org/vee-validate/-/vee-validate-4.15.1.tgz",
+ "integrity": "sha512-DkFsiTwEKau8VIxyZBGdO6tOudD+QoUBPuHj3e6QFqmbfCRj1ArmYWue9lEp6jLSWBIw4XPlDLjFIZNLdRAMSg==",
"license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^7.5.2",
+ "type-fest": "^4.8.3"
+ },
"peerDependencies": {
- "vue": "^2.5.18"
+ "vue": "^3.4.26"
}
},
"node_modules/vite": {
"version": "6.3.6",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.6.tgz",
"integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
@@ -12186,25 +12625,44 @@
"@jridgewell/sourcemap-codec": "^1.5.0"
}
},
- "node_modules/vite-plugin-vue2-svg": {
- "version": "0.4.0",
- "resolved": "https://registry.npmjs.org/vite-plugin-vue2-svg/-/vite-plugin-vue2-svg-0.4.0.tgz",
- "integrity": "sha512-Z9NT9PTGwEKMSfxruPeBeAgMWAgMz5DiE6u8ZNtkYXODGWceUi6Dhp/f0NFO02sEoDiDxkokzr8u4qK4+9rXJQ==",
+ "node_modules/vite-plugin-vuetify": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/vite-plugin-vuetify/-/vite-plugin-vuetify-2.1.2.tgz",
+ "integrity": "sha512-I/wd6QS+DO6lHmuGoi1UTyvvBTQ2KDzQZ9oowJQEJ6OcjWfJnscYXx2ptm6S7fJSASuZT8jGRBL3LV4oS3LpaA==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "@vuetify/loader-shared": "^2.1.1",
+ "debug": "^4.3.3",
+ "upath": "^2.0.1"
+ },
+ "engines": {
+ "node": "^18.0.0 || >=20.0.0"
+ },
+ "peerDependencies": {
+ "vite": ">=5",
+ "vue": "^3.0.0",
+ "vuetify": "^3.0.0"
+ }
+ },
+ "node_modules/vite-svg-loader": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/vite-svg-loader/-/vite-svg-loader-5.1.0.tgz",
+ "integrity": "sha512-M/wqwtOEjgb956/+m5ZrYT/Iq6Hax0OakWbokj8+9PXOnB7b/4AxESHieEtnNEy7ZpjsjYW1/5nK8fATQMmRxw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@vue/component-compiler-utils": "^3.2.0",
- "svgo": "^3.0.0"
+ "svgo": "^3.0.2"
},
"peerDependencies": {
- "vue-template-compiler": "^2.2.0"
+ "vue": ">=3.2.13"
}
},
"node_modules/vite/node_modules/fdir": {
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
@@ -12222,7 +12680,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
"integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
- "dev": true,
+ "devOptional": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -12341,14 +12799,24 @@
}
},
"node_modules/vue": {
- "version": "2.7.15",
- "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.15.tgz",
- "integrity": "sha512-a29fsXd2G0KMRqIFTpRgpSbWaNBK3lpCTOLuGLEDnlHWdjB8fwl6zyYZ8xCrqkJdatwZb4mGHiEfJjnw0Q6AwQ==",
- "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.",
+ "version": "3.5.21",
+ "resolved": "https://registry.npmjs.org/vue/-/vue-3.5.21.tgz",
+ "integrity": "sha512-xxf9rum9KtOdwdRkiApWL+9hZEMWE90FHh8yS1+KJAiWYh+iGWV1FquPjoO9VUHQ+VIhsCXNNyZ5Sf4++RVZBA==",
"license": "MIT",
"dependencies": {
- "@vue/compiler-sfc": "2.7.15",
- "csstype": "^3.1.0"
+ "@vue/compiler-dom": "3.5.21",
+ "@vue/compiler-sfc": "3.5.21",
+ "@vue/runtime-dom": "3.5.21",
+ "@vue/server-renderer": "3.5.21",
+ "@vue/shared": "3.5.21"
+ },
+ "peerDependencies": {
+ "typescript": "*"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
"node_modules/vue-axios": {
@@ -12361,6 +12829,13 @@
"vue": "^3.0.0 || ^2.0.0"
}
},
+ "node_modules/vue-component-type-helpers": {
+ "version": "2.2.12",
+ "resolved": "https://registry.npmjs.org/vue-component-type-helpers/-/vue-component-type-helpers-2.2.12.tgz",
+ "integrity": "sha512-YbGqHZ5/eW4SnkPNR44mKVc6ZKQoRs/Rux1sxC6rdwXb4qpbOSYfDr9DsTHolOTGmIKgM9j141mZbBeg05R1pw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/vue-eslint-parser": {
"version": "10.2.0",
"resolved": "https://registry.npmjs.org/vue-eslint-parser/-/vue-eslint-parser-10.2.0.tgz",
@@ -12444,28 +12919,106 @@
}
},
"node_modules/vue-i18n": {
- "version": "8.28.2",
- "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-8.28.2.tgz",
- "integrity": "sha512-C5GZjs1tYlAqjwymaaCPDjCyGo10ajUphiwA922jKt9n7KPpqR7oM1PCwYzhB/E7+nT3wfdG3oRre5raIT1rKA==",
- "deprecated": "Vue I18n v8.x has reached EOL and is no longer actively maintained. About maintenance status, see https://vue-i18n.intlify.dev/guide/maintenance.html",
+ "version": "11.1.11",
+ "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-11.1.11.tgz",
+ "integrity": "sha512-LvyteQoXeQiuILbzqv13LbyBna/TEv2Ha+4ZWK2AwGHUzZ8+IBaZS0TJkCgn5izSPLcgZwXy9yyTrewCb2u/MA==",
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/core-base": "11.1.11",
+ "@intlify/shared": "11.1.11",
+ "@vue/devtools-api": "^6.5.0"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.0"
+ }
+ },
+ "node_modules/vue-i18n/node_modules/@intlify/core-base": {
+ "version": "11.1.11",
+ "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-11.1.11.tgz",
+ "integrity": "sha512-1Z0N8jTfkcD2Luq9HNZt+GmjpFe4/4PpZF3AOzoO1u5PTtSuXZcfhwBatywbfE2ieB/B5QHIoOFmCXY2jqVKEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/message-compiler": "11.1.11",
+ "@intlify/shared": "11.1.11"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/vue-i18n/node_modules/@intlify/message-compiler": {
+ "version": "11.1.11",
+ "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-11.1.11.tgz",
+ "integrity": "sha512-7PC6neomoc/z7a8JRjPBbu0T2TzR2MQuY5kn2e049MP7+o32Ve7O8husylkA7K9fQRe4iNXZWTPnDJ6vZdtS1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@intlify/shared": "11.1.11",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/vue-i18n/node_modules/@intlify/shared": {
+ "version": "11.1.11",
+ "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-11.1.11.tgz",
+ "integrity": "sha512-RIBFTIqxZSsxUqlcyoR7iiC632bq7kkOwYvZlvcVObHfrF4NhuKc4FKvu8iPCrEO+e3XsY7/UVpfgzg+M7ETzA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 16"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/kazupon"
+ }
+ },
+ "node_modules/vue-i18n/node_modules/@vue/devtools-api": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
"node_modules/vue-recaptcha-v3": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/vue-recaptcha-v3/-/vue-recaptcha-v3-1.9.0.tgz",
- "integrity": "sha512-WQIlhcOcETk3SYbEC88podUSq1J7UjmHpKgJhSy0Xm3DAjTIPjl19g9kn5KRdyTxNLiS/eR5C6H3Jk3c7b9baA==",
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/vue-recaptcha-v3/-/vue-recaptcha-v3-2.0.1.tgz",
+ "integrity": "sha512-isEDtOfHU4wWRrZZuxciAELtQmPOeEEdicPNa0f1AOyLPy3sCcBEcpFt+FOcO3RQv5unJ3Yn5NlsWtXv9rXqjg==",
"license": "Apache-2.0",
"dependencies": {
"recaptcha-v3": "^1.8.0"
},
"peerDependencies": {
- "vue": "^2.6.11"
+ "vue": "^3.0.11"
}
},
"node_modules/vue-router": {
- "version": "3.6.5",
- "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-3.6.5.tgz",
- "integrity": "sha512-VYXZQLtjuvKxxcshuRAwjHnciqZVoXAjTjcqBTz4rKc8qih9g9pI3hbDjmqXaHdgL3v8pV6P8Z335XvHzESxLQ==",
+ "version": "4.5.1",
+ "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.5.1.tgz",
+ "integrity": "sha512-ogAF3P97NPm8fJsE4by9dwSYtDwXIY1nFY9T6DyQnGHd1E2Da94w9JIolpe42LJGIl0DwOHBi8TcRPlPGwbTtw==",
+ "license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^6.6.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/posva"
+ },
+ "peerDependencies": {
+ "vue": "^3.2.0"
+ }
+ },
+ "node_modules/vue-router/node_modules/@vue/devtools-api": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
"license": "MIT"
},
"node_modules/vue-template-compiler": {
@@ -12479,87 +13032,78 @@
"he": "^1.2.0"
}
},
- "node_modules/vue-template-es2015-compiler": {
- "version": "1.9.1",
- "resolved": "https://registry.npmjs.org/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz",
- "integrity": "sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/vue-toastification": {
- "version": "1.7.14",
- "resolved": "https://registry.npmjs.org/vue-toastification/-/vue-toastification-1.7.14.tgz",
- "integrity": "sha512-khZR8t3NWZ/JJ2MZxXLbesHrRJ8AKa75PY5Zq8yMifF9x8lHq8ljYkC0d2PD9yahooygQB5tcFyRDkbbIPx8hw==",
+ "version": "2.0.0-rc.5",
+ "resolved": "https://registry.npmjs.org/vue-toastification/-/vue-toastification-2.0.0-rc.5.tgz",
+ "integrity": "sha512-q73e5jy6gucEO/U+P48hqX+/qyXDozAGmaGgLFm5tXX4wJBcVsnGp4e/iJqlm9xzHETYOilUuwOUje2Qg1JdwA==",
"license": "MIT",
"peerDependencies": {
- "vue": "^2.0.0"
- }
- },
- "node_modules/vue-ts-types": {
- "version": "1.6.2",
- "resolved": "https://registry.npmjs.org/vue-ts-types/-/vue-ts-types-1.6.2.tgz",
- "integrity": "sha512-iOBe4oMoTeISs+Wxlt5E6q3W6lEX6UtkwWCPu6727IoweBE3O3Vp/2/BtO+Kf5pISQU7WpX71hXTeMGqHsmecA==",
- "license": "MIT",
- "peerDependencies": {
- "vue": "^2.6 || ^3.2"
- }
- },
- "node_modules/vue/node_modules/@vue/compiler-sfc": {
- "version": "2.7.15",
- "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.15.tgz",
- "integrity": "sha512-FCvIEevPmgCgqFBH7wD+3B97y7u7oj/Wr69zADBf403Tui377bThTjBvekaZvlRr4IwUAu3M6hYZeULZFJbdYg==",
- "dependencies": {
- "@babel/parser": "^7.18.4",
- "postcss": "^8.4.14",
- "source-map": "^0.6.1"
- }
- },
- "node_modules/vue/node_modules/source-map": {
- "version": "0.6.1",
- "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
- "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
- "license": "BSD-3-Clause",
- "engines": {
- "node": ">=0.10.0"
+ "vue": "^3.0.2"
}
},
"node_modules/vuedraggable": {
- "version": "2.24.3",
- "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-2.24.3.tgz",
- "integrity": "sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==",
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/vuedraggable/-/vuedraggable-4.1.0.tgz",
+ "integrity": "sha512-FU5HCWBmsf20GpP3eudURW3WdWTKIbEIQxh9/8GE806hydR9qZqRRxRE3RjqX7PkuLuMQG/A7n3cfj9rCEchww==",
"license": "MIT",
"dependencies": {
- "sortablejs": "1.10.2"
+ "sortablejs": "1.14.0"
+ },
+ "peerDependencies": {
+ "vue": "^3.0.1"
}
},
"node_modules/vuetify": {
- "version": "2.7.2",
- "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-2.7.2.tgz",
- "integrity": "sha512-qr04ww7uzAPQbpk751x4fSdjsJ+zREzjQ/rBlcQGuWS6MIMFMXcXcwvp4+/tnGsULZxPMWfQ0kmZmg5Yc/XzgQ==",
- "deprecated": "This version is deprecated",
+ "version": "3.10.4",
+ "resolved": "https://registry.npmjs.org/vuetify/-/vuetify-3.10.4.tgz",
+ "integrity": "sha512-aatUQ0RM0i6VdkJaFyj3gydecuFmAgACNO3RwWznvjvIvRENQXxMRiv+vlGFoshbw2UG+zOPPMhO12OCPSa2UQ==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/johnleider"
},
"peerDependencies": {
- "vue": "^2.6.4"
+ "typescript": ">=4.7",
+ "vite-plugin-vuetify": ">=2.1.0",
+ "vue": "^3.5.0",
+ "webpack-plugin-vuetify": ">=3.1.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ },
+ "vite-plugin-vuetify": {
+ "optional": true
+ },
+ "webpack-plugin-vuetify": {
+ "optional": true
+ }
}
},
"node_modules/vuex": {
- "version": "3.6.2",
- "resolved": "https://registry.npmjs.org/vuex/-/vuex-3.6.2.tgz",
- "integrity": "sha512-ETW44IqCgBpVomy520DT5jf8n0zoCac+sxWnn+hMe/CzaSejb/eVw2YToiXYX+Ex/AuHHia28vWTq4goAexFbw==",
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/vuex/-/vuex-4.0.2.tgz",
+ "integrity": "sha512-M6r8uxELjZIK8kTKDGgZTYX/ahzblnzC4isU1tpmEuOIIKmV+TRdc+H4s8ds2NuZ7wpUTdGRzJRtoj+lI+pc0Q==",
"license": "MIT",
+ "dependencies": {
+ "@vue/devtools-api": "^6.0.0-beta.11"
+ },
"peerDependencies": {
- "vue": "^2.0.0"
+ "vue": "^3.0.2"
}
},
+ "node_modules/vuex/node_modules/@vue/devtools-api": {
+ "version": "6.6.4",
+ "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-6.6.4.tgz",
+ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==",
+ "license": "MIT"
+ },
"node_modules/w3c-keyname": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz",
"integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==",
- "license": "MIT"
+ "license": "MIT",
+ "peer": true
},
"node_modules/w3c-xmlserializer": {
"version": "5.0.0",
@@ -12960,15 +13504,6 @@
"resolved": "https://registry.npmjs.org/yoga-layout/-/yoga-layout-3.2.1.tgz",
"integrity": "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ==",
"license": "MIT"
- },
- "node_modules/zhead": {
- "version": "2.2.4",
- "resolved": "https://registry.npmjs.org/zhead/-/zhead-2.2.4.tgz",
- "integrity": "sha512-8F0OI5dpWIA5IGG5NHUg9staDwz/ZPxZtvGVf01j7vHqSyZ0raHY+78atOVxRqb73AotX22uV1pXt3gYSstGag==",
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/harlan-zw"
- }
}
}
}
diff --git a/frontend/package.json b/frontend/package.json
index 750d303e74..b223da806b 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -20,6 +20,7 @@
"postinstall": "./scripts/install-twemoji.sh && patch-package"
},
"dependencies": {
+ "@date-io/dayjs": "^3.2.0",
"@intlify/core": "11.1.12",
"@leeoniya/ufuzzy": "1.0.19",
"@mdi/font": "7.4.47",
@@ -30,24 +31,26 @@
"@react-pdf/render": "4.3.1",
"@sentry/browser": "9.46.0",
"@sentry/vue": "9.46.0",
- "@tiptap/extension-bold": "2.26.2",
- "@tiptap/extension-bubble-menu": "2.26.2",
- "@tiptap/extension-bullet-list": "2.26.2",
- "@tiptap/extension-document": "2.26.2",
- "@tiptap/extension-hard-break": "2.26.2",
- "@tiptap/extension-heading": "2.26.2",
- "@tiptap/extension-history": "2.26.2",
- "@tiptap/extension-italic": "2.26.2",
- "@tiptap/extension-list-item": "2.26.2",
- "@tiptap/extension-ordered-list": "2.26.2",
- "@tiptap/extension-paragraph": "2.26.2",
- "@tiptap/extension-placeholder": "2.26.2",
- "@tiptap/extension-strike": "2.26.2",
- "@tiptap/extension-text": "2.26.2",
- "@tiptap/extension-underline": "2.26.2",
- "@tiptap/pm": "2.26.2",
- "@tiptap/vue-2": "2.26.2",
- "@unhead/vue": "1.11.20",
+ "@tiptap/extension-bold": "3.0.9",
+ "@tiptap/extension-bubble-menu": "3.0.9",
+ "@tiptap/extension-bullet-list": "3.0.9",
+ "@tiptap/extension-document": "3.0.9",
+ "@tiptap/extension-hard-break": "3.0.9",
+ "@tiptap/extension-heading": "3.0.9",
+ "@tiptap/extension-history": "3.0.9",
+ "@tiptap/extension-italic": "3.0.9",
+ "@tiptap/extension-list-item": "3.0.9",
+ "@tiptap/extension-ordered-list": "3.0.9",
+ "@tiptap/extension-paragraph": "3.0.9",
+ "@tiptap/extension-placeholder": "3.0.9",
+ "@tiptap/extension-strike": "3.0.9",
+ "@tiptap/extension-text": "3.0.9",
+ "@tiptap/extension-underline": "3.0.9",
+ "@tiptap/vue-3": "3.0.9",
+ "@unhead/vue": "2.0.14",
+ "@vee-validate/i18n": "4.15.1",
+ "@vee-validate/rules": "4.15.1",
+ "@vue/compat": "3.5.21",
"@zxcvbn-ts/core": "3.0.4",
"@zxcvbn-ts/language-common": "3.0.4",
"@zxcvbn-ts/language-de": "3.0.2",
@@ -72,31 +75,36 @@
"url-template": "3.1.1",
"util": "0.12.5",
"v-resize-observer": "2.1.0",
- "vee-validate": "3.4.15",
- "vue": "2.7.15",
+ "vee-validate": "4.15.1",
+ "vue": "3.5.21",
"vue-axios": "3.5.2",
- "vue-i18n": "8.28.2",
- "vue-recaptcha-v3": "1.9.0",
- "vue-router": "3.6.5",
- "vue-toastification": "1.7.14",
- "vuedraggable": "2.24.3",
- "vuetify": "2.7.2",
- "vuex": "3.6.2",
+ "vue-i18n": "11.1.11",
+ "vue-recaptcha-v3": "2.0.1",
+ "vue-router": "4.5.1",
+ "vue-toastification": "2.0.0-rc.5",
+ "vuedraggable": "4.1.0",
+ "vuetify": "3.10.4",
+ "vuex": "4.0.2",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz"
},
"devDependencies": {
"@babel/eslint-parser": "7.28.4",
"@eslint/compat": "1.4.0",
"@eslint/js": "9.36.0",
- "@sentry/vite-plugin": "3.6.1",
+ "@sentry/vite-plugin": "4.0.2",
"@testing-library/jest-dom": "6.8.0",
"@testing-library/user-event": "14.6.1",
- "@testing-library/vue": "5.9.0",
- "@vitejs/plugin-vue2": "2.3.3",
+ "@testing-library/vue": "8.1.0",
+ "@vitejs/plugin-vue": "6.0.1",
"@vitest/coverage-v8": "3.2.4",
"@vue/babel-preset-app": "5.0.9",
+ "@vue/compiler-dom": "3.5.21",
+ "@vue/compiler-sfc": "3.5.21",
"@vue/eslint-config-prettier": "10.2.0",
- "@vue/test-utils": "1.3.6",
+ "@vue/runtime-dom": "3.5.21",
+ "@vue/server-renderer": "3.5.21",
+ "@vue/shared": "3.5.21",
+ "@vue/test-utils": "2.4.6",
"autoprefixer": "10.4.21",
"babel-plugin-require-context-hook": "1.0.0",
"eslint": "9.36.0",
@@ -109,17 +117,17 @@
"eslint-plugin-vue-scoped-css": "2.12.0",
"flush-promises": "1.0.2",
"globals": "16.4.0",
- "jest-serializer-vue-tjw": "3.20.0",
+ "jest-serializer-vue-tjw": "4.0.0",
"jsdom": "26.1.0",
"lint-staged": "16.2.1",
"patch-package": "8.0.0",
"prettier": "3.6.2",
- "sass": "1.78.0",
- "source-map": "0.7.6",
+ "sass-embedded": "1.89.2",
"unplugin-vue-components": "29.1.0",
"vite": "6.3.6",
"vite-plugin-comlink": "5.3.0",
- "vite-plugin-vue2-svg": "0.4.0",
+ "vite-plugin-vuetify": "2.1.2",
+ "vite-svg-loader": "5.1.0",
"vitest": "3.2.4",
"vitest-canvas-mock": "0.3.3",
"vue-template-compiler": "2.7.15"
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 71fd3afb85..f3bdad92f2 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -13,21 +13,40 @@
- {{ $tc('global.info.offline.title') }}
- {{ $tc('global.info.offline.description') }}
+ {{ $t('global.info.offline.title') }}
+ {{ $t('global.info.offline.description') }}
+
-
diff --git a/frontend/src/components/activity/content/layout/LayoutNodeCard.vue b/frontend/src/components/activity/content/layout/LayoutNodeCard.vue
index d6f32a6886..06b5ec3da3 100644
--- a/frontend/src/components/activity/content/layout/LayoutNodeCard.vue
+++ b/frontend/src/components/activity/content/layout/LayoutNodeCard.vue
@@ -9,7 +9,7 @@
>
diff --git a/frontend/src/components/activity/content/storyboard/StoryboardDialogRemoveSection.vue b/frontend/src/components/activity/content/storyboard/StoryboardDialogRemoveSection.vue
index d3ad0a188b..0d3a25e8cf 100644
--- a/frontend/src/components/activity/content/storyboard/StoryboardDialogRemoveSection.vue
+++ b/frontend/src/components/activity/content/storyboard/StoryboardDialogRemoveSection.vue
@@ -3,10 +3,10 @@
v-model="showDialog"
icon="mdi-delete"
:title="
- $tc('components.activity.content.storyboard.storyboardDialogRemoveSection.title')
+ $t('components.activity.content.storyboard.storyboardDialogRemoveSection.title')
"
:submit-action="submit"
- :submit-label="$tc('global.button.delete')"
+ :submit-label="$t('global.button.delete')"
submit-color="error"
submit-icon="mdi-delete"
cancel-icon=""
@@ -16,7 +16,7 @@
{{
- $tc(
+ $t(
'components.activity.content.storyboard.storyboardDialogRemoveSection.deleteWarning'
)
}}
diff --git a/frontend/src/components/activity/content/storyboard/StoryboardRowDefault.vue b/frontend/src/components/activity/content/storyboard/StoryboardRowDefault.vue
index c8a9546c92..6800cbc2ce 100644
--- a/frontend/src/components/activity/content/storyboard/StoryboardRowDefault.vue
+++ b/frontend/src/components/activity/content/storyboard/StoryboardRowDefault.vue
@@ -3,10 +3,10 @@
@@ -39,14 +39,14 @@
|
-
+
mdi-delete-outline
diff --git a/frontend/src/components/activity/content/storyboard/StoryboardRowDense.vue b/frontend/src/components/activity/content/storyboard/StoryboardRowDense.vue
index 108706c738..f529f3d4ca 100644
--- a/frontend/src/components/activity/content/storyboard/StoryboardRowDense.vue
+++ b/frontend/src/components/activity/content/storyboard/StoryboardRowDense.vue
@@ -3,10 +3,10 @@
@@ -15,7 +15,7 @@
-
+
mdi-delete-outline
@@ -115,6 +115,7 @@ export default {
}
}
+/* eslint-disable-next-line vue-scoped-css/no-unused-selector */
.e-form-container + .e-form-container {
margin-top: 0;
}
diff --git a/frontend/src/components/activity/content/storyboard/StoryboardSortable.vue b/frontend/src/components/activity/content/storyboard/StoryboardSortable.vue
index 39008e127c..f4ee2f6c1c 100644
--- a/frontend/src/components/activity/content/storyboard/StoryboardSortable.vue
+++ b/frontend/src/components/activity/content/storyboard/StoryboardSortable.vue
@@ -12,10 +12,10 @@
@start="dragging = true"
@end="dragging = false"
>
-
+
= 0 && newIndex < list.length) {
// swap spaces in sortedKeys
const movingListItem = list[oldIndex]
- this.$set(list, oldIndex, list[newIndex])
- this.$set(list, newIndex, movingListItem)
+ list[oldIndex] = list[newIndex]
+ list[newIndex] = movingListItem
this.onSort()
}
@@ -180,12 +180,14 @@ export default {
+
diff --git a/frontend/src/components/comments/Comments.vue b/frontend/src/components/comments/Comments.vue
index 6658ff4772..f32180f6fe 100644
--- a/frontend/src/components/comments/Comments.vue
+++ b/frontend/src/components/comments/Comments.vue
@@ -10,7 +10,7 @@
>
{{
- $date(comment.createTime).format($tc('global.datetime.dateTimeLong'))
+ $date(comment.createTime).format($t('global.datetime.dateTimeLong'))
}}
@@ -37,13 +37,13 @@
/>
+ >
{{ authUser.displayName }}
{{ title }}
{{ progressLabel }}
@@ -54,7 +54,7 @@
{{ location }}
-
+
{{ progressLabel }}
@@ -153,7 +153,7 @@ export default {
}
tr + tr :is(td, th) {
- border-top: 1px solid #ddd;
+ border-top: 1px solid rgba(var(--v-border-color), var(--v-border-opacity));
}
:is(td, th) {
@@ -177,7 +177,7 @@ tr + tr :is(td, th) {
.e-subtitle {
font-size: 0.9em;
- color: #666;
+ color: rgba(var(--v-theme-on-surface), var(--v-medium-emphasis-opacity));
}
.e-subtitle--smaller {
@@ -192,7 +192,7 @@ tr + tr :is(td, th) {
font-size: 0.75em;
}
-.my-6px {
+:deep(.my-6px) {
margin-top: 6px;
margin-bottom: 6px;
}
diff --git a/frontend/src/components/dashboard/BooleanFilter.vue b/frontend/src/components/dashboard/BooleanFilter.vue
index ced32fb1b4..a8fb93ea29 100644
--- a/frontend/src/components/dashboard/BooleanFilter.vue
+++ b/frontend/src/components/dashboard/BooleanFilter.vue
@@ -1,5 +1,11 @@
- {{ label }}
@@ -11,9 +17,10 @@ export default {
name: 'BooleanFilter',
components: { CountBadge },
props: {
- value: Boolean,
+ modelValue: Boolean,
label: { type: String, required: true },
resultCount: { type: Number, default: null },
},
+ emits: ['update:modelValue'],
}
diff --git a/frontend/src/components/dashboard/CountBadge.vue b/frontend/src/components/dashboard/CountBadge.vue
index b565599a2a..742a0d45d7 100644
--- a/frontend/src/components/dashboard/CountBadge.vue
+++ b/frontend/src/components/dashboard/CountBadge.vue
@@ -1,5 +1,5 @@
-
+
-
-
diff --git a/frontend/src/components/layout/ContentGroup.vue b/frontend/src/components/layout/ContentGroup.vue
index 809d4ce4c1..2c02db830a 100644
--- a/frontend/src/components/layout/ContentGroup.vue
+++ b/frontend/src/components/layout/ContentGroup.vue
@@ -8,9 +8,9 @@ Displays the content wrapped inside a card.
- {{ icon }}
+ {{ icon }}
{{ title }}
@@ -34,9 +34,12 @@ export default {
}
-
diff --git a/frontend/src/components/navigation/UserMeta.vue b/frontend/src/components/navigation/UserMeta.vue
index 6eefa15abd..b32fdf7cac 100644
--- a/frontend/src/components/navigation/UserMeta.vue
+++ b/frontend/src/components/navigation/UserMeta.vue
@@ -4,23 +4,24 @@
v-model="open"
offset-y
dark
- right
+ location="right"
rounded
:content-class="
- ['ec-usermenu my-4', $vuetify.breakpoint.xsOnly && 'rounded-lg mt-2'].join(' ')
+ ['ec-usermenu py-4', !$vuetify.display.xs ? 'rounded-lg mt-2' : 'rounded-md'].join(
+ ' '
+ )
"
transition="slide-y-transition"
:close-on-content-click="false"
z-index="5"
>
-
+
@@ -36,35 +37,32 @@
:camp-collaboration="currentCampCollaboration"
:size="40"
/>
+
+ {{ authUser.displayName }}
+
-
- {{ authUser.displayName }}
-
-
-
-
-
+
-
+
+
{{ authUser.displayName }}
@@ -77,12 +75,12 @@
:to="{ name: 'profile', query: { isDetail: true } }"
@click="open = false"
>
- mdi-account
- {{ $tc('components.navigation.userMeta.profile') }}
+
+ {{ $t('components.navigation.userMeta.profile') }}
- mdi-format-list-bulleted-triangle
- {{ $tc('components.navigation.userMeta.myCamps') }}
+
+ {{ $t('components.navigation.userMeta.myCamps') }}
- mdi-email
- {{ $tc('components.navigation.userMeta.invitations') }}
-
-
-
+ mdi-email
+ {{ $t('components.navigation.userMeta.invitations') }}
+
+
+
- mdi-coffee
- {{ $tc('components.navigation.userMeta.admin') }}
+
+ {{ $t('components.navigation.userMeta.admin') }}
- mdi-help-circle
- {{ $tc('global.navigation.help') }}
-
- mdi-open-in-new
+
+ {{ $t('global.navigation.help') }}
+
+
+
- mdi-script-text-outline
- {{ $tc('global.navigation.news') }}
-
- mdi-open-in-new
+ mdi-script-text-outline
+ {{ $t('global.navigation.news') }}
+
+
+
@@ -133,9 +139,9 @@
size="18"
class="mr-2"
/>
- mdi-logout
+
- {{ $tc('components.navigation.userMeta.logOut') }}
+ {{ $t('components.navigation.userMeta.logOut') }}
@@ -193,7 +199,8 @@ export default {
currentCampCollaboration() {
if (
typeof this.camp?.campCollaborations !== 'function' ||
- this.camp.campCollaborations()._meta.loading
+ this.camp.campCollaborations()._meta.loading ||
+ !this.authUser
) {
return undefined
}
@@ -201,7 +208,7 @@ export default {
?.campCollaborations()
.items.find(
(collaboration) =>
- this.authUser?._meta?.self === collaboration.user?.()?._meta?.self
+ this.authUser._meta?.self === collaboration.user?.()?._meta?.self
)
},
},
diff --git a/frontend/src/components/personal_invitations/DialogPersonalInvitationReject.vue b/frontend/src/components/personal_invitations/DialogPersonalInvitationReject.vue
index 5a22f40878..e65608e9a3 100644
--- a/frontend/src/components/personal_invitations/DialogPersonalInvitationReject.vue
+++ b/frontend/src/components/personal_invitations/DialogPersonalInvitationReject.vue
@@ -4,16 +4,12 @@
type="error"
icon="mdi-email"
:title="
- $tc(
- 'components.personalInvitations.dialogPersonalInvitationReject.rejectInvitation'
- )
+ $t('components.personalInvitations.dialogPersonalInvitationReject.rejectInvitation')
"
:error="error"
:submit-action="submitAction"
:submit-label="
- $tc(
- 'components.personalInvitations.dialogPersonalInvitationReject.rejectInvitation'
- )
+ $t('components.personalInvitations.dialogPersonalInvitationReject.rejectInvitation')
"
submit-color="error"
submit-icon="mdi-cancel"
@@ -26,7 +22,7 @@
{{
- $tc(
+ $t(
'components.personalInvitations.dialogPersonalInvitationReject.warningText',
0,
{ campTitle: campTitle }
diff --git a/frontend/src/components/personal_invitations/PersonalInvitations.vue b/frontend/src/components/personal_invitations/PersonalInvitations.vue
index 4ad264aa70..7afa92d06b 100644
--- a/frontend/src/components/personal_invitations/PersonalInvitations.vue
+++ b/frontend/src/components/personal_invitations/PersonalInvitations.vue
@@ -1,45 +1,45 @@
-
+
{{
- $tc('components.personalInvitations.personalInvitations.noOpenInvitations', 0, {
+ $t('components.personalInvitations.personalInvitations.noOpenInvitations', 0, {
email: authUser.profile().email,
})
}}
-
-
-
- {{ invitation.campTitle }}
-
-
-
-
-
- {{ $tc('components.personalInvitations.personalInvitations.reject') }}
-
-
-
-
-
-
- {{ $tc('components.personalInvitations.personalInvitations.accept') }}
-
-
+
+
+ {{ invitation.campTitle }}
+
+
+
+
+
+ {{ $t('components.personalInvitations.personalInvitations.reject') }}
+
+
+
+
+ {{ $t('components.personalInvitations.personalInvitations.accept') }}
+
+
+
-
-
- {{ invitation.campTitle }}
-
+
+
@@ -48,17 +48,15 @@
:camp-title="invitation.campTitle"
@submit="rejectInvitation(invitation)"
>
-
-
- {{ $tc('components.personalInvitations.personalInvitations.reject') }}
+
+
+ {{ $t('components.personalInvitations.personalInvitations.reject') }}
-
-
-
+
- {{ $tc('components.personalInvitations.personalInvitations.accept') }}
+ {{ $t('components.personalInvitations.personalInvitations.accept') }}
@@ -71,6 +69,7 @@ import { errorToMultiLineToast } from '../toast/toasts.js'
import { isNavigationFailure, NavigationFailureType } from 'vue-router'
import DialogPersonalInvitationReject from './DialogPersonalInvitationReject.vue'
import { mapGetters } from 'vuex'
+import { useToast } from 'vue-toastification'
const ignoreNavigationFailure = (e) => {
if (!isNavigationFailure(e, NavigationFailureType.redirected)) {
@@ -81,6 +80,10 @@ const ignoreNavigationFailure = (e) => {
export default {
name: 'PersonalInvitations',
components: { DialogPersonalInvitationReject },
+ setup() {
+ const toast = useToast()
+ return { toast }
+ },
computed: {
invitations() {
return this.api.get().personalInvitations()
@@ -89,6 +92,10 @@ export default {
authUser: 'getLoggedInUser',
}),
},
+ async mounted() {
+ const user = await this.$auth.loadUser()
+ await user.profile()._meta.load
+ },
methods: {
acceptInvitation(invitation) {
this.api
@@ -110,7 +117,7 @@ export default {
.then(() => {
this.invitations.$reload()
})
- .catch((e) => this.$toast.error(errorToMultiLineToast(e)))
+ .catch((e) => this.toast.error(errorToMultiLineToast(e)))
},
rejectInvitation(invitation) {
this.api
@@ -124,7 +131,7 @@ export default {
.then(() => {
this.invitations.$reload()
})
- .catch((e) => this.$toast.error(errorToMultiLineToast(e)))
+ .catch((e) => this.toast.error(errorToMultiLineToast(e)))
},
campLink(invitation) {
return {
diff --git a/frontend/src/components/print/PrintConfigurator.vue b/frontend/src/components/print/PrintConfigurator.vue
index 72e1498e44..4dd96b72dc 100644
--- a/frontend/src/components/print/PrintConfigurator.vue
+++ b/frontend/src/components/print/PrintConfigurator.vue
@@ -5,7 +5,7 @@
-
+
@@ -44,7 +43,7 @@
"
>
- {{ $tc('components.print.printConfigurator.config.' + idx) }}
+ {{ $t('components.print.printConfigurator.config.' + idx) }}
@@ -52,27 +51,27 @@
-
- {{ $tc('components.print.printConfigurator.options') }}
-
-
+
+ {{ $t('components.print.printConfigurator.options') }}
+
+
-
+
-
-
+ View Print-Config
-
-
+
+
{{ cnf }}
-
+
@@ -125,7 +124,7 @@ import DownloadNuxtPdfButton from '@/components/print/print-nuxt/DownloadNuxtPdf
import DownloadClientPdfButton from '@/components/print/print-client/DownloadClientPdfButton.vue'
import { getEnv } from '@/environment.js'
import cloneDeep from 'lodash-es/cloneDeep'
-import VueI18n from '../../plugins/i18n/index.js'
+import { componentI18n } from '../../plugins/i18n/index.js'
import repairConfig from './repairPrintConfig.js'
import StoryConfig from '@/components/print/config/StoryConfig.vue'
import SafetyConsiderationsConfig from '@/components/print/config/SafetyConsiderationsConfig.vue'
@@ -284,7 +283,7 @@ export default {
return repairConfig(
config,
this.camp,
- VueI18n.availableLocales,
+ componentI18n.availableLocales,
this.lang,
repairers,
this.defaultContents()
diff --git a/frontend/src/components/print/config/ActivityConfig.vue b/frontend/src/components/print/config/ActivityConfig.vue
index 1df660008c..c77fd56c97 100644
--- a/frontend/src/components/print/config/ActivityConfig.vue
+++ b/frontend/src/components/print/config/ActivityConfig.vue
@@ -4,7 +4,7 @@
v-if="!loading"
v-model="optionsScheduleEntry"
:items="scheduleEntries"
- :label="$tc('components.print.config.activityConfig.activity')"
+ :label="$t('components.print.config.activityConfig.activity')"
:filled="false"
@input="$emit('input')"
/>
diff --git a/frontend/src/components/print/config/ActivityListConfig.vue b/frontend/src/components/print/config/ActivityListConfig.vue
index 8401300b36..76aef76f4b 100644
--- a/frontend/src/components/print/config/ActivityListConfig.vue
+++ b/frontend/src/components/print/config/ActivityListConfig.vue
@@ -3,7 +3,7 @@
import DialogScheduleEntryFilter from './DialogScheduleEntryFilter.vue'
import { filterMatchScheduleEntry } from '@/common/helpers/filterMatchScheduleEntry.js'
-import { repairPrintFilterConfig } from '../repairPrintConfig.js'
+import repairFilterConfig from '../../program/repairFilterConfig.js'
export default {
name: 'ActivityListConfig',
@@ -86,7 +86,8 @@ export default {
return knownPeriods.includes(period)
})
}
- return repairPrintFilterConfig(config, camp, knownPeriods)
+ config.options.filter = repairFilterConfig(config, camp)
+ return config
},
}
diff --git a/frontend/src/components/print/config/DialogScheduleEntryFilter.vue b/frontend/src/components/print/config/DialogScheduleEntryFilter.vue
index 837eaec22e..52684cfa92 100644
--- a/frontend/src/components/print/config/DialogScheduleEntryFilter.vue
+++ b/frontend/src/components/print/config/DialogScheduleEntryFilter.vue
@@ -2,23 +2,22 @@
-
+
- mdi-filter
+ mdi-filter
{{ activatorLabel }}
@@ -64,7 +63,7 @@ export default {
},
activatorLabel() {
if (this.anyFilter)
- return this.$tc(
+ return this.$t(
'components.print.config.dialogScheduleEntryFilter.filterActive',
1,
{
@@ -72,7 +71,7 @@ export default {
total: this.filterFn({}).length,
}
)
- return this.$tc(
+ return this.$t(
'components.print.config.dialogScheduleEntryFilter.filterActivities',
1,
{
@@ -81,14 +80,10 @@ export default {
)
},
resultCountLabel() {
- return this.$tc(
- 'components.print.config.dialogScheduleEntryFilter.resultCount',
- 1,
- {
- filtered: this.filteredCount,
- total: this.filterFn({}).length,
- }
- )
+ return this.$t('components.print.config.dialogScheduleEntryFilter.resultCount', 1, {
+ filtered: this.filteredCount,
+ total: this.filterFn({}).length,
+ })
},
anyFilter() {
return (
diff --git a/frontend/src/components/print/config/PicassoConfig.vue b/frontend/src/components/print/config/PicassoConfig.vue
index aecd1e43ba..5981b1b2b2 100644
--- a/frontend/src/components/print/config/PicassoConfig.vue
+++ b/frontend/src/components/print/config/PicassoConfig.vue
@@ -2,7 +2,7 @@
import DialogScheduleEntryFilter from './DialogScheduleEntryFilter.vue'
import { filterMatchScheduleEntry } from '@/common/helpers/filterMatchScheduleEntry.js'
-import { repairPrintFilterConfig } from '../repairPrintConfig.js'
+import repairFilterConfig from '../../program/repairFilterConfig.js'
export default {
name: 'PicassoConfig',
@@ -44,11 +44,11 @@ export default {
orientations: [
{
value: 'L',
- text: this.$tc('components.print.config.picassoConfig.landscape'),
+ text: this.$t('components.print.config.picassoConfig.landscape'),
},
{
value: 'P',
- text: this.$tc('components.print.config.picassoConfig.portrait'),
+ text: this.$t('components.print.config.picassoConfig.portrait'),
},
],
}
@@ -111,7 +111,8 @@ export default {
if (!['L', 'P'].includes(config.options.orientation)) {
config.options.orientation = 'L'
}
- return repairPrintFilterConfig(config, camp, knownPeriods)
+ config.options.filter = repairFilterConfig(config, camp)
+ return config
},
}
diff --git a/frontend/src/components/print/config/ProgramConfig.vue b/frontend/src/components/print/config/ProgramConfig.vue
index f5dc92d793..d5f5f367ed 100644
--- a/frontend/src/components/print/config/ProgramConfig.vue
+++ b/frontend/src/components/print/config/ProgramConfig.vue
@@ -3,7 +3,7 @@
@@ -28,7 +28,7 @@
diff --git a/frontend/src/components/print/config/SafetyConsiderationsConfig.vue b/frontend/src/components/print/config/SafetyConsiderationsConfig.vue
index fa02d08ace..b033049e5c 100644
--- a/frontend/src/components/print/config/SafetyConsiderationsConfig.vue
+++ b/frontend/src/components/print/config/SafetyConsiderationsConfig.vue
@@ -3,7 +3,7 @@
diff --git a/frontend/src/components/print/config/StoryConfig.vue b/frontend/src/components/print/config/StoryConfig.vue
index 63fc415f04..2f87508b94 100644
--- a/frontend/src/components/print/config/StoryConfig.vue
+++ b/frontend/src/components/print/config/StoryConfig.vue
@@ -3,7 +3,7 @@
diff --git a/frontend/src/components/print/configurator/PagesConfig.vue b/frontend/src/components/print/configurator/PagesConfig.vue
index 23dfd525d8..d5fff399c7 100644
--- a/frontend/src/components/print/configurator/PagesConfig.vue
+++ b/frontend/src/components/print/configurator/PagesConfig.vue
@@ -1,5 +1,5 @@
-
+
-
+
-
+
mdi-plus
@@ -95,6 +95,9 @@ export default {
diff --git a/frontend/src/components/print/configurator/PagesOverview.vue b/frontend/src/components/print/configurator/PagesOverview.vue
index ded9c4240f..e1a4ffc1e5 100644
--- a/frontend/src/components/print/configurator/PagesOverview.vue
+++ b/frontend/src/components/print/configurator/PagesOverview.vue
@@ -5,7 +5,6 @@
filter=".e-pages-config--template"
class="e-pages-overview__grid pa-0 pa-md-8"
v-bind="$attrs"
- v-on="$listeners"
>
@@ -23,7 +22,10 @@ export default {
diff --git a/frontend/src/components/program/ScheduleEntryFilters.vue b/frontend/src/components/program/ScheduleEntryFilters.vue
index ffb1bfd427..39ef8b4537 100644
--- a/frontend/src/components/program/ScheduleEntryFilters.vue
+++ b/frontend/src/components/program/ScheduleEntryFilters.vue
@@ -8,7 +8,7 @@
@@ -24,11 +24,11 @@
updateFilter({ period: val })"
+ :label="$t('components.program.scheduleEntryFilters.period')"
+ @update:model-value="(val) => updateFilter({ period: val })"
/>
updateFilter({ responsible: val })"
+ :label="$t('components.program.scheduleEntryFilters.responsible')"
+ @update:model-value="(val) => updateFilter({ responsible: val })"
>
@@ -70,12 +70,12 @@
/>
updateFilter({ category: val })"
+ :label="$t('components.program.scheduleEntryFilters.category')"
+ @update:model-value="(val) => updateFilter({ category: val })"
>
@@ -92,12 +92,12 @@
updateFilter({ day: val })"
+ :label="$t('components.program.scheduleEntryFilters.day')"
+ @update:model-value="(val) => updateFilter({ day: val })"
/>
updateFilter({ progressLabel: val })"
+ :label="$t('components.program.scheduleEntryFilters.progressLabel')"
+ @update:model-value="(val) => updateFilter({ progressLabel: val })"
>
{{ progressLabels[item.value].title }}
@@ -127,10 +127,17 @@
height="32"
width="100"
/>
-
+
​
- mdi-close
- {{ $tc('components.program.scheduleEntryFilters.clearFilters') }}
+ mdi-close
+ {{ $t('components.program.scheduleEntryFilters.clearFilters') }}
@@ -161,7 +168,7 @@ export default {
UserAvatar,
},
props: {
- value: {
+ modelValue: {
type: Object,
default: () => ({
period: null,
@@ -199,6 +206,7 @@ export default {
default: false,
},
},
+ emits: ['update:modelValue', 'height-changed'],
computed: {
periodItems() {
return keyBy(
@@ -217,7 +225,7 @@ export default {
this.camp.periods().items.flatMap((period) =>
period.days().items.map((day) => ({
...day,
- label: this.$tc('components.program.scheduleEntryFilters.dayLabel', 0, {
+ label: this.$t('components.program.scheduleEntryFilters.dayLabel', 0, {
dayNumber: day.number,
date: this.$date.utc(day.start).format('dd. DD. MMM'),
}),
@@ -232,7 +240,7 @@ export default {
}),
loggedInCampCollaboration() {
return Object.values(this.campCollaborations).find((collaboration) => {
- if (typeof collaboration.user !== 'function') {
+ if (typeof collaboration.user !== 'function' || !this.loggedInUser) {
return false
}
return this.loggedInUser?._meta?.self === collaboration.user?.()?._meta?.self
@@ -242,21 +250,21 @@ export default {
return {
none: {
exclusiveNone: true,
- label: this.$tc('components.program.scheduleEntryFilters.responsibleNone'),
+ label: this.$t('components.program.scheduleEntryFilters.responsibleNone'),
_meta: { self: 'none' },
resultCount: this.resultCountWithModifiedFilter('responsible', ['none']),
},
...keyBy(
sortBy(this.camp.campCollaborations().items, (u) =>
- campCollaborationDisplayName(u, this.$tc.bind(this)).toLowerCase()
+ campCollaborationDisplayName(u, this.$t.bind(this)).toLowerCase()
).map((campCollaboration) => {
return {
...campCollaboration,
resultCount: this.resultCountWithModifiedFilter(
'responsible',
- this.value.responsible?.includes('none')
+ this.modelValue.responsible?.includes('none')
? [campCollaboration._meta.self]
- : [...(this.value.responsible ?? []), campCollaboration._meta.self]
+ : [...(this.modelValue.responsible ?? []), campCollaboration._meta.self]
),
}
}),
@@ -281,7 +289,7 @@ export default {
const labels = sortBy(this.camp.progressLabels().items, (l) => l.position)
return {
none: {
- title: this.$tc('components.program.scheduleEntryFilters.progressLabelNone'),
+ title: this.$t('components.program.scheduleEntryFilters.progressLabelNone'),
_meta: { self: 'none' },
resultCount: this.resultCountWithModifiedFilter('progressLabel', ['none']),
},
@@ -299,7 +307,7 @@ export default {
}
},
filteredPropertiesCount() {
- return Object.values(this.value).filter((item) =>
+ return Object.values(this.modelValue).filter((item) =>
Array.isArray(item) ? item.length : !!item
).length
},
@@ -309,11 +317,11 @@ export default {
showOnlyMyActivities: {
get() {
return (
- filterEquals(this.value.responsible, [this.loggedInCampCollaboration]) &&
- filterEquals(this.value.category, []) &&
- filterEquals(this.value.day, []) &&
- filterEquals(this.value.period, null) &&
- filterEquals(this.value.progressLabel, [])
+ filterEquals(this.modelValue.responsible, [this.loggedInCampCollaboration]) &&
+ filterEquals(this.modelValue.category, []) &&
+ filterEquals(this.modelValue.day, []) &&
+ filterEquals(this.modelValue.period, null) &&
+ filterEquals(this.modelValue.progressLabel, [])
)
},
set(value) {
@@ -344,7 +352,7 @@ export default {
},
methods: {
campCollaborationDisplayName(campCollaboration) {
- return campCollaborationDisplayName(campCollaboration, this.$tc.bind(this))
+ return campCollaborationDisplayName(campCollaboration, this.$t.bind(this))
},
loadEndpointData(endpoint, filterKey, hasNone = false) {
this.camp[endpoint]()._meta.load.then(({ allItems }) => {
@@ -354,7 +362,8 @@ export default {
}
this.updateFilter({
[filterKey]:
- this.value[filterKey].filter((value) => collection.includes(value)) ?? null,
+ this.modelValue[filterKey].filter((value) => collection.includes(value)) ??
+ null,
})
this.loadingEndpoints[endpoint] = false
})
@@ -373,14 +382,14 @@ export default {
},
resultCountWithModifiedFilter(filterName, filterValue) {
return this.filterFn({
- ...this.value,
+ ...this.modelValue,
[filterName]: filterValue,
}).length
},
updateFilter(updates = {}) {
- const valueClone = clone(this.value)
+ const valueClone = clone(this.modelValue)
Object.assign(valueClone, updates)
- this.$emit('input', valueClone)
+ this.$emit('update:modelValue', valueClone)
},
},
}
diff --git a/frontend/src/components/program/__tests__/repairFilterConfig.spec.js b/frontend/src/components/program/__tests__/repairFilterConfig.spec.js
new file mode 100644
index 0000000000..bba7bda3c5
--- /dev/null
+++ b/frontend/src/components/program/__tests__/repairFilterConfig.spec.js
@@ -0,0 +1,465 @@
+import { describe, expect, test } from 'vitest'
+import repairFilterConfig from '../repairFilterConfig.js'
+
+describe('repairFilterConfig', () => {
+ const camp = {
+ _meta: { self: '/camps/1a2b3c4d' },
+ shortTitle: 'test camp',
+ periods: () => ({
+ _meta: { loading: false },
+ items: [
+ {
+ _meta: { self: '/periods/1a2b3c4d' },
+ days: () => ({
+ _meta: { loading: false },
+ items: [
+ {
+ _meta: { self: '/days/1a2b3c4d' },
+ },
+ ],
+ }),
+ },
+ ],
+ }),
+ categories: () => ({
+ _meta: { loading: false },
+ items: [
+ {
+ _meta: { self: '/categories/1a2b3c4d' },
+ },
+ ],
+ }),
+ campCollaborations: () => ({
+ _meta: { loading: false },
+ items: [
+ {
+ _meta: { self: '/camp_collaborations/1a2b3c4d' },
+ },
+ ],
+ }),
+ progressLabels: () => ({
+ _meta: { loading: false },
+ items: [
+ {
+ _meta: { self: '/progress_labels/1a2b3c4d' },
+ },
+ ],
+ }),
+ }
+
+ const args = [camp]
+
+ const defaultFilter = {
+ period: null,
+ day: [],
+ category: [],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ test('leaves valid filter as is', () => {
+ // given
+ const config = {
+ period: '/periods/1a2b3c4d',
+ day: ['/days/1a2b3c4d'],
+ category: ['/categories/1a2b3c4d'],
+ progressLabel: ['/progress_labels/1a2b3c4d'],
+ responsible: ['/camp_collaborations/1a2b3c4d'],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual({
+ period: '/periods/1a2b3c4d',
+ day: ['/days/1a2b3c4d'],
+ category: ['/categories/1a2b3c4d'],
+ progressLabel: ['/progress_labels/1a2b3c4d'],
+ responsible: ['/camp_collaborations/1a2b3c4d'],
+ activityCount: 0,
+ })
+ })
+
+ test('adds period if missing', () => {
+ // given
+ const config = {
+ day: [],
+ category: [],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual(defaultFilter)
+ })
+
+ test('removes invalid period', () => {
+ // given
+ const config = {
+ period: '/periods/00000000',
+ day: [],
+ category: [],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual(defaultFilter)
+ })
+
+ test('ignores invalid period if periods are still loading', () => {
+ // given
+ const config = {
+ period: '/periods/00000000',
+ day: [],
+ category: [],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, {
+ ...camp,
+ periods: () => ({
+ _meta: { loading: true },
+ }),
+ })
+
+ // then
+ expect(result).toEqual({
+ period: '/periods/00000000',
+ day: [],
+ category: [],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ })
+ })
+
+ test('adds responsible filter when missing', () => {
+ // given
+ const config = {
+ period: null,
+ day: [],
+ category: [],
+ progressLabel: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual(defaultFilter)
+ })
+
+ test('removes invalid responsible', () => {
+ // given
+ const config = {
+ period: null,
+ day: [],
+ category: [],
+ progressLabel: [],
+ responsible: ['/camp_collaborations/00000000'],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual(defaultFilter)
+ })
+
+ test('ignores invalid responsible if campCollaborations are still loading', () => {
+ // given
+ const config = {
+ period: null,
+ day: [],
+ category: [],
+ progressLabel: [],
+ responsible: ['/camp_collaborations/00000000'],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, {
+ ...camp,
+ campCollaborations: () => ({
+ _meta: { loading: true },
+ }),
+ })
+
+ // then
+ expect(result).toEqual({
+ period: null,
+ day: [],
+ category: [],
+ progressLabel: [],
+ responsible: ['/camp_collaborations/00000000'],
+ activityCount: 0,
+ })
+ })
+
+ test('adds category filter when missing', () => {
+ // given
+ const config = {
+ period: null,
+ day: [],
+ responsible: [],
+ progressLabel: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual(defaultFilter)
+ })
+
+ test('removes invalid category', () => {
+ // given
+ const config = {
+ period: null,
+ day: [],
+ category: ['/categories/00000000'],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual(defaultFilter)
+ })
+
+ test('ignores invalid category when categories are still loading', () => {
+ // given
+ const config = {
+ period: null,
+ day: [],
+ category: ['/categories/00000000'],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, {
+ ...camp,
+ categories: () => ({
+ _meta: { loading: true },
+ }),
+ })
+
+ // then
+ expect(result).toEqual({
+ period: null,
+ day: [],
+ category: ['/categories/00000000'],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ })
+ })
+
+ test('adds day filter when missing', () => {
+ // given
+ const config = {
+ period: null,
+ category: [],
+ responsible: [],
+ progressLabel: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual(defaultFilter)
+ })
+
+ test('removes invalid day', () => {
+ // given
+ const config = {
+ period: null,
+ day: ['/days/00000000'],
+ category: [],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual(defaultFilter)
+ })
+
+ test('removes invalid day when days are still loading', () => {
+ // given
+ const config = {
+ period: null,
+ day: ['/days/00000000'],
+ category: [],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, {
+ ...camp,
+ periods: () => ({
+ _meta: { loading: false },
+ items: [
+ {
+ _meta: { self: '/periods/1a2b3c4d' },
+ days: () => ({
+ _meta: { loading: true },
+ }),
+ },
+ ],
+ }),
+ })
+
+ // then
+ expect(result).toEqual({
+ period: null,
+ day: ['/days/00000000'],
+ category: [],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ })
+ })
+
+ test('removes invalid day when periods are still loading', () => {
+ // given
+ const config = {
+ period: null,
+ day: ['/days/00000000'],
+ category: [],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, {
+ ...camp,
+ periods: () => ({
+ _meta: { loading: true },
+ }),
+ })
+
+ // then
+ expect(result).toEqual({
+ period: null,
+ day: ['/days/00000000'],
+ category: [],
+ progressLabel: [],
+ responsible: [],
+ activityCount: 0,
+ })
+ })
+
+ test('adds progressLabel filter when missing', () => {
+ // given
+ const config = {
+ period: null,
+ day: [],
+ category: [],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual(defaultFilter)
+ })
+
+ test('removes invalid progressLabel', () => {
+ // given
+ const config = {
+ period: null,
+ day: [],
+ category: [],
+ progressLabel: ['/progress_labels/00000000'],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual(defaultFilter)
+ })
+
+ test('ignores invalid progressLabel when progressLabels are still loading', () => {
+ // given
+ const config = {
+ period: null,
+ day: [],
+ category: [],
+ progressLabel: ['/progress_labels/00000000'],
+ responsible: [],
+ activityCount: 0,
+ }
+
+ // when
+ const result = repairFilterConfig(config, {
+ ...camp,
+ progressLabels: () => ({
+ _meta: { loading: true },
+ }),
+ })
+
+ // then
+ expect(result).toEqual({
+ period: null,
+ day: [],
+ category: [],
+ progressLabel: ['/progress_labels/00000000'],
+ responsible: [],
+ activityCount: 0,
+ })
+ })
+
+ test('adds dummy activityCount if missing', () => {
+ // given
+ const config = {
+ period: null,
+ day: [],
+ category: [],
+ progressLabel: [],
+ responsible: [],
+ }
+
+ // when
+ const result = repairFilterConfig(config, ...args)
+
+ // then
+ expect(result).toEqual(defaultFilter)
+ })
+})
diff --git a/frontend/src/components/program/picasso/DayResponsibles.vue b/frontend/src/components/program/picasso/DayResponsibles.vue
index 3c71c3432f..2e6e8107d9 100644
--- a/frontend/src/components/program/picasso/DayResponsibles.vue
+++ b/frontend/src/components/program/picasso/DayResponsibles.vue
@@ -25,7 +25,7 @@
@input="onInput"
>
- {{ $tc('entity.day.fields.dayResponsibles') }}
+ {{ $t('entity.day.fields.dayResponsibles') }}
@@ -81,7 +81,7 @@ export default {
// following structure is defined by vuetify v-select items property
return {
value: value._meta.self,
- text: campCollaborationDisplayName(value, this.$tc.bind(this)),
+ text: campCollaborationDisplayName(value, this.$t.bind(this)),
}
})
},
diff --git a/frontend/src/components/program/picasso/Picasso.vue b/frontend/src/components/program/picasso/Picasso.vue
index 0e34e9c931..3e2e42b582 100644
--- a/frontend/src/components/program/picasso/Picasso.vue
+++ b/frontend/src/components/program/picasso/Picasso.vue
@@ -12,7 +12,7 @@ Listing all given activity schedule entries in a calendar view.
:events="events"
event-start="startTimestamp"
event-end="endTimestamp"
- event-color="transparent"
+ :event-color="getEventColor"
:interval-height="computedIntervalHeight"
interval-width="46"
:interval-format="intervalFormat"
@@ -25,15 +25,15 @@ Listing all given activity schedule entries in a calendar view.
:day-format="dayFormat"
:type="type"
:max-days="maxDays"
- :weekday-format="weekdayFormat"
:weekdays="[1, 2, 3, 4, 5, 6, 0]"
color="primary"
:event-ripple="false"
- v-on="vCalendarListeners"
- @mouseleave.native="onMouseleave"
- @mousedown.native.prevent="
- /*this prevents from middle button to start scroll behavior*/
- "
+ @mousedown:event="vCalendarListeners.entryMouseDown"
+ @mousedown:time="vCalendarListeners.timeMouseDown"
+ @mouseleave="onMouseleave"
+ @mousemove:time="vCalendarListeners.timeMouseMove"
+ @mouseup:time="vCalendarListeners.timeMouseUp"
+ @mousedown.prevent="preventMiddleButtonFromStartingScrollBehaviour"
>
@@ -43,11 +43,11 @@ Listing all given activity schedule entries in a calendar view.
entryWidth > 140
? $date
.utc(date)
- .format($tc('components.program.picasso.picasso.datetime.fullDate'))
+ .format($t('components.program.picasso.picasso.datetime.fullDate'))
: $date
.utc(date)
.format(
- $tc(
+ $t(
'components.program.picasso.picasso.datetime.smallDate',
widthPluralization
)
@@ -71,12 +71,12 @@ Listing all given activity schedule entries in a calendar view.
mdi-loading
- {{ $tc('global.button.saving') }}
+ {{ $t('global.button.saving') }}
diff --git a/frontend/src/views/Invitations.vue b/frontend/src/views/Invitations.vue
index 0fa795a5c5..6f9666bd98 100644
--- a/frontend/src/views/Invitations.vue
+++ b/frontend/src/views/Invitations.vue
@@ -1,12 +1,12 @@
-
+
@@ -34,16 +34,13 @@ export default {
},
head() {
return {
- title: this.$tc('views.invitations.personalInvitations'),
+ title: this.$t('views.invitations.personalInvitations'),
}
},
computed: {
loading() {
return this.api.get().personalInvitations().loading
},
- ...mapGetters({
- user: 'getLoggedInUser',
- }),
},
}
diff --git a/frontend/src/views/NavigationDefault.vue b/frontend/src/views/NavigationDefault.vue
index 20fa38c2da..1f282741ad 100644
--- a/frontend/src/views/NavigationDefault.vue
+++ b/frontend/src/views/NavigationDefault.vue
@@ -1,17 +1,17 @@
-
-
- {{ $tc('global.navigation.help') }}
- mdi-open-in-new
+
+
+ {{ $t('global.navigation.help') }}
+
diff --git a/frontend/src/views/PageNotFound.vue b/frontend/src/views/PageNotFound.vue
index 021a19ebae..a961f8139b 100644
--- a/frontend/src/views/PageNotFound.vue
+++ b/frontend/src/views/PageNotFound.vue
@@ -3,24 +3,24 @@
-
+
-
-
+
+
-
+
-
- mdi-tent
- {{ $tc('views.pageNotFound.backToHome') }}
+
+ mdi-tent
+ {{ $t('views.pageNotFound.backToHome') }}
diff --git a/frontend/src/views/Profile.vue b/frontend/src/views/Profile.vue
index beabc2683e..bf70ade93e 100644
--- a/frontend/src/views/Profile.vue
+++ b/frontend/src/views/Profile.vue
@@ -4,12 +4,12 @@
v-if="user"
max-width="800"
:title="
- $tc('views.profile.profile') + (user._meta.loading ? '' : ': ' + user.displayName)
+ $t('views.profile.profile') + (user._meta.loading ? '' : ': ' + user.displayName)
"
toolbar
>
-
+
@@ -25,9 +25,9 @@
>
-
-
- {{ $tc('views.profile.changeEmail') }}
+
+
+ {{ $t('views.profile.changeEmail') }}
@@ -51,15 +51,15 @@
- {{ $tc('global.button.logout') }}
+ {{ $t('global.button.logout') }}
@@ -98,7 +98,7 @@ export default {
},
head() {
return {
- title: this.$tc('views.profile.profile'),
+ title: this.$t('views.profile.profile'),
}
},
computed: {
@@ -109,22 +109,22 @@ export default {
return this.user?.profile()
},
availableLocales() {
- return VueI18n.availableLocales.map((l) => ({
+ return VueI18n.global.availableLocales.map((l) => ({
value: l,
- text: this.$tc('global.language', 1, l),
+ text: this.$t('global.language', 1, { locale: l }),
}))
},
},
watch: {
profile() {
- if (VueI18n.availableLocales.includes(this.profile?.language)) {
+ if (VueI18n.global.availableLocales.includes(this.profile?.language)) {
this.$store.commit('setLanguage', this.profile?.language)
}
},
},
methods: {
reloadUser() {
- this.api.reload(this.user)
+ if (this.user) this.api.reload(this.user)
},
},
}
diff --git a/frontend/src/views/admin/Debug.vue b/frontend/src/views/admin/Debug.vue
index 453a601059..c5dfe22481 100644
--- a/frontend/src/views/admin/Debug.vue
+++ b/frontend/src/views/admin/Debug.vue
@@ -22,7 +22,7 @@
{{ deploymentTime }}
-
+
{{ key }} |
@@ -31,7 +31,7 @@
-
+
@@ -64,7 +64,7 @@ export default {
deploymentTime() {
const timestamp = this.environment.DEPLOYMENT_TIME
const dateTime = timestamp ? this.$date.unix(timestamp) : this.$date()
- return dateTime.format(this.$tc('global.datetime.dateTimeLong'))
+ return dateTime.format(this.$t('global.datetime.dateTimeLong'))
},
version() {
return this.environment.VERSION || ''
@@ -81,7 +81,10 @@ export default {
diff --git a/frontend/src/views/auth/LoginCallback.vue b/frontend/src/views/auth/LoginCallback.vue
index 01361020d5..65d4d1eff2 100644
--- a/frontend/src/views/auth/LoginCallback.vue
+++ b/frontend/src/views/auth/LoginCallback.vue
@@ -4,7 +4,7 @@
- {{$tc('views.auth.loginCallback.loginInProgress')
+ {{ $t('views.auth.loginCallback.loginInProgress') }}
diff --git a/frontend/src/views/auth/NavigationAuth.vue b/frontend/src/views/auth/NavigationAuth.vue
index 8e2941bcd9..872bf3928d 100644
--- a/frontend/src/views/auth/NavigationAuth.vue
+++ b/frontend/src/views/auth/NavigationAuth.vue
@@ -1,32 +1,31 @@
-
- mdi-script-text-outline
- {{ $tc('global.navigation.news') }}
+
+ mdi-script-text-outline
+ {{ $t('global.navigation.news') }}
mdi-help
- {{ $tc('global.navigation.help') }}
+ {{ $t('global.navigation.help') }}
- mdi-help
- {{ $tc('global.navigation.help') }}
+
+ {{ $t('global.navigation.help') }}
diff --git a/frontend/src/views/auth/Register.vue b/frontend/src/views/auth/Register.vue
index d67d7823d1..d7da6bb34c 100644
--- a/frontend/src/views/auth/Register.vue
+++ b/frontend/src/views/auth/Register.vue
@@ -1,67 +1,59 @@
- {{ $tc('views.auth.register.title') }}
-
+ {{ $t('views.auth.register.title') }}
+
+
-
-
-
+
@@ -70,48 +62,46 @@
-
+
-
- {{ $tc('views.auth.register.acceptTermsOfService') }}
+
+ {{ $t('views.auth.register.acceptTermsOfService') }}
- mdi-open-in-new
+ mdi-open-in-new
@@ -119,25 +109,25 @@
*
- {{ $tc('views.auth.register.requiredField') }}
+ {{ $t('views.auth.register.requiredField') }}
-
+
- {{ $tc('views.auth.register.register') }}
+ {{ $t('views.auth.register.register') }}
-
+
- {{ $tc('views.auth.register.alreadyHaveAnAccount') }}
+ {{ $t('views.auth.register.alreadyHaveAnAccount') }}
- {{ $tc('global.button.login') }}
+ {{ $t('global.button.login') }}
@@ -147,21 +137,26 @@
import { load } from 'recaptcha-v3'
import AuthContainer from '@/components/layout/AuthContainer.vue'
import { errorToMultiLineToast } from '@/components/toast/toasts'
-import VueI18n from '@/plugins/i18n'
-import { ValidationObserver } from 'vee-validate'
+import { componentI18n } from '@/plugins/i18n'
import { passwordStrengthMixin } from '../../mixins/passwordStrengthMixin.js'
import { parseTemplate } from 'url-template'
import { getEnv } from '@/environment.js'
import EForm from '@/components/form/base/EForm.vue'
+import { Form as VeeForm } from 'vee-validate'
+import { useToast } from 'vue-toastification'
export default {
name: 'Register',
components: {
EForm,
AuthContainer,
- ValidationObserver,
+ VeeForm,
},
mixins: [passwordStrengthMixin],
+ setup() {
+ const toast = useToast()
+ return { toast }
+ },
data() {
return {
registering: false,
@@ -178,7 +173,7 @@ export default {
},
head() {
return {
- title: this.$tc('views.auth.register.register'),
+ title: this.$t('views.auth.register.register'),
}
},
computed: {
@@ -193,22 +188,23 @@ export default {
}
},
availableLocales() {
- return VueI18n.availableLocales.map((l) => ({
+ return componentI18n.availableLocales.map((l) => ({
value: l,
- text: this.$tc('global.language', 1, l),
+ text: this.$t('global.language', 1, { locale: l }),
}))
},
termsOfServiceLink() {
+ const currentLanguage = this.language || ''
return (
parseTemplate(getEnv().TERMS_OF_SERVICE_LINK_TEMPLATE || '').expand({
- lang: this.language.substring(0, 2),
+ lang: currentLanguage.substring(0, 2),
}) || false
)
},
},
watch: {
language() {
- if (VueI18n.availableLocales.includes(this.language)) {
+ if (componentI18n.availableLocales.includes(this.language)) {
this.$store.commit('setLanguage', this.language)
}
},
@@ -247,7 +243,7 @@ export default {
})
.then(() => this.$router.push({ name: 'register-done' }))
.catch((e) => {
- this.$toast.error(errorToMultiLineToast(e))
+ this.toast.error(errorToMultiLineToast(e))
this.registering = false
})
},
diff --git a/frontend/src/views/auth/RegisterDone.vue b/frontend/src/views/auth/RegisterDone.vue
index 21182d0534..0fbcae25cf 100644
--- a/frontend/src/views/auth/RegisterDone.vue
+++ b/frontend/src/views/auth/RegisterDone.vue
@@ -1,17 +1,17 @@
- {{ $tc('views.auth.registerDone.title') }}
+ {{ $t('views.auth.registerDone.title') }}
- {{ $tc('views.auth.registerDone.success') }}
+ {{ $t('views.auth.registerDone.success') }}
- {{ $tc('views.auth.registerDone.message') }}
+ {{ $t('views.auth.registerDone.message') }}
-
- {{ $tc('global.button.login') }}
+
+ {{ $t('global.button.login') }}
diff --git a/frontend/src/views/auth/ResendActivation.vue b/frontend/src/views/auth/ResendActivation.vue
index 447fcd9c1c..cadf4976fa 100644
--- a/frontend/src/views/auth/ResendActivation.vue
+++ b/frontend/src/views/auth/ResendActivation.vue
@@ -1,15 +1,15 @@
-
- {{ $tc('views.auth.resendActivation.title') }}
+
+ {{ $t('views.auth.resendActivation.title') }}
- {{ $tc('views.auth.resendActivation.successMessage') }}
+ {{ $t('views.auth.resendActivation.successMessage') }}
- {{ $tc('views.auth.resendActivation.errorMessage') }}
+ {{ $t('views.auth.resendActivation.errorMessage') }}
- $vuetify.icons.ecamp
+ $ecamp
- {{ $tc('views.auth.resendActivation.send') }}
+ {{ $t('views.auth.resendActivation.send') }}
- {{ $tc('global.button.login') }}
+ {{ $t('global.button.login') }}
diff --git a/frontend/src/views/auth/ResetPassword.vue b/frontend/src/views/auth/ResetPassword.vue
index 9430a38944..8f0ad4e256 100644
--- a/frontend/src/views/auth/ResetPassword.vue
+++ b/frontend/src/views/auth/ResetPassword.vue
@@ -1,7 +1,7 @@
-
- {{ $tc('views.auth.resetPassword.title') }}
+
+ {{ $t('views.auth.resetPassword.title') }}
@@ -9,96 +9,96 @@
- {{ $tc('views.auth.resetPassword.invalidRequest') }}
+ {{ $t('views.auth.resetPassword.invalidRequest') }}
- {{ $tc('views.auth.resetPassword.successMessage') }}
+ {{ $t('views.auth.resetPassword.successMessage') }}
- {{ $tc('views.auth.resetPassword.errorMessage') }}
+ {{ $t('views.auth.resetPassword.errorMessage') }}
-
-
- -->
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
- $vuetify.icons.ecamp
-
- {{ $tc('views.auth.resetPassword.send') }}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+ $ecamp
+
+ {{ $t('views.auth.resetPassword.send') }}
+
+
+
+
+
+
- {{ $tc('global.button.login') }}
+ {{ $t('global.button.login') }}
@@ -106,7 +106,7 @@
diff --git a/frontend/src/views/dev/Controls.vue b/frontend/src/views/dev/Controls.vue
index 138b2ee516..45c2185046 100644
--- a/frontend/src/views/dev/Controls.vue
+++ b/frontend/src/views/dev/Controls.vue
@@ -32,18 +32,27 @@
-
+
+
+
+
+
+
+
FFFFFFFFFF',
- checkboxValue: false,
colorValue: null,
- selectValue: null,
- dateValue: '2020-01-01',
- timeValue: '2020-01-01T14:45:00+00:00',
- timeValue2: '00:00',
+
+ values: {
+ textfield: 'FFFFFFFFFF',
+ checkbox: false,
+ switch: false,
+ select: null,
+ numberfield: 10,
+ datepicker: '2020-01-01',
+ timepicker: '2020-01-01T14:45:00+00:00',
+ timefield: '05:00',
+ colorpicker: '#FF9800',
+ colorfield: '#229800',
+ },
headers: [
{ text: 'Type', value: 'id' },
{ text: 'v-input', value: 'v', sortable: false },
{ text: 'e-input', value: 'e', sortable: false },
+ { text: 'e-input readonly', value: 'e-ro', sortable: false },
{ text: 'api-input', value: 'api', sortable: false },
+ { text: 'api-input readonly', value: 'api-ro', sortable: false },
{ text: 'api-input.autosave', value: 'api.autosave', sortable: false },
],
}),
@@ -162,19 +189,18 @@ export default {
items() {
return [
{
- id: 'text-field',
+ id: 'textfield',
component: (type) => `${type}-text-field`,
- value: this.textfieldValue,
props: {
placeholder: this.placeholder,
path: 'nickname',
uri: this.profileUri,
+ veeRules: 'required',
},
},
{
- id: 'number-field',
+ id: 'numberfield',
component: (type) => (type === 'v' ? '' : `${type}-number-field`),
- value: this.numberfieldValue,
props: {
placeholder: this.placeholder,
inputmode: 'decimal',
@@ -182,43 +208,44 @@ export default {
uri: this.materialUri,
},
},
- {
- id: 'textarea',
- component: (type) => `${type}-textarea`,
- value: this.textareaValue,
- props: {
- placeholder: this.placeholder,
- rows: 3,
- path: 'data.html',
- uri: this.singleTextUri,
- },
- },
- {
- id: 'richtext',
- component: (type) => (type === 'v' ? 'v-tiptap-editor' : `${type}-richtext`),
- value: this.richtextValue,
- props: {
- placeholder: this.placeholder,
- rows: 3,
- path: 'data.html',
- uri: this.singleTextUri,
- },
- },
+ // {
+ // id: 'textarea',
+ // component: (type) => `${type}-textarea`,
+ // value: this.textareaValue,
+ // props: {
+ // placeholder: this.placeholder,
+ // rows: 3,
+ // path: 'data.html',
+ // uri: this.singleTextUri,
+ // },
+ // },
+ // {
+ // id: 'richtext',
+ // component: (type) => (type === 'v' ? 'v-tiptap-editor' : `${type}-richtext`),
+ // value: this.richtextValue,
+ // props: {
+ // placeholder: this.placeholder,
+ // rows: 3,
+ // path: 'data.html',
+ // uri: this.singleTextUri,
+ // },
+ // },
{
id: 'select',
component: (type) => `${type}-select`,
- value: this.selectValue,
props: {
path: 'language',
placeholder: this.placeholder,
items: this.availableLocales,
uri: this.profileUri,
+ itemTitle: 'text',
+ itemValue: 'value',
+ veeRules: 'required',
},
},
{
id: 'checkbox',
component: (type) => `${type}-checkbox`,
- value: this.checkboxValue,
props: {
path: 'printYSLogoOnPicasso',
uri: this.campUri,
@@ -227,16 +254,14 @@ export default {
{
id: 'switch',
component: (type) => `${type}-switch`,
- value: this.checkboxValue,
props: {
path: 'printYSLogoOnPicasso',
uri: this.campUri,
},
},
{
- id: 'date-picker',
+ id: 'datepicker',
component: (type) => (type === 'v' ? '' : `${type}-date-picker`),
- value: this.dateValue,
props: {
placeholder: this.placeholder,
path: 'start',
@@ -244,9 +269,8 @@ export default {
},
},
{
- id: 'time-picker',
+ id: 'timepicker',
component: (type) => (type === 'v' ? '' : `${type}-time-picker`),
- value: this.timeValue,
props: {
placeholder: this.placeholder,
'value-format': 'YYYY-MM-DDTHH:mm:ssZ',
@@ -255,9 +279,17 @@ export default {
},
},
{
- id: 'color-picker',
+ id: 'timefield',
+ component: (type) => (type === 'e' ? `${type}-time-field` : ''),
+ props: {
+ placeholder: this.placeholder,
+ path: 'start',
+ uri: this.scheduleEntryUri,
+ },
+ },
+ {
+ id: 'colorpicker',
component: (type) => (type === 'v' ? '' : `${type}-color-picker`),
- value: this.colorValue,
props: {
placeholder: this.placeholder,
path: 'color',
@@ -266,9 +298,8 @@ export default {
},
},
{
- id: 'color-field',
+ id: 'colorfield',
component: (type) => (type !== 'v' ? `${type}-color-field` : ''),
- value: this.colorValue,
props: {
placeholder: this.placeholder,
path: 'color',
@@ -276,16 +307,6 @@ export default {
veeRules: 'required',
},
},
- {
- id: 'time-field',
- component: (type) => (type === 'e' ? `${type}-time-field` : ''),
- value: this.timeValue2,
- props: {
- placeholder: this.placeholder,
- path: 'start',
- uri: this.scheduleEntryUri,
- },
- },
]
},
profileUri() {
@@ -313,9 +334,9 @@ export default {
return '/camp_collaborations/3229d273decd' // Harry Potter - Snoopy
},
availableLocales() {
- return VueI18n.availableLocales.map((l) => ({
+ return VueI18n.global.availableLocales.map((l) => ({
value: l,
- text: this.$tc('global.language', 1, l),
+ text: this.$t('global.language', 1, { locale: l }),
}))
},
config() {
diff --git a/frontend/vite.config.js b/frontend/vite.config.js
index 6220af50c8..fa746f9c3d 100644
--- a/frontend/vite.config.js
+++ b/frontend/vite.config.js
@@ -1,23 +1,40 @@
import { defineConfig } from 'vite'
-import vue from '@vitejs/plugin-vue2'
-import { createSvgPlugin } from 'vite-plugin-vue2-svg'
+import vue from '@vitejs/plugin-vue'
import { comlink } from 'vite-plugin-comlink'
import * as path from 'path'
-import { VuetifyResolver } from 'unplugin-vue-components/resolvers'
import Components from 'unplugin-vue-components/vite'
import { sentryVitePlugin } from '@sentry/vite-plugin'
import { configDefaults } from 'vitest/config'
+import svgLoader from 'vite-svg-loader'
+import Vuetify from 'vite-plugin-vuetify'
+import { readdirSync } from 'fs'
+
+const componentsPath = 'node_modules/vuetify/lib/components'
+const vuetifyComponents = readdirSync(componentsPath)
+ .filter((file) => file.startsWith('V'))
+ .map((file) => `vuetify/components/${file}`)
const plugins = [
comlink(), // must be first
- vue(),
+ vue({
+ template: {
+ compilerOptions: {
+ compatConfig: {
+ MODE: 2,
+ },
+ },
+ },
+ }),
Components({
- resolvers: [
- // Vuetify
- VuetifyResolver(),
- ],
+ resolvers: [],
+ }),
+ svgLoader(),
+ Vuetify({
+ autoImport: {
+ labs: true,
+ },
+ styles: { configFile: 'src/scss/settings.scss' },
}),
- createSvgPlugin(),
]
const sentryAuthToken = process.env.SENTRY_AUTH_TOKEN
if (sentryAuthToken) {
@@ -48,7 +65,9 @@ export default defineConfig(({ mode }) => ({
},
optimizeDeps: {
include: [
+ ...vuetifyComponents.filter((dep) => !dep.includes('VOverflowBtn')),
'@intlify/core',
+ '@leeoniya/ufuzzy',
'@react-pdf/font',
'@react-pdf/layout',
'@react-pdf/pdfkit',
@@ -105,13 +124,16 @@ export default defineConfig(({ mode }) => ({
'vue',
'vuedraggable',
'vue-toastification',
- 'vuetify/es5/components/VCalendar/modes/column.js',
- 'vuetify/es5/components/VCalendar/util/events.js',
+ // 'vuetify/es5/components/VCalendar/modes/column.js',
+ // 'vuetify/es5/components/VCalendar/util/events.js',
],
},
build: {
sourcemap: true,
minify: mode === 'development' ? false : 'esbuild',
+ rollupOptions: {
+ external: ['vuetify/lib'],
+ },
},
resolve: {
alias: [
@@ -146,16 +168,12 @@ export default defineConfig(({ mode }) => ({
css: {
preprocessorOptions: {
scss: {
- // support for legacy api will be removed in vite 7. https://vite.dev/guide/migration.html#sass-now-uses-modern-api-by-default
- api: 'legacy',
- additionalData: '@import "./node_modules/vuetify/src/styles/styles.sass";\n', // original default variables from vuetify
- silenceDeprecations: ['slash-div', 'mixed-decls'],
+ api: 'modern-compiler',
+ silenceDeprecations: ['mixed-decls'],
},
sass: {
- // support for legacy api will be removed in vite 7. https://vite.dev/guide/migration.html#sass-now-uses-modern-api-by-default
- api: 'legacy',
- additionalData: '@import "./src/scss/variables.scss"\n', // vuetify variable overrides
- silenceDeprecations: ['slash-div', 'mixed-decls'],
+ api: 'modern-compiler',
+ silenceDeprecations: ['mixed-decls'],
},
},
},
diff --git a/reverse-proxy-nginx.conf b/reverse-proxy-nginx.conf
index 114ceaed2c..544142847c 100644
--- a/reverse-proxy-nginx.conf
+++ b/reverse-proxy-nginx.conf
@@ -20,31 +20,35 @@ http {
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
-
+
upstream frontend {
server frontend:3000;
}
-
+
+ upstream frontend-old {
+ server frontend-old:3000;
+ }
+
upstream api {
server api:3001;
}
-
+
upstream http-cache {
server http-cache:8080;
}
-
+
upstream print {
server print:3003;
}
-
+
upstream mail {
server mail:1080;
}
-
+
upstream pgadmin {
server pg-admin:80;
}
-
+
server {
listen 3000;
server_name localhost;
@@ -54,7 +58,13 @@ http {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
-
+
+ location /old {
+ proxy_pass http://frontend-old;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ }
+
location /api/ {
# the Set-Cookie: XDEBUG_SESSION=PHPSTORM; path=/; SameSite=Lax header is set too many times
# temporary workaround from https://stackoverflow.com/a/27551259
@@ -67,13 +77,13 @@ http {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
-
+
location /print {
proxy_pass http://print;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
-
+
location /mail {
proxy_pass http://mail;
proxy_set_header Upgrade $http_upgrade;
@@ -96,7 +106,13 @@ http {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
-
+
+ location /old/ {
+ proxy_pass http://frontend-old;
+ proxy_set_header Upgrade $http_upgrade;
+ proxy_set_header Connection $connection_upgrade;
+ }
+
location /api/ {
# the Set-Cookie: XDEBUG_SESSION=PHPSTORM; path=/; SameSite=Lax header is set too many times
# temporary workaround from https://stackoverflow.com/a/27551259
@@ -109,13 +125,13 @@ http {
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
-
+
location /print {
proxy_pass http://print;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
}
-
+
location /mail {
proxy_pass http://mail;
proxy_set_header Upgrade $http_upgrade;
|