6.4 KiB
6.4 KiB
Task Documents — Design Spec
Overview
Ajout d'un système de documents attachés aux tickets (tasks). Les utilisateurs peuvent uploader des fichiers via drag & drop ou sélection, les visualiser (images, PDF) dans une modale plein écran, et les télécharger.
Contraintes
- Taille max par fichier : 50 Mo
- Types acceptés : tous types de fichiers
- Nombre par ticket : illimité
- Stockage : filesystem local (
var/uploads/documents/) - Permissions : ROLE_ADMIN pour créer/supprimer, ROLE_USER pour lire
Backend
Entité TaskDocument
| Champ | Type | Description |
|---|---|---|
id |
int (auto) | Clé primaire |
task |
ManyToOne → Task | Ticket parent (CASCADE on delete) |
originalName |
string (255) | Nom original du fichier uploadé |
fileName |
string (255) | Nom unique sur disque ({uuid}.{extension}) |
mimeType |
string (100) | Type MIME (ex: image/png, application/pdf) |
size |
int | Taille en octets |
createdAt |
DateTimeImmutable | Date d'upload |
uploadedBy |
ManyToOne → User | Utilisateur ayant uploadé (SET NULL on delete) |
Relation inverse sur Task
Task.documents: OneToMany → TaskDocument- Sérialisé dans le groupe
task:readpour charger les documents avec le ticket
Stockage filesystem
- Répertoire :
var/uploads/documents/ - Nommage :
{uuid}.{extension}— évite les collisions et les caractères spéciaux - Volume Docker dédié pour persister les uploads
- Servir les fichiers via Nginx (
/uploads/{fileName}) ou un endpoint Symfony dédié pour contrôle d'accès
API Endpoints
| Méthode | Route | Description | Accès |
|---|---|---|---|
POST |
/api/task_documents |
Upload multipart/form-data | ROLE_ADMIN |
GET |
/api/task_documents?task=/api/tasks/{id} |
Liste documents d'un ticket | ROLE_USER |
GET |
/api/task_documents/{id} |
Métadonnées d'un document | ROLE_USER |
DELETE |
/api/task_documents/{id} |
Supprime document + fichier | ROLE_ADMIN |
State Processor — POST (TaskDocumentProcessor)
- Reçoit le fichier via multipart/form-data + IRI de la task
- Valide : fichier non vide, taille ≤ 50 Mo
- Génère un UUID v4, extrait l'extension du nom original
- Déplace le fichier uploadé dans
var/uploads/documents/{uuid}.{ext} - Crée et persiste l'entité
TaskDocumentavec toutes les métadonnées - Set
uploadedBydepuis le token JWT courant
State Processor — DELETE
- Récupère le
fileNamede l'entité - Supprime le fichier du filesystem (
var/uploads/documents/{fileName}) - Supprime l'entité de la base de données
Validation
- Contrainte sur
originalName: NotBlank - Contrainte sur
task: NotNull - Validation dans le Processor : taille fichier ≤ 50 Mo, fichier présent dans la requête
- PHP
upload_max_filesizeetpost_max_sizeà configurer ≥ 50 Mo
Configuration PHP/Nginx
php.ini:upload_max_filesize = 50M,post_max_size = 55M- Nginx :
client_max_body_size 55m;
Frontend
Placement dans l'UI
La zone de documents est placée sous la description dans le TaskModal, visible en mode édition.
Composants à créer
Tous dans frontend/components/task/ :
TaskDocumentUpload.vue
- Zone drag & drop avec bordure pointillée
- Texte : "Glisser des fichiers ici ou cliquer pour sélectionner"
- Input file caché (
multiple,accept="*") - Événements :
dragover,dragleave,droppour le feedback visuel - Barre de progression par fichier pendant l'upload
- Upload séquentiel ou parallèle (un POST multipart par fichier)
- Émet un événement quand l'upload est terminé pour rafraîchir la liste
TaskDocumentList.vue
- Grille de cartes compactes pour chaque document
- Images (
image/*) : miniature 64x64 enobject-fit: cover, chargée depuis l'URL du fichier - Autres fichiers : icône selon le type MIME :
- PDF → icône PDF
- Word/Excel → icônes Office
- Archives → icône archive
- Défaut → icône fichier générique
- Informations affichées : nom original (tronqué si > ~30 chars), taille formatée (Ko/Mo)
- Clic sur un document → ouvre
TaskDocumentPreview - Bouton supprimer (visible uniquement pour ROLE_ADMIN, avec confirmation)
TaskDocumentPreview.vue
- Modale plein écran (overlay sombre semi-transparent)
- Contenu selon le type :
- Images (
image/*) :<img>centré, taille adaptative - PDF (
application/pdf) :<iframe>intégré - Autres : grande icône + nom du fichier + taille + bouton "Télécharger"
- Images (
- Navigation : flèches gauche/droite pour parcourir les documents du ticket
- Fermeture : bouton X en haut à droite, clic sur l'overlay, touche Escape
- Raccourcis clavier : flèches pour naviguer, Escape pour fermer
Service API
frontend/services/task-documents.ts :
// Fonctions du service
getByTask(taskId: number): Promise<TaskDocument[]>
upload(taskId: number, file: File): Promise<TaskDocument> // POST multipart, un fichier à la fois
remove(id: number): Promise<void>
getFileUrl(fileName: string): string // Construit l'URL publique du fichier
DTO TypeScript
frontend/services/dto/task-document.ts :
type TaskDocument = {
id: number
task: string // IRI
originalName: string
fileName: string
mimeType: string
size: number
createdAt: string
uploadedBy: string // IRI
}
Intégration dans TaskModal
- Import des 3 composants dans
TaskModal.vue - Sous le champ description :
TaskDocumentUpload(si mode édition, ROLE_ADMIN)TaskDocumentList(toujours visible, passe les documents du ticket)
TaskDocumentPreviewmonté conditionnellement (v-if sur document sélectionné)- Chargement des documents : via la relation
task.documentsdéjà sérialisée, ou appel séparé au service
Migration
- Nouvelle table
task_documentavec les colonnes correspondant à l'entité - Index sur
task_idpour les requêtes filtrées - Clé étrangère
task_id→task.idON DELETE CASCADE - Clé étrangère
uploaded_by_id→user.idON DELETE SET NULL
Docker
- Ajouter un volume dans
docker-compose.ymlpourvar/uploads/afin de persister les fichiers - Vérifier la config PHP pour
upload_max_filesizeetpost_max_size - Ajouter une location Nginx pour servir
/uploads/si accès direct souhaité