From 859f3062ea9602a165d7e113fa5836e0d75fa396 Mon Sep 17 00:00:00 2001 From: cballevre Date: Thu, 31 Jul 2025 15:36:11 +0200 Subject: [PATCH 1/4] fix(interventions): Order list by date desc --- src/interventions/pages/list.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/interventions/pages/list.tsx b/src/interventions/pages/list.tsx index 3b46bb9..119e550 100644 --- a/src/interventions/pages/list.tsx +++ b/src/interventions/pages/list.tsx @@ -14,7 +14,7 @@ const InterventionList = () => { useInfiniteList({ resource: 'interventions', pagination: { pageSize: 50 }, - sorters: [{ field: 'id', order: 'desc' }], + sorters: [{ field: 'date', order: 'desc' }], }); const { getLocale, translate } = useTranslation(); From a3243d9d71cd53ffa3113456e10bdae306d5bd18 Mon Sep 17 00:00:00 2001 From: cballevre Date: Fri, 1 Aug 2025 09:35:45 +0200 Subject: [PATCH 2/4] feat: Add photos to equipment and intervention --- public/locales/en.json | 12 ++++- public/locales/fr.json | 12 ++++- src/equipments/pages/show.tsx | 11 +++- src/interventions/pages/show.tsx | 11 +++- src/shared/components/attachment-list.tsx | 54 ++++++++++++++++--- ...01073345_add_type_to_attachment_tables.sql | 13 +++++ supabase/schemas/equipment_attachments.sql | 1 + supabase/schemas/intervention_attachments.sql | 1 + 8 files changed, 102 insertions(+), 13 deletions(-) create mode 100644 supabase/migrations/20250801073345_add_type_to_attachment_tables.sql diff --git a/public/locales/en.json b/public/locales/en.json index 2d1a191..cf66186 100644 --- a/public/locales/en.json +++ b/public/locales/en.json @@ -282,8 +282,16 @@ }, "shared": { "attachments": { - "title": "Attachments", - "add": "Add an attachment", + "photo": { + "title": "Photos", + "empty": "No photos found", + "add": "Add photo" + }, + "document": { + "title": "Documents", + "empty": "No documents found", + "add": "Add document" + }, "download": "Download", "delete": "Delete" }, diff --git a/public/locales/fr.json b/public/locales/fr.json index 5e10621..1ffe8a9 100644 --- a/public/locales/fr.json +++ b/public/locales/fr.json @@ -282,8 +282,16 @@ }, "shared": { "attachments": { - "title": "Pièces jointes", - "add": "Ajouter une pièce jointe", + "photo": { + "title": "Photos", + "empty": "Aucune photo trouvée", + "add": "Ajouter une photo" + }, + "document": { + "title": "Documents", + "empty": "Aucun document trouvé", + "add": "Ajouter un document" + }, "download": "Télécharger", "delete": "Supprimer" }, diff --git a/src/equipments/pages/show.tsx b/src/equipments/pages/show.tsx index 81b1514..3eda91f 100644 --- a/src/equipments/pages/show.tsx +++ b/src/equipments/pages/show.tsx @@ -89,7 +89,16 @@ const ShowEquipment = () => { ) : null} - + + ); }; diff --git a/src/interventions/pages/show.tsx b/src/interventions/pages/show.tsx index 5bd2b1e..16b45b1 100644 --- a/src/interventions/pages/show.tsx +++ b/src/interventions/pages/show.tsx @@ -62,7 +62,16 @@ const ShowIntervention = () => { ) : null} - + + ); }; diff --git a/src/shared/components/attachment-list.tsx b/src/shared/components/attachment-list.tsx index 85655eb..cc147b5 100644 --- a/src/shared/components/attachment-list.tsx +++ b/src/shared/components/attachment-list.tsx @@ -4,7 +4,16 @@ import { PaperClipOutlined, } from '@ant-design/icons'; import { useCreate, useDelete, useList, useTranslate } from '@refinedev/core'; -import { Button, Card, Col, List, Row, Upload, type UploadProps } from 'antd'; +import { + Button, + Card, + Col, + Empty, + List, + Row, + Upload, + type UploadProps, +} from 'antd'; import type { FC } from 'react'; import { useCurrentBoat } from '@/boats/hooks/use-current-boat'; @@ -13,11 +22,16 @@ import { SectionHeader } from '@/shared/components/section-header'; import type { EquipmentAttachment } from '@/shared/types/models'; export type AttachmentListProps = { - resource: string; + resource: 'intervention' | 'equipment'; resourceId?: string; + type: 'photo' | 'document'; }; -const AttachmentList: FC = ({ resource, resourceId }) => { +const AttachmentList: FC = ({ + resource, + resourceId, + type, +}) => { const translate = useTranslate(); const { data: boat } = useCurrentBoat(); @@ -27,7 +41,10 @@ const AttachmentList: FC = ({ resource, resourceId }) => { const { data: attachments } = useList({ resource: attachmentResource, - filters: [{ field: resourceForeignKey, operator: 'eq', value: resourceId }], + filters: [ + { field: resourceForeignKey, operator: 'eq', value: resourceId }, + { field: 'type', operator: 'eq', value: type }, + ], }); const { mutate: createAttachment } = useCreate({ @@ -62,6 +79,7 @@ const AttachmentList: FC = ({ resource, resourceId }) => { file_name: uploadedFile.name, file_path: filePath, file_type: uploadedFile.type, + type, }, }); onSuccess?.('File uploaded successfully!'); @@ -107,9 +125,31 @@ const AttachmentList: FC = ({ resource, resourceId }) => { } }; + if (attachments?.data.length === 0) { + return ( +
+ + + + + + + + +
+ ); + } + return (
- + = ({ resource, resourceId }) => { )} />
diff --git a/supabase/migrations/20250801073345_add_type_to_attachment_tables.sql b/supabase/migrations/20250801073345_add_type_to_attachment_tables.sql new file mode 100644 index 0000000..b8bc183 --- /dev/null +++ b/supabase/migrations/20250801073345_add_type_to_attachment_tables.sql @@ -0,0 +1,13 @@ +alter table "public"."equipment_attachments" add column "type" text not null default 'document'; + +alter table "public"."intervention_attachments" add column "type" text not null default 'document'; + +alter table "public"."equipment_attachments" add constraint "equipment_attachments_type_check" CHECK ((type = ANY (ARRAY['photo'::text, 'document'::text]))) not valid; + +alter table "public"."equipment_attachments" validate constraint "equipment_attachments_type_check"; + +alter table "public"."intervention_attachments" add constraint "intervention_attachments_type_check" CHECK ((type = ANY (ARRAY['photo'::text, 'document'::text]))) not valid; + +alter table "public"."intervention_attachments" validate constraint "intervention_attachments_type_check"; + + diff --git a/supabase/schemas/equipment_attachments.sql b/supabase/schemas/equipment_attachments.sql index 2df4baf..fbf2586 100644 --- a/supabase/schemas/equipment_attachments.sql +++ b/supabase/schemas/equipment_attachments.sql @@ -4,6 +4,7 @@ create table public.equipment_attachments ( file_path text not null, file_name text not null, file_type text, + type text not null check (type in ('photo', 'document')) default 'document', description text, uploaded_at timestamp with time zone not null default now(), constraint equipment_attachments_pkey primary key (id), diff --git a/supabase/schemas/intervention_attachments.sql b/supabase/schemas/intervention_attachments.sql index 733e66d..2a73156 100644 --- a/supabase/schemas/intervention_attachments.sql +++ b/supabase/schemas/intervention_attachments.sql @@ -4,6 +4,7 @@ create table public.intervention_attachments ( file_path text not null, file_name text not null, file_type text, + type text not null check (type in ('photo', 'document')) default 'document', description text, uploaded_at timestamp with time zone not null default now(), constraint intervention_attachments_pkey primary key (id), From 0843001f4024b05050f2900bf4bf799d7f9b9d59 Mon Sep 17 00:00:00 2001 From: cballevre Date: Fri, 1 Aug 2025 09:43:16 +0200 Subject: [PATCH 3/4] feat(attachments): Add file name sanitization for uploads --- src/shared/components/attachment-list.tsx | 4 +++- src/shared/utils/sanitize-file-name.ts | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 src/shared/utils/sanitize-file-name.ts diff --git a/src/shared/components/attachment-list.tsx b/src/shared/components/attachment-list.tsx index cc147b5..3b61663 100644 --- a/src/shared/components/attachment-list.tsx +++ b/src/shared/components/attachment-list.tsx @@ -20,6 +20,7 @@ import { useCurrentBoat } from '@/boats/hooks/use-current-boat'; import { supabaseClient as supabase } from '@/core/utils/supabaseClient'; import { SectionHeader } from '@/shared/components/section-header'; import type { EquipmentAttachment } from '@/shared/types/models'; +import { sanitizeFileName } from '@/shared/utils/sanitize-file-name'; export type AttachmentListProps = { resource: 'intervention' | 'equipment'; @@ -60,7 +61,8 @@ const AttachmentList: FC = ({ }) => { try { const uploadedFile = file as File; - const filePath = `${boat?.data?.id}/${resource}s/${resourceId}/attachments/${Date.now()}_${uploadedFile.name}`; + const safeName = sanitizeFileName(uploadedFile.name); + const filePath = `${boat?.data?.id}/${resource}s/${resourceId}/attachments/${Date.now()}_${safeName}`; const { error: uploadError } = await supabase.storage .from(boatAttachmentBucket) diff --git a/src/shared/utils/sanitize-file-name.ts b/src/shared/utils/sanitize-file-name.ts new file mode 100644 index 0000000..c1bafb8 --- /dev/null +++ b/src/shared/utils/sanitize-file-name.ts @@ -0,0 +1,11 @@ +// Cleans a file name for S3/Supabase storage: +// - removes accents +// - replaces special characters with _ +// - keeps only letters, numbers, . _ - + +export function sanitizeFileName(name: string): string { + return name + .normalize('NFD') // removes accents + .replace(/[\u0300-\u036f]/g, '') + .replace(/[^a-zA-Z0-9._-]/g, '_'); // replaces everything except letters, numbers, . _ - +} From 66d2e9b1351e83a10af647b5b8b069fe77f7a5c4 Mon Sep 17 00:00:00 2001 From: cballevre Date: Fri, 1 Aug 2025 09:44:37 +0200 Subject: [PATCH 4/4] chore(copilot): Add instruction for commit message --- .github/copilot-instructions.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 0f311ab..8e7917e 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -23,6 +23,14 @@ This document guides AI agents to contribute effectively to the VesselVigil proj - **I18n**: translation files in `public/locales/`. - **Authentication**: handled via Supabase and Refine, see `src/auth/providers/auth-provider.ts`. +## Commit messages +- Use the conventional commits format: `type(scope): Description`. +- Types include: `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`. +- Use the imperative mood in the description (e.g., `feat: Add new boat management feature`). +- Include a short description of the change and, if necessary, a longer explanation in the body. +- Example: `feat(boats): Add boat management page with list and add functionality`. +- Always write in English + ## React librairies - **Refine**: used for data management and UI components. - **Ant Design**: UI components library.