From 18bc96082f7cc07960b1fcfab785db1c1a3cbc38 Mon Sep 17 00:00:00 2001 From: matthieu Date: Tue, 19 May 2026 20:00:55 +0200 Subject: [PATCH] =?UTF-8?q?feat(workflow)=20:=20DTOs=20front=20Workflow=20?= =?UTF-8?q?+=20category=20sur=20TaskStatus=20+=20workflow=20embarqu=C3=A9?= =?UTF-8?q?=20sur=20Project=20+=20service=20+=20i18n?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/i18n/locales/fr.json | 31 ++++++++++++++++ frontend/services/dto/project.ts | 3 ++ frontend/services/dto/task-status.ts | 6 +++ frontend/services/dto/workflow.ts | 27 ++++++++++++++ frontend/services/workflows.ts | 55 ++++++++++++++++++++++++++++ 5 files changed, 122 insertions(+) create mode 100644 frontend/services/dto/workflow.ts create mode 100644 frontend/services/workflows.ts diff --git a/frontend/i18n/locales/fr.json b/frontend/i18n/locales/fr.json index b30adc6..df24394 100644 --- a/frontend/i18n/locales/fr.json +++ b/frontend/i18n/locales/fr.json @@ -56,6 +56,37 @@ "moveTo": "Déplacer vers", "backlog": "Backlog (sans statut)" }, + "workflows": { + "title": "Workflows", + "addWorkflow": "Ajouter un workflow", + "editWorkflow": "Modifier le workflow", + "name": "Nom", + "isDefault": "Workflow par défaut", + "statuses": "Statuts", + "addStatus": "Ajouter un statut", + "category": "Catégorie", + "created": "Workflow créé", + "updated": "Workflow mis à jour", + "deleted": "Workflow supprimé", + "switched": "Workflow du projet changé", + "switchTitle": "Changer de workflow", + "switchTargetLabel": "Nouveau workflow", + "switchMappingTitle": "Mapping des statuts", + "switchSourceCol": "Statut actuel", + "switchTargetCol": "Statut cible", + "switchTaskCountCol": "Tâches", + "switchToBacklog": "Mapper vers le backlog", + "switchConfirm": "Confirmer la migration", + "switchSummary": "{count} tâche(s) migrée(s), projet sur workflow « {name} »", + "deleteUsedBy": "Workflow utilisé par {count} projet(s) — impossible de supprimer.", + "categories": { + "todo": "À faire", + "in_progress": "En cours", + "blocked": "Bloqué", + "review": "En validation", + "done": "Terminé" + } + }, "taskEfforts": { "created": "Effort créé avec succès.", "updated": "Effort mis à jour avec succès.", diff --git a/frontend/services/dto/project.ts b/frontend/services/dto/project.ts index 10cbb3c..8e36770 100644 --- a/frontend/services/dto/project.ts +++ b/frontend/services/dto/project.ts @@ -1,4 +1,5 @@ import type { Client } from './client' +import type { Workflow } from './workflow' export type Project = { id: number @@ -8,6 +9,7 @@ export type Project = { description: string | null color: string client: Client | null + workflow: Workflow giteaOwner: string | null giteaRepo: string | null bookstackShelfId: number | null @@ -22,6 +24,7 @@ export type ProjectWrite = { description: string | null color: string client: string | null // IRI : "/api/clients/1" ou null + workflow?: string // IRI : "/api/workflows/1" giteaOwner?: string | null giteaRepo?: string | null bookstackShelfId?: number | null diff --git a/frontend/services/dto/task-status.ts b/frontend/services/dto/task-status.ts index 0b2de86..610d3b5 100644 --- a/frontend/services/dto/task-status.ts +++ b/frontend/services/dto/task-status.ts @@ -1,3 +1,5 @@ +import type { StatusCategory } from './workflow' + export type TaskStatus = { id: number '@id'?: string @@ -5,6 +7,8 @@ export type TaskStatus = { color: string position: number isFinal: boolean + category: StatusCategory + workflow?: { '@id': string, id: number } | string } export type TaskStatusWrite = { @@ -12,4 +16,6 @@ export type TaskStatusWrite = { color: string position: number isFinal: boolean + category: StatusCategory + workflow?: string } diff --git a/frontend/services/dto/workflow.ts b/frontend/services/dto/workflow.ts new file mode 100644 index 0000000..783dd1f --- /dev/null +++ b/frontend/services/dto/workflow.ts @@ -0,0 +1,27 @@ +import type { TaskStatus, TaskStatusWrite } from './task-status' + +export type StatusCategory = 'todo' | 'in_progress' | 'blocked' | 'review' | 'done' + +export const STATUS_CATEGORY_LABEL: Record = { + todo: 'À faire', + in_progress: 'En cours', + blocked: 'Bloqué', + review: 'En validation', + done: 'Terminé', +} + +export type Workflow = { + id: number + '@id'?: string + name: string + isDefault: boolean + position: number + statuses: TaskStatus[] +} + +export type WorkflowWrite = { + name: string + isDefault: boolean + position: number + statuses?: TaskStatusWrite[] +} diff --git a/frontend/services/workflows.ts b/frontend/services/workflows.ts new file mode 100644 index 0000000..1fd86cc --- /dev/null +++ b/frontend/services/workflows.ts @@ -0,0 +1,55 @@ +import type { Workflow, WorkflowWrite } from './dto/workflow' +import type { HydraCollection } from '~/utils/api' +import { extractHydraMembers } from '~/utils/api' + +type SwitchPayload = { + workflowId: number + mapping: Record +} + +type SwitchResult = { + projectId: number + workflowId: number + migratedTaskCount: number +} + +export function useWorkflowService() { + const api = useApi() + + async function getAll(): Promise { + const data = await api.get>('/workflows') + return extractHydraMembers(data) + } + + async function getOne(id: number): Promise { + return api.get(`/workflows/${id}`) + } + + async function create(payload: WorkflowWrite): Promise { + return api.post('/workflows', payload as Record, { + toastSuccessKey: 'workflows.created', + }) + } + + async function update(id: number, payload: Partial): Promise { + return api.patch(`/workflows/${id}`, payload as Record, { + toastSuccessKey: 'workflows.updated', + }) + } + + async function remove(id: number): Promise { + await api.delete(`/workflows/${id}`, {}, { + toastSuccessKey: 'workflows.deleted', + }) + } + + async function switchOnProject(projectId: number, payload: SwitchPayload): Promise { + return api.post( + `/projects/${projectId}/switch-workflow`, + payload as unknown as Record, + { toastSuccessKey: 'workflows.switched' }, + ) + } + + return { getAll, getOne, create, update, remove, switchOnProject } +}