Merge pull request #45 from AlexisNP/features/add-limit-to-event-text-fields
Add limit to event text fields
This commit is contained in:
@@ -75,7 +75,7 @@ function deployDeleteModal() {
|
|||||||
>
|
>
|
||||||
<div ref="eventDetails" class="grid gap-1">
|
<div ref="eventDetails" class="grid gap-1">
|
||||||
<header class="pr-12">
|
<header class="pr-12">
|
||||||
<div class="text-lg font-semibold">
|
<div class="mb-2 max-h-24 overflow-y-auto text-lg font-semibold">
|
||||||
{{ event.title }}
|
{{ event.title }}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -100,18 +100,26 @@ function deployDeleteModal() {
|
|||||||
|
|
||||||
<div class="mb-1 space-y-1">
|
<div class="mb-1 space-y-1">
|
||||||
<template v-if="event.location">
|
<template v-if="event.location">
|
||||||
<p class="text-sm italic dark:opacity-75 flex items-center gap-1">
|
<div class="text-sm italic dark:opacity-75 flex items-center gap-1">
|
||||||
<PhMapPinArea size="16" weight="fill" /> {{ event.location }}
|
<div>
|
||||||
</p>
|
<PhMapPinArea size="16" weight="fill" />
|
||||||
|
</div>
|
||||||
|
<p class="max-h-12 overflow-y-auto">{{ event.location }}</p>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<p class="text-sm italic dark:opacity-75 flex items-center gap-1">
|
<div class="text-sm italic dark:opacity-75 flex items-center gap-1">
|
||||||
<PhAlarm size="16" weight="fill" /> {{ dateDifference }}
|
<div>
|
||||||
</p>
|
<PhAlarm size="16" weight="fill" />
|
||||||
|
</div>
|
||||||
|
<p>{{ dateDifference }}</p>
|
||||||
|
</div>
|
||||||
<template v-if="dateDuration">
|
<template v-if="dateDuration">
|
||||||
<p class="text-sm italic dark:opacity-75 flex items-center gap-1">
|
<div class="text-sm italic dark:opacity-75 flex items-center gap-1">
|
||||||
|
<div>
|
||||||
<PhHourglassMedium size="16" weight="fill" />
|
<PhHourglassMedium size="16" weight="fill" />
|
||||||
{{ $t('entity.calendar.date.while', { duration: dateDuration } )}}
|
</div>
|
||||||
</p>
|
<p>{{ $t('entity.calendar.date.while', { duration: dateDuration } )}}</p>
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -134,7 +142,7 @@ function deployDeleteModal() {
|
|||||||
<template v-if="event.description">
|
<template v-if="event.description">
|
||||||
<hr class="border-slate-500 mt-2" >
|
<hr class="border-slate-500 mt-2" >
|
||||||
|
|
||||||
<div class="mt-2 text-sm text-slate-600 dark:text-slate-300">
|
<div class="max-h-48 overflow-y-auto mt-2 text-sm text-slate-600 dark:text-slate-300">
|
||||||
{{ event.description }}
|
{{ event.description }}
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -5,6 +5,9 @@ import { PhAlarm, PhCircleNotch, PhEye, PhEyeClosed, PhMapPinArea, PhTag } from
|
|||||||
const { eventSkeleton, operationInProgress } = storeToRefs(useCalendar())
|
const { eventSkeleton, operationInProgress } = storeToRefs(useCalendar())
|
||||||
const { resetSkeleton, submitSkeleton, cancelLatestRequest } = useCalendar()
|
const { resetSkeleton, submitSkeleton, cancelLatestRequest } = useCalendar()
|
||||||
const popoverOpen = ref(false)
|
const popoverOpen = ref(false)
|
||||||
|
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
|
||||||
const formErrors = reactive<{ message: string | null }>({
|
const formErrors = reactive<{ message: string | null }>({
|
||||||
@@ -88,7 +91,7 @@ function handleCancel() {
|
|||||||
:collision-padding="60"
|
:collision-padding="60"
|
||||||
:disable-outside-pointer-events="true"
|
:disable-outside-pointer-events="true"
|
||||||
:trap-focus="true"
|
:trap-focus="true"
|
||||||
class="pl-3 min-w-96 border-indigo-200 dark:bg-slate-950 dark:border-indigo-950"
|
class="pl-3 w-[30rem] max-w-full border-indigo-200 dark:bg-slate-950 dark:border-indigo-950"
|
||||||
@escape-key-down="handleClosing"
|
@escape-key-down="handleClosing"
|
||||||
@focus-outside="handleClosing"
|
@focus-outside="handleClosing"
|
||||||
@interact-outside="handleClosing"
|
@interact-outside="handleClosing"
|
||||||
@@ -104,8 +107,14 @@ function handleCancel() {
|
|||||||
name="new-event-title"
|
name="new-event-title"
|
||||||
required
|
required
|
||||||
:placeholder="$t('entity.calendar.event.title')"
|
:placeholder="$t('entity.calendar.event.title')"
|
||||||
class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
|
:maxlength="120"
|
||||||
|
pattern="([A-Za-zÀ-ÖØ-öø-ÿ0-9\s\&\-\~]+){3,120}"
|
||||||
|
class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600 invalid:border-red-500"
|
||||||
>
|
>
|
||||||
|
|
||||||
|
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||||
|
{{ t('entity.calendar.event.patterns.title') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-2 my-2 pl-8">
|
<div class="col-span-2 my-2 pl-8">
|
||||||
@@ -113,8 +122,13 @@ function handleCancel() {
|
|||||||
id="new-event-description"
|
id="new-event-description"
|
||||||
v-model="eventSkeleton.description"
|
v-model="eventSkeleton.description"
|
||||||
name="new-event-description"
|
name="new-event-description"
|
||||||
:placeholder="$t('entity.addDescription')" class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
|
:placeholder="$t('entity.addDescription')"
|
||||||
|
:maxlength="1200"
|
||||||
|
class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600 invalid:border-red-500"
|
||||||
/>
|
/>
|
||||||
|
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||||
|
{{ t('entity.calendar.event.patterns.description') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
@@ -142,7 +156,10 @@ function handleCancel() {
|
|||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<PhTag size="18" weight="fill" />
|
<PhTag size="18" weight="fill" />
|
||||||
|
|
||||||
<CalendarInputEventCategory v-model="eventSkeleton.category" :placeholder="$t('entity.category.addPrimary')" />
|
<CalendarInputEventCategory
|
||||||
|
v-model="eventSkeleton.category"
|
||||||
|
:placeholder="$t('entity.category.addPrimary')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -158,13 +175,21 @@ function handleCancel() {
|
|||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<PhMapPinArea size="18" weight="fill" />
|
<PhMapPinArea size="18" weight="fill" />
|
||||||
|
|
||||||
|
<div class="grow">
|
||||||
<input
|
<input
|
||||||
id="new-event-location"
|
id="new-event-location"
|
||||||
v-model="eventSkeleton.location"
|
v-model="eventSkeleton.location"
|
||||||
type="text"
|
type="text"
|
||||||
name="new-event-location"
|
name="new-event-location"
|
||||||
:placeholder="$t('entity.calendar.event.addLocation')"
|
:placeholder="$t('entity.calendar.event.addLocation')"
|
||||||
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600">
|
:maxlength="160"
|
||||||
|
pattern="([A-Za-zÀ-ÖØ-öø-ÿ0-9\s\&\-\~]+){3,160}"
|
||||||
|
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
|
||||||
|
>
|
||||||
|
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||||
|
{{ t('entity.calendar.event.patterns.location') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,14 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { PhAlarm, PhCircleNotch, PhEye, PhEyeClosed, PhMapPinArea, PhPencilSimpleLine, PhTag } from "@phosphor-icons/vue"
|
import { PhAlarm, PhCircleNotch, PhEye, PhEyeClosed, PhMapPinArea, PhPencilSimpleLine, PhTag } from "@phosphor-icons/vue"
|
||||||
|
import { useToast } from "~/components/ui/toast";
|
||||||
|
import type { APIError } from "~/models/Errors";
|
||||||
|
|
||||||
const { resetSkeleton, updateEventFromSkeleton, cancelLatestRequest } = useCalendar()
|
const { resetSkeleton, updateEventFromSkeleton, cancelLatestRequest } = useCalendar()
|
||||||
const { eventSkeleton, lastActiveEvent, isEditEventModalOpen } = storeToRefs(useCalendar())
|
const { eventSkeleton, lastActiveEvent, isEditEventModalOpen } = storeToRefs(useCalendar())
|
||||||
|
|
||||||
|
const { toast } = useToast()
|
||||||
|
const { t } = useI18n()
|
||||||
|
|
||||||
const isLoading = ref(false)
|
const isLoading = ref(false)
|
||||||
|
|
||||||
const formErrors = reactive<{ message: string | null }>({
|
const formErrors = reactive<{ message: string | null }>({
|
||||||
@@ -24,15 +29,22 @@ async function handleAction() {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await updateEventFromSkeleton()
|
await updateEventFromSkeleton()
|
||||||
|
|
||||||
isEditEventModalOpen.value = false
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
if (err instanceof Error) {
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
formErrors.message = err.message
|
const apiError = (err as any).data as APIError
|
||||||
}
|
|
||||||
|
apiError.data.errors.forEach((error) => {
|
||||||
|
toast({
|
||||||
|
title: t("entity.calendar.event.editErrors.toastTitle"),
|
||||||
|
variant: "destructive",
|
||||||
|
description: t(`entity.calendar.event.editErrors.${error.path[1]}_${error.code}`),
|
||||||
|
duration: 2000,
|
||||||
|
})
|
||||||
|
})
|
||||||
} finally {
|
} finally {
|
||||||
resetSkeleton()
|
resetSkeleton()
|
||||||
isLoading.value = false
|
isLoading.value = false
|
||||||
|
isEditEventModalOpen.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,6 +97,7 @@ function handleCancel() {
|
|||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<PhPencilSimpleLine size="20" weight="fill" />
|
<PhPencilSimpleLine size="20" weight="fill" />
|
||||||
|
|
||||||
|
<div class="grow">
|
||||||
<input
|
<input
|
||||||
id="new-event-title"
|
id="new-event-title"
|
||||||
v-model="eventSkeleton.title"
|
v-model="eventSkeleton.title"
|
||||||
@@ -92,8 +105,14 @@ function handleCancel() {
|
|||||||
name="new-event-title"
|
name="new-event-title"
|
||||||
required
|
required
|
||||||
:placeholder="$t('entity.calendar.event.title')"
|
:placeholder="$t('entity.calendar.event.title')"
|
||||||
class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
|
:maxlength="120"
|
||||||
|
pattern="([A-Za-zÀ-ÖØ-öø-ÿ0-9\s\&\-\~]+){3,120}"
|
||||||
|
class="w-full -my-1 py-1 -mx-1 px-1 text-lg border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600 invalid:border-red-500"
|
||||||
>
|
>
|
||||||
|
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||||
|
{{ t('entity.calendar.event.patterns.title') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -103,8 +122,12 @@ function handleCancel() {
|
|||||||
v-model="eventSkeleton.description"
|
v-model="eventSkeleton.description"
|
||||||
name="new-event-description"
|
name="new-event-description"
|
||||||
:placeholder="$t('entity.addDescription')"
|
:placeholder="$t('entity.addDescription')"
|
||||||
class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600"
|
:maxlength="1200"
|
||||||
|
class="w-full -my-1 py-1 -mx-1 px-1 min-h-24 max-h-36 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600 invalid:border-red-500"
|
||||||
/>
|
/>
|
||||||
|
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||||
|
{{ t('entity.calendar.event.patterns.description') }}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-span-2">
|
<div class="col-span-2">
|
||||||
@@ -133,7 +156,10 @@ function handleCancel() {
|
|||||||
<PhTag size="18" weight="fill" />
|
<PhTag size="18" weight="fill" />
|
||||||
|
|
||||||
<div class="w-1/2">
|
<div class="w-1/2">
|
||||||
<CalendarInputEventCategory v-model="eventSkeleton.category" placeholder="Ajouter une catégorie principale" />
|
<CalendarInputEventCategory
|
||||||
|
v-model="eventSkeleton.category"
|
||||||
|
:placeholder="$t('entity.category.addPrimary')"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -143,7 +169,7 @@ function handleCancel() {
|
|||||||
<PhTag size="18" weight="fill" />
|
<PhTag size="18" weight="fill" />
|
||||||
|
|
||||||
<div class="w-1/2">
|
<div class="w-1/2">
|
||||||
<CalendarInputEventCategories v-model="eventSkeleton.secondaryCategories" :placeholder="Ajouter des catégories secondaires" />
|
<CalendarInputEventCategories v-model="eventSkeleton.secondaryCategories" placeholder="Ajouter des catégories secondaires" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
@@ -152,13 +178,22 @@ function handleCancel() {
|
|||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center gap-4">
|
||||||
<PhMapPinArea size="18" weight="fill" />
|
<PhMapPinArea size="18" weight="fill" />
|
||||||
|
|
||||||
|
<div class="grow">
|
||||||
<input
|
<input
|
||||||
id="new-event-location"
|
id="new-event-location"
|
||||||
v-model="eventSkeleton.location"
|
v-model="eventSkeleton.location"
|
||||||
type="text"
|
type="text"
|
||||||
name="new-event-location"
|
name="new-event-location"
|
||||||
:placeholder="$t('entity.calendar.event.addLocation')"
|
:placeholder="$t('entity.calendar.event.addLocation')"
|
||||||
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600">
|
:maxlength="160"
|
||||||
|
pattern="([A-Za-zÀ-ÖØ-öø-ÿ0-9\s\&\-\~]+){3,160}"
|
||||||
|
class="w-full -my-1 py-2 px-2 text-sm border-b-[1px] bg-transparent focus-visible:outline-none focus-visible:border-blue-600 invalid:border-red-500"
|
||||||
|
>
|
||||||
|
|
||||||
|
<div class="mt-2 mb-1 text-xs opacity-50">
|
||||||
|
{{ t('entity.calendar.event.patterns.location') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -13,5 +13,5 @@ const delegatedProps = computed(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ToastViewport v-bind="delegatedProps" :class="cn('fixed top-0 z-[100] flex max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]', props.class)" />
|
<ToastViewport v-bind="delegatedProps" :class="cn('fixed top-0 z-[100] flex gap-y-2 max-h-screen w-full flex-col-reverse p-4 sm:bottom-0 sm:right-0 sm:top-auto sm:flex-col md:max-w-[420px]', props.class)" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -136,10 +136,21 @@ export default defineI18nConfig(() => ({
|
|||||||
addLocation: "Add a place",
|
addLocation: "Add a place",
|
||||||
prevPage: "Previous page with events",
|
prevPage: "Previous page with events",
|
||||||
nextPage: "Next page with events",
|
nextPage: "Next page with events",
|
||||||
|
patterns: {
|
||||||
|
title: "Between 3 and 120 caracters, without special symbols, except & - ~ ()",
|
||||||
|
description: "1200 caracters max.",
|
||||||
|
location: "Between 3 and 160 caracters, without special symbols, except & - ~ ()",
|
||||||
|
},
|
||||||
editDialog: {
|
editDialog: {
|
||||||
title: "Edit event",
|
title: "Edit event",
|
||||||
subtitle: "Update event data",
|
subtitle: "Update event data",
|
||||||
},
|
},
|
||||||
|
editErrors: {
|
||||||
|
toastTitle: "Event wasn't updated.",
|
||||||
|
title_too_big: "Title should be less than 120 characters long.",
|
||||||
|
description_too_big: "Description should be less than 1200 characters long.",
|
||||||
|
location_too_big: "Location should be less than 160 characters long.",
|
||||||
|
},
|
||||||
deleteDialog: {
|
deleteDialog: {
|
||||||
title: "Delete this event",
|
title: "Delete this event",
|
||||||
subtitle: "Data associated with this event will be lost and you won't be able to retrieve it !",
|
subtitle: "Data associated with this event will be lost and you won't be able to retrieve it !",
|
||||||
@@ -344,10 +355,21 @@ export default defineI18nConfig(() => ({
|
|||||||
addLocation: "Ajouter un endroit",
|
addLocation: "Ajouter un endroit",
|
||||||
prevPage: "Précédente page à évènements",
|
prevPage: "Précédente page à évènements",
|
||||||
nextPage: "Prochaine page à évènements",
|
nextPage: "Prochaine page à évènements",
|
||||||
|
patterns: {
|
||||||
|
title: "Entre 3 et 120 charactères, sans symboles spéciaux, sauf & - ~ ()",
|
||||||
|
description: "Maximum 1200 charactères.",
|
||||||
|
location: "Entre 3 et 160 charactères, sans symboles spéciaux, sauf & - ~ ()",
|
||||||
|
},
|
||||||
editDialog: {
|
editDialog: {
|
||||||
title: "Modifier l'évènement",
|
title: "Modifier l'évènement",
|
||||||
subtitle: "Mettre à jour les données de l'évènement",
|
subtitle: "Mettre à jour les données de l'évènement",
|
||||||
},
|
},
|
||||||
|
editErrors: {
|
||||||
|
toastTitle: "L'évènement n'a pas été modifié",
|
||||||
|
title_too_big: "Le titre doit être inférieur à 120 caractères.",
|
||||||
|
description_too_big: "La description doit être inférieure à 1200 caractères.",
|
||||||
|
location_too_big: "La localisation doit être inférieure à 160 caractères.",
|
||||||
|
},
|
||||||
deleteDialog: {
|
deleteDialog: {
|
||||||
title: "Supprimer l'évènement",
|
title: "Supprimer l'évènement",
|
||||||
subtitle: "Les données associés à cet évènement seront supprimées et vous ne pourrez plus les récupérer !",
|
subtitle: "Les données associés à cet évènement seront supprimées et vous ne pourrez plus les récupérer !",
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ export interface CalendarEvent {
|
|||||||
*/
|
*/
|
||||||
export const postEventBodySchema = z.object({
|
export const postEventBodySchema = z.object({
|
||||||
event: z.object({
|
event: z.object({
|
||||||
title: z.string(),
|
title: z.string().max(120),
|
||||||
description: z.string().optional().nullable(),
|
description: z.string().max(1200).optional().nullable(),
|
||||||
location: z.string().optional().nullable(),
|
location: z.string().max(160).optional().nullable(),
|
||||||
startDate: dateSchema.required(),
|
startDate: dateSchema.required(),
|
||||||
endDate: dateSchema.optional().nullable(),
|
endDate: dateSchema.optional().nullable(),
|
||||||
hidden: z.boolean().optional().nullable(),
|
hidden: z.boolean().optional().nullable(),
|
||||||
|
|||||||
14
models/Errors.ts
Normal file
14
models/Errors.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
export interface ValidationError {
|
||||||
|
path: (string | number)[]
|
||||||
|
message: string
|
||||||
|
code: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface APIError {
|
||||||
|
statusCode: number
|
||||||
|
statusMessage: string
|
||||||
|
message: string
|
||||||
|
data: {
|
||||||
|
errors: ValidationError[]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// https://nuxt.com/docs/api/configuration/nuxt-config
|
// https://nuxt.com/docs/api/configuration/nuxt-config
|
||||||
export default defineNuxtConfig({
|
export default defineNuxtConfig({
|
||||||
devtools: { enabled: true },
|
devtools: { enabled: false },
|
||||||
|
|
||||||
modules: [
|
modules: [
|
||||||
"@nuxtjs/supabase",
|
"@nuxtjs/supabase",
|
||||||
@@ -16,12 +16,21 @@ export default defineNuxtConfig({
|
|||||||
|
|
||||||
i18n: {
|
i18n: {
|
||||||
vueI18n: "./i18n.config.ts",
|
vueI18n: "./i18n.config.ts",
|
||||||
|
strategy: "no_prefix",
|
||||||
detectBrowserLanguage: {
|
detectBrowserLanguage: {
|
||||||
useCookie: true,
|
useCookie: true,
|
||||||
cookieKey: "tttools_lang",
|
cookieKey: "tttools_lang",
|
||||||
redirectOn: "all",
|
},
|
||||||
alwaysRedirect: true,
|
locales: [
|
||||||
|
{
|
||||||
|
code: "en",
|
||||||
|
name: "English"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
code: "fr",
|
||||||
|
name: "Français"
|
||||||
}
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
|
|
||||||
supabase: {
|
supabase: {
|
||||||
|
|||||||
@@ -22,12 +22,22 @@ export default defineEventHandler(async (event) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (bodyError) {
|
if (bodyError) {
|
||||||
throw createError({
|
const error = createError({
|
||||||
cause: "Utilisateur",
|
cause: "Utilisateur",
|
||||||
fatal: false,
|
fatal: false,
|
||||||
message: "Le schéma de la requête n'est pas complet ou mal renseigné.",
|
statusCode: 401,
|
||||||
status: 401,
|
statusMessage: "Validation Error",
|
||||||
|
message: "Erreur de validation du schéma, probablement dûe à une erreur utilisateur.",
|
||||||
|
data: {
|
||||||
|
errors: bodyError.issues.map(issue => ({
|
||||||
|
path: issue.path,
|
||||||
|
message: issue.message,
|
||||||
|
code: issue.code
|
||||||
|
}))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -7,12 +7,22 @@ export default defineEventHandler(async (event) => {
|
|||||||
const { data: bodyData, error: schemaError } = await readValidatedBody(event, body => postEventBodySchema.safeParse(body))
|
const { data: bodyData, error: schemaError } = await readValidatedBody(event, body => postEventBodySchema.safeParse(body))
|
||||||
|
|
||||||
if (schemaError) {
|
if (schemaError) {
|
||||||
throw createError({
|
const error = createError({
|
||||||
cause: "Utilisateur",
|
cause: "Utilisateur",
|
||||||
fatal: false,
|
fatal: false,
|
||||||
message: "Le schéma de la requête n'est pas complet ou mal renseigné.",
|
statusCode: 401,
|
||||||
status: 401,
|
statusMessage: "Validation Error",
|
||||||
|
message: "Erreur de validation du schéma, probablement dûe à une erreur utilisateur.",
|
||||||
|
data: {
|
||||||
|
errors: schemaError.issues.map(issue => ({
|
||||||
|
path: issue.path,
|
||||||
|
message: issue.message,
|
||||||
|
code: issue.code
|
||||||
|
}))
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
throw error
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|||||||
@@ -869,8 +869,8 @@ export const useCalendar = defineStore("calendar", () => {
|
|||||||
|
|
||||||
const eventIndex = baseEvents.value.findIndex(e => e.id === eventSkeleton.value.id)
|
const eventIndex = baseEvents.value.findIndex(e => e.id === eventSkeleton.value.id)
|
||||||
baseEvents.value[eventIndex] = res
|
baseEvents.value[eventIndex] = res
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
return res
|
||||||
} finally {
|
} finally {
|
||||||
abortController = null
|
abortController = null
|
||||||
isUpdatingEvent.value = false
|
isUpdatingEvent.value = false
|
||||||
|
|||||||
@@ -247,18 +247,14 @@ export const useCalendarEvents = defineStore("calendar-events", () => {
|
|||||||
abortController = new AbortController()
|
abortController = new AbortController()
|
||||||
isUpdatingEvent.value = true
|
isUpdatingEvent.value = true
|
||||||
|
|
||||||
try {
|
|
||||||
const res = await $fetch(`/api/calendars/events/${eventSkeleton.value.id}`, { method: "PATCH", body: { event : eventSkeleton.value, calendarId: activeCalendar?.id }, signal: abortController.signal })
|
const res = await $fetch(`/api/calendars/events/${eventSkeleton.value.id}`, { method: "PATCH", body: { event : eventSkeleton.value, calendarId: activeCalendar?.id }, signal: abortController.signal })
|
||||||
|
|
||||||
const eventIndex = baseEvents.value.findIndex(e => e.id === eventSkeleton.value.id)
|
const eventIndex = baseEvents.value.findIndex(e => e.id === eventSkeleton.value.id)
|
||||||
baseEvents.value[eventIndex] = res
|
baseEvents.value[eventIndex] = res
|
||||||
} catch (err) {
|
|
||||||
console.log(err)
|
|
||||||
} finally {
|
|
||||||
abortController = null
|
abortController = null
|
||||||
isUpdatingEvent.value = false
|
isUpdatingEvent.value = false
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
async function deleteEventFromSkeleton() {
|
async function deleteEventFromSkeleton() {
|
||||||
abortController = new AbortController()
|
abortController = new AbortController()
|
||||||
|
|||||||
@@ -88,6 +88,15 @@ create table public.calendar_events (
|
|||||||
);
|
);
|
||||||
comment on table public.calendar_events is 'Events linked to a world';
|
comment on table public.calendar_events is 'Events linked to a world';
|
||||||
|
|
||||||
|
-- Calendar Events restrictions
|
||||||
|
alter table public.calendar_events
|
||||||
|
add constraint calendar_events_maxlen_check
|
||||||
|
check (
|
||||||
|
char_length(title) <= 120 AND
|
||||||
|
(description IS NULL OR char_length(description) <= 1200) AND
|
||||||
|
(location IS NULL OR char_length(location) <= 160)
|
||||||
|
);
|
||||||
|
|
||||||
-- Link table for events - categories
|
-- Link table for events - categories
|
||||||
create table public.calendar_event_categories_links (
|
create table public.calendar_event_categories_links (
|
||||||
calendar_event_id bigint references public.calendar_events on delete cascade,
|
calendar_event_id bigint references public.calendar_events on delete cascade,
|
||||||
|
|||||||
Reference in New Issue
Block a user