Backend: - Add MCP Serializer to centralize entity-to-array conversion (~300 lines deduped) - Fix race condition in task/ticket number generation (SELECT FOR UPDATE + transaction) - Add unique constraint on task (project_id, number) with migration - Fix MIME type validation: use server-detected finfo instead of client-supplied type - Add allowlist of permitted MIME types for uploads - Fix TaskDocumentDownloadController: allow ROLE_CLIENT access, add priority:1 - Fix notification sent even when ticket status unchanged - Remove redundant exception constructors - Simplify services (BookStackApi double fetch, TokenEncryptor, GiteaApi) - Consolidate duplicate checks in processors Frontend: - Fix useApi isHandlingUnauthorized scope (module-level to prevent double 401 redirect) - Fix client-tickets toast key copy-paste bug - Merge duplicated tasks service methods (getByProject + getByProjectArchived) - Extract shared uploadWithRelation helper in task-documents service - Extract formatFileSize utility from duplicated component code - Extract status transition logic into useClientTicketHelpers composable - Remove dead code (unused router, handleLogout, empty script blocks) - Merge duplicate watchers and onMounted calls - Normalize arrow functions to function declarations per convention Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
49 lines
1.8 KiB
TypeScript
49 lines
1.8 KiB
TypeScript
import type { ClientTicketStatus } from '~/services/dto/client-ticket'
|
|
|
|
export function useClientTicketHelpers() {
|
|
function typeBadgeClass(type: string): string {
|
|
switch (type) {
|
|
case 'bug': return 'bg-red-500'
|
|
case 'improvement': return 'bg-blue-500'
|
|
default: return 'bg-neutral-500'
|
|
}
|
|
}
|
|
|
|
function statusBadgeClass(status: string): string {
|
|
switch (status) {
|
|
case 'new': return 'bg-blue-100 text-blue-700'
|
|
case 'in_progress': return 'bg-yellow-100 text-yellow-700'
|
|
case 'done': return 'bg-green-100 text-green-700'
|
|
case 'rejected': return 'bg-red-100 text-red-700'
|
|
default: return 'bg-neutral-100 text-neutral-700'
|
|
}
|
|
}
|
|
|
|
function formatDate(iso: string): string {
|
|
return new Date(iso).toLocaleDateString('fr-FR', {
|
|
day: 'numeric',
|
|
month: 'short',
|
|
year: 'numeric',
|
|
})
|
|
}
|
|
|
|
function getAvailableStatusTransitions(
|
|
current: ClientTicketStatus,
|
|
t: (key: string) => string,
|
|
): { label: string; value: ClientTicketStatus }[] {
|
|
const allStatuses: { label: string; value: ClientTicketStatus }[] = [
|
|
{ label: t('clientTicket.status.new'), value: 'new' },
|
|
{ label: t('clientTicket.status.in_progress'), value: 'in_progress' },
|
|
{ label: t('clientTicket.status.done'), value: 'done' },
|
|
{ label: t('clientTicket.status.rejected'), value: 'rejected' },
|
|
]
|
|
return allStatuses.filter(s => {
|
|
if (s.value === current) return false
|
|
if ((current === 'done' || current === 'rejected') && s.value === 'new') return false
|
|
return true
|
|
})
|
|
}
|
|
|
|
return { typeBadgeClass, statusBadgeClass, formatDate, getAvailableStatusTransitions }
|
|
}
|