create-crm-tmp 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/bin/create-crm-tmp.js +7 -3
  2. package/package.json +1 -1
  3. package/template/README.md +70 -5
  4. package/template/WORKFLOWS_CRON.md +49 -27
  5. package/template/package.json +18 -16
  6. package/template/prisma/migrations/20260210114913_add_dashboard_widget/migration.sql +20 -0
  7. package/template/prisma/schema.prisma +17 -0
  8. package/template/src/app/(dashboard)/agenda/page.tsx +279 -225
  9. package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +1 -5
  10. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +20 -47
  11. package/template/src/app/(dashboard)/automatisation/new/page.tsx +0 -2
  12. package/template/src/app/(dashboard)/closing/page.tsx +5 -57
  13. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +60 -44
  14. package/template/src/app/(dashboard)/contacts/page.tsx +156 -210
  15. package/template/src/app/(dashboard)/dashboard/page.tsx +438 -91
  16. package/template/src/app/(dashboard)/settings/page.tsx +179 -77
  17. package/template/src/app/(dashboard)/users/layout.tsx +30 -0
  18. package/template/src/app/(dashboard)/users/list/page.tsx +213 -159
  19. package/template/src/app/(dashboard)/users/page.tsx +13 -46
  20. package/template/src/app/(dashboard)/users/permissions/page.tsx +0 -2
  21. package/template/src/app/(dashboard)/users/roles/page.tsx +0 -2
  22. package/template/src/app/api/audit-logs/route.ts +0 -2
  23. package/template/src/app/api/auth/google/status/route.ts +46 -7
  24. package/template/src/app/api/closing-reasons/route.ts +0 -2
  25. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +2 -1
  26. package/template/src/app/api/contacts/[id]/files/route.ts +25 -20
  27. package/template/src/app/api/contacts/[id]/route.ts +2 -3
  28. package/template/src/app/api/contacts/export/route.ts +14 -11
  29. package/template/src/app/api/contacts/import/route.ts +2 -6
  30. package/template/src/app/api/contacts/route.ts +1 -1
  31. package/template/src/app/api/dashboard/stats/route.ts +7 -0
  32. package/template/src/app/api/dashboard/widgets/[id]/route.ts +47 -0
  33. package/template/src/app/api/dashboard/widgets/route.ts +181 -0
  34. package/template/src/app/api/integrations/google-sheet/sync/route.ts +58 -28
  35. package/template/src/app/api/reminders/route.ts +4 -2
  36. package/template/src/app/api/roles/route.ts +1 -1
  37. package/template/src/app/api/settings/closing-reasons/[id]/route.ts +1 -6
  38. package/template/src/app/api/settings/closing-reasons/route.ts +0 -2
  39. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +10 -5
  40. package/template/src/app/api/settings/google-sheet/route.ts +3 -3
  41. package/template/src/app/api/tasks/[id]/route.ts +4 -4
  42. package/template/src/app/api/tasks/meet/route.ts +1 -2
  43. package/template/src/app/api/tasks/route.ts +16 -18
  44. package/template/src/app/api/users/for-agenda/route.ts +1 -2
  45. package/template/src/app/api/workflows/[id]/route.ts +2 -9
  46. package/template/src/app/api/workflows/route.ts +0 -1
  47. package/template/src/app/globals.css +96 -0
  48. package/template/src/components/dashboard/activity-chart.tsx +37 -37
  49. package/template/src/components/dashboard/add-widget-dialog.tsx +161 -0
  50. package/template/src/components/dashboard/color-picker.tsx +65 -0
  51. package/template/src/components/dashboard/contacts-chart.tsx +36 -30
  52. package/template/src/components/dashboard/interactions-by-type-chart.tsx +121 -0
  53. package/template/src/components/dashboard/recent-activity.tsx +79 -86
  54. package/template/src/components/dashboard/sales-analytics-chart.tsx +4 -8
  55. package/template/src/components/dashboard/stat-card.tsx +42 -40
  56. package/template/src/components/dashboard/status-distribution-chart.tsx +64 -27
  57. package/template/src/components/dashboard/tasks-pie-chart.tsx +37 -34
  58. package/template/src/components/dashboard/top-contacts-list.tsx +41 -51
  59. package/template/src/components/dashboard/upcoming-tasks-list.tsx +71 -78
  60. package/template/src/components/dashboard/widget-wrapper.tsx +39 -0
  61. package/template/src/components/header.tsx +21 -12
  62. package/template/src/components/page-header.tsx +14 -47
  63. package/template/src/components/sidebar.tsx +3 -4
  64. package/template/src/contexts/dashboard-theme-context.tsx +58 -0
  65. package/template/src/lib/audit-log.ts +0 -2
  66. package/template/src/lib/dashboard-themes.ts +140 -0
  67. package/template/src/lib/default-widgets.ts +14 -0
  68. package/template/src/lib/google-drive.ts +38 -30
  69. package/template/src/lib/permissions.ts +56 -1
  70. package/template/src/lib/prisma.ts +0 -1
  71. package/template/src/lib/widget-registry.ts +177 -0
  72. package/template/src/lib/workflow-executor.ts +7 -13
  73. package/README.md +0 -89
