From 1e0d840b41f93ab696c9c7d00a04301371757a0b Mon Sep 17 00:00:00 2001 From: Alexis <35.alexis.pele@gmail.com> Date: Tue, 15 Apr 2025 20:32:03 +0200 Subject: [PATCH] Added category deletion interface --- components/calendar/category/Table.vue | 34 ++++++++ components/calendar/category/TableRow.vue | 29 ++++++- components/calendar/dialog/Categories.vue | 5 +- components/calendar/form/Categories.vue | 7 -- components/calendar/form/DeleteCategory.vue | 85 +++++++++++++++++++ i18n.config.ts | 8 ++ .../api/calendars/categories/[id].delete.ts | 43 ++++++++++ stores/CategoryStore.ts | 33 ++++++- supabase/migrations/202401_init.sql | 18 +++- 9 files changed, 245 insertions(+), 17 deletions(-) delete mode 100644 components/calendar/form/Categories.vue create mode 100644 components/calendar/form/DeleteCategory.vue create mode 100644 server/api/calendars/categories/[id].delete.ts diff --git a/components/calendar/category/Table.vue b/components/calendar/category/Table.vue index c90a9c8..c77ecd1 100644 --- a/components/calendar/category/Table.vue +++ b/components/calendar/category/Table.vue @@ -2,11 +2,23 @@ import type { Category } from "~/models/Category"; import { ScrollAreaRoot, ScrollAreaViewport, ScrollAreaScrollbar, ScrollAreaThumb } from "radix-vue" +const { t } = useI18n() + const { categories } = defineProps<{ categories: Category[] }>() const sortedCategories = computed(() => categories.toSorted((a, b) => a.name.localeCompare(b.name))) + +const deleteDialogOpened = ref(false) + +function openDeleteDialog() { + deleteDialogOpened.value = true +} + +function closeDeleteDialog() { + deleteDialogOpened.value = false +} diff --git a/components/calendar/category/TableRow.vue b/components/calendar/category/TableRow.vue index 4690e3e..7d6f3ad 100644 --- a/components/calendar/category/TableRow.vue +++ b/components/calendar/category/TableRow.vue @@ -1,5 +1,5 @@ diff --git a/components/calendar/dialog/Categories.vue b/components/calendar/dialog/Categories.vue index 5cf96d1..e001d48 100644 --- a/components/calendar/dialog/Categories.vue +++ b/components/calendar/dialog/Categories.vue @@ -1,9 +1,8 @@ - - diff --git a/components/calendar/form/DeleteCategory.vue b/components/calendar/form/DeleteCategory.vue new file mode 100644 index 0000000..5d3f406 --- /dev/null +++ b/components/calendar/form/DeleteCategory.vue @@ -0,0 +1,85 @@ + + + diff --git a/i18n.config.ts b/i18n.config.ts index dbbf6c7..40670e6 100644 --- a/i18n.config.ts +++ b/i18n.config.ts @@ -98,6 +98,10 @@ export default defineI18nConfig(() => ({ title: "Manage categories", subtitle: "Add and change the categories of your calendar", }, + deleteDialog: { + title: "Delete this category ?", + subtitle: "The events attached to this category won't be deleted, but you'll lose the category for this calendar.", + }, addedToast: { title: "The category \"{category}\" has been added to the calendar.", titleError: "An error has occured and the category \"{category}\" wasn't added to the calendar.", @@ -413,6 +417,10 @@ export default defineI18nConfig(() => ({ title: "Gestion des catégories", subtitle: "Créer et modifier les catégories de votre calendrier", }, + deleteDialog: { + title: "Supprimer cette catégorie ?", + subtitle: "Les évènements l'utilisant ne seront pas supprimés.", + }, addedToast: { title: "La catégorie \"{category}\" a été ajoutée au calendrier.", titleError: "Une erreur s'est produite et la catégorie \"{category}\" n'a pas pu être ajoutée.", diff --git a/server/api/calendars/categories/[id].delete.ts b/server/api/calendars/categories/[id].delete.ts new file mode 100644 index 0000000..fdafa6b --- /dev/null +++ b/server/api/calendars/categories/[id].delete.ts @@ -0,0 +1,43 @@ +import { z } from "zod" +import { serverSupabaseClient } from "#supabase/server" +import type { Category } from "~/models/Category" + +const paramsSchema = z.object({ + id: z.number({ coerce: true }).positive().int() +}) + +export default defineEventHandler(async (event) => { + const client = await serverSupabaseClient(event) + + const { data: params, error: paramsError} = await getValidatedRouterParams(event, paramsSchema.safeParse) + + if (paramsError) { + throw createError({ + cause: "Utilisateur", + fatal: false, + message: "L'identifiant de l'évènement est manquant ou mal renseigné.", + status: 401, + }) + } + + try { + const { data, error } = await client + .from("calendar_event_categories") + .delete() + .eq("id", params.id) + .maybeSingle() + + console.log(error) + + if (error) throw error + + return data + } catch (err) { + throw createError({ + cause: "Serveur", + status: 500, + fatal: false, + message: "Une erreur inconnue est survenue." + }) + } +}) diff --git a/stores/CategoryStore.ts b/stores/CategoryStore.ts index e357294..c278006 100644 --- a/stores/CategoryStore.ts +++ b/stores/CategoryStore.ts @@ -73,6 +73,35 @@ export const useCategoryStore = defineStore("calendar-category", () => { resetSkeleton() } + async function deleteCategoryFromSkeleton() { + if (!categorySkeleton.value) return + + abortController = new AbortController() + isDeletingCategory.value = true + + const { error } = await tryCatch( + $fetch(`/api/calendars/categories/${categorySkeleton.value.id}`, { method: "DELETE", signal: abortController.signal }) + ) + + if (error) { + isDeletingCategory.value = false + throw error + } + + const categoryIndex = categories.value.findIndex(c => c.id === categorySkeleton.value!.id) + categories.value.splice(categoryIndex, 1) + + abortController = null + isDeletingCategory.value = false + resetSkeleton() + } + + function cancelLatestRequest() { + if (abortController) { + abortController.abort() + } + } + return { isCreatingCategory, isUpdatingCategory, @@ -81,6 +110,8 @@ export const useCategoryStore = defineStore("calendar-category", () => { categorySkeleton, resetSkeleton, addCategoryFromSkeleton, - updateCategoryFromSkeleton + updateCategoryFromSkeleton, + deleteCategoryFromSkeleton, + cancelLatestRequest } }) diff --git a/supabase/migrations/202401_init.sql b/supabase/migrations/202401_init.sql index bc58698..d2ce672 100644 --- a/supabase/migrations/202401_init.sql +++ b/supabase/migrations/202401_init.sql @@ -106,7 +106,7 @@ create table public.calendar_events ( location text, start_date json not null, end_date json, - category bigint references public.calendar_event_categories on delete cascade, + category bigint references public.calendar_event_categories on delete set null, hidden boolean default false, wiki text, created_at timestamptz default now(), @@ -150,7 +150,7 @@ create table public.characters ( description text, birth json not null, death json, - category bigint references public.character_categories on delete cascade, + category bigint references public.character_categories on delete set null, hidden_birth boolean, hidden_death boolean, wiki text, @@ -468,6 +468,20 @@ create policy "Allow GMs to update their events categories" ) ); +create policy "Allow GMs to delete their events categories" + on public.calendar_event_categories + for delete + using ( + exists ( + select 1 + from public.calendars c + join public.worlds w on w.id = c.world_id + where + c.id = calendar_event_categories.calendar_id + and w.gm_id = auth.uid() + ) +); + -- Send "previous data" on change alter table public.users replica identity full;