diff --git a/components/FolderModal.css b/components/FolderModal.css index d4f624a..69a7cc2 100644 --- a/components/FolderModal.css +++ b/components/FolderModal.css @@ -1,17 +1,20 @@ -/* Folder Modal Styles */ +/* Container */ .folder-container { padding: 1.5rem; max-height: 70vh; overflow-y: auto; + background-color: #ffffff; + border-radius: 0.75rem; } +/* Form */ .folder-form { display: flex; flex-direction: column; gap: 2rem; } -/* Form Sections */ +/* Section */ .form-section { display: flex; flex-direction: column; @@ -21,13 +24,13 @@ .section-title { font-size: 1.125rem; font-weight: 600; - color: #374151; - margin: 0 0 0.5rem 0; + color: #111827; + margin: 0; padding-bottom: 0.5rem; border-bottom: 2px solid #e5e7eb; } -/* Form Layout */ +/* Layout */ .form-row { display: grid; grid-template-columns: 1fr 1fr; @@ -37,43 +40,44 @@ .form-field { display: flex; flex-direction: column; - gap: 0.5rem; + gap: 0.4rem; } .form-field label { - font-size: 0.875rem; + font-size: 0.85rem; font-weight: 500; color: #374151; } .required { color: #dc2626; + margin-left: 0.25rem; } -/* Form Inputs */ +/* Inputs */ .form-field input, .form-field textarea, .form-field select { padding: 0.75rem; border: 1px solid #d1d5db; border-radius: 0.5rem; - font-size: 0.875rem; + font-size: 0.9rem; transition: border-color 0.2s, box-shadow 0.2s; - background-color: white; + background-color: #fff; } .form-field input:focus, .form-field textarea:focus, .form-field select:focus { outline: none; - border-color: #3b82f6; - box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1); + border-color: #2563eb; + box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.15); } .form-field input:disabled, .form-field textarea:disabled, .form-field select:disabled { - background-color: #f9fafb; + background-color: #f3f4f6; color: #6b7280; cursor: not-allowed; } @@ -83,7 +87,7 @@ color: #9ca3af; } -/* Tag System */ +/* Tags */ .tag-list { display: flex; flex-wrap: wrap; @@ -94,24 +98,25 @@ .tag-item { display: flex; align-items: center; - gap: 0.5rem; - background-color: #eff6ff; - color: #1d4ed8; - padding: 0.375rem 0.75rem; + gap: 0.4rem; + background-color: #f0f9ff; + color: #0369a1; + padding: 0.35rem 0.75rem; border-radius: 9999px; - font-size: 0.875rem; - border: 1px solid #bfdbfe; + font-size: 0.85rem; + border: 1px solid #bae6fd; + font-weight: 500; } .tag-remove { display: flex; align-items: center; justify-content: center; - width: 1.25rem; - height: 1.25rem; + width: 1.1rem; + height: 1.1rem; border: none; background: none; - color: #1d4ed8; + color: #0369a1; cursor: pointer; border-radius: 50%; font-size: 1rem; @@ -120,7 +125,7 @@ } .tag-remove:hover { - background-color: #dbeafe; + background-color: #e0f2fe; } .tag-input-container { @@ -134,12 +139,12 @@ } .btn-add-tag { - padding: 0.75rem 1rem; - background-color: #3b82f6; + padding: 0.6rem 1rem; + background-color: #2563eb; color: white; border: none; border-radius: 0.5rem; - font-size: 0.875rem; + font-size: 0.85rem; font-weight: 500; cursor: pointer; transition: background-color 0.2s; @@ -147,10 +152,10 @@ } .btn-add-tag:hover { - background-color: #2563eb; + background-color: #1d4ed8; } -/* Form Actions */ +/* Actions */ .form-actions { display: flex; justify-content: flex-end; @@ -160,37 +165,37 @@ margin-top: 1rem; } -.btn-cancel { +.btn-cancel, +.btn-submit { padding: 0.75rem 1.5rem; - background-color: white; - color: #374151; - border: 1px solid #d1d5db; border-radius: 0.5rem; - font-size: 0.875rem; + font-size: 0.9rem; font-weight: 500; cursor: pointer; transition: all 0.2s; } +/* Cancel button */ +.btn-cancel { + background-color: white; + color: #374151; + border: 1px solid #d1d5db; +} + .btn-cancel:hover { background-color: #f9fafb; border-color: #9ca3af; } +/* Submit button */ .btn-submit { - padding: 0.75rem 1.5rem; - background-color: #059669; + background-color: #10b981; color: white; border: none; - border-radius: 0.5rem; - font-size: 0.875rem; - font-weight: 500; - cursor: pointer; - transition: background-color 0.2s; } .btn-submit:hover { - background-color: #047857; + background-color: #059669; } .btn-submit:disabled { @@ -198,7 +203,7 @@ cursor: not-allowed; } -/* Responsive Design */ +/* Responsive */ @media (max-width: 768px) { .folder-container { padding: 1rem; @@ -224,7 +229,7 @@ } } -/* Custom scrollbar for the container */ +/* Scrollbar */ .folder-container::-webkit-scrollbar { width: 6px; } @@ -242,3 +247,101 @@ .folder-container::-webkit-scrollbar-thumb:hover { background: #94a3b8; } + +/* --- Thèmes par type de dossier --- */ + +/* Contrats */ +.folder-contrat .section-title { + border-bottom-color: #2563eb; +} +.folder-contrat .tag-item { + background-color: #eff6ff; + color: #2563eb; + border-color: #bfdbfe; +} +.folder-contrat .btn-submit { + background-color: #2563eb; +} +.folder-contrat .btn-submit:hover { + background-color: #1d4ed8; +} + +/* Projets */ +.folder-projet .section-title { + border-bottom-color: #059669; +} +.folder-projet .tag-item { + background-color: #ecfdf5; + color: #059669; + border-color: #a7f3d0; +} +.folder-projet .btn-submit { + background-color: #059669; +} +.folder-projet .btn-submit:hover { + background-color: #047857; +} + +/* Rapports */ +.folder-rapport .section-title { + border-bottom-color: #7c3aed; +} +.folder-rapport .tag-item { + background-color: #f5f3ff; + color: #7c3aed; + border-color: #ddd6fe; +} +.folder-rapport .btn-submit { + background-color: #7c3aed; +} +.folder-rapport .btn-submit:hover { + background-color: #6d28d9; +} + +/* Finance */ +.folder-finance .section-title { + border-bottom-color: #d97706; +} +.folder-finance .tag-item { + background-color: #fffbeb; + color: #d97706; + border-color: #fcd34d; +} +.folder-finance .btn-submit { + background-color: #d97706; +} +.folder-finance .btn-submit:hover { + background-color: #b45309; +} + +/* Ressources Humaines */ +.folder-rh .section-title { + border-bottom-color: #db2777; +} +.folder-rh .tag-item { + background-color: #fdf2f8; + color: #db2777; + border-color: #f9a8d4; +} +.folder-rh .btn-submit { + background-color: #db2777; +} +.folder-rh .btn-submit:hover { + background-color: #be185d; +} + +/* Marketing */ +.folder-marketing .section-title { + border-bottom-color: #4f46e5; +} +.folder-marketing .tag-item { + background-color: #eef2ff; + color: #4f46e5; + border-color: #c7d2fe; +} +.folder-marketing .btn-submit { + background-color: #4f46e5; +} +.folder-marketing .btn-submit:hover { + background-color: #3730a3; +} \ No newline at end of file diff --git a/components/FolderModal.tsx b/components/FolderModal.tsx index a148fa8..9a5c804 100644 --- a/components/FolderModal.tsx +++ b/components/FolderModal.tsx @@ -1,8 +1,10 @@ -import React, { useState, memo } from 'react'; +import React, { useEffect, useState, memo } from 'react'; import Modal from './ui/modal/Modal'; import './FolderModal.css'; import type { FolderData } from '../lib/4nk/models/FolderData'; +type FolderType = 'contrat' | 'projet' | 'rapport' | 'finance' | 'rh' | 'marketing' | 'autre'; + interface FolderModalProps { folder?: FolderData; onSave?: (folderData: FolderData) => void; @@ -10,6 +12,11 @@ interface FolderModalProps { readOnly?: boolean; isOpen: boolean; onClose: () => void; + folderType?: FolderType; + renderExtraFields?: ( + folderData: FolderData, + setFolderData: React.Dispatch> + ) => React.ReactNode; } const defaultFolder: FolderData = { @@ -27,80 +34,92 @@ const defaultFolder: FolderData = { stakeholders: [] }; +function capitalize(s?: string) { + if (!s) return ''; + return s.charAt(0).toUpperCase() + s.slice(1); +} + function FolderModal({ folder = defaultFolder, onSave, onCancel, readOnly = false, isOpen, - onClose + onClose, + folderType = 'autre', + renderExtraFields }: FolderModalProps) { - const [folderData, setFolderData] = useState(folder); + const [folderData, setFolderData] = useState({ ...defaultFolder, ...folder }); const [currentCustomer, setCurrentCustomer] = useState(''); const [currentStakeholder, setCurrentStakeholder] = useState(''); const [currentNote, setCurrentNote] = useState(''); - if (!isOpen) return null; + // Sync when modal opens or when folder prop changes (useful pour Edit) + useEffect(() => { + if (isOpen) { + // Merge with defaultFolder to ensure arrays exist + setFolderData({ ...defaultFolder, ...(folder || {}) }); + setCurrentCustomer(''); + setCurrentStakeholder(''); + setCurrentNote(''); + } + }, [isOpen, folder]); - const handleInputChange = (e: React.ChangeEvent) => { + // Generic input change handler + const handleInputChange = ( + e: React.ChangeEvent + ) => { const { name, value } = e.target; - setFolderData(prev => ({ - ...prev, - [name]: value - })); + // cast to avoid TS complaints when updating dynamic fields + setFolderData(prev => ({ ...(prev as any), [name]: value } as FolderData)); }; + /* ---------- Customers ---------- */ const addCustomer = () => { - if (currentCustomer.trim() && !folderData.customers.includes(currentCustomer.trim())) { - setFolderData(prev => ({ - ...prev, - customers: [...prev.customers, currentCustomer.trim()] - })); - setCurrentCustomer(''); + const v = currentCustomer.trim(); + if (!v) return; + if (!Array.isArray(folderData.customers)) folderData.customers = []; + if (!folderData.customers.includes(v)) { + setFolderData(prev => ({ ...prev, customers: [...(prev.customers || []), v] })); } + setCurrentCustomer(''); }; const removeCustomer = (customer: string) => { - setFolderData(prev => ({ - ...prev, - customers: prev.customers.filter(c => c !== customer) - })); + setFolderData(prev => ({ ...prev, customers: (prev.customers || []).filter(c => c !== customer) })); }; + /* ---------- Stakeholders ---------- */ const addStakeholder = () => { - if (currentStakeholder.trim() && !folderData.stakeholders.includes(currentStakeholder.trim())) { - setFolderData(prev => ({ - ...prev, - stakeholders: [...prev.stakeholders, currentStakeholder.trim()] - })); - setCurrentStakeholder(''); + const v = currentStakeholder.trim(); + if (!v) return; + if (!Array.isArray(folderData.stakeholders)) folderData.stakeholders = []; + if (!folderData.stakeholders.includes(v)) { + setFolderData(prev => ({ ...prev, stakeholders: [...(prev.stakeholders || []), v] })); } + setCurrentStakeholder(''); }; const removeStakeholder = (stakeholder: string) => { - setFolderData(prev => ({ - ...prev, - stakeholders: prev.stakeholders.filter(s => s !== stakeholder) - })); + setFolderData(prev => ({ ...prev, stakeholders: (prev.stakeholders || []).filter(s => s !== stakeholder) })); }; + /* ---------- Notes ---------- */ const addNote = () => { - if (currentNote.trim() && !folderData.notes.includes(currentNote.trim())) { - setFolderData(prev => ({ - ...prev, - notes: [...prev.notes, currentNote.trim()] - })); - setCurrentNote(''); + const v = currentNote.trim(); + if (!v) return; + if (!Array.isArray(folderData.notes)) folderData.notes = []; + if (!folderData.notes.includes(v)) { + setFolderData(prev => ({ ...prev, notes: [...(prev.notes || []), v] })); } + setCurrentNote(''); }; const removeNote = (note: string) => { - setFolderData(prev => ({ - ...prev, - notes: prev.notes.filter(m => m !== note) - })); + setFolderData(prev => ({ ...prev, notes: (prev.notes || []).filter(n => n !== note) })); }; + /* ---------- Submit / Cancel ---------- */ const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (onSave) { @@ -109,22 +128,32 @@ function FolderModal({ updated_at: new Date().toISOString() }); } + if (onClose) { + onClose(); // ← ça ferme le modal après sauvegarde + } }; const handleCancel = () => { if (onCancel) { - onCancel(); - } else { - onClose(); + onCancel(); // ton callback spécifique + } else if (onClose) { + onClose(); // fallback si pas de onCancel } }; + // Title text + const title = `Créer un dossier ${capitalize(folderType)}`; + return ( - -
+ // On ne fait PAS "if (!isOpen) return null" : Modal gère l'animation/visibilité + +
+ + {/* Informations principales */}

Informations principales

+
+
-
-
- - -
-
- - -
-
-