feat(workflow) : DTOs front Workflow + category sur TaskStatus + workflow embarqué sur Project + service + i18n

This commit is contained in:
2026-05-19 20:00:55 +02:00
parent 6a084489ea
commit 18bc96082f
5 changed files with 122 additions and 0 deletions

View File

@@ -56,6 +56,37 @@
"moveTo": "Déplacer vers", "moveTo": "Déplacer vers",
"backlog": "Backlog (sans statut)" "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": { "taskEfforts": {
"created": "Effort créé avec succès.", "created": "Effort créé avec succès.",
"updated": "Effort mis à jour avec succès.", "updated": "Effort mis à jour avec succès.",

View File

@@ -1,4 +1,5 @@
import type { Client } from './client' import type { Client } from './client'
import type { Workflow } from './workflow'
export type Project = { export type Project = {
id: number id: number
@@ -8,6 +9,7 @@ export type Project = {
description: string | null description: string | null
color: string color: string
client: Client | null client: Client | null
workflow: Workflow
giteaOwner: string | null giteaOwner: string | null
giteaRepo: string | null giteaRepo: string | null
bookstackShelfId: number | null bookstackShelfId: number | null
@@ -22,6 +24,7 @@ export type ProjectWrite = {
description: string | null description: string | null
color: string color: string
client: string | null // IRI : "/api/clients/1" ou null client: string | null // IRI : "/api/clients/1" ou null
workflow?: string // IRI : "/api/workflows/1"
giteaOwner?: string | null giteaOwner?: string | null
giteaRepo?: string | null giteaRepo?: string | null
bookstackShelfId?: number | null bookstackShelfId?: number | null

View File

@@ -1,3 +1,5 @@
import type { StatusCategory } from './workflow'
export type TaskStatus = { export type TaskStatus = {
id: number id: number
'@id'?: string '@id'?: string
@@ -5,6 +7,8 @@ export type TaskStatus = {
color: string color: string
position: number position: number
isFinal: boolean isFinal: boolean
category: StatusCategory
workflow?: { '@id': string, id: number } | string
} }
export type TaskStatusWrite = { export type TaskStatusWrite = {
@@ -12,4 +16,6 @@ export type TaskStatusWrite = {
color: string color: string
position: number position: number
isFinal: boolean isFinal: boolean
category: StatusCategory
workflow?: string
} }

View File

@@ -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<StatusCategory, string> = {
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[]
}

View File

@@ -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<string, number | null>
}
type SwitchResult = {
projectId: number
workflowId: number
migratedTaskCount: number
}
export function useWorkflowService() {
const api = useApi()
async function getAll(): Promise<Workflow[]> {
const data = await api.get<HydraCollection<Workflow>>('/workflows')
return extractHydraMembers(data)
}
async function getOne(id: number): Promise<Workflow> {
return api.get<Workflow>(`/workflows/${id}`)
}
async function create(payload: WorkflowWrite): Promise<Workflow> {
return api.post<Workflow>('/workflows', payload as Record<string, unknown>, {
toastSuccessKey: 'workflows.created',
})
}
async function update(id: number, payload: Partial<WorkflowWrite>): Promise<Workflow> {
return api.patch<Workflow>(`/workflows/${id}`, payload as Record<string, unknown>, {
toastSuccessKey: 'workflows.updated',
})
}
async function remove(id: number): Promise<void> {
await api.delete(`/workflows/${id}`, {}, {
toastSuccessKey: 'workflows.deleted',
})
}
async function switchOnProject(projectId: number, payload: SwitchPayload): Promise<SwitchResult> {
return api.post<SwitchResult>(
`/projects/${projectId}/switch-workflow`,
payload as unknown as Record<string, unknown>,
{ toastSuccessKey: 'workflows.switched' },
)
}
return { getAll, getOne, create, update, remove, switchOnProject }
}