create-crm-tmp 2.0.0 → 2.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 (190) hide show
  1. package/bin/create-crm-tmp.js +56 -35
  2. package/package.json +1 -1
  3. package/template/README.md +230 -115
  4. package/template/eslint.config.mjs +13 -0
  5. package/template/next.config.ts +14 -0
  6. package/template/package.json +15 -2
  7. package/template/prisma/migrations/20260318095700_init_db/migration.sql +978 -0
  8. package/template/prisma/migrations/migration_lock.toml +3 -0
  9. package/template/prisma/schema.prisma +132 -637
  10. package/template/src/app/(auth)/invite/[token]/page.tsx +10 -8
  11. package/template/src/app/(auth)/layout.tsx +1 -1
  12. package/template/src/app/(auth)/reset-password/complete/page.tsx +11 -8
  13. package/template/src/app/(auth)/reset-password/page.tsx +4 -4
  14. package/template/src/app/(auth)/reset-password/verify/page.tsx +4 -4
  15. package/template/src/app/(auth)/signin/page.tsx +14 -6
  16. package/template/src/app/(dashboard)/agenda/page.tsx +2243 -988
  17. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +18 -104
  18. package/template/src/app/(dashboard)/automatisation/page.tsx +10 -26
  19. package/template/src/app/(dashboard)/closing/page.tsx +78 -62
  20. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +2082 -1080
  21. package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +46 -47
  22. package/template/src/app/(dashboard)/contacts/page.tsx +1062 -780
  23. package/template/src/app/(dashboard)/dashboard/page.tsx +533 -37
  24. package/template/src/app/(dashboard)/dev/page.tsx +1291 -0
  25. package/template/src/app/(dashboard)/layout.tsx +6 -2
  26. package/template/src/app/(dashboard)/settings/page.tsx +797 -2582
  27. package/template/src/app/(dashboard)/templates/page.tsx +55 -54
  28. package/template/src/app/(dashboard)/users/list/page.tsx +51 -48
  29. package/template/src/app/(dashboard)/users/page.tsx +1 -1
  30. package/template/src/app/(dashboard)/users/permissions/page.tsx +2 -2
  31. package/template/src/app/(dashboard)/users/roles/page.tsx +7 -5
  32. package/template/src/app/api/agenda/google-events/route.ts +92 -0
  33. package/template/src/app/api/auth/check-active/route.ts +3 -2
  34. package/template/src/app/api/auth/google/route.ts +2 -1
  35. package/template/src/app/api/auth/google/status/route.ts +7 -31
  36. package/template/src/app/api/companies/[id]/activities/route.ts +1 -3
  37. package/template/src/app/api/companies/[id]/route.ts +1 -2
  38. package/template/src/app/api/companies/route.ts +42 -12
  39. package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +9 -31
  40. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +14 -32
  41. package/template/src/app/api/contacts/[id]/files/route.ts +112 -212
  42. package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +27 -1
  43. package/template/src/app/api/contacts/[id]/interactions/route.ts +16 -16
  44. package/template/src/app/api/contacts/[id]/kyc/route.ts +21 -11
  45. package/template/src/app/api/contacts/[id]/meet/route.ts +19 -2
  46. package/template/src/app/api/contacts/[id]/route.ts +106 -34
  47. package/template/src/app/api/contacts/[id]/send-email/route.ts +27 -11
  48. package/template/src/app/api/contacts/[id]/workflows/run/route.ts +6 -0
  49. package/template/src/app/api/contacts/export/route.ts +9 -13
  50. package/template/src/app/api/contacts/import/route.ts +55 -25
  51. package/template/src/app/api/contacts/import-preview/route.ts +1 -1
  52. package/template/src/app/api/contacts/origins/route.ts +63 -0
  53. package/template/src/app/api/contacts/route.ts +153 -41
  54. package/template/src/app/api/cron/cleanup-editor-images/route.ts +166 -0
  55. package/template/src/app/api/dashboard/widgets/[id]/route.ts +44 -0
  56. package/template/src/app/api/dashboard/widgets/route.ts +181 -0
  57. package/template/src/app/api/dev/reminders/test/route.ts +114 -0
  58. package/template/src/app/api/editor/upload-image/route.ts +61 -0
  59. package/template/src/app/api/integrations/google-sheet/jobs/[jobId]/route.ts +47 -0
  60. package/template/src/app/api/integrations/google-sheet/jobs/usage/route.ts +50 -0
  61. package/template/src/app/api/integrations/google-sheet/sync/route.ts +24 -556
  62. package/template/src/app/api/jobs/google-sheet/process/route.ts +84 -0
  63. package/template/src/app/api/jobs/google-sheet/schedule/route.ts +50 -0
  64. package/template/src/app/api/reminders/clear/route.ts +120 -0
  65. package/template/src/app/api/reminders/clear/undo/route.ts +112 -0
  66. package/template/src/app/api/reminders/route.ts +164 -39
  67. package/template/src/app/api/reminders/state/route.ts +164 -0
  68. package/template/src/app/api/reset-password/request/route.ts +1 -1
  69. package/template/src/app/api/reset-password/verify/route.ts +1 -1
  70. package/template/src/app/api/send/route.ts +16 -4
  71. package/template/src/app/api/settings/google-ads/route.ts +14 -0
  72. package/template/src/app/api/settings/google-calendar/calendars/route.ts +97 -0
  73. package/template/src/app/api/settings/google-calendar/route.ts +124 -0
  74. package/template/src/app/api/settings/google-sheet/[id]/route.ts +28 -0
  75. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +37 -4
  76. package/template/src/app/api/settings/google-sheet/preview/route.ts +9 -3
  77. package/template/src/app/api/settings/google-sheet/route.ts +14 -0
  78. package/template/src/app/api/settings/integrations/logs/route.ts +93 -0
  79. package/template/src/app/api/settings/integrations/notifications/route.ts +67 -0
  80. package/template/src/app/api/settings/meta-leads/[id]/route.ts +0 -1
  81. package/template/src/app/api/settings/meta-leads/route.ts +14 -2
  82. package/template/src/app/api/settings/smtp/route.ts +53 -6
  83. package/template/src/app/api/tasks/[id]/attendees/route.ts +24 -8
  84. package/template/src/app/api/tasks/[id]/route.ts +234 -58
  85. package/template/src/app/api/tasks/meet/route.ts +27 -19
  86. package/template/src/app/api/tasks/route.ts +62 -17
  87. package/template/src/app/api/users/[id]/route.ts +20 -14
  88. package/template/src/app/api/users/list/route.ts +57 -19
  89. package/template/src/app/api/webhooks/google-ads/route.ts +34 -14
  90. package/template/src/app/api/webhooks/meta-leads/route.ts +32 -12
  91. package/template/src/app/api/workflows/[id]/route.ts +0 -4
  92. package/template/src/app/api/workflows/process/route.ts +22 -51
  93. package/template/src/app/api/workflows/route.ts +0 -4
  94. package/template/src/app/globals.css +342 -4
  95. package/template/src/app/layout.tsx +11 -3
  96. package/template/src/app/page.tsx +1 -1
  97. package/template/src/components/address-autocomplete.tsx +7 -6
  98. package/template/src/components/config-error-alert.tsx +46 -0
  99. package/template/src/components/contacts/filter-bar.tsx +12 -3
  100. package/template/src/components/contacts/filter-builder.tsx +28 -43
  101. package/template/src/components/contacts/save-view-dialog.tsx +1 -1
  102. package/template/src/components/contacts/views-tab-bar.tsx +15 -6
  103. package/template/src/components/dashboard/activity-chart.tsx +41 -28
  104. package/template/src/components/dashboard/add-widget-dialog.tsx +157 -0
  105. package/template/src/components/dashboard/color-picker.tsx +64 -0
  106. package/template/src/components/dashboard/contacts-chart.tsx +69 -0
  107. package/template/src/components/dashboard/interactions-by-type-chart.tsx +121 -0
  108. package/template/src/components/dashboard/recent-activity.tsx +154 -0
  109. package/template/src/components/dashboard/stat-card.tsx +40 -40
  110. package/template/src/components/dashboard/status-distribution-chart.tsx +81 -0
  111. package/template/src/components/dashboard/tasks-pie-chart.tsx +37 -34
  112. package/template/src/components/dashboard/top-contacts-list.tsx +113 -0
  113. package/template/src/components/dashboard/upcoming-tasks-list.tsx +72 -81
  114. package/template/src/components/dashboard/widget-wrapper.tsx +36 -0
  115. package/template/src/components/date-picker.tsx +9 -6
  116. package/template/src/components/editor/upload-editor-image.ts +42 -0
  117. package/template/src/components/editor.tsx +161 -22
  118. package/template/src/components/email-template.tsx +2 -2
  119. package/template/src/components/global-search.tsx +30 -28
  120. package/template/src/components/header.tsx +178 -80
  121. package/template/src/components/inactive-account-guard.tsx +58 -0
  122. package/template/src/components/integration-notifications-listener.tsx +12 -0
  123. package/template/src/components/invitation-email-template.tsx +2 -2
  124. package/template/src/components/meet-cancellation-email-template.tsx +3 -3
  125. package/template/src/components/meet-confirmation-email-template.tsx +3 -3
  126. package/template/src/components/meet-update-email-template.tsx +3 -3
  127. package/template/src/components/page-header.tsx +5 -5
  128. package/template/src/components/protected-page.tsx +1 -1
  129. package/template/src/components/reset-password-email-template.tsx +2 -2
  130. package/template/src/components/settings/integrations/GoogleAdsIntegration.tsx +428 -0
  131. package/template/src/components/settings/integrations/GoogleSheetConfigMonitoringModal.tsx +680 -0
  132. package/template/src/components/settings/integrations/GoogleSheetIntegration.tsx +809 -0
  133. package/template/src/components/settings/integrations/ImportResultDialog.tsx +124 -0
  134. package/template/src/components/settings/integrations/IntegrationLogPanel.tsx +57 -0
  135. package/template/src/components/settings/integrations/IntegrationLogsTable.tsx +186 -0
  136. package/template/src/components/settings/integrations/MetaLeadIntegration.tsx +451 -0
  137. package/template/src/components/sidebar.tsx +45 -26
  138. package/template/src/components/skeleton.tsx +40 -43
  139. package/template/src/components/ui/accordion.tsx +2 -2
  140. package/template/src/components/ui/alert-dialog.tsx +1 -1
  141. package/template/src/components/ui/button.tsx +20 -9
  142. package/template/src/components/ui/components.tsx +1 -1
  143. package/template/src/components/ui/date-picker.tsx +422 -0
  144. package/template/src/components/ui/datetime-picker.tsx +338 -0
  145. package/template/src/components/ui/status-select.tsx +271 -0
  146. package/template/src/components/ui/tooltip.tsx +37 -0
  147. package/template/src/components/view-as-modal.tsx +13 -7
  148. package/template/src/contexts/app-toast-context.tsx +245 -57
  149. package/template/src/contexts/dashboard-theme-context.tsx +53 -0
  150. package/template/src/contexts/sidebar-context.tsx +22 -17
  151. package/template/src/contexts/task-reminder-context.tsx +134 -160
  152. package/template/src/contexts/view-as-context.tsx +33 -6
  153. package/template/src/hooks/use-focus-trap.ts +2 -2
  154. package/template/src/hooks/useIntegrationNotifications.ts +49 -0
  155. package/template/src/lib/auth.ts +8 -1
  156. package/template/src/lib/config-links.ts +14 -0
  157. package/template/src/lib/contact-duplicate.ts +79 -61
  158. package/template/src/lib/contact-interactions.ts +21 -21
  159. package/template/src/lib/contact-view-filters.ts +24 -64
  160. package/template/src/lib/contacts-list-url.ts +190 -0
  161. package/template/src/lib/dashboard-stats.ts +65 -7
  162. package/template/src/lib/dashboard-themes.ts +135 -0
  163. package/template/src/lib/date-utils.ts +127 -0
  164. package/template/src/lib/default-widgets.ts +12 -0
  165. package/template/src/lib/editor-html-image-dimensions.ts +172 -0
  166. package/template/src/lib/editor-image-limits.ts +19 -0
  167. package/template/src/lib/email-html-sanitize.ts +19 -0
  168. package/template/src/lib/encryption.ts +9 -6
  169. package/template/src/lib/fr-geography.ts +192 -0
  170. package/template/src/lib/google-calendar-agenda.ts +201 -0
  171. package/template/src/lib/google-calendar.ts +255 -5
  172. package/template/src/lib/google-sheet-sync-jobs.ts +96 -0
  173. package/template/src/lib/google-sheet-sync-runner.ts +514 -0
  174. package/template/src/lib/integration-import-log.ts +21 -0
  175. package/template/src/lib/permissions.ts +40 -10
  176. package/template/src/lib/prisma.ts +4 -1
  177. package/template/src/lib/qstash.ts +65 -0
  178. package/template/src/lib/reminder-state-server.ts +80 -0
  179. package/template/src/lib/reminder-state.ts +29 -0
  180. package/template/src/lib/supabase-storage.ts +113 -0
  181. package/template/src/lib/template-variables.ts +164 -23
  182. package/template/src/lib/utils.ts +45 -0
  183. package/template/src/lib/widget-registry.ts +173 -0
  184. package/template/src/lib/workflow-executor.ts +16 -70
  185. package/template/src/proxy.ts +1 -0
  186. package/template/vercel.json +3 -10
  187. package/template/skills-lock.json +0 -25
  188. package/template/src/components/dashboard/dashboard-content.tsx +0 -79
  189. package/template/src/lib/google-drive.ts +0 -1101
  190. package/template/src/types/yousign.ts +0 -52
