From f3fd97e3cd33158a80d4e4937ee6b37dc7121386 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Fri, 14 Mar 2025 22:25:50 +0100
Subject: [PATCH 01/24] Changed categories to be linked to calendars and worlds
---
supabase/migrations/202401_init.sql | 107 ++++++++++++++++------------
supabase/seed.sql | 102 +++++++++++++-------------
2 files changed, 111 insertions(+), 98 deletions(-)
diff --git a/supabase/migrations/202401_init.sql b/supabase/migrations/202401_init.sql
index 9cdfde2..e4a11c0 100644
--- a/supabase/migrations/202401_init.sql
+++ b/supabase/migrations/202401_init.sql
@@ -14,40 +14,40 @@ create type public.calendar_state as enum ('published', 'draft', 'archived');
-- DATA STRUCTURES
-- Users
create table public.users (
- id uuid references auth.users not null primary key, -- UUID from auth.users
- username text
+ id uuid references auth.users not null primary key, -- UUID from auth.users
+ username text
);
comment on table public.users is 'Profile data for each user.';
comment on column public.users.id is 'References the internal Supabase Auth user.';
-- Roles
create table public.user_roles (
- id bigint generated by default as identity primary key,
- role app_role not null,
- user_id uuid references public.users on delete cascade not null,
+ id bigint generated by default as identity primary key,
+ role app_role not null,
+ user_id uuid references public.users on delete cascade not null,
unique (user_id, role)
);
comment on table public.user_roles is 'Application roles for each user.';
-- Permissions
create table public.role_permissions (
- id bigint generated by default as identity primary key,
- role app_role not null,
- permission app_permission not null,
+ id bigint generated by default as identity primary key,
+ role app_role not null,
+ permission app_permission not null,
unique (role, permission)
);
comment on table public.role_permissions is 'Application permissions for each role.';
-- Worlds
create table public.worlds (
- id bigint generated by default as identity primary key,
- name text not null,
- description text,
- color app_colors default 'black',
- state world_state default 'draft',
- created_at timestamptz default now(),
- updated_at timestamptz,
- gm_id uuid references public.users on delete cascade
+ id bigint generated by default as identity primary key,
+ name text not null,
+ description text,
+ color app_colors default 'black',
+ state world_state default 'draft',
+ created_at timestamptz default now(),
+ updated_at timestamptz,
+ gm_id uuid references public.users on delete cascade
);
comment on table public.worlds is 'Worlds belonging to a single user ; a game master.';
create trigger handle_updated_at before update on public.worlds
@@ -55,9 +55,9 @@ create trigger handle_updated_at before update on public.worlds
-- World Players (join table)
create table public.world_players (
- world_id bigint references public.worlds on delete cascade,
- player_id uuid references public.users on delete cascade,
- joined_at timestamptz default now(),
+ world_id bigint references public.worlds on delete cascade,
+ player_id uuid references public.users on delete cascade,
+ joined_at timestamptz default now(),
primary key (world_id, player_id)
);
comment on table public.world_players is 'Players (users) that belong to specific worlds';
@@ -91,27 +91,27 @@ comment on table public.calendar_months is 'A calendar month.';
-- Calendar Event categories
create table public.calendar_event_categories (
- id bigint generated by default as identity primary key,
- name text not null,
- color app_colors default 'black',
- unique (name)
+ id bigint generated by default as identity primary key,
+ name text not null,
+ color app_colors default 'black',
+ calendar_id bigint references public.calendars on delete cascade not null
);
comment on table public.calendar_event_categories is 'Categories describing events.';
-- Calendar Events
create table public.calendar_events (
- id bigint generated by default as identity primary key,
- title text not null,
- description text,
- location text,
- start_date json not null,
- end_date json,
- category bigint references public.calendar_event_categories on delete cascade,
- hidden boolean default false,
- wiki text,
- created_at timestamptz default now(),
- updated_at timestamptz,
- calendar_id bigint references public.calendars on delete cascade not null
+ id bigint generated by default as identity primary key,
+ title text not null,
+ description text,
+ location text,
+ start_date json not null,
+ end_date json,
+ category bigint references public.calendar_event_categories on delete cascade,
+ hidden boolean default false,
+ wiki text,
+ created_at timestamptz default now(),
+ updated_at timestamptz,
+ calendar_id bigint references public.calendars on delete cascade not null
);
comment on table public.calendar_events is 'Events linked to a world';
@@ -134,12 +134,12 @@ comment on table public.calendar_event_categories_links is 'Link tables for mult
-- Character categories
create table public.character_categories (
- id bigint generated by default as identity primary key,
- name text not null,
- color app_colors default 'black',
- created_at timestamptz default now(),
- updated_at timestamptz,
- unique (name)
+ id bigint generated by default as identity primary key,
+ name text not null,
+ color app_colors default 'black',
+ created_at timestamptz default now(),
+ updated_at timestamptz,
+ world_id bigint references public.worlds on delete cascade not null
);
comment on table public.character_categories is 'Categories describing characters';
@@ -420,12 +420,25 @@ create policy "Allow individual update access for GMs" on public.characters for
)
);
--- Categories are public to view but not to insert
--- Needs to be refactored maybe, if in the future we want a default set AND user defined ones
-create policy "Allow all read access" on public.calendar_event_categories for select to authenticated, anon using ( true );
-create policy "Allow all read access" on public.calendar_event_categories_links for select to authenticated, anon using ( true );
-create policy "Allow logged-in read access" on public.character_categories for select using ( auth.role() = 'authenticated' );
-create policy "Allow logged-in read access" on public.character_categories_links for select using ( auth.role() = 'authenticated' );
+-- Categories policies
+create policy "Allow anonymous access to published event categories" on public.calendar_event_categories
+ for select
+ to authenticated, anon
+ using (
+ exists (
+ select 1
+ from public.calendars c
+ join public.calendar_event_categories_links l on l.calendar_event_category_id = calendar_event_categories.id
+ where c.id = calendar_event_categories.calendar_id
+ and c.state = 'published'
+ )
+ or exists (
+ select 1
+ from public.calendars c
+ where c.id = calendar_event_categories.calendar_id
+ and c.state = 'published'
+ )
+ );
-- Send "previous data" on change
alter table public.users replica identity full;
diff --git a/supabase/seed.sql b/supabase/seed.sql
index d3d9d24..4a2c029 100644
--- a/supabase/seed.sql
+++ b/supabase/seed.sql
@@ -1,57 +1,57 @@
insert into public.role_permissions (role, permission) values ('sa', 'events.see.hidden');
insert into public.role_permissions (role, permission) values ('sa', 'users.ban');
--- Event categories
-insert into public.calendar_event_categories (name, color) values ('Naissance', 'white');
-insert into public.calendar_event_categories (name, color) values ('Mort', 'black');
-insert into public.calendar_event_categories (name, color) values ('Catastrophe', 'orange');
-insert into public.calendar_event_categories (name, color) values ('Catastrophe naturelle', 'red');
-insert into public.calendar_event_categories (name, color) values ('Inauguration', 'green');
-insert into public.calendar_event_categories (name, color) values ('Religion', 'violet');
-insert into public.calendar_event_categories (name, color) values ('Invention', 'teal');
-insert into public.calendar_event_categories (name, color) values ('Science', 'indigo');
-insert into public.calendar_event_categories (name, color) values ('Bénédiction', 'white');
-insert into public.calendar_event_categories (name, color) values ('Joueurs', 'white');
-insert into public.calendar_event_categories (name, color) values ('Découverte', 'purple');
-insert into public.calendar_event_categories (name, color) values ('Exploration', 'lime');
-insert into public.calendar_event_categories (name, color) values ('Construction', 'blue');
-insert into public.calendar_event_categories (name, color) values ('Arcanologie', 'cyan');
-insert into public.calendar_event_categories (name, color) values ('Criminalité', 'rose');
-insert into public.calendar_event_categories (name, color) values ('Scandale', 'pink');
-insert into public.calendar_event_categories (name, color) values ('Commerce', 'amber');
-insert into public.calendar_event_categories (name, color) values ('Législation', 'blue');
-
--- Character categories
-insert into public.character_categories (name, color) values ('Joueur', 'white');
-insert into public.character_categories (name, color) values ('Comte', 'emerald');
-insert into public.character_categories (name, color) values ('Scientifique', 'indigo');
-insert into public.character_categories (name, color) values ('Mage', 'cyan');
-insert into public.character_categories (name, color) values ('Professeur', 'teal');
-insert into public.character_categories (name, color) values ('Criminel', 'rose');
-insert into public.character_categories (name, color) values ('Étincelle', 'lime');
-insert into public.character_categories (name, color) values ('Buse blanche', 'yellow');
-insert into public.character_categories (name, color) values ('Ecclésiastique', 'violet');
-insert into public.character_categories (name, color) values ('Militaire', 'orange');
-insert into public.character_categories (name, color) values ('Activiste', 'sky');
-insert into public.character_categories (name, color) values ('Commerçant', 'amber');
-
-- Worlds
insert into public.worlds (name, description, color, state) values ('Léïm', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis aliquet congue aliquet. Curabitur eu iaculis diam. Nunc iaculis nibh orci, eu semper nunc congue congue. Praesent euismod tortor eget metus tristique lobortis vel in risus. In volutpat ligula orci, id pharetra lectus egestas at.', 'black', 'published');
+-- Character categories
+insert into public.character_categories (name, color, world_id) values ('Joueur', 'white', 1);
+insert into public.character_categories (name, color, world_id) values ('Comte', 'emerald', 1);
+insert into public.character_categories (name, color, world_id) values ('Scientifique', 'indigo', 1);
+insert into public.character_categories (name, color, world_id) values ('Mage', 'cyan', 1);
+insert into public.character_categories (name, color, world_id) values ('Professeur', 'teal', 1);
+insert into public.character_categories (name, color, world_id) values ('Criminel', 'rose', 1);
+insert into public.character_categories (name, color, world_id) values ('Étincelle', 'lime', 1);
+insert into public.character_categories (name, color, world_id) values ('Buse blanche', 'yellow', 1);
+insert into public.character_categories (name, color, world_id) values ('Ecclésiastique', 'violet', 1);
+insert into public.character_categories (name, color, world_id) values ('Militaire', 'orange', 1);
+insert into public.character_categories (name, color, world_id) values ('Activiste', 'sky', 1);
+insert into public.character_categories (name, color, world_id) values ('Commerçant', 'amber', 1);
+
-- Worlds' calendars
insert into public.calendars (world_id, name, today, state) values (1, 'Calendrier solaire', '{ "day": 23, "month": 8, "year": 3209 }', 'published');
-- Calendar's months
-insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Jalen', 32, 1);
-insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Malsen', 32, 2);
-insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Verlys', 32, 3);
-insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Nalys', 32, 4);
-insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Verdore', 32, 5);
-insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Sidore', 32, 6);
-insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Lyllion', 32, 7);
-insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Rion', 32, 8);
-insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Farene', 32, 9);
-insert into public.calendar_months (calendar_id, name, days, position) values (1, 'Dalvene', 32, 10);
+insert into public.calendar_months (name, days, position, calendar_id) values ('Jalen', 32, 1, 1);
+insert into public.calendar_months (name, days, position, calendar_id) values ('Malsen', 32, 2, 1);
+insert into public.calendar_months (name, days, position, calendar_id) values ('Verlys', 32, 3, 1);
+insert into public.calendar_months (name, days, position, calendar_id) values ('Nalys', 32, 4, 1);
+insert into public.calendar_months (name, days, position, calendar_id) values ('Verdore', 32, 5, 1);
+insert into public.calendar_months (name, days, position, calendar_id) values ('Sidore', 32, 6, 1);
+insert into public.calendar_months (name, days, position, calendar_id) values ('Lyllion', 32, 7, 1);
+insert into public.calendar_months (name, days, position, calendar_id) values ('Rion', 32, 8, 1);
+insert into public.calendar_months (name, days, position, calendar_id) values ('Farene', 32, 9, 1);
+insert into public.calendar_months (name, days, position, calendar_id) values ('Dalvene', 32, 10, 1);
+
+-- Event categories
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Naissance', 'white', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Mort', 'black', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Catastrophe', 'orange', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Catastrophe naturelle', 'red', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Inauguration', 'green', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Religion', 'violet', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Invention', 'teal', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Science', 'indigo', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Bénédiction', 'white', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Joueurs', 'white', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Découverte', 'purple', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Exploration', 'lime', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Construction', 'blue', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Magie', 'cyan', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Criminalité', 'rose', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Scandale', 'pink', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Commerce', 'amber', 1);
+insert into public.calendar_event_categories (name, color, calendar_id) values ('Législation', 'blue', 1);
-- Events
insert into public.calendar_events (title, description, start_date, category, hidden, wiki, calendar_id) values (
@@ -203,7 +203,7 @@ insert into public.calendar_events (title, description, start_date, end_date, ca
'Celui qu''on surnomme la Bête d''Ambrose arrive à Handany, où il purgera sa peine.',
'{ "day": 14, "month": 7, "year": 3209 }',
null,
- 18,
+ 15,
false,
'https://alexcreates.fr/leim/index.php/Tivian_Rodhus',
1
@@ -221,7 +221,7 @@ insert into public.calendar_events (title, description, location, start_date, en
'Tourgrise',
'{ "day": 4, "month": 8, "year": 3209 }',
null,
- 18,
+ 15,
false,
null,
1
@@ -245,7 +245,7 @@ insert into public.calendar_events (title, description, location, start_date, en
'{ "day": 28, "month": 7, "year": 3209 }',
null,
2,
- true,
+ false,
null,
1
);
@@ -256,7 +256,7 @@ insert into public.calendar_events (title, description, location, start_date, en
'{ "day": 32, "month": 7, "year": 3209 }',
null,
2,
- true,
+ false,
null,
1
);
@@ -267,7 +267,7 @@ insert into public.calendar_events (title, description, location, start_date, en
'{ "day": 10, "month": 8, "year": 3209 }',
null,
2,
- true,
+ false,
null,
1
);
@@ -278,7 +278,7 @@ insert into public.calendar_events (title, description, location, start_date, en
'{ "day": 19, "month": 8, "year": 3209 }',
null,
2,
- true,
+ false,
null,
1
);
@@ -289,7 +289,7 @@ insert into public.calendar_events (title, description, location, start_date, en
'{ "day": 22, "month": 8, "year": 3209 }',
null,
2,
- true,
+ false,
null,
1
);
From 6992bca9571a02a89b8a1bd5634cba951e3c75c9 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Fri, 14 Mar 2025 22:30:10 +0100
Subject: [PATCH 02/24] Changed hidden translations
---
i18n.config.ts | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/i18n.config.ts b/i18n.config.ts
index 67fbf01..510fe8c 100644
--- a/i18n.config.ts
+++ b/i18n.config.ts
@@ -183,8 +183,8 @@ export default defineI18nConfig(() => ({
title: "Event title",
isStart: "Start",
isEnd: "End",
- isHidden: "Hidden event",
- isPublic: "Public event",
+ isHidden: "Hidden",
+ isPublic: "Public",
hiddenTooltip: "This event is visible only to game masters.",
addLocation: "Add a place",
prevPage: "Previous page with events",
@@ -479,8 +479,8 @@ export default defineI18nConfig(() => ({
title: "Titre de l'évènement",
isStart: "Début",
isEnd: "Fin",
- isHidden: "Évènement privé",
- isPublic: "Évènement public",
+ isHidden: "Privé",
+ isPublic: "Public",
hiddenTooltip: "Cet évènement est uniquement visible pour les maîtres du jeu.",
addLocation: "Ajouter un endroit",
prevPage: "Précédente page à évènements",
From 7f3a2b8d56712a33aa57f3eb6ac4bc023077cb5b Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Sat, 15 Mar 2025 14:52:26 +0100
Subject: [PATCH 03/24] Fixed long RLS name
---
supabase/migrations/202401_init.sql | 6 +++---
supabase/seed.sql | 4 ++--
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/supabase/migrations/202401_init.sql b/supabase/migrations/202401_init.sql
index e4a11c0..e8683e5 100644
--- a/supabase/migrations/202401_init.sql
+++ b/supabase/migrations/202401_init.sql
@@ -93,7 +93,7 @@ comment on table public.calendar_months is 'A calendar month.';
create table public.calendar_event_categories (
id bigint generated by default as identity primary key,
name text not null,
- color app_colors default 'black',
+ color app_colors default 'white',
calendar_id bigint references public.calendars on delete cascade not null
);
comment on table public.calendar_event_categories is 'Categories describing events.';
@@ -136,7 +136,7 @@ comment on table public.calendar_event_categories_links is 'Link tables for mult
create table public.character_categories (
id bigint generated by default as identity primary key,
name text not null,
- color app_colors default 'black',
+ color app_colors default 'white',
created_at timestamptz default now(),
updated_at timestamptz,
world_id bigint references public.worlds on delete cascade not null
@@ -335,7 +335,7 @@ create policy "Allow GMs to delete their calendar's months"
);
-- Event policies
-create policy "Allow anonymous access to non-hidden events in published calendars" on public.calendar_events
+create policy "Allow anon access to non-hidden events in published calendars" on public.calendar_events
for select
to authenticated, anon
using (
diff --git a/supabase/seed.sql b/supabase/seed.sql
index 4a2c029..45f7f10 100644
--- a/supabase/seed.sql
+++ b/supabase/seed.sql
@@ -2,7 +2,7 @@ insert into public.role_permissions (role, permission) values ('sa', 'events.see
insert into public.role_permissions (role, permission) values ('sa', 'users.ban');
-- Worlds
-insert into public.worlds (name, description, color, state) values ('Léïm', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis aliquet congue aliquet. Curabitur eu iaculis diam. Nunc iaculis nibh orci, eu semper nunc congue congue. Praesent euismod tortor eget metus tristique lobortis vel in risus. In volutpat ligula orci, id pharetra lectus egestas at.', 'black', 'published');
+insert into public.worlds (name, description, color, state) values ('Léïm', 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis aliquet congue aliquet. Curabitur eu iaculis diam. Nunc iaculis nibh orci, eu semper nunc congue congue. Praesent euismod tortor eget metus tristique lobortis vel in risus. In volutpat ligula orci, id pharetra lectus egestas at.', 'white', 'published');
-- Character categories
insert into public.character_categories (name, color, world_id) values ('Joueur', 'white', 1);
@@ -19,7 +19,7 @@ insert into public.character_categories (name, color, world_id) values ('Activis
insert into public.character_categories (name, color, world_id) values ('Commerçant', 'amber', 1);
-- Worlds' calendars
-insert into public.calendars (world_id, name, today, state) values (1, 'Calendrier solaire', '{ "day": 23, "month": 8, "year": 3209 }', 'published');
+insert into public.calendars (world_id, name, today, color, state) values (1, 'Calendrier solaire', '{ "day": 23, "month": 8, "year": 3209 }', 'white', 'published');
-- Calendar's months
insert into public.calendar_months (name, days, position, calendar_id) values ('Jalen', 32, 1, 1);
From 149d0794e3f57bb830d5e51f56564967a3298adc Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Fri, 28 Mar 2025 12:11:54 +0100
Subject: [PATCH 04/24] Moved navigation to sub nav
---
components/calendar/CalendarCurrentDate.vue | 19 +-
components/calendar/CalendarMenu.vue | 7 +-
components/calendar/CalendarMenuNav.vue | 200 -----------------
components/calendar/CalendarMenuSubnav.vue | 234 ++++++++++++++------
i18n.config.ts | 4 +-
5 files changed, 186 insertions(+), 278 deletions(-)
delete mode 100644 components/calendar/CalendarMenuNav.vue
diff --git a/components/calendar/CalendarCurrentDate.vue b/components/calendar/CalendarCurrentDate.vue
index 279e514..bdb88cc 100644
--- a/components/calendar/CalendarCurrentDate.vue
+++ b/components/calendar/CalendarCurrentDate.vue
@@ -4,26 +4,29 @@ import { computed } from "vue"
import { PhMapPin } from "@phosphor-icons/vue"
-const { defaultDate, getFormattedDateTitle, getRelativeString } = useCalendar()
+const { defaultDate, getFormattedDateTitle, getRelativeString, getDifferenceInDays } = useCalendar()
const { selectedDate } = storeToRefs(useCalendar())
const mainDateTitle = computed(() => getFormattedDateTitle(selectedDate.value, true))
// const mainDateTitle = computed(() => convertDateToDays(selectedDate.value))
const dateDifference = computed(() => getRelativeString(defaultDate, selectedDate.value))
+const isToday = computed(() => getDifferenceInDays(defaultDate, selectedDate.value) === 0)
-
- {{ mainDateTitle }}
-
-
- {{ dateDifference }}
-
+
+
+ {{ mainDateTitle }}
+
+
+ – {{ dateDifference }}
+
+
-
+
diff --git a/components/calendar/CalendarMenu.vue b/components/calendar/CalendarMenu.vue
index f37698f..29a8920 100644
--- a/components/calendar/CalendarMenu.vue
+++ b/components/calendar/CalendarMenu.vue
@@ -8,8 +8,8 @@ const { isReadOnly } = storeToRefs(useCalendar())
-
-
+
+
-
+
+
+
+
+
+
+
+
+
+
+ {{ activeDirectionLabels.futureNear }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ activeDirectionLabels.futureFar }}
+
+
+
+
diff --git a/i18n.config.ts b/i18n.config.ts
index 510fe8c..48bbf02 100644
--- a/i18n.config.ts
+++ b/i18n.config.ts
@@ -99,7 +99,7 @@ export default defineI18nConfig(() => ({
addDescription: "Add a description",
deleteOne: "Delete \"{entity}\"",
advancedSearch: {
- title: "Advanced search",
+ title: "Search",
subtitle: "Search through calendar and world data",
older: "Older",
newer: "Newer",
@@ -395,7 +395,7 @@ export default defineI18nConfig(() => ({
addDescription: "Ajouter une description",
deleteOne: "Supprimer \"{entity}\"",
advancedSearch: {
- title: "Recherche avancée",
+ title: "Rechercher",
subtitle: "Rechercher les données disponibles sur le calendrier",
older: "Plus ancien",
newer: "Plus récent",
From d2295024bd49c93e6f0e64626695a43f0cfa3b84 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Fri, 28 Mar 2025 12:12:37 +0100
Subject: [PATCH 05/24] Changed span to kbd
---
components/ui/button/Button.vue | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/components/ui/button/Button.vue b/components/ui/button/Button.vue
index f366883..1d50302 100644
--- a/components/ui/button/Button.vue
+++ b/components/ui/button/Button.vue
@@ -28,7 +28,7 @@ const props = withDefaults(defineProps(), {
v-if="props.searchSlash"
class="h-4 p-1 ml-1 grid place-items-center text-[0.7em] leading-none font-semibold text-slate-500 bg-slate-100 border-slate-300 border-[1px] rounded-[3px] shadow-sm group-hover:text-slate-600 group-hover:bg-slate-200 group-hover:border-slate-400 transition-colors"
>
- CTRL + :
+ CTRL + :
From d3a1ff92e288eb6d05bf518151ddc0c9fc16a47d Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Fri, 28 Mar 2025 15:23:38 +0100
Subject: [PATCH 06/24] Added option menu for calendar
---
components/calendar/CalendarMenu.vue | 9 +--
components/calendar/CalendarOptions.vue | 57 +++++++++++++++++++
components/calendar/CalendarSwitch.vue | 34 -----------
.../dropdown-menu/DropdownMenuSubTrigger.vue | 18 ++++--
i18n.config.ts | 16 +++++-
stores/CalendarStore.ts | 8 +--
6 files changed, 94 insertions(+), 48 deletions(-)
create mode 100644 components/calendar/CalendarOptions.vue
delete mode 100644 components/calendar/CalendarSwitch.vue
diff --git a/components/calendar/CalendarMenu.vue b/components/calendar/CalendarMenu.vue
index 29a8920..3c29a07 100644
--- a/components/calendar/CalendarMenu.vue
+++ b/components/calendar/CalendarMenu.vue
@@ -2,6 +2,7 @@
import { useCalendar } from "@/stores/CalendarStore"
import { PhMagnifyingGlass } from "@phosphor-icons/vue"
+import CalendarOptions from "./CalendarOptions.vue"
const { revealAdvancedSearch } = useCalendar()
const { isReadOnly } = storeToRefs(useCalendar())
@@ -26,13 +27,13 @@ const { isReadOnly } = storeToRefs(useCalendar())
- {{ $t('entity.advancedSearch.title') }}
+
+ {{ $t('entity.advancedSearch.title') }}
+
-
-
-
+
diff --git a/components/calendar/CalendarOptions.vue b/components/calendar/CalendarOptions.vue
new file mode 100644
index 0000000..aba7ad3
--- /dev/null
+++ b/components/calendar/CalendarOptions.vue
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('entity.calendar.seeOptions') }}
+
+
+
+
+ {{ $t('ui.displayMode') }}
+
+
+
+
+
+
+
+
+
+ {{ getViewTypeTitle(option) }}
+
+
+
+
+
+
+
+
+ {{ $t('entity.category.namePlural') }}
+
+
+
+
+
+
diff --git a/components/calendar/CalendarSwitch.vue b/components/calendar/CalendarSwitch.vue
deleted file mode 100644
index 4937c81..0000000
--- a/components/calendar/CalendarSwitch.vue
+++ /dev/null
@@ -1,34 +0,0 @@
-
-
-
-
-
-
-
-
- {{ viewTypeTitle }}
-
-
-
-
- {{ $t('ui.displayMode') }}
-
-
-
- {{ getViewTypeTitle(option) }}
-
-
-
-
diff --git a/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue b/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue
index 12cd05b..2c3ac29 100644
--- a/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue
+++ b/components/ui/dropdown-menu/DropdownMenuSubTrigger.vue
@@ -5,10 +5,10 @@ import {
type DropdownMenuSubTriggerProps,
useForwardProps
} from "radix-vue"
-import { ChevronRight } from "lucide-vue-next"
import { cn } from "@/lib/utils"
+import { PhCaretLeft, PhCaretRight } from "@phosphor-icons/vue";
-const props = defineProps()
+const props = defineProps()
const delegatedProps = computed(() => {
const { class: _, ...delegated } = props
@@ -29,7 +29,17 @@ const forwardedProps = useForwardProps(delegatedProps)
)
"
>
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/i18n.config.ts b/i18n.config.ts
index 48bbf02..175d7a1 100644
--- a/i18n.config.ts
+++ b/i18n.config.ts
@@ -79,7 +79,7 @@ export default defineI18nConfig(() => ({
dark: "Dark",
light: "Light",
system: "System",
- displayMode: "Display mode"
+ displayMode: "Display"
},
common: {
title: "Title",
@@ -148,6 +148,7 @@ export default defineI18nConfig(() => ({
backToList: "Go back to calendars",
isLoading: "Calendar is loading…",
hasXEvents: "{count} available events",
+ seeOptions: "Calendar options",
date: {
start: "Start date",
end: "End date",
@@ -242,26 +243,31 @@ export default defineI18nConfig(() => ({
},
millennia: {
nameSingular: "Millennia",
+ displayMode: "Millennial",
nextSingular: "Next millennia",
prevSingular: "Last millennia",
},
centuries: {
nameSingular: "Century",
+ displayMode: "Centuries",
nextSingular: "Next century",
prevSingular: "Last century",
},
decades: {
nameSingular: "Decade",
+ displayMode: "Decadal",
nextSingular: "Next decade",
prevSingular: "Last decade",
},
years: {
nameSingular: "Year",
+ displayMode: "Yearly",
nextSingular: "Next year",
prevSingular: "Last year",
},
months: {
nameSingular: "Month",
+ displayMode: "Monthly",
nextSingular: "Next month",
prevSingular: "Last month",
inputName: "Month's name",
@@ -375,7 +381,7 @@ export default defineI18nConfig(() => ({
dark: "Sombre",
light: "Clair",
system: "Système",
- displayMode: "Mode d'affichage",
+ displayMode: "Affichage",
},
common: {
title: "Titre",
@@ -444,6 +450,7 @@ export default defineI18nConfig(() => ({
backToList: "Retourner aux calendriers",
isLoading: "Chargement du calendrier…",
hasXEvents: "{count} évènements disponibles",
+ seeOptions: "Options du calendrier",
date: {
start: "Date de début",
end: "Date de fin",
@@ -541,26 +548,31 @@ export default defineI18nConfig(() => ({
},
millennia: {
nameSingular: "Millénaire",
+ displayMode: "Par millénaire",
nextSingular: "Millénaire suivant",
prevSingular: "Millénaire précédent",
},
centuries: {
nameSingular: "Siècle",
+ displayMode: "Par siècle",
nextSingular: "Siècle suivant",
prevSingular: "Siècle précédent",
},
decades: {
nameSingular: "Décennie",
+ displayMode: "Par décennie",
nextSingular: "Décennie suivante",
prevSingular: "Décennie précédente",
},
years: {
nameSingular: "Année",
+ displayMode: "Annuel",
nextSingular: "Année suivante",
prevSingular: "Année précédente",
},
months: {
nameSingular: "Mois",
+ displayMode: "Mensuel",
nextSingular: "Mois suivant",
prevSingular: "Mois précédent",
inputName: "Nom du mois",
diff --git a/stores/CalendarStore.ts b/stores/CalendarStore.ts
index b678d44..1eab643 100644
--- a/stores/CalendarStore.ts
+++ b/stores/CalendarStore.ts
@@ -345,17 +345,17 @@ export const useCalendar = defineStore("calendar", () => {
function getViewTypeTitle(viewType: CalendarViewType): string {
switch (viewType) {
case "year":
- return t("entity.calendar.years.nameSingular")
+ return t("entity.calendar.years.displayMode")
case "decade":
- return "Décennie"
+ return t("entity.calendar.decades.displayMode")
case "century":
- return "Siècle"
+ return t("entity.calendar.centuries.displayMode")
case "month":
default:
- return t("entity.calendar.months.nameSingular")
+ return t("entity.calendar.months.displayMode")
}
}
From 2856185a3c5a4c7984e13ef8c6cef9b4d6fec4ce Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Fri, 28 Mar 2025 15:34:30 +0100
Subject: [PATCH 07/24] Display category menu cta only if not read only
---
components/calendar/CalendarOptions.vue | 24 ++++++++++++++++++------
1 file changed, 18 insertions(+), 6 deletions(-)
diff --git a/components/calendar/CalendarOptions.vue b/components/calendar/CalendarOptions.vue
index aba7ad3..8628365 100644
--- a/components/calendar/CalendarOptions.vue
+++ b/components/calendar/CalendarOptions.vue
@@ -4,13 +4,23 @@ import { useCalendar } from "@/stores/CalendarStore"
import { PhCalendarBlank, PhCheckCircle, PhGear, PhTag } from "@phosphor-icons/vue"
import { computed } from "vue"
+const optionsOpened = ref(false)
+
+const user = useSupabaseUser()
+
+function closeMenu() {
+ optionsOpened.value = false
+}
+watch(user, closeMenu)
+
const { currentConfig, viewTypeOptions, getViewTypeTitle, setViewType } = useCalendar()
+const { isReadOnly } = storeToRefs(useCalendar())
const viewTypeTitle = computed(() => getViewTypeTitle(currentConfig.viewType))
-
+
@@ -45,13 +55,15 @@ const viewTypeTitle = computed(() => getViewTypeTitle(currentConfig.viewType))
-
+
+
-
- {{ $t('entity.category.namePlural') }}
+
+ {{ $t('entity.category.namePlural') }}
-
-
+
+
+
From 637a5cd7e3246e394cf7076b0469b5dc7046c7dc Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Fri, 28 Mar 2025 20:54:12 +0100
Subject: [PATCH 08/24] Added sample behaviour for category modal
---
assets/main.css | 2 +-
components/calendar/Calendar.vue | 1 +
components/calendar/CalendarMenu.vue | 8 +-
components/calendar/CategoriesCTA.vue | 23 ++
.../{CalendarOptions.vue => OptionsCTA.vue} | 27 +-
components/calendar/dialog/Categories.vue | 36 +++
.../ui/alert-dialog/AlertDialogContent.vue | 2 +-
components/ui/button/index.ts | 2 +-
components/ui/dialog/DialogContent.vue | 2 +-
components/ui/dialog/DialogScrollContent.vue | 2 +-
.../ui/dropdown-menu/DropdownMenuArrow.vue | 13 +
components/ui/dropdown-menu/index.ts | 1 +
i18n.config.ts | 4 +-
stores/CalendarStore.ts | 15 +-
stores/EventStore.ts | 306 ------------------
15 files changed, 104 insertions(+), 340 deletions(-)
create mode 100644 components/calendar/CategoriesCTA.vue
rename components/calendar/{CalendarOptions.vue => OptionsCTA.vue} (74%)
create mode 100644 components/calendar/dialog/Categories.vue
create mode 100644 components/ui/dropdown-menu/DropdownMenuArrow.vue
delete mode 100644 stores/EventStore.ts
diff --git a/assets/main.css b/assets/main.css
index a480816..579d8b4 100644
--- a/assets/main.css
+++ b/assets/main.css
@@ -22,7 +22,7 @@
--primary: 245 58% 51%;
--primary-foreground: 0 0% 100%;
- --secondary: 210 40% 96.1%;
+ --secondary: 210 50% 95%;
--secondary-foreground: 222.2 47.4% 11.2%;
--accent: 210 40% 96.1%;
diff --git a/components/calendar/Calendar.vue b/components/calendar/Calendar.vue
index 07234d7..49c6411 100644
--- a/components/calendar/Calendar.vue
+++ b/components/calendar/Calendar.vue
@@ -52,6 +52,7 @@ onMounted(() => {
+
diff --git a/components/calendar/CalendarMenu.vue b/components/calendar/CalendarMenu.vue
index 3c29a07..b8dce29 100644
--- a/components/calendar/CalendarMenu.vue
+++ b/components/calendar/CalendarMenu.vue
@@ -2,7 +2,6 @@
import { useCalendar } from "@/stores/CalendarStore"
import { PhMagnifyingGlass } from "@phosphor-icons/vue"
-import CalendarOptions from "./CalendarOptions.vue"
const { revealAdvancedSearch } = useCalendar()
const { isReadOnly } = storeToRefs(useCalendar())
@@ -13,7 +12,7 @@ const { isReadOnly } = storeToRefs(useCalendar())
diff --git a/components/calendar/CategoriesCTA.vue b/components/calendar/CategoriesCTA.vue
new file mode 100644
index 0000000..17f1066
--- /dev/null
+++ b/components/calendar/CategoriesCTA.vue
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ $t('entity.calendar.seeCategories') }}
+
+
+
+
+
diff --git a/components/calendar/CalendarOptions.vue b/components/calendar/OptionsCTA.vue
similarity index 74%
rename from components/calendar/CalendarOptions.vue
rename to components/calendar/OptionsCTA.vue
index 8628365..7a61982 100644
--- a/components/calendar/CalendarOptions.vue
+++ b/components/calendar/OptionsCTA.vue
@@ -1,32 +1,23 @@
-
+
+
+
{{ $t('entity.calendar.seeOptions') }}
@@ -54,16 +45,6 @@ const viewTypeTitle = computed(() => getViewTypeTitle(currentConfig.viewType))
-
-
-
-
-
- {{ $t('entity.category.namePlural') }}
-
-
-
-
diff --git a/components/calendar/dialog/Categories.vue b/components/calendar/dialog/Categories.vue
new file mode 100644
index 0000000..d63b49e
--- /dev/null
+++ b/components/calendar/dialog/Categories.vue
@@ -0,0 +1,36 @@
+
+
+
+
+ e.preventDefault()"
+ >
+
+
+ Gestion des catégories
+
+
+
+
+
+
+
+ catégories là
+
+
+
diff --git a/components/ui/alert-dialog/AlertDialogContent.vue b/components/ui/alert-dialog/AlertDialogContent.vue
index 776974c..ef4d8ec 100644
--- a/components/ui/alert-dialog/AlertDialogContent.vue
+++ b/components/ui/alert-dialog/AlertDialogContent.vue
@@ -25,7 +25,7 @@ const forwarded = useForwardPropsEmits(delegatedProps, emits)
+import { DropdownMenuArrow, type DropdownMenuArrowProps, useForwardProps } from "radix-vue"
+
+const props = defineProps()
+
+const forwardedProps = useForwardProps(props)
+
+
+
+
+
+
+
diff --git a/components/ui/dropdown-menu/index.ts b/components/ui/dropdown-menu/index.ts
index 7e62fd1..b56627d 100644
--- a/components/ui/dropdown-menu/index.ts
+++ b/components/ui/dropdown-menu/index.ts
@@ -1,6 +1,7 @@
export { DropdownMenuPortal } from "radix-vue"
export { default as DropdownMenu } from "./DropdownMenu.vue"
+export { default as DropdownMenuArrow } from "./DropdownMenuArrow.vue"
export { default as DropdownMenuTrigger } from "./DropdownMenuTrigger.vue"
export { default as DropdownMenuContent } from "./DropdownMenuContent.vue"
export { default as DropdownMenuGroup } from "./DropdownMenuGroup.vue"
diff --git a/i18n.config.ts b/i18n.config.ts
index 175d7a1..13d913f 100644
--- a/i18n.config.ts
+++ b/i18n.config.ts
@@ -148,6 +148,7 @@ export default defineI18nConfig(() => ({
backToList: "Go back to calendars",
isLoading: "Calendar is loading…",
hasXEvents: "{count} available events",
+ seeCategories: "Modify categories",
seeOptions: "Calendar options",
date: {
start: "Start date",
@@ -395,7 +396,7 @@ export default defineI18nConfig(() => ({
search: "Rechercher les catégories",
notFoundAny: "Aucune catégorie trouvée.",
addPrimary: "Ajouter une catégorie principale",
- addSecondaries: "Ajouter des catégories secondaires"
+ addSecondaries: "Ajouter des catégories secondaires",
},
isLoading: "Chargement en cours…",
addDescription: "Ajouter une description",
@@ -450,6 +451,7 @@ export default defineI18nConfig(() => ({
backToList: "Retourner aux calendriers",
isLoading: "Chargement du calendrier…",
hasXEvents: "{count} évènements disponibles",
+ seeCategories: "Gestion des catégories",
seeOptions: "Options du calendrier",
date: {
start: "Date de début",
diff --git a/stores/CalendarStore.ts b/stores/CalendarStore.ts
index 1eab643..57a94fe 100644
--- a/stores/CalendarStore.ts
+++ b/stores/CalendarStore.ts
@@ -819,7 +819,7 @@ export const useCalendar = defineStore("calendar", () => {
}
/**
- * State for event modal edition
+ * State for event modal deletion
*/
const isDeleteEventModalOpen = ref(false)
@@ -910,6 +910,15 @@ export const useCalendar = defineStore("calendar", () => {
}
}
+ /**
+ * State for categories modal
+ */
+ const isCategoriesModalOpen = ref(false)
+
+ function toggleCategoriesModal(state: boolean) {
+ isCategoriesModalOpen.value = state
+ }
+
return {
isReadOnly,
setReadStatus,
@@ -969,6 +978,8 @@ export const useCalendar = defineStore("calendar", () => {
isEditEventModalOpen,
revealEditEventModal,
isDeleteEventModalOpen,
- revealDeleteEventModal
+ revealDeleteEventModal,
+ isCategoriesModalOpen,
+ toggleCategoriesModal
}
})
diff --git a/stores/EventStore.ts b/stores/EventStore.ts
deleted file mode 100644
index 935c59c..0000000
--- a/stores/EventStore.ts
+++ /dev/null
@@ -1,306 +0,0 @@
-import type { CalendarEvent } from "@/models/CalendarEvent"
-import type { RPGDate } from "@/models/Date"
-import { defineStore } from "pinia"
-import { ref, watch, type Ref } from "vue"
-import type { Category } from "~/models/Category"
-import { useCalendar } from "./CalendarStore"
-
-export const useCalendarEvents = defineStore("calendar-events", () => {
- const { activeCalendar, defaultDate, currentConfig, convertDateToDays, compareDates } = useCalendar()
- const { currentRPGDate } = storeToRefs(useCalendar())
-
- const baseEvents = ref([])
-
- async function fetchCalendarEvents(calendarId: number) {
- try {
- const res = await $fetch("/api/calendars/events/query", { query: { calendarId } })
-
- const eventData = res.data as CalendarEvent[]
-
- if (!eventData.length) return
-
- baseEvents.value = eventData
- } catch (err) {
- console.log(err)
- }
- }
-
- const categories = ref([])
-
- function setCategories(data: Category[]) {
- categories.value = data
- }
-
- // Order base events by dates
- const allEvents = computed(() => baseEvents.value.sort((a, b) => {
- return compareDates(a.startDate, b.startDate, "desc")
- }))
-
- // Gets all current event in its default state
- const currentEvents: Ref = ref([])
-
- // Watch for currentDate or events' list changes
- // This is deep because we're watching an array, and changes need to trigger and mutations like .push and .splice
- watch([currentRPGDate, allEvents], () => {
- currentEvents.value = computeCurrentEvents()
- }, { deep: true, immediate: true })
-
- /**
- * Determines if the event can appear in the front end
- *
- * This function takes into consideration the viewType of the calendar config
- *
- * @param event The event to analyze
- * @returns Whether the event should appear in the current view
- */
- function shouldEventBeDisplayed(event: CalendarEvent): boolean {
- const isEventOnCurrentScreen =
- (event.startDate.year === currentRPGDate.value.day &&
- event.startDate.month === currentRPGDate.value.month) ||
- (event.endDate &&
- event.endDate.year === currentRPGDate.value.year &&
- event.endDate.month === currentRPGDate.value.month)
-
- switch (currentConfig.viewType) {
- case "month":
- return isEventOnCurrentScreen!
-
- case "year":
- return event.startDate.year === currentRPGDate.value.year
-
- case "decade":
- return (
- event.startDate.year >= currentRPGDate.value.year &&
- event.startDate.year <= currentRPGDate.value.year + 10
- )
-
- case "century":
- return (
- event.startDate.year >= currentRPGDate.value.year &&
- event.startDate.year <= currentRPGDate.value.year + 100
- )
-
- default:
- return false
- }
- }
-
- /**
- * Fetches all the current events for the current view
- *
- * @returns A list of events that can appear in the current view
- */
- function computeCurrentEvents(): CalendarEvent[] {
- return allEvents.value.filter((event) => shouldEventBeDisplayed(event))
- }
-
- /**
- * From a base event, gets the next or previous one in the timeline
- *
- * @param event The event at a given position in the data
- * @param position Whether we should get the next or previous event
- * @returns The next event in chronological order
- */
- function getRelativeEventFromEvent(
- event: CalendarEvent,
- position: "next" | "prev" = "next",
- initialIsEnd: boolean = false
- ): { event: CalendarEvent; targetDate: RPGDate } {
- let dateToParse: RPGDate // Day value of the date that the user interacted with
-
- if (initialIsEnd && event.endDate) {
- dateToParse = event.endDate
- } else {
- dateToParse = event.startDate
- }
-
- return getRelativeEventFromDate(dateToParse, position)
- }
-
- /**
- * From a date, gets the next or previous event in the timeline
- *
- * @param date The starting date from which to get the next event
- * @param position Whether we should get the next or previous event
- * @returns The next event in chronological order
- */
- function getRelativeEventFromDate(
- date: RPGDate,
- position: "next" | "prev" = "next"
- ): { event: CalendarEvent; targetDate: RPGDate } {
- const pivotValue = convertDateToDays(date)
- let t: { eventData: CalendarEvent; distance: number; targetKey: "startDate" | "endDate" }[] = []
-
- // Loop over all event once to convert the structure to a usable one
- for (let i = 0; i < allEvents.value.length; i++) {
- const e: CalendarEvent = allEvents.value[i]
-
- // Estimate distance from pivot
- const startDateDays: number = convertDateToDays(e.startDate)
- const startDistance: number = startDateDays - pivotValue
-
- // Push startDate to comparator array
- t.push({
- eventData: e,
- distance: startDistance,
- targetKey: "startDate"
- })
-
- // Check the same things for endDate
- if (e.endDate) {
- const endDateDays: number = convertDateToDays(e.endDate)
- const endDistance: number = endDateDays - pivotValue
-
- // Push optional endDate to comparator array
- t.push({
- eventData: e,
- distance: endDistance,
- targetKey: "endDate"
- })
- }
- }
-
- // Based on the direction, either ignore negative distance (past) or positive distance (future)
- t = t.filter((i) => {
- return position === "next" ? i.distance > 0 : i.distance < 0
- })
-
- if (!t.length) {
- throw new Error(
- "Aucun évènement suivant ou précédent trouvé ; Peut-être l'évènement se situe au début ou à la fin du calendrier ?"
- )
- }
-
- // Get event with remaining minimum distance
- const closestEvent = t.reduce((a, b) => {
- return Math.abs(b.distance) < Math.abs(a.distance) ? b : a
- })
-
- return {
- event: closestEvent.eventData,
- targetDate: closestEvent.eventData[closestEvent.targetKey]!
- }
- }
-
- /**
- * State for event modal edition
- */
- const isEditEventModalOpen: Ref = ref(false)
-
- function revealEditEventModal() {
- isEditEventModalOpen.value = true
- }
-
- /**
- * State for event modal edition
- */
- const isDeleteEventModalOpen: Ref = ref(false)
-
- function revealDeleteEventModal() {
- isDeleteEventModalOpen.value = true
- }
-
- /**
- * EVENT CREATION FUNCTIONS
- */
- const lastActiveEvent = ref()
- const isCreatingEvent = ref(false)
- const isUpdatingEvent = ref(false)
- const isDeletingEvent = ref(false)
- const operationInProgress = computed(() => isCreatingEvent.value || isUpdatingEvent.value || isDeletingEvent.value)
- let abortController: AbortController | null = null
-
- /**
- * Dummy event to hold creation data
- */
- const eventSkeleton: Ref = ref({ title: "", startDate: defaultDate })
-
- /**
- * Resets the dummy event data
- */
- function resetSkeleton() {
- eventSkeleton.value = { title: "", startDate: defaultDate }
- }
-
- /**
- * Submits the skeleton event and creates a real event from its data
- *
- * We assume it's been sanitized by the caller
- */
- async function submitSkeleton() {
- abortController = new AbortController()
- isCreatingEvent.value = true
-
- try {
- const res = await $fetch("/api/calendars/events/create", { method: "POST", body: { event : eventSkeleton.value, calendarId: activeCalendar?.id }, signal: abortController.signal })
-
- baseEvents.value.push(res)
- } catch (err) {
- console.log(err)
- } finally {
- abortController = null
- isCreatingEvent.value = false
- }
- }
-
- async function updateEventFromSkeleton() {
- abortController = new AbortController()
- isUpdatingEvent.value = true
-
- 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)
- baseEvents.value[eventIndex] = res
-
- abortController = null
- isUpdatingEvent.value = false
- }
-
- async function deleteEventFromSkeleton() {
- abortController = new AbortController()
- isDeletingEvent.value = true
-
- try {
- await $fetch(`/api/calendars/events/${eventSkeleton.value.id}`, { method: "DELETE", signal: abortController.signal })
-
- const eventIndex = baseEvents.value.findIndex(e => e.id === eventSkeleton.value.id)
- baseEvents.value.splice(eventIndex, 1)
- } catch (err) {
- console.log(err)
- } finally {
- abortController = null
- isDeletingEvent.value = false
- }
- }
-
- function cancelLatestRequest() {
- if (abortController) {
- abortController.abort()
- }
- }
-
- return {
- allEvents,
- fetchCalendarEvents,
- categories,
- setCategories,
- currentEvents,
- getRelativeEventFromDate,
- getRelativeEventFromEvent,
- cancelLatestRequest,
- isCreatingEvent,
- isUpdatingEvent,
- isDeletingEvent,
- operationInProgress,
- eventSkeleton,
- resetSkeleton,
- submitSkeleton,
- lastActiveEvent,
- updateEventFromSkeleton,
- deleteEventFromSkeleton,
- isEditEventModalOpen,
- revealEditEventModal,
- isDeleteEventModalOpen,
- revealDeleteEventModal
- }
-})
From d4d74db5d923185fd0b733d69b8e47418386db14 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Sun, 30 Mar 2025 16:48:30 +0200
Subject: [PATCH 09/24] Refactored some stuff
---
...alendarCurrentDate.vue => CurrentDate.vue} | 0
components/calendar/category/Item.vue | 13 +++++++++
components/calendar/category/List.vue | 15 ++++++++++
components/calendar/dialog/Categories.vue | 19 ++++++++----
.../Details.vue} | 0
.../{CalendarEvent.vue => event/Event.vue} | 0
components/calendar/form/Categories.vue | 7 +++++
.../{CalendarMenu.vue => menu/Menu.vue} | 0
.../MenuSubnav.vue} | 0
.../MenuToday.vue} | 0
components/calendar/state/monthly/DayTile.vue | 2 +-
i18n.config.ts | 10 ++++++-
pages/calendars/[id].vue | 4 +--
pages/my/calendars/[id].vue | 29 ++-----------------
server/api/calendars/categories/query.get.ts | 5 ++--
stores/CalendarStore.ts | 2 ++
16 files changed, 67 insertions(+), 39 deletions(-)
rename components/calendar/{CalendarCurrentDate.vue => CurrentDate.vue} (100%)
create mode 100644 components/calendar/category/Item.vue
create mode 100644 components/calendar/category/List.vue
rename components/calendar/{CalendarEventDetails.vue => event/Details.vue} (100%)
rename components/calendar/{CalendarEvent.vue => event/Event.vue} (100%)
create mode 100644 components/calendar/form/Categories.vue
rename components/calendar/{CalendarMenu.vue => menu/Menu.vue} (100%)
rename components/calendar/{CalendarMenuSubnav.vue => menu/MenuSubnav.vue} (100%)
rename components/calendar/{CalendarMenuToday.vue => menu/MenuToday.vue} (100%)
diff --git a/components/calendar/CalendarCurrentDate.vue b/components/calendar/CurrentDate.vue
similarity index 100%
rename from components/calendar/CalendarCurrentDate.vue
rename to components/calendar/CurrentDate.vue
diff --git a/components/calendar/category/Item.vue b/components/calendar/category/Item.vue
new file mode 100644
index 0000000..ebcfea3
--- /dev/null
+++ b/components/calendar/category/Item.vue
@@ -0,0 +1,13 @@
+
+
+
+
+ {{ category }}
+
+
diff --git a/components/calendar/category/List.vue b/components/calendar/category/List.vue
new file mode 100644
index 0000000..fa230f7
--- /dev/null
+++ b/components/calendar/category/List.vue
@@ -0,0 +1,15 @@
+
+
+
+
+
diff --git a/components/calendar/dialog/Categories.vue b/components/calendar/dialog/Categories.vue
index d63b49e..96753ba 100644
--- a/components/calendar/dialog/Categories.vue
+++ b/components/calendar/dialog/Categories.vue
@@ -1,4 +1,5 @@
+
+
+
+
diff --git a/components/calendar/CalendarMenu.vue b/components/calendar/menu/Menu.vue
similarity index 100%
rename from components/calendar/CalendarMenu.vue
rename to components/calendar/menu/Menu.vue
diff --git a/components/calendar/CalendarMenuSubnav.vue b/components/calendar/menu/MenuSubnav.vue
similarity index 100%
rename from components/calendar/CalendarMenuSubnav.vue
rename to components/calendar/menu/MenuSubnav.vue
diff --git a/components/calendar/CalendarMenuToday.vue b/components/calendar/menu/MenuToday.vue
similarity index 100%
rename from components/calendar/CalendarMenuToday.vue
rename to components/calendar/menu/MenuToday.vue
diff --git a/components/calendar/state/monthly/DayTile.vue b/components/calendar/state/monthly/DayTile.vue
index 7d8f32a..4d596be 100644
--- a/components/calendar/state/monthly/DayTile.vue
+++ b/components/calendar/state/monthly/DayTile.vue
@@ -5,7 +5,7 @@ import { useElementBounding } from "@vueuse/core"
import { storeToRefs } from "pinia"
import { computed, ref, type ComputedRef } from "vue"
-import CalendarEventButton from "../../CalendarEvent.vue"
+import CalendarEventButton from "../../event/Event.vue"
const props = defineProps<{
date: RPGDate
diff --git a/i18n.config.ts b/i18n.config.ts
index 13d913f..6715aad 100644
--- a/i18n.config.ts
+++ b/i18n.config.ts
@@ -93,7 +93,11 @@ export default defineI18nConfig(() => ({
search: "Search categories",
notFoundAny: "No categories found.",
addPrimary: "Add a primary category",
- addSecondaries: "Add secondary categories"
+ addSecondaries: "Add secondary categories",
+ manageDialog: {
+ title: "Manage categories",
+ subtitle: "Add and change the categories of your calendar",
+ }
},
isLoading: "Loading in progress…",
addDescription: "Add a description",
@@ -397,6 +401,10 @@ export default defineI18nConfig(() => ({
notFoundAny: "Aucune catégorie trouvée.",
addPrimary: "Ajouter une catégorie principale",
addSecondaries: "Ajouter des catégories secondaires",
+ manageDialog: {
+ title: "Gestion des catégories",
+ subtitle: "Créer et modifier les catégories de votre calendrier",
+ }
},
isLoading: "Chargement en cours…",
addDescription: "Ajouter une description",
diff --git a/pages/calendars/[id].vue b/pages/calendars/[id].vue
index 2b523f4..e17b120 100644
--- a/pages/calendars/[id].vue
+++ b/pages/calendars/[id].vue
@@ -14,7 +14,7 @@ const {
refresh: calRefresh
} = await useLazyFetch<{ data: Calendar }>("/api/calendars/query",
{
- key: `calendar-${shortId}`,
+ key: "active-calendar",
query: {
shortId,
full: true
@@ -27,7 +27,7 @@ const {
status: categoriesStatus,
refresh: catRefresh
} = await useLazyFetch<{ data: Category[] }>("/api/calendars/categories/query",
- { key: `categories-${shortId}` }
+ { key: "active-categories" }
)
const isLoading = computed(() => calendarStatus.value === "pending" || categoriesStatus.value === "pending")
diff --git a/pages/my/calendars/[id].vue b/pages/my/calendars/[id].vue
index 219053f..5d0e96a 100644
--- a/pages/my/calendars/[id].vue
+++ b/pages/my/calendars/[id].vue
@@ -24,7 +24,7 @@ const {
status: calendarStatus
} = await useLazyFetch<{ data: Calendar }>("/api/calendars/query",
{
- key: `calendar-${id}`,
+ key: "active-calendar",
query: {
id,
full: true
@@ -36,35 +36,10 @@ const {
data: categories,
status: categoriesStatus
} = await useLazyFetch<{ data: Category[] }>("/api/calendars/categories/query",
- { key: `categories-${id}` }
+ { key: "active-categories" }
)
const isLoading = computed(() => calendarStatus.value === "pending" || categoriesStatus.value === "pending")
-
-// Set custom menu
-// This should be reserved for actions, not for breadcrumbs
-//
-// const { t } = useI18n()
-// const { setCurrentMenu } = useUiStore()
-
-// // Set menu once we have the calendar data
-// watch(calendar, (n) => {
-// if (n?.data) {
-// setCurrentMenu([
-// {
-// tooltip: t("entity.world.backToMy"),
-// to: "/my",
-// phIcon: "universe",
-// phIconWeight: "regular"
-// },
-// {
-// tooltip: t("entity.world.backToSingle", { world: calendar.value?.data.world?.name }),
-// to: `/my/worlds/${calendar.value?.data.world?.id}`,
-// phIcon: "world"
-// },
-// ])
-// }
-// }, { immediate: true })
diff --git a/server/api/calendars/categories/query.get.ts b/server/api/calendars/categories/query.get.ts
index 8239d95..92da7ee 100644
--- a/server/api/calendars/categories/query.get.ts
+++ b/server/api/calendars/categories/query.get.ts
@@ -16,12 +16,13 @@ export default defineEventHandler(async (event) => {
.from("calendar_event_categories")
.select(`
id,
- name
+ name,
+ color
`)
if (query.id) {
return output.eq("id", query.id).limit(1).single()
}
- return output.returns()
+ return output.overrideTypes()
})
diff --git a/stores/CalendarStore.ts b/stores/CalendarStore.ts
index 57a94fe..63277b3 100644
--- a/stores/CalendarStore.ts
+++ b/stores/CalendarStore.ts
@@ -61,6 +61,8 @@ export const useCalendar = defineStore("calendar", () => {
const months = ref([])
function setActiveCalendar(calendarData: Calendar, categoryData: Category[]) {
+ console.log("proced")
+
try {
if (!calendarData.id) return
From 8005a8e9a2648c64a0958d8ea04e81b422164328 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Mon, 31 Mar 2025 19:10:11 +0200
Subject: [PATCH 10/24] Added read only category table
---
assets/main.css | 75 ++++++++++
components/calendar/category/List.vue | 2 +-
.../category/{Item.vue => ListItem.vue} | 8 +-
components/calendar/category/Table.vue | 42 ++++++
components/calendar/category/TableRow.vue | 137 ++++++++++++++++++
components/calendar/dialog/Categories.vue | 3 +-
components/calendar/form/Categories.vue | 2 +-
components/global/input/Color.vue | 93 ++----------
components/ui/select/SelectTrigger.vue | 2 +-
components/ui/table/Table.vue | 16 ++
components/ui/table/TableBody.vue | 14 ++
components/ui/table/TableCaption.vue | 14 ++
components/ui/table/TableCell.vue | 21 +++
components/ui/table/TableEmpty.vue | 38 +++++
components/ui/table/TableFooter.vue | 14 ++
components/ui/table/TableHead.vue | 14 ++
components/ui/table/TableHeader.vue | 14 ++
components/ui/table/TableRow.vue | 14 ++
components/ui/table/index.ts | 9 ++
19 files changed, 440 insertions(+), 92 deletions(-)
rename components/calendar/category/{Item.vue => ListItem.vue} (50%)
create mode 100644 components/calendar/category/Table.vue
create mode 100644 components/calendar/category/TableRow.vue
create mode 100644 components/ui/table/Table.vue
create mode 100644 components/ui/table/TableBody.vue
create mode 100644 components/ui/table/TableCaption.vue
create mode 100644 components/ui/table/TableCell.vue
create mode 100644 components/ui/table/TableEmpty.vue
create mode 100644 components/ui/table/TableFooter.vue
create mode 100644 components/ui/table/TableHead.vue
create mode 100644 components/ui/table/TableHeader.vue
create mode 100644 components/ui/table/TableRow.vue
create mode 100644 components/ui/table/index.ts
diff --git a/assets/main.css b/assets/main.css
index 579d8b4..98cf58b 100644
--- a/assets/main.css
+++ b/assets/main.css
@@ -338,6 +338,81 @@
--border-color: color-mix(in srgb, var(--base-color), var(--color-slate-800) 50%);
}
+.bgc {
+ display: flex;
+ align-items: center;
+ flex-wrap: nowrap;
+}
+
+.bgc::before {
+ content: "";
+ display: inline-block;
+ width: .75rem;
+ aspect-ratio: 1;
+ margin-right: 0.5em;
+ border-radius: .25rem;
+ background-color: red;
+ border: 1px solid transparent;
+}
+
+.bgc-red::before {
+ @apply bg-red-500;
+}
+.bgc-orange::before {
+ @apply bg-orange-500;
+}
+.bgc-amber::before {
+ @apply bg-amber-500;
+}
+.bgc-yellow::before {
+ @apply bg-yellow-500;
+}
+.bgc-lime::before {
+ @apply bg-lime-500;
+}
+.bgc-green::before {
+ @apply bg-green-500;
+}
+.bgc-emerald::before {
+ @apply bg-emerald-600;
+}
+.bgc-teal::before {
+ @apply bg-teal-600;
+}
+.bgc-cyan::before {
+ @apply bg-cyan-600;
+}
+.bgc-sky::before {
+ @apply bg-sky-600;
+}
+.bgc-blue::before {
+ @apply bg-blue-600;
+}
+.bgc-indigo::before {
+ @apply bg-indigo-600;
+}
+.bgc-violet::before {
+ @apply bg-violet-600;
+}
+.bgc-purple::before {
+ @apply bg-purple-600;
+}
+.bgc-fuchsia::before {
+ @apply bg-fuchsia-600;
+}
+.bgc-pink::before {
+ @apply bg-pink-600;
+}
+.bgc-rose::before {
+ @apply bg-rose-600;
+}
+.bgc-black::before {
+ @apply bg-black dark:border-[1px] dark:border-slate-300;
+}
+.bgc-white::before {
+ @apply bg-white border-[1px] border-slate-700;
+}
+
.fade-enter-active,
.fade-leave-active {
transition: all .5s ease;
diff --git a/components/calendar/category/List.vue b/components/calendar/category/List.vue
index fa230f7..e4a527e 100644
--- a/components/calendar/category/List.vue
+++ b/components/calendar/category/List.vue
@@ -9,7 +9,7 @@ defineProps<{
diff --git a/components/calendar/category/Item.vue b/components/calendar/category/ListItem.vue
similarity index 50%
rename from components/calendar/category/Item.vue
rename to components/calendar/category/ListItem.vue
index ebcfea3..83efb48 100644
--- a/components/calendar/category/Item.vue
+++ b/components/calendar/category/ListItem.vue
@@ -7,7 +7,9 @@ defineProps<{
-
- {{ category }}
-
+
+
+ {{ category.name }}
+
+
diff --git a/components/calendar/category/Table.vue b/components/calendar/category/Table.vue
new file mode 100644
index 0000000..7169384
--- /dev/null
+++ b/components/calendar/category/Table.vue
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+ Nom
+
+
+ Couleur
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/calendar/category/TableRow.vue b/components/calendar/category/TableRow.vue
new file mode 100644
index 0000000..85c33d0
--- /dev/null
+++ b/components/calendar/category/TableRow.vue
@@ -0,0 +1,137 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/calendar/dialog/Categories.vue b/components/calendar/dialog/Categories.vue
index 96753ba..5cf96d1 100644
--- a/components/calendar/dialog/Categories.vue
+++ b/components/calendar/dialog/Categories.vue
@@ -13,10 +13,9 @@ function handleClosing() {
e.preventDefault()"
diff --git a/components/calendar/form/Categories.vue b/components/calendar/form/Categories.vue
index ad03ed9..e944f9c 100644
--- a/components/calendar/form/Categories.vue
+++ b/components/calendar/form/Categories.vue
@@ -3,5 +3,5 @@ const { categories } = storeToRefs(useCalendar())
-
+
diff --git a/components/global/input/Color.vue b/components/global/input/Color.vue
index 871b22c..6fd5656 100644
--- a/components/global/input/Color.vue
+++ b/components/global/input/Color.vue
@@ -2,8 +2,10 @@
import { cn } from "@/lib/utils";
import { type RPGColor, rpgColors } from "~/models/Color";
-defineProps<{
+const { id, theme = "normal", position = "popper" } = defineProps<{
id: string
+ theme?: "normal" | "subtle"
+ position?: "item-aligned" | "popper" | undefined
}>();
const model = defineModel({ default: "white" });
@@ -11,27 +13,23 @@ const model = defineModel({ default: "white" });
-
+
-
+
@@ -41,76 +39,3 @@ const model = defineModel({ default: "white" });
-
-
diff --git a/components/ui/select/SelectTrigger.vue b/components/ui/select/SelectTrigger.vue
index 3bd70da..cd9940d 100644
--- a/components/ui/select/SelectTrigger.vue
+++ b/components/ui/select/SelectTrigger.vue
@@ -20,7 +20,7 @@ const forwardedProps = useForwardProps(delegatedProps)
v-bind="forwardedProps"
:class="
cn(
- 'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background dark:bg-slate-950 contrast-more:dark:bg-slate-900 px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1',
+ 'flex h-10 w-full items-center justify-between rounded-md border border-input bg-background dark:bg-slate-950 contrast-more:dark:bg-slate-900 px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
props.class
)
"
diff --git a/components/ui/table/Table.vue b/components/ui/table/Table.vue
new file mode 100644
index 0000000..26c7c45
--- /dev/null
+++ b/components/ui/table/Table.vue
@@ -0,0 +1,16 @@
+
+
+
+
+
diff --git a/components/ui/table/TableBody.vue b/components/ui/table/TableBody.vue
new file mode 100644
index 0000000..fadb9a7
--- /dev/null
+++ b/components/ui/table/TableBody.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/components/ui/table/TableCaption.vue b/components/ui/table/TableCaption.vue
new file mode 100644
index 0000000..a2bf57f
--- /dev/null
+++ b/components/ui/table/TableCaption.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/components/ui/table/TableCell.vue b/components/ui/table/TableCell.vue
new file mode 100644
index 0000000..4a86e3a
--- /dev/null
+++ b/components/ui/table/TableCell.vue
@@ -0,0 +1,21 @@
+
+
+
+
+
+ |
+
diff --git a/components/ui/table/TableEmpty.vue b/components/ui/table/TableEmpty.vue
new file mode 100644
index 0000000..a9d62c9
--- /dev/null
+++ b/components/ui/table/TableEmpty.vue
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/components/ui/table/TableFooter.vue b/components/ui/table/TableFooter.vue
new file mode 100644
index 0000000..7f3bf64
--- /dev/null
+++ b/components/ui/table/TableFooter.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/components/ui/table/TableHead.vue b/components/ui/table/TableHead.vue
new file mode 100644
index 0000000..ddf635c
--- /dev/null
+++ b/components/ui/table/TableHead.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+ |
+
diff --git a/components/ui/table/TableHeader.vue b/components/ui/table/TableHeader.vue
new file mode 100644
index 0000000..66e1e60
--- /dev/null
+++ b/components/ui/table/TableHeader.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/components/ui/table/TableRow.vue b/components/ui/table/TableRow.vue
new file mode 100644
index 0000000..036db30
--- /dev/null
+++ b/components/ui/table/TableRow.vue
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
diff --git a/components/ui/table/index.ts b/components/ui/table/index.ts
new file mode 100644
index 0000000..2b4ce39
--- /dev/null
+++ b/components/ui/table/index.ts
@@ -0,0 +1,9 @@
+export { default as Table } from './Table.vue'
+export { default as TableBody } from './TableBody.vue'
+export { default as TableCaption } from './TableCaption.vue'
+export { default as TableCell } from './TableCell.vue'
+export { default as TableEmpty } from './TableEmpty.vue'
+export { default as TableFooter } from './TableFooter.vue'
+export { default as TableHead } from './TableHead.vue'
+export { default as TableHeader } from './TableHeader.vue'
+export { default as TableRow } from './TableRow.vue'
From b32fab81f67522cfe9872e78f0f7e2c9360edb29 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Sun, 6 Apr 2025 14:41:46 +0200
Subject: [PATCH 11/24] Added category update
---
components/calendar/category/Table.vue | 8 +-
components/calendar/category/TableRow.vue | 179 ++++++++++--------
models/Category.ts | 2 +-
server/api/calendars/categories/[id].patch.ts | 72 +++++++
stores/CalendarStore.ts | 2 -
stores/CategoryStore.ts | 62 ++++++
supabase/migrations/202401_init.sql | 14 ++
utils/TryCatch.ts | 31 +++
8 files changed, 281 insertions(+), 89 deletions(-)
create mode 100644 server/api/calendars/categories/[id].patch.ts
create mode 100644 stores/CategoryStore.ts
create mode 100644 utils/TryCatch.ts
diff --git a/components/calendar/category/Table.vue b/components/calendar/category/Table.vue
index 7169384..f048008 100644
--- a/components/calendar/category/Table.vue
+++ b/components/calendar/category/Table.vue
@@ -2,9 +2,13 @@
import type { Category } from "~/models/Category";
import { ScrollAreaRoot, ScrollAreaViewport, ScrollAreaScrollbar, ScrollAreaThumb } from "radix-vue"
-defineProps<{
+const { categories } = defineProps<{
categories: Category[]
}>()
+
+const sortedCategories = computed(() => {
+ return categories.toSorted((a, b) => a.name.localeCompare(b.name))
+})
@@ -23,7 +27,7 @@ defineProps<{
diff --git a/components/calendar/category/TableRow.vue b/components/calendar/category/TableRow.vue
index 85c33d0..3db811e 100644
--- a/components/calendar/category/TableRow.vue
+++ b/components/calendar/category/TableRow.vue
@@ -1,18 +1,18 @@
-
-
+
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+ {{ $t(`ui.colors.${category.color}`) }}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/models/Category.ts b/models/Category.ts
index af2849a..b099386 100644
--- a/models/Category.ts
+++ b/models/Category.ts
@@ -10,5 +10,5 @@ export interface Category {
export const categorySchema = z.object({
id: z.number().int(),
name: z.string(),
- color: z.string().default("black")
+ color: z.string().default("black"),
})
diff --git a/server/api/calendars/categories/[id].patch.ts b/server/api/calendars/categories/[id].patch.ts
new file mode 100644
index 0000000..26d7131
--- /dev/null
+++ b/server/api/calendars/categories/[id].patch.ts
@@ -0,0 +1,72 @@
+import { z } from "zod"
+import { serverSupabaseClient } from "#supabase/server"
+import type { Category} from "~/models/Category";
+import { categorySchema } 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)
+ const { data: bodyData, error: bodyError } = await readValidatedBody(event, body => categorySchema.safeParse(body))
+
+ if (paramsError) {
+ throw createError({
+ cause: "Utilisateur",
+ fatal: false,
+ message: "L'identifiant de la catégorie est manquant ou mal renseigné.",
+ status: 401,
+ })
+ }
+
+ if (bodyError) {
+ const error = createError({
+ cause: "Utilisateur",
+ fatal: false,
+ statusCode: 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 {
+ const { data, error } = await client
+ .from("calendar_event_categories")
+ .update(
+ {
+ name: bodyData.name,
+ color: bodyData.color,
+ } as never
+ )
+ .eq("id", params.id)
+ .select(`
+ id,
+ name,
+ color
+ `)
+ .single()
+
+ 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/CalendarStore.ts b/stores/CalendarStore.ts
index 63277b3..57a94fe 100644
--- a/stores/CalendarStore.ts
+++ b/stores/CalendarStore.ts
@@ -61,8 +61,6 @@ export const useCalendar = defineStore("calendar", () => {
const months = ref([])
function setActiveCalendar(calendarData: Calendar, categoryData: Category[]) {
- console.log("proced")
-
try {
if (!calendarData.id) return
diff --git a/stores/CategoryStore.ts b/stores/CategoryStore.ts
new file mode 100644
index 0000000..70a00f3
--- /dev/null
+++ b/stores/CategoryStore.ts
@@ -0,0 +1,62 @@
+import type { Category } from "~/models/Category";
+
+export const useCategoryStore = defineStore("calendar-category", () => {
+ const { categories } = storeToRefs(useCalendar())
+
+ /**
+ * Dummy event to hold creation data
+ */
+ const isCreatingCategory = ref(false)
+ const isUpdatingCategory = ref(false)
+ const isDeletingCategory = ref(false)
+ const categorySkeleton = ref(null);
+
+ const operationInProgress = computed(() => isCreatingCategory.value || isUpdatingCategory.value || isDeletingCategory.value)
+ let abortController: AbortController | null = null
+
+ /**
+ * Resets the dummy category data
+ */
+ function resetSkeleton() {
+ categorySkeleton.value = null
+ }
+
+ async function updateCategoryFromSkeleton() {
+ if (!categorySkeleton.value) return
+
+ abortController = new AbortController()
+ isUpdatingCategory.value = true
+
+ const body = categorySkeleton.value
+
+ const { data, error } = await tryCatch(
+ $fetch(`/api/calendars/categories/${categorySkeleton.value.id}`, { method: "PATCH", body, signal: abortController.signal })
+ )
+
+ if (error) {
+ isUpdatingCategory.value = false
+ throw error
+ }
+
+ // Update the category in the store
+ categories.value = categories.value.map((category) => {
+ if (category.id === data.id) {
+ return { ...category, ...data }
+ }
+ return category
+ })
+
+ abortController = null
+ isUpdatingCategory.value = false
+ }
+
+ return {
+ isCreatingCategory,
+ isUpdatingCategory,
+ isDeletingCategory,
+ operationInProgress,
+ categorySkeleton,
+ resetSkeleton,
+ updateCategoryFromSkeleton
+ }
+})
diff --git a/supabase/migrations/202401_init.sql b/supabase/migrations/202401_init.sql
index e8683e5..45f19a8 100644
--- a/supabase/migrations/202401_init.sql
+++ b/supabase/migrations/202401_init.sql
@@ -440,6 +440,20 @@ create policy "Allow anonymous access to published event categories" on public.c
)
);
+create policy "Allow GMs to update their events categories"
+ on public.calendar_event_categories
+ for update
+ 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;
diff --git a/utils/TryCatch.ts b/utils/TryCatch.ts
new file mode 100644
index 0000000..312514d
--- /dev/null
+++ b/utils/TryCatch.ts
@@ -0,0 +1,31 @@
+// Types for the result object with discriminated union
+type Success = {
+ data: T;
+ error: null;
+};
+
+type Failure = {
+ data: null;
+ error: E;
+};
+
+type Result = Success | Failure;
+
+/**
+ * A utility function to handle promises with try-catch
+ * and return a result object, courtesy of t3.
+ *
+ * @param promise The promise to be executed
+ * @returns A promise that resolves to an object with either the data or the error
+ * @link https://gist.github.com/t3dotgg/a486c4ae66d32bf17c09c73609dacc5b
+ */
+export async function tryCatch(
+ promise: Promise,
+): Promise> {
+ try {
+ const data = await promise;
+ return { data, error: null };
+ } catch (error) {
+ return { data: null, error: error as E };
+ }
+}
From aa76b17c3750ab7e4263931220f531bb4b90ba23 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Sun, 6 Apr 2025 15:45:26 +0200
Subject: [PATCH 12/24] Changed lazy fetch for reactive store data
---
components/calendar/Calendar.vue | 11 -----------
pages/calendars/[id].vue | 10 +++++++++-
pages/my/calendars/[id].vue | 13 ++++++++++---
3 files changed, 19 insertions(+), 15 deletions(-)
diff --git a/components/calendar/Calendar.vue b/components/calendar/Calendar.vue
index 49c6411..d39f995 100644
--- a/components/calendar/Calendar.vue
+++ b/components/calendar/Calendar.vue
@@ -8,17 +8,6 @@ import CenturyLayout from "./state/centennially/Layout.vue"
import DecadeLayout from "./state/decennially/Layout.vue"
import YearLayout from "./state/yearly/Layout.vue"
-import type { Calendar } from "~/models/CalendarConfig"
-import type { Category } from "~/models/Category"
-
-const props = defineProps<{
- calendarData: Calendar,
- categories: Category[]
-}>()
-
-const { setActiveCalendar } = useCalendar()
-setActiveCalendar(props.calendarData, props.categories)
-
const { currentConfig, jumpToDate, selectedDate } = useCalendar()
const { isReadOnly } = storeToRefs(useCalendar())
diff --git a/pages/calendars/[id].vue b/pages/calendars/[id].vue
index e17b120..cf01f48 100644
--- a/pages/calendars/[id].vue
+++ b/pages/calendars/[id].vue
@@ -36,6 +36,14 @@ watch(user, () => {
calRefresh()
catRefresh()
})
+
+const { setActiveCalendar } = useCalendar()
+watch(isLoading, (n) => {
+ if (!n && calendar.value?.data && categories.value?.data) {
+ setActiveCalendar(calendar.value?.data, categories.value.data)
+ }
+}, { immediate: true }
+)
@@ -57,7 +65,7 @@ watch(user, () => {
{{ calendar.data.name }}
-
+
diff --git a/pages/my/calendars/[id].vue b/pages/my/calendars/[id].vue
index 5d0e96a..7291f2d 100644
--- a/pages/my/calendars/[id].vue
+++ b/pages/my/calendars/[id].vue
@@ -34,12 +34,19 @@ const {
const {
data: categories,
- status: categoriesStatus
+ status: categoriesStatus,
} = await useLazyFetch<{ data: Category[] }>("/api/calendars/categories/query",
{ key: "active-categories" }
)
-
const isLoading = computed(() => calendarStatus.value === "pending" || categoriesStatus.value === "pending")
+
+const { setActiveCalendar } = useCalendar()
+watch(isLoading, (n) => {
+ if (!n && calendar.value?.data && categories.value?.data) {
+ setActiveCalendar(calendar.value?.data, categories.value.data)
+ }
+}, { immediate: true }
+)
@@ -72,7 +79,7 @@ const isLoading = computed(() => calendarStatus.value === "pending" || categorie
/>
-
+
From 02fba36421bc321a188c1c3bc50f3de239150cb2 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Sun, 6 Apr 2025 16:08:45 +0200
Subject: [PATCH 13/24] Add watch function if categories change
---
stores/CalendarStore.ts | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
diff --git a/stores/CalendarStore.ts b/stores/CalendarStore.ts
index 57a94fe..3799877 100644
--- a/stores/CalendarStore.ts
+++ b/stores/CalendarStore.ts
@@ -910,6 +910,28 @@ export const useCalendar = defineStore("calendar", () => {
}
}
+ /**
+ * Updates all events categories
+ * This is used when the user updates a category and we want to update all events in the client
+ * @param categories The new categories
+ * @returns void
+ */
+ function updateAllEventsCategories(categories: Category[]) {
+ baseEvents.value.forEach((event) => {
+ if (event.category?.id) {
+ const category = categories.find((c) => c.id === event.category?.id)
+ if (category) {
+ event.category = category
+ }
+ }
+ })
+ }
+
+ // Watch for categories changes
+ watch(categories, (n) => {
+ updateAllEventsCategories(n)
+ })
+
/**
* State for categories modal
*/
From f00e049f76d47dd0d36bb1238ee7056bc8da71a1 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Sun, 6 Apr 2025 16:18:09 +0200
Subject: [PATCH 14/24] Added toast messages
---
components/calendar/category/TableRow.vue | 25 +++++++++++++++++++----
i18n.config.ts | 12 +++++++++--
2 files changed, 31 insertions(+), 6 deletions(-)
diff --git a/components/calendar/category/TableRow.vue b/components/calendar/category/TableRow.vue
index 3db811e..ed0986d 100644
--- a/components/calendar/category/TableRow.vue
+++ b/components/calendar/category/TableRow.vue
@@ -1,9 +1,14 @@
diff --git a/i18n.config.ts b/i18n.config.ts
index 6715aad..e2ead9a 100644
--- a/i18n.config.ts
+++ b/i18n.config.ts
@@ -97,7 +97,11 @@ export default defineI18nConfig(() => ({
manageDialog: {
title: "Manage categories",
subtitle: "Add and change the categories of your calendar",
- }
+ },
+ updatedToast: {
+ title: "The category \"{category}\" has been successfuly updated.",
+ titleError: "An error has occured and the category \"{category}\" wasn't updated.",
+ },
},
isLoading: "Loading in progress…",
addDescription: "Add a description",
@@ -404,7 +408,11 @@ export default defineI18nConfig(() => ({
manageDialog: {
title: "Gestion des catégories",
subtitle: "Créer et modifier les catégories de votre calendrier",
- }
+ },
+ updatedToast: {
+ title: "La catégorie \"{category}\" a été modifiée avec succès.",
+ titleError: "Une erreur s'est produite et la catégorie \"{category}\" n'a pas pu être modifiée.",
+ },
},
isLoading: "Chargement en cours…",
addDescription: "Ajouter une description",
From c25c10f7b75ad67dd40b00d63d3336d5d0c6a17a Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Tue, 15 Apr 2025 11:18:26 +0200
Subject: [PATCH 15/24] Fixed categories not updating
---
components/calendar/category/Table.vue | 4 +---
pages/my/calendars/[id].vue | 7 +++----
stores/CalendarStore.ts | 9 ++++-----
3 files changed, 8 insertions(+), 12 deletions(-)
diff --git a/components/calendar/category/Table.vue b/components/calendar/category/Table.vue
index f048008..bf22252 100644
--- a/components/calendar/category/Table.vue
+++ b/components/calendar/category/Table.vue
@@ -6,9 +6,7 @@ const { categories } = defineProps<{
categories: Category[]
}>()
-const sortedCategories = computed(() => {
- return categories.toSorted((a, b) => a.name.localeCompare(b.name))
-})
+const sortedCategories = computed(() => categories.toSorted((a, b) => a.name.localeCompare(b.name)))
diff --git a/pages/my/calendars/[id].vue b/pages/my/calendars/[id].vue
index 7291f2d..a515e64 100644
--- a/pages/my/calendars/[id].vue
+++ b/pages/my/calendars/[id].vue
@@ -41,12 +41,11 @@ const {
const isLoading = computed(() => calendarStatus.value === "pending" || categoriesStatus.value === "pending")
const { setActiveCalendar } = useCalendar()
-watch(isLoading, (n) => {
- if (!n && calendar.value?.data && categories.value?.data) {
+watch([calendar, categories], () => {
+ if (calendar.value?.data && categories.value?.data) {
setActiveCalendar(calendar.value?.data, categories.value.data)
}
-}, { immediate: true }
-)
+}, { immediate: true })
diff --git a/stores/CalendarStore.ts b/stores/CalendarStore.ts
index 3799877..c5f775b 100644
--- a/stores/CalendarStore.ts
+++ b/stores/CalendarStore.ts
@@ -918,11 +918,10 @@ export const useCalendar = defineStore("calendar", () => {
*/
function updateAllEventsCategories(categories: Category[]) {
baseEvents.value.forEach((event) => {
- if (event.category?.id) {
- const category = categories.find((c) => c.id === event.category?.id)
- if (category) {
- event.category = category
- }
+ const category = categories.find((c) => c.id === event.category?.id)
+
+ if (category && event.category?.id) {
+ event.category = category
}
})
}
From 58f3783b13ee09a55f271e506e42a17a323c233b Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Tue, 15 Apr 2025 15:34:05 +0200
Subject: [PATCH 16/24] Added category creation interface
---
components/calendar/category/Table.vue | 12 +-
components/calendar/category/TableFooter.vue | 130 ++++++++++++++++++
components/calendar/category/TableHeader.vue | 19 +++
components/calendar/category/TableRow.vue | 21 +--
components/global/input/Color.vue | 2 +-
i18n.config.ts | 8 ++
models/CalendarEvent.ts | 8 +-
models/Category.ts | 10 +-
server/api/calendars/categories/[id].patch.ts | 5 +-
.../api/calendars/categories/create.post.ts | 56 ++++++++
stores/CategoryStore.ts | 32 ++++-
supabase/migrations/202401_init.sql | 14 ++
12 files changed, 285 insertions(+), 32 deletions(-)
create mode 100644 components/calendar/category/TableFooter.vue
create mode 100644 components/calendar/category/TableHeader.vue
create mode 100644 server/api/calendars/categories/create.post.ts
diff --git a/components/calendar/category/Table.vue b/components/calendar/category/Table.vue
index bf22252..c90a9c8 100644
--- a/components/calendar/category/Table.vue
+++ b/components/calendar/category/Table.vue
@@ -11,15 +11,7 @@ const sortedCategories = computed(() => categories.toSorted((a, b) => a.name.loc
-
-
- Nom
-
-
- Couleur
-
-
-
+
@@ -40,5 +32,7 @@ const sortedCategories = computed(() => categories.toSorted((a, b) => a.name.loc
/>
+
+
diff --git a/components/calendar/category/TableFooter.vue b/components/calendar/category/TableFooter.vue
new file mode 100644
index 0000000..4b4616f
--- /dev/null
+++ b/components/calendar/category/TableFooter.vue
@@ -0,0 +1,130 @@
+
+
+
+
+
diff --git a/components/calendar/category/TableHeader.vue b/components/calendar/category/TableHeader.vue
new file mode 100644
index 0000000..7f454b7
--- /dev/null
+++ b/components/calendar/category/TableHeader.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+ Nom
+
+
+ Couleur
+
+
+
+
+
diff --git a/components/calendar/category/TableRow.vue b/components/calendar/category/TableRow.vue
index ed0986d..4690e3e 100644
--- a/components/calendar/category/TableRow.vue
+++ b/components/calendar/category/TableRow.vue
@@ -34,10 +34,10 @@ function toggleView(options: ToggleViewOptions = { execution: "now" }) {
currentMode.value = "view"
if (options.execution === "now") {
- inputFocused.value = false;
+ inputFocused.value = false
} else {
nextTick(() => {
- inputFocused.value = false;
+ inputFocused.value = false
})
}
}
@@ -46,11 +46,11 @@ function toggleView(options: ToggleViewOptions = { execution: "now" }) {
* Toggle edit mode
*/
function toggleEdit() {
- currentMode.value = "edit";
+ currentMode.value = "edit"
categorySkeleton.value = structuredClone(toRaw(category))
nextTick(() => {
- inputFocused.value = true;
+ inputFocused.value = true
})
}
@@ -66,7 +66,6 @@ onUnmounted(() => {
/**
* Submit the update
- * TODO: Implement the update logic
*/
async function submitUpdate() {
const { error } = await tryCatch(updateCategoryFromSkeleton())
@@ -90,18 +89,20 @@ async function submitUpdate() {
-
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;
From ed1252add4347b6db7e1639526fd59f2c3ead1ed Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Tue, 15 Apr 2025 20:33:41 +0200
Subject: [PATCH 18/24] Cleanup console logs
---
server/api/calendars/categories/[id].delete.ts | 2 --
1 file changed, 2 deletions(-)
diff --git a/server/api/calendars/categories/[id].delete.ts b/server/api/calendars/categories/[id].delete.ts
index fdafa6b..fb2e568 100644
--- a/server/api/calendars/categories/[id].delete.ts
+++ b/server/api/calendars/categories/[id].delete.ts
@@ -27,8 +27,6 @@ export default defineEventHandler(async (event) => {
.eq("id", params.id)
.maybeSingle()
- console.log(error)
-
if (error) throw error
return data
From a50824bd4fd02e5bf66f9ba961db6f224dc00a63 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Tue, 15 Apr 2025 20:34:01 +0200
Subject: [PATCH 19/24] Cleaned up more console logs
---
server/api/calendars/[id].patch.ts | 2 --
server/api/calendars/create.post.ts | 1 -
server/api/calendars/months/[id].patch.ts | 2 --
server/api/calendars/months/create.post.ts | 1 -
server/api/worlds/create.post.ts | 1 -
5 files changed, 7 deletions(-)
diff --git a/server/api/calendars/[id].patch.ts b/server/api/calendars/[id].patch.ts
index 5cd66f6..7a143c5 100644
--- a/server/api/calendars/[id].patch.ts
+++ b/server/api/calendars/[id].patch.ts
@@ -23,8 +23,6 @@ export default defineEventHandler(async (event) => {
}
if (bodyError) {
- console.log(bodyData)
- console.log(bodyError)
const error = createError({
cause: "Utilisateur",
fatal: false,
diff --git a/server/api/calendars/create.post.ts b/server/api/calendars/create.post.ts
index ffd9678..bacd4b9 100644
--- a/server/api/calendars/create.post.ts
+++ b/server/api/calendars/create.post.ts
@@ -8,7 +8,6 @@ export default defineEventHandler(async (event) => {
const { data: bodyData, error: schemaError } = await readValidatedBody(event, body => postCalendarSchema.safeParse(body))
if (schemaError) {
- console.log(schemaError)
throw createError({
cause: "Utilisateur",
fatal: false,
diff --git a/server/api/calendars/months/[id].patch.ts b/server/api/calendars/months/[id].patch.ts
index 428c517..7e7c2ad 100644
--- a/server/api/calendars/months/[id].patch.ts
+++ b/server/api/calendars/months/[id].patch.ts
@@ -13,7 +13,6 @@ export default defineEventHandler(async (event) => {
const { data: bodyData, error: bodyError } = await readValidatedBody(event, body => calendarMonthSchema.safeParse(body))
if (paramsError) {
- console.log(paramsError)
throw createError({
cause: "Utilisateur",
fatal: false,
@@ -23,7 +22,6 @@ export default defineEventHandler(async (event) => {
}
if (bodyError) {
- console.log(bodyData)
throw createError({
cause: "Utilisateur",
fatal: false,
diff --git a/server/api/calendars/months/create.post.ts b/server/api/calendars/months/create.post.ts
index 789c19a..aa7c0b1 100644
--- a/server/api/calendars/months/create.post.ts
+++ b/server/api/calendars/months/create.post.ts
@@ -6,7 +6,6 @@ export default defineEventHandler(async (event) => {
const { data: bodyData, error: schemaError } = await readValidatedBody(event, body => calendarMonthSchema.safeParse(body))
if (schemaError) {
- console.log(schemaError)
throw createError({
cause: "Utilisateur",
fatal: false,
diff --git a/server/api/worlds/create.post.ts b/server/api/worlds/create.post.ts
index 198d5db..8591d3d 100644
--- a/server/api/worlds/create.post.ts
+++ b/server/api/worlds/create.post.ts
@@ -8,7 +8,6 @@ export default defineEventHandler(async (event) => {
const { data: bodyData, error: schemaError } = await readValidatedBody(event, body => postWorldSchema.safeParse(body))
if (schemaError) {
- console.log(schemaError)
throw createError({
cause: "Utilisateur",
fatal: false,
From f1abae1cce0343d3d5253abf94ffa0bb9e3dddc6 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Tue, 15 Apr 2025 21:09:02 +0200
Subject: [PATCH 20/24] Fixed client side error
---
components/calendar/dialog/CreateEvent.vue | 7 +++----
components/calendar/dialog/QuickCreateEvent.vue | 9 +++------
2 files changed, 6 insertions(+), 10 deletions(-)
diff --git a/components/calendar/dialog/CreateEvent.vue b/components/calendar/dialog/CreateEvent.vue
index b28d68a..866ce99 100644
--- a/components/calendar/dialog/CreateEvent.vue
+++ b/components/calendar/dialog/CreateEvent.vue
@@ -35,8 +35,7 @@ function openEventCreatePopover() {
*
* @param e The closing event (can be keydown or click)
*/
-function handleClosing(e: Event) {
- e.preventDefault()
+function handleClosing() {
popoverOpen.value = false
resetSkeleton()
}
@@ -54,8 +53,8 @@ function handleClosing(e: Event) {
:disable-outside-pointer-events="true"
:trap-focus="true"
class="pl-3 w-[30rem] max-w-full border-indigo-200 dark:bg-slate-950 dark:border-indigo-950"
- @escape-key-down="handleClosing"
- @pointer-down-outside="handleClosing"
+ @escape-key-down.prevent="handleClosing"
+ @pointer-down-outside.prevent="handleClosing"
>
diff --git a/components/calendar/dialog/QuickCreateEvent.vue b/components/calendar/dialog/QuickCreateEvent.vue
index 53f0446..41a9218 100644
--- a/components/calendar/dialog/QuickCreateEvent.vue
+++ b/components/calendar/dialog/QuickCreateEvent.vue
@@ -11,11 +11,8 @@ function toggleDialog() {
/**
* Prevents the modal from closing if's still loading
- *
- * @param e The closing event (can be keydown or click)
*/
-function handleClosing(e: Event) {
- e.preventDefault()
+function handleClosing() {
setTimeout(() => resetSkeleton(), 100)
}
@@ -32,8 +29,8 @@ function handleClosing(e: Event) {
{{ $t("entity.calendar.event.addSingle") }}
From 6abf89e8dbf491f1184cf2ccf92d5a1184099fbc Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Wed, 16 Apr 2025 10:49:11 +0200
Subject: [PATCH 21/24] Moved categories to the same scope as calendar events
---
components/calendar/search/CalendarSearch.vue | 5 ++---
models/CalendarConfig.ts | 5 +++--
pages/my/calendars/[id].vue | 17 +++++------------
server/api/calendars/categories/query.get.ts | 5 +++++
server/api/calendars/query.get.ts | 5 +++++
stores/CalendarStore.ts | 4 ++--
6 files changed, 22 insertions(+), 19 deletions(-)
diff --git a/components/calendar/search/CalendarSearch.vue b/components/calendar/search/CalendarSearch.vue
index 92e4b80..531413b 100644
--- a/components/calendar/search/CalendarSearch.vue
+++ b/components/calendar/search/CalendarSearch.vue
@@ -26,7 +26,7 @@ import {
import SearchList from "./lists/SearchList.vue"
import type { Category } from "~/models/Category"
-const { isAdvancedSearchOpen, allEvents } = storeToRefs(useCalendar())
+const { isAdvancedSearchOpen, allEvents, categories } = storeToRefs(useCalendar())
const { characters } = storeToRefs(useCharacters())
const searchInput = shallowRef()
@@ -207,8 +207,7 @@ watch([currentPage, selectedEntity], () => {
})
// Compute categories based on current selectedEntity
-const { data: resCategories } = await useFetch("/api/calendars/categories/query")
-const currentCategories = ref(resCategories.value?.data as Category[])
+const currentCategories = computed(() => categories.value)
const selectedCategories = ref<(Category)[]>([])
const categoryFilterOpened = ref(false)
diff --git a/models/CalendarConfig.ts b/models/CalendarConfig.ts
index 159a4f2..8c8d7e8 100644
--- a/models/CalendarConfig.ts
+++ b/models/CalendarConfig.ts
@@ -5,7 +5,7 @@ import { dateSchema, type RPGDate } from "./Date"
import type { World } from "./World"
import type { ContentState } from "./Entity"
import type { RPGColor } from "./Color"
-
+import type { Category } from "./Category"
export interface CalendarConfig {
months: CalendarMonth[]
@@ -16,7 +16,8 @@ export interface Calendar extends CalendarConfig {
id?: number
shortId?: string
name: string
- events: CalendarEvent[]
+ events: CalendarEvent[],
+ categories: Category[]
eventNb?: Array<{ count: number }>
state: ContentState
color?: RPGColor
diff --git a/pages/my/calendars/[id].vue b/pages/my/calendars/[id].vue
index a515e64..7d1ba95 100644
--- a/pages/my/calendars/[id].vue
+++ b/pages/my/calendars/[id].vue
@@ -1,7 +1,6 @@
@@ -62,7 +55,7 @@ watch([calendar, categories], () => {
-
+
{{ calendar.data.name }}
diff --git a/server/api/calendars/categories/query.get.ts b/server/api/calendars/categories/query.get.ts
index 92da7ee..4627884 100644
--- a/server/api/calendars/categories/query.get.ts
+++ b/server/api/calendars/categories/query.get.ts
@@ -4,6 +4,7 @@ import type { Category } from "~/models/Category";
const querySchema = z.object({
id: z.number({ coerce: true }).positive().int().optional(),
+ calendarId: z.number({ coerce: true }).positive().int()
})
export default defineEventHandler(async (event) => {
@@ -24,5 +25,9 @@ export default defineEventHandler(async (event) => {
return output.eq("id", query.id).limit(1).single()
}
+ if (query.calendarId) {
+ output.eq("calendar_id", query.calendarId)
+ }
+
return output.overrideTypes()
})
diff --git a/server/api/calendars/query.get.ts b/server/api/calendars/query.get.ts
index d3adcb5..d5b67c1 100644
--- a/server/api/calendars/query.get.ts
+++ b/server/api/calendars/query.get.ts
@@ -52,6 +52,11 @@ export default defineEventHandler(async (event) => {
category:calendar_event_categories!calendar_events_category_fkey (*),
secondaryCategories:calendar_event_categories!calendar_event_categories_links (*)
),
+ categories:calendar_event_categories (
+ id,
+ name,
+ color
+ ),
eventNb:calendar_events(count),
world:worlds (
id,
diff --git a/stores/CalendarStore.ts b/stores/CalendarStore.ts
index c5f775b..5e6545f 100644
--- a/stores/CalendarStore.ts
+++ b/stores/CalendarStore.ts
@@ -60,7 +60,7 @@ export const useCalendar = defineStore("calendar", () => {
*/
const months = ref([])
- function setActiveCalendar(calendarData: Calendar, categoryData: Category[]) {
+ function setActiveCalendar(calendarData: Calendar) {
try {
if (!calendarData.id) return
@@ -88,7 +88,7 @@ export const useCalendar = defineStore("calendar", () => {
months.value = calendarData.months
baseEvents.value = calendarData.events
- categories.value = categoryData
+ categories.value = calendarData.categories
} catch (err) {
console.log(err)
}
From db6ae367434643db6732f68cdc2c162a0c483bd6 Mon Sep 17 00:00:00 2001
From: Alexis <35.alexis.pele@gmail.com>
Date: Wed, 16 Apr 2025 10:50:39 +0200
Subject: [PATCH 22/24] Fixed short id calendar page data
---
pages/calendars/[id].vue | 22 +++++-----------------
1 file changed, 5 insertions(+), 17 deletions(-)
diff --git a/pages/calendars/[id].vue b/pages/calendars/[id].vue
index cf01f48..f459de0 100644
--- a/pages/calendars/[id].vue
+++ b/pages/calendars/[id].vue
@@ -1,7 +1,6 @@