@@ -14,11 +14,7 @@ export default function EditWorkflowPage() {
14
14
  title="Modifier une automatisation"
15
15
  description="Ajustez le déclencheur et les actions de votre workflow."
16
16
  />
17
- <div className="p-4 sm:p-6 lg:p-8">
18
- {id ? <WorkflowEditor workflowId={id} /> : null}
19
- </div>
17
+ <div className="p-4 sm:p-6 lg:p-8">{id ? <WorkflowEditor workflowId={id} /> : null}</div>
20
18
  </div>
21
19
  );
22
20
  }
23
-
24
-
@@ -2,15 +2,7 @@
2
2
 
3
3
  import { useEffect, useRef, useState } from 'react';
4
4
  import { useRouter } from 'next/navigation';
5
- import {
6
- Plus,
7
- Trash2,
8
- Mail,
9
- MessageSquare,
10
- Tag,
11
- CheckSquare,
12
- Clock,
13
- } from 'lucide-react';
5
+ import { Plus, Trash2, Mail, MessageSquare, Tag, CheckSquare, Clock } from 'lucide-react';
14
6
  import { cn } from '@/lib/utils';
15
7
 
16
8
  interface Status {
@@ -131,10 +123,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
131
123
  // Fermer le menu d'actions en cliquant en dehors
132
124
  useEffect(() => {
133
125
  const handleClickOutside = (event: MouseEvent) => {
134
- if (
135
- actionMenuRef.current &&
136
- !actionMenuRef.current.contains(event.target as Node)
137
- ) {
126
+ if (actionMenuRef.current && !actionMenuRef.current.contains(event.target as Node)) {
138
127
  setShowActionMenu(false);
139
128
  }
140
129
  };
@@ -308,15 +297,15 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
308
297
 
309
298
  <div className="relative space-y-4">
310
299
  {/* Ligne verticale */}
311
- <div className="pointer-events-none absolute left-6 top-0 h-full w-px bg-linear-to-b from-indigo-300 via-gray-200 to-gray-200" />
300
+ <div className="pointer-events-none absolute top-0 left-6 h-full w-px bg-linear-to-b from-indigo-300 via-gray-200 to-gray-200" />
312
301
 
313
302
  {/* Déclencheur */}
314
303
  <div className="relative pl-10">
315
- <div className="absolute left-5 top-6 h-3 w-3 -translate-x-1/2 rounded-full border border-white bg-indigo-500 shadow-sm" />
304
+ <div className="absolute top-6 left-5 h-3 w-3 -translate-x-1/2 rounded-full border border-white bg-indigo-500 shadow-sm" />
316
305
  <div className="rounded-xl border border-indigo-100 bg-white px-4 py-3 shadow-sm">
317
306
  <div className="flex items-center justify-between">
318
307
  <div>
319
- <p className="text-xs font-semibold uppercase tracking-wide text-indigo-600">
308
+ <p className="text-xs font-semibold tracking-wide text-indigo-600 uppercase">
320
309
  Déclencheur
321
310
  </p>
322
311
  <p className="mt-1 text-sm font-medium text-gray-900">
@@ -330,7 +319,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
330
319
  {/* Actions */}
331
320
  {actions.length === 0 ? (
332
321
  <div className="relative pl-10">
333
- <div className="absolute left-5 top-6 h-3 w-3 -translate-x-1/2 rounded-full border border-dashed border-gray-300 bg-white" />
322
+ <div className="absolute top-6 left-5 h-3 w-3 -translate-x-1/2 rounded-full border border-dashed border-gray-300 bg-white" />
334
323
  <div className="flex flex-col items-center justify-center rounded-xl border border-dashed border-gray-300 bg-gray-50 px-4 py-8 text-center">
335
324
  <Clock className="mb-2 h-8 w-8 text-gray-300" />
336
325
  <p className="text-sm font-medium text-gray-900">Aucune action définie</p>
@@ -342,7 +331,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
342
331
  ) : (
343
332
  actions.map((action, index) => (
344
333
  <div key={index} className="relative pl-10">
345
- <div className="absolute left-5 top-6 h-3 w-3 -translate-x-1/2 rounded-full border border-white bg-indigo-500 shadow-sm" />
334
+ <div className="absolute top-6 left-5 h-3 w-3 -translate-x-1/2 rounded-full border border-white bg-indigo-500 shadow-sm" />
346
335
  <div className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm transition-shadow hover:shadow-md">
347
336
  <div className="mb-3 flex items-center justify-between">
348
337
  <div className="flex items-center gap-3">
@@ -410,9 +399,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
410
399
  />
411
400
  </div>
412
401
  <div>
413
- <label className="block text-xs font-medium text-gray-700">
414
- Heures
415
- </label>
402
+ <label className="block text-xs font-medium text-gray-700">Heures</label>
416
403
  <input
417
404
  type="number"
418
405
  min="0"
@@ -580,7 +567,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
580
567
 
581
568
  {/* Bouton ajouter une action */}
582
569
  <div className="relative pl-10">
583
- <div className="absolute left-5 top-6 h-3 w-3 -translate-x-1/2 rounded-full border border-dashed border-indigo-300 bg-white" />
570
+ <div className="absolute top-6 left-5 h-3 w-3 -translate-x-1/2 rounded-full border border-dashed border-indigo-300 bg-white" />
584
571
  <div className="flex items-center justify-center">
585
572
  <div className="relative inline-flex items-center" ref={actionMenuRef}>
586
573
  <button
@@ -592,8 +579,8 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
592
579
  Ajouter une action
593
580
  </button>
594
581
  {showActionMenu && (
595
- <div className="absolute left-1/2 top-full z-10 mt-2 w-60 -translate-x-1/2 rounded-xl border border-gray-200 bg-white p-2 text-xs shadow-lg">
596
- <p className="px-2 pb-1 text-[11px] font-medium uppercase tracking-wide text-gray-500">
582
+ <div className="absolute top-full left-1/2 z-10 mt-2 w-60 -translate-x-1/2 rounded-xl border border-gray-200 bg-white p-2 text-xs shadow-lg">
583
+ <p className="px-2 pb-1 text-[11px] font-medium tracking-wide text-gray-500 uppercase">
597
584
  Types d’actions
598
585
  </p>
599
586
  <button
@@ -664,7 +651,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
664
651
  <div className="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm">
665
652
  <div className="mb-4 flex items-center justify-between">
666
653
  <div>
667
- <p className="text-xs font-semibold uppercase tracking-wide text-gray-500">
654
+ <p className="text-xs font-semibold tracking-wide text-gray-500 uppercase">
668
655
  Paramètres du workflow
669
656
  </p>
670
657
  <p className="mt-1 text-sm font-medium text-gray-900">
@@ -674,9 +661,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
674
661
  <span
675
662
  className={cn(
676
663
  'inline-flex items-center rounded-full px-2.5 py-0.5 text-[11px] font-medium',
677
- formData.active
678
- ? 'bg-green-50 text-green-700'
679
- : 'bg-gray-100 text-gray-600',
664
+ formData.active ? 'bg-green-50 text-green-700' : 'bg-gray-100 text-gray-600',
680
665
  )}
681
666
  >
682
667
  {formData.active ? 'Actif' : 'Brouillon'}
@@ -685,9 +670,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
685
670
 
686
671
  <div className="space-y-4">
687
672
  <div>
688
- <label className="block text-xs font-medium text-gray-700">
689
- Nom du workflow *
690
- </label>
673
+ <label className="block text-xs font-medium text-gray-700">Nom du workflow *</label>
691
674
  <input
692
675
  type="text"
693
676
  required
@@ -704,9 +687,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
704
687
  </div>
705
688
 
706
689
  <div>
707
- <label className="block text-xs font-medium text-gray-700">
708
- Description
709
- </label>
690
+ <label className="block text-xs font-medium text-gray-700">Description</label>
710
691
  <textarea
711
692
  value={formData.description ?? ''}
712
693
  onChange={(e) =>
@@ -754,7 +735,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
754
735
  </div>
755
736
  </div>
756
737
 
757
- <div className="rounded-2xl border border-gray-200 bg-white p-5 shadow-sm space-y-4">
738
+ <div className="space-y-4 rounded-2xl border border-gray-200 bg-white p-5 shadow-sm">
758
739
  <div>
759
740
  <label className="block text-xs font-medium text-gray-700">
760
741
  Type de déclencheur *
@@ -801,9 +782,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
801
782
  </select>
802
783
  </div>
803
784
  <div>
804
- <label className="block text-xs font-medium text-gray-700">
805
- Vers le statut
806
- </label>
785
+ <label className="block text-xs font-medium text-gray-700">Vers le statut</label>
807
786
  <select
808
787
  value={formData.triggerToStatusId ?? ''}
809
788
  onChange={(e) =>
@@ -828,9 +807,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
828
807
  {formData.triggerType === 'TIME_BASED' && (
829
808
  <div className="grid gap-3 md:grid-cols-2">
830
809
  <div>
831
- <label className="block text-xs font-medium text-gray-700">
832
- Délai (jours)
833
- </label>
810
+ <label className="block text-xs font-medium text-gray-700">Délai (jours)</label>
834
811
  <input
835
812
  type="number"
836
813
  min="0"
@@ -845,9 +822,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
845
822
  />
846
823
  </div>
847
824
  <div>
848
- <label className="block text-xs font-medium text-gray-700">
849
- Délai (heures)
850
- </label>
825
+ <label className="block text-xs font-medium text-gray-700">Délai (heures)</label>
851
826
  <input
852
827
  type="number"
853
828
  min="0"
@@ -891,7 +866,7 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
891
866
  <button
892
867
  type="submit"
893
868
  disabled={saving || loading}
894
- className="w-full cursor-pointer rounded-xl bg-indigo-600 px-4 py-2.5 text-sm font-medium text-white shadow-sm transition-colors hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:bg-indigo-400 sm:w-auto"
869
+ className="w-full cursor-pointer rounded-xl bg-indigo-600 px-4 py-2.5 text-sm font-medium text-white shadow-sm transition-colors hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:bg-indigo-400 sm:w-auto"
895
870
  >
896
871
  {saving ? 'Enregistrement...' : 'Enregistrer le workflow'}
897
872
  </button>
@@ -901,5 +876,3 @@ export function WorkflowEditor({ workflowId }: WorkflowEditorProps) {
901
876
  </form>
902
877
  );
903
878
  }
904
-
905
-
@@ -16,5 +16,3 @@ export default function NewWorkflowPage() {
16
16
  </div>
17
17
  );
18
18
  }
19
-
20
-
@@ -15,7 +15,6 @@ import {
15
15
  Eye,
16
16
  } from 'lucide-react';
17
17
  import { cn } from '@/lib/utils';
18
- import { useMobileMenuContext } from '@/contexts/mobile-menu-context';
19
18
 
20
19
  interface Status {
21
20
  id: string;
@@ -225,7 +224,6 @@ function createDefaultColumns(statuses: Status[]): ClosingColumn[] {
225
224
  }
226
225
 
227
226
  export default function ClosingPage() {
228
- const { toggle: toggleMobileMenu, isOpen: isMobileMenuOpen } = useMobileMenuContext();
229
227
  const [statuses, setStatuses] = useState<Status[]>([]);
230
228
  const [contacts, setContacts] = useState<Contact[]>([]);
231
229
  const [loading, setLoading] = useState(true);
@@ -571,37 +569,11 @@ export default function ClosingPage() {
571
569
  return (
572
570
  <div className="h-full">
573
571
  <div className="border-b border-gray-200 bg-white px-4 py-4 sm:px-6 lg:px-8 lg:py-6">
574
- <div className="flex items-start gap-3">
575
- {/* Mobile menu button */}
576
- <button
577
- onClick={toggleMobileMenu}
578
- className="mt-1 shrink-0 cursor-pointer rounded-lg p-2 text-gray-700 transition-colors hover:bg-gray-100 lg:hidden"
579
- aria-label="Toggle menu"
580
- >
581
- <svg className="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
582
- {isMobileMenuOpen ? (
583
- <path
584
- strokeLinecap="round"
585
- strokeLinejoin="round"
586
- strokeWidth={2}
587
- d="M6 18L18 6M6 6l12 12"
588
- />
589
- ) : (
590
- <path
591
- strokeLinecap="round"
592
- strokeLinejoin="round"
593
- strokeWidth={2}
594
- d="M4 6h16M4 12h16M4 18h16"
595
- />
596
- )}
597
- </svg>
598
- </button>
599
- <div className="min-w-0 flex-1">
600
- <h1 className="text-xl font-bold text-gray-900 sm:text-2xl">Pipeline de Closing</h1>
601
- <p className="mt-1 text-sm text-gray-600">
602
- Visualisez et gérez vos opportunités commerciales
603
- </p>
604
- </div>
572
+ <div>
573
+ <h1 className="text-xl font-bold text-gray-900 sm:text-2xl">Pipeline de Closing</h1>
574
+ <p className="mt-1 text-sm text-gray-600">
575
+ Visualisez et gérez vos opportunités commerciales
576
+ </p>
605
577
  </div>
606
578
  </div>
607
579
  <div className="p-4 sm:p-6 lg:p-8">
@@ -620,30 +592,6 @@ export default function ClosingPage() {
620
592
  {/* En-tête personnalisé avec filtres intégrés */}
621
593
  <div className="border-b border-gray-200 bg-white px-4 py-4 sm:px-6 lg:px-8 lg:py-6">
622
594
  <div className="flex items-start gap-3">
623
- {/* Mobile menu button */}
624
- <button
625
- onClick={toggleMobileMenu}
626
- className="mt-1 shrink-0 cursor-pointer rounded-lg p-2 text-gray-700 transition-colors hover:bg-gray-100 lg:hidden"
627
- aria-label="Toggle menu"
628
- >
629
- <svg className="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
630
- {isMobileMenuOpen ? (
631
- <path
632
- strokeLinecap="round"
633
- strokeLinejoin="round"
634
- strokeWidth={2}
635
- d="M6 18L18 6M6 6l12 12"
636
- />
637
- ) : (
638
- <path
639
- strokeLinecap="round"
640
- strokeLinejoin="round"
641
- strokeWidth={2}
642
- d="M4 6h16M4 12h16M4 18h16"
643
- />
644
- )}
645
- </svg>
646
- </button>
647
595
  <div className="flex min-w-0 flex-1 items-start justify-between gap-4">
648
596
  <div className="min-w-0 flex-1">
649
597
  <h1 className="text-xl font-bold text-gray-900 sm:text-2xl">Pipeline de Closing</h1>
@@ -22,6 +22,7 @@ import {
22
22
  Upload,
23
23
  File,
24
24
  Target,
25
+ ChevronDown,
25
26
  } from 'lucide-react';
26
27
  import Link from 'next/link';
27
28
  import { Editor, type DefaultTemplateRef } from '@/components/editor';
@@ -42,6 +43,25 @@ interface User {
42
43
  role?: string;
43
44
  }
44
45
 
46
+ function getNewsFeedCardColor(type: string): string {
47
+ switch (type) {
48
+ case 'STATUS_CHANGE':
49
+ return 'bg-purple-50 border-purple-200';
50
+ case 'CONTACT_UPDATE':
51
+ return 'bg-indigo-50 border-indigo-200';
52
+ case 'ASSIGNMENT_CHANGE':
53
+ return 'bg-teal-50 border-teal-200';
54
+ case 'FILE_UPLOADED':
55
+ return 'bg-green-50 border-green-200';
56
+ case 'FILE_REPLACED':
57
+ return 'bg-orange-50 border-orange-200';
58
+ case 'FILE_DELETED':
59
+ return 'bg-red-50 border-red-200';
60
+ default:
61
+ return 'bg-gray-50 border-gray-200';
62
+ }
63
+ }
64
+
45
65
  interface Interaction {
46
66
  id: string;
47
67
  type:
@@ -132,6 +152,7 @@ export default function ContactDetailPage() {
132
152
  const [tasks, setTasks] = useState<any[]>([]);
133
153
  const [sendingEmail, setSendingEmail] = useState(false);
134
154
  const [creatingTask, setCreatingTask] = useState(false);
155
+ const [showNewsFeed, setShowNewsFeed] = useState(false);
135
156
  const [emailTemplates, setEmailTemplates] = useState<any[]>([]);
136
157
  const [noteTemplates, setNoteTemplates] = useState<any[]>([]);
137
158
  const emailEditorRef = useRef<DefaultTemplateRef | null>(null);
@@ -207,6 +228,7 @@ export default function ContactDetailPage() {
207
228
  });
208
229
  const meetEditorRef = useRef<DefaultTemplateRef | null>(null);
209
230
  const [googleAccountConnected, setGoogleAccountConnected] = useState(false);
231
+ const [googleDriveConnected, setGoogleDriveConnected] = useState(false);
210
232
  const [smtpConfigured, setSmtpConfigured] = useState<boolean | null>(null);
211
233
 
212
234
  // Modal d'édition Google Meet
@@ -509,13 +531,15 @@ export default function ContactDetailPage() {
509
531
  useEffect(() => {
510
532
  const checkIntegrations = async () => {
511
533
  try {
512
- // Statut Google
534
+ // Statut Google (Drive admin + Calendar personnel)
513
535
  const googleResponse = await fetch('/api/auth/google/status');
514
536
  if (googleResponse.ok) {
515
537
  const data = await googleResponse.json();
516
- setGoogleAccountConnected(!!data.connected);
538
+ setGoogleAccountConnected(!!data.calendar?.connected);
539
+ setGoogleDriveConnected(!!data.drive?.connected);
517
540
  } else {
518
541
  setGoogleAccountConnected(false);
542
+ setGoogleDriveConnected(false);
519
543
  }
520
544
 
521
545
  // Configuration SMTP
@@ -652,9 +676,9 @@ export default function ContactDetailPage() {
652
676
  setError('');
653
677
 
654
678
  try {
655
- if (!googleAccountConnected) {
679
+ if (!googleDriveConnected) {
656
680
  setError(
657
- 'Vous devez connecter votre compte Google dans les paramètres avant de pouvoir uploader un fichier.',
681
+ 'Aucun compte Google Drive configuré. Veuillez demander à un administrateur de connecter son compte Google Drive dans les paramètres.',
658
682
  );
659
683
  setUploadingFile(false);
660
684
  setUploadProgress(0);
@@ -2591,44 +2615,36 @@ export default function ContactDetailPage() {
2591
2615
 
2592
2616
  {/* Section Fil d'actualités */}
2593
2617
  <div>
2594
- <div className="mb-4 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
2595
- <h2 className="text-lg font-semibold text-gray-900">Fil d'actualités</h2>
2596
- </div>
2597
- <div className="space-y-4">
2598
- {Object.keys(groupedNewsFeed).length === 0 ? (
2599
- <p className="py-6 text-center text-sm text-gray-500">
2600
- Aucune modification
2601
- </p>
2602
- ) : (
2603
- Object.entries(groupedNewsFeed).map(([date, interactions]) => (
2604
- <div key={date}>
2605
- <h3 className="mb-3 text-sm font-semibold text-gray-700">{date}</h3>
2606
- <div className="space-y-2">
2607
- {interactions.map((interaction) => {
2608
- const getCardColor = (type: string) => {
2609
- switch (type) {
2610
- case 'STATUS_CHANGE':
2611
- return 'bg-purple-50 border-purple-200';
2612
- case 'CONTACT_UPDATE':
2613
- return 'bg-indigo-50 border-indigo-200';
2614
- case 'ASSIGNMENT_CHANGE':
2615
- return 'bg-teal-50 border-teal-200';
2616
- case 'FILE_UPLOADED':
2617
- return 'bg-green-50 border-green-200';
2618
- case 'FILE_REPLACED':
2619
- return 'bg-orange-50 border-orange-200';
2620
- case 'FILE_DELETED':
2621
- return 'bg-red-50 border-red-200';
2622
- default:
2623
- return 'bg-gray-50 border-gray-200';
2624
- }
2625
- };
2626
- return (
2618
+ <button
2619
+ type="button"
2620
+ onClick={() => setShowNewsFeed((prev) => !prev)}
2621
+ className="mb-4 flex w-full cursor-pointer items-center justify-between rounded-xl border border-transparent px-3 py-2 text-left transition-colors hover:border-gray-200 hover:bg-gray-50"
2622
+ >
2623
+ <span className="text-lg font-semibold text-gray-900">Fil d'actualités</span>
2624
+ <ChevronDown
2625
+ className={cn(
2626
+ 'h-4 w-4 text-gray-500 transition-transform',
2627
+ showNewsFeed ? 'rotate-180' : 'rotate-0',
2628
+ )}
2629
+ />
2630
+ </button>
2631
+ {showNewsFeed && (
2632
+ <div className="space-y-4">
2633
+ {Object.keys(groupedNewsFeed).length === 0 ? (
2634
+ <p className="py-6 text-center text-sm text-gray-500">
2635
+ Aucune modification
2636
+ </p>
2637
+ ) : (
2638
+ Object.entries(groupedNewsFeed).map(([date, interactions]) => (
2639
+ <div key={date}>
2640
+ <h3 className="mb-3 text-sm font-semibold text-gray-700">{date}</h3>
2641
+ <div className="space-y-2">
2642
+ {interactions.map((interaction) => (
2627
2643
  <div
2628
2644
  key={interaction.id}
2629
2645
  className={cn(
2630
2646
  'relative rounded-lg border p-3 sm:p-4',
2631
- getCardColor(interaction.type),
2647
+ getNewsFeedCardColor(interaction.type),
2632
2648
  )}
2633
2649
  >
2634
2650
  <div className="flex items-start gap-2 sm:gap-3">
@@ -2664,13 +2680,13 @@ export default function ContactDetailPage() {
2664
2680
  </div>
2665
2681
  </div>
2666
2682
  </div>
2667
- );
2668
- })}
2683
+ ))}
2684
+ </div>
2669
2685
  </div>
2670
- </div>
2671
- ))
2672
- )}
2673
- </div>
2686
+ ))
2687
+ )}
2688
+ </div>
2689
+ )}
2674
2690
  </div>
2675
2691
  </div>
2676
2692
  )}