@@ -14,11 +14,13 @@ import {
14
14
  Calendar,
15
15
  Eye,
16
16
  } from 'lucide-react';
17
- import { cn } from '@/lib/utils';
17
+ import { cn, devToast } from '@/lib/utils';
18
18
  import { PageLoader } from '@/components/skeleton';
19
+ import { StatusSelect } from '@/components/ui/status-select';
19
20
  import { useUserRole } from '@/hooks/use-user-role';
20
21
  import { ProtectedPage } from '@/components/protected-page';
21
22
  import { useFetch } from '@/hooks/use-fetch';
23
+ import { useAppToast } from '@/contexts/app-toast-context';
22
24
 
23
25
  interface Status {
24
26
  id: string;
@@ -129,7 +131,7 @@ function KanbanContactCard({
129
131
  onDragStart={canDrag ? onDragStart : undefined}
130
132
  onDragEnd={canDrag ? onDragEnd : undefined}
131
133
  className={cn(
132
- 'relative rounded-lg border border-border bg-card p-4 text-[13px] shadow-(--shadow-card) transition-transform hover:-translate-y-0.5',
134
+ 'border-border bg-card relative rounded-lg border p-4 text-[13px] shadow-(--shadow-card) transition-transform hover:-translate-y-0.5',
133
135
  canDrag && 'cursor-grab active:cursor-grabbing',
134
136
  !canDrag && 'cursor-default',
135
137
  isDragging && 'opacity-80 ring-2 ring-blue-400',
@@ -138,7 +140,7 @@ function KanbanContactCard({
138
140
  {/* Icône Eye en haut à droite */}
139
141
  <Link
140
142
  href={`/contacts/${contact.id}`}
141
- className="absolute top-2 right-2 z-10 rounded p-1 transition-colors duration-200 hover:bg-muted"
143
+ className="hover:bg-muted absolute top-2 right-2 z-10 rounded p-1 transition-colors duration-200"
142
144
  onClick={(e) => e.stopPropagation()}
143
145
  >
144
146
  <Eye className="h-4 w-4 stroke-gray-400" />
@@ -155,7 +157,9 @@ function KanbanContactCard({
155
157
  {(contact.firstName?.[0] || contact.lastName?.[0] || '?').toUpperCase()}
156
158
  </div>
157
159
  <div className="min-w-0 flex-1">
158
- <p className="truncate text-sm font-semibold text-foreground">{fullName || 'Sans nom'}</p>
160
+ <p className="text-foreground truncate text-sm font-semibold">
161
+ {fullName || 'Sans nom'}
162
+ </p>
159
163
  </div>
160
164
  </div>
161
165
 
@@ -229,6 +233,7 @@ function createDefaultColumns(statuses: Status[]): ClosingColumn[] {
229
233
  }
230
234
 
231
235
  export default function ClosingPage() {
236
+ const toast = useAppToast();
232
237
  const { hasPermission, isLoading: roleLoading } = useUserRole();
233
238
  const {
234
239
  data: statuses = [],
@@ -403,8 +408,10 @@ export default function ClosingPage() {
403
408
  if (!res.ok) {
404
409
  throw new Error('Erreur lors de la mise à jour du statut');
405
410
  }
411
+ toast.success('Statut du contact mis à jour');
406
412
  } catch (e) {
407
413
  console.error(e);
414
+ toast.error(devToast('Erreur lors du déplacement du contact', e));
408
415
  } finally {
409
416
  setIsSavingDrag(false);
410
417
  }
@@ -556,15 +563,41 @@ export default function ClosingPage() {
556
563
  return Array.from(companyMap.values());
557
564
  }, [contacts]);
558
565
 
566
+ const uniqueCommercials = useMemo(
567
+ () =>
568
+ Array.from(
569
+ new Map(
570
+ contacts
571
+ .filter((c) => c.assignedCommercial)
572
+ .map((c) => [c.assignedCommercialId, c.assignedCommercial!]),
573
+ ).values(),
574
+ ),
575
+ [contacts],
576
+ );
577
+
578
+ const uniqueTelepros = useMemo(
579
+ () =>
580
+ Array.from(
581
+ new Map(
582
+ contacts
583
+ .filter((c) => c.assignedTelepro)
584
+ .map((c) => [c.assignedTeleproId, c.assignedTelepro!]),
585
+ ).values(),
586
+ ),
587
+ [contacts],
588
+ );
589
+
559
590
  return (
560
591
  <ProtectedPage requiredPermission="contacts.view_closing_pipeline">
561
592
  {statusesLoading || contactsLoading || roleLoading ? (
562
593
  <div className="h-full">
563
- <div className="border-b border-border bg-background/95 px-4 py-4 backdrop-blur-sm sm:px-6 lg:px-8 lg:py-6">
594
+ <div className="border-border bg-background/95 border-b px-4 py-4 backdrop-blur-sm sm:px-6 lg:px-8 lg:py-6">
564
595
  <div className="flex items-start gap-3">
565
596
  <div className="min-w-0 flex-1">
566
- <h1 className="text-xl font-bold text-foreground sm:text-2xl">Pipeline de Closing</h1>
567
- <p className="mt-1 text-sm text-muted-foreground">
597
+ <h1 className="text-foreground text-xl font-bold sm:text-2xl">
598
+ Pipeline de Closing
599
+ </h1>
600
+ <p className="text-muted-foreground mt-1 text-sm">
568
601
  Visualisez et gérez vos opportunités commerciales
569
602
  </p>
570
603
  </div>
@@ -579,25 +612,25 @@ export default function ClosingPage() {
579
612
  {' '}
580
613
  {/* md:overflow-y-hidden */}
581
614
  {/* En-tête personnalisé avec filtres intégrés */}
582
- <div className="border-b border-border bg-background/95 px-4 py-4 backdrop-blur-sm sm:px-6 lg:px-8 lg:py-6">
615
+ <div className="border-border bg-background/95 border-b px-4 py-4 backdrop-blur-sm sm:px-6 lg:px-8 lg:py-6">
583
616
  <div className="flex items-start gap-3">
584
617
  <div className="flex min-w-0 flex-1 items-start justify-between gap-4">
585
618
  <div className="min-w-0 flex-1">
586
- <h1 className="text-xl font-bold text-foreground sm:text-2xl">
619
+ <h1 className="text-foreground text-xl font-bold sm:text-2xl">
587
620
  Pipeline de Closing
588
621
  </h1>
589
- <p className="mt-1 text-sm text-muted-foreground">
622
+ <p className="text-muted-foreground mt-1 text-sm">
590
623
  Visualisez et gérez vos opportunités commerciales
591
624
  </p>
592
625
 
593
626
  {/* Filtres intégrés dans l'en-tête */}
594
627
  <div className="mt-4 flex flex-col gap-3 sm:flex-row">
595
628
  <div className="flex items-center gap-2">
596
- <span className="text-xs font-medium text-muted-foreground">Période:</span>
629
+ <span className="text-muted-foreground text-xs font-medium">Période:</span>
597
630
  <div className="relative">
598
- <Filter className="absolute top-1/2 left-2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground" />
631
+ <Filter className="text-muted-foreground absolute top-1/2 left-2 h-3.5 w-3.5 -translate-y-1/2" />
599
632
  <select
600
- className="rounded-lg border border-border bg-card py-1.5 pr-2 pl-7 text-sm text-foreground focus:border-primary/50 focus:ring-2 focus:ring-primary/20 focus:outline-none"
633
+ className="border-border bg-card text-foreground focus:border-primary/50 focus:ring-primary/20 rounded-lg border py-1.5 pr-2 pl-7 text-sm focus:ring-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
601
634
  value={selectedPeriod}
602
635
  onChange={(e) => setSelectedPeriod(e.target.value)}
603
636
  >
@@ -610,22 +643,16 @@ export default function ClosingPage() {
610
643
  </div>
611
644
 
612
645
  <div className="flex items-center gap-2">
613
- <span className="text-xs font-medium text-muted-foreground">Commercial:</span>
646
+ <span className="text-muted-foreground text-xs font-medium">Commercial:</span>
614
647
  <select
615
- className="rounded-lg border border-border bg-card px-2 py-1.5 text-sm text-foreground focus:border-primary/50 focus:ring-2 focus:ring-primary/20 focus:outline-none"
648
+ className="border-border bg-card text-foreground focus:border-primary/50 focus:ring-primary/20 rounded-lg border px-2 py-1.5 text-sm focus:ring-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
616
649
  value={selectedCommercialId}
617
650
  onChange={(e) =>
618
651
  setSelectedCommercialId(e.target.value === 'all' ? 'all' : e.target.value)
619
652
  }
620
653
  >
621
654
  <option value="all">Tous</option>
622
- {Array.from(
623
- new Map(
624
- contacts
625
- .filter((c) => c.assignedCommercial)
626
- .map((c) => [c.assignedCommercialId, c.assignedCommercial!]),
627
- ).values(),
628
- ).map((user) => (
655
+ {uniqueCommercials.map((user) => (
629
656
  <option key={user.id} value={user.id}>
630
657
  {user.name}
631
658
  </option>
@@ -634,22 +661,16 @@ export default function ClosingPage() {
634
661
  </div>
635
662
 
636
663
  <div className="flex items-center gap-2">
637
- <span className="text-xs font-medium text-muted-foreground">Télépro:</span>
664
+ <span className="text-muted-foreground text-xs font-medium">Télépro:</span>
638
665
  <select
639
- className="rounded-lg border border-border bg-card px-2 py-1.5 text-sm text-foreground focus:border-primary/50 focus:ring-2 focus:ring-primary/20 focus:outline-none"
666
+ className="border-border bg-card text-foreground focus:border-primary/50 focus:ring-primary/20 rounded-lg border px-2 py-1.5 text-sm focus:ring-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
640
667
  value={selectedTeleproId}
641
668
  onChange={(e) =>
642
669
  setSelectedTeleproId(e.target.value === 'all' ? 'all' : e.target.value)
643
670
  }
644
671
  >
645
672
  <option value="all">Tous</option>
646
- {Array.from(
647
- new Map(
648
- contacts
649
- .filter((c) => c.assignedTelepro)
650
- .map((c) => [c.assignedTeleproId, c.assignedTelepro!]),
651
- ).values(),
652
- ).map((user) => (
673
+ {uniqueTelepros.map((user) => (
653
674
  <option key={user.id} value={user.id}>
654
675
  {user.name}
655
676
  </option>
@@ -658,7 +679,7 @@ export default function ClosingPage() {
658
679
  </div>
659
680
 
660
681
  {isSavingDrag && (
661
- <span className="self-end text-xs text-muted-foreground">
682
+ <span className="text-muted-foreground self-end text-xs">
662
683
  Sauvegarde des changements…
663
684
  </span>
664
685
  )}
@@ -669,7 +690,7 @@ export default function ClosingPage() {
669
690
  <button
670
691
  type="button"
671
692
  onClick={openConfigModal}
672
- className="inline-flex cursor-pointer items-center gap-2 rounded-lg border border-border bg-card px-3 py-2 text-sm font-semibold text-foreground shadow-sm transition-colors duration-200 hover:bg-muted focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:outline-none"
693
+ className="border-border bg-card text-foreground hover:bg-muted focus-visible:ring-primary inline-flex cursor-pointer items-center gap-2 rounded-lg border px-3 py-2 text-sm font-semibold shadow-sm transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none"
673
694
  >
674
695
  <SettingsIcon className="h-4 w-4" />
675
696
  Configurer
@@ -697,21 +718,21 @@ export default function ClosingPage() {
697
718
  `}</style>
698
719
  {sortedColumns.length === 0 ? (
699
720
  <div className="flex w-full items-center justify-center py-16">
700
- <div className="max-w-md rounded-xl border-2 border-dashed border-border bg-card p-8 text-center shadow-(--shadow-card)">
701
- <div className="mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-primary/15">
702
- <Filter className="h-8 w-8 text-primary" />
721
+ <div className="border-border bg-card max-w-md rounded-xl border-2 border-dashed p-8 text-center shadow-(--shadow-card)">
722
+ <div className="bg-primary/15 mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full">
723
+ <Filter className="text-primary h-8 w-8" />
703
724
  </div>
704
- <h3 className="mb-2 text-lg font-semibold text-foreground">
725
+ <h3 className="text-foreground mb-2 text-lg font-semibold">
705
726
  Votre pipeline est vide
706
727
  </h3>
707
- <p className="mb-6 text-sm text-muted-foreground">
728
+ <p className="text-muted-foreground mb-6 text-sm">
708
729
  Commencez par configurer les colonnes de votre pipeline de closing en cliquant
709
730
  sur le bouton ci-dessous.
710
731
  </p>
711
732
  <button
712
733
  type="button"
713
734
  onClick={openConfigModal}
714
- className="inline-flex cursor-pointer items-center gap-2 rounded-lg bg-primary px-4 py-2.5 text-sm font-semibold text-primary-foreground shadow-(--shadow-card) transition-colors duration-200 hover:bg-primary/90 focus-visible:ring-2 focus-visible:ring-primary focus-visible:ring-offset-2 focus-visible:outline-none"
735
+ className="bg-primary text-primary-foreground hover:bg-primary/90 focus-visible:ring-primary inline-flex cursor-pointer items-center gap-2 rounded-lg px-4 py-2.5 text-sm font-semibold shadow-(--shadow-card) transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none"
715
736
  >
716
737
  <SettingsIcon className="h-4 w-4" />
717
738
  Configurer ma pipeline
@@ -727,10 +748,9 @@ export default function ClosingPage() {
727
748
  <div
728
749
  key={column.id}
729
750
  className={cn(
730
- 'flex shrink-0 flex-col rounded-xl bg-muted shadow-sm transition-colors',
751
+ 'bg-muted flex shrink-0 flex-col rounded-xl shadow-sm transition-colors',
731
752
  'h-auto max-h-[calc(100vh-220px)]',
732
- dragOverStatusId === column.statusId &&
733
- 'ring-2 ring-blue-400 ring-offset-2',
753
+ dragOverStatusId === column.statusId && 'ring-2 ring-blue-400 ring-offset-2',
734
754
  dragOverColumnId === column.id &&
735
755
  draggedColumnId &&
736
756
  'ring-2 ring-blue-500 ring-offset-2',
@@ -898,7 +918,7 @@ export default function ClosingPage() {
898
918
  }
899
919
  className={cn(
900
920
  'flex w-80 shrink-0 flex-col rounded-xl border border-gray-200 bg-gray-50',
901
- 'cursor-grab transition-all duration-150 active:cursor-grabbing',
921
+ 'cursor-grab transition-[transform,box-shadow] duration-150 active:cursor-grabbing',
902
922
  'hover:-translate-y-1 hover:shadow-lg',
903
923
  dragOverConfigColumnId === column.id &&
904
924
  'ring-2 ring-blue-400 ring-offset-2',
@@ -912,7 +932,7 @@ export default function ClosingPage() {
912
932
  onChange={(e) =>
913
933
  updateConfigColumn(column.id, { title: e.target.value })
914
934
  }
915
- className="w-full rounded-lg border border-gray-300 px-2 py-1.5 text-sm text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus:outline-none"
935
+ className="w-full rounded-lg border border-gray-300 px-2 py-1.5 text-sm text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
916
936
  placeholder="Nom de la colonne"
917
937
  />
918
938
  <button
@@ -927,30 +947,26 @@ export default function ClosingPage() {
927
947
  <label className="mb-2 block text-xs font-medium text-gray-600">
928
948
  Statut associé
929
949
  </label>
930
- <select
950
+ <StatusSelect
951
+ statuses={statuses}
931
952
  value={column.statusId || ''}
932
- onChange={(e) =>
953
+ onChange={(value) =>
933
954
  updateConfigColumn(column.id, {
934
- statusId: e.target.value || null,
955
+ statusId: value || null,
935
956
  title:
936
- e.target.value && statusesById[e.target.value]
937
- ? statusesById[e.target.value].name
957
+ value && statusesById[value]
958
+ ? statusesById[value].name
938
959
  : column.title,
939
960
  color:
940
- e.target.value && statusesById[e.target.value]
941
- ? statusesById[e.target.value].color
961
+ value && statusesById[value]
962
+ ? statusesById[value].color
942
963
  : column.color,
943
964
  })
944
965
  }
945
- className="mb-3 w-full rounded-lg border border-gray-300 px-2 py-1.5 text-sm text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus:outline-none"
946
- >
947
- <option value="">Sélectionnez un statut</option>
948
- {statuses.map((status) => (
949
- <option key={status.id} value={status.id}>
950
- {status.name}
951
- </option>
952
- ))}
953
- </select>
966
+ placeholder="Sélectionnez un statut"
967
+ size="sm"
968
+ className="mb-3"
969
+ />
954
970
 
955
971
  <label className="mb-1 block text-xs font-medium text-gray-600">
956
972
  Couleur de la colonne
@@ -970,7 +986,7 @@ export default function ClosingPage() {
970
986
  onChange={(e) =>
971
987
  updateConfigColumn(column.id, { color: e.target.value })
972
988
  }
973
- className="flex-1 rounded-lg border border-gray-300 px-2 py-1.5 text-xs text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus:outline-none"
989
+ className="flex-1 rounded-lg border border-gray-300 px-2 py-1.5 text-xs text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
974
990
  />
975
991
  </div>
976
992
 
@@ -1017,7 +1033,7 @@ export default function ClosingPage() {
1017
1033
  <button
1018
1034
  type="button"
1019
1035
  onClick={handleSaveConfig}
1020
- className="cursor-pointer rounded-lg bg-primary px-4 py-2 text-sm font-semibold text-primary-foreground shadow-sm transition-colors hover:bg-primary/90"
1036
+ className="bg-primary text-primary-foreground hover:bg-primary/90 cursor-pointer rounded-lg px-4 py-2 text-sm font-semibold shadow-sm transition-colors"
1021
1037
  >
1022
1038
  Enregistrer la configuration
1023
1039
  </button>