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
@@ -15,13 +15,14 @@ import {
15
15
  Phone,
16
16
  Mail,
17
17
  } from 'lucide-react';
18
- import { cn, normalizePhoneNumber } from '@/lib/utils';
18
+ import { cn, normalizePhoneNumber, devToast } from '@/lib/utils';
19
19
  import { useConfirm } from '@/hooks/use-confirm';
20
20
  import { useUserRole } from '@/hooks/use-user-role';
21
21
  import { ProtectedPage } from '@/components/protected-page';
22
22
  import AddressAutocomplete from '@/components/address-autocomplete';
23
23
  import { Spinner } from '@/components/skeleton';
24
24
  import { useAppToast } from '@/contexts/app-toast-context';
25
+ import { StatusSelect } from '@/components/ui/status-select';
25
26
 
26
27
  interface CompanyContact {
27
28
  id: string;
@@ -152,7 +153,7 @@ export default function CompanyDetailPage() {
152
153
 
153
154
  const originalFormDataRef = useRef<typeof companyFormData | null>(null);
154
155
 
155
- const canEdit = hasPermission('companies.edit') || hasPermission('contacts.edit');
156
+ const canEdit = hasPermission('companies.edit');
156
157
  const canDelete = hasPermission('companies.delete') || hasPermission('contacts.delete');
157
158
  const canCreate = hasPermission('companies.create') || hasPermission('contacts.create');
158
159
  const canViewActivities = hasPermission('companies.view_activities');
@@ -181,7 +182,7 @@ export default function CompanyDetailPage() {
181
182
  setCompanyFormData(initialForm);
182
183
  originalFormDataRef.current = { ...initialForm };
183
184
  } catch (err: unknown) {
184
- setError(err instanceof Error ? err.message : 'Erreur');
185
+ setError(devToast("Erreur lors du chargement de l'entreprise", err));
185
186
  } finally {
186
187
  setLoading(false);
187
188
  }
@@ -280,8 +281,9 @@ export default function CompanyDetailPage() {
280
281
  setLastSavedCompany(new Date());
281
282
  originalFormDataRef.current = { ...companyFormData };
282
283
  await fetchActivities();
284
+ if (!silent) toast.success('Entreprise mise à jour');
283
285
  } catch (err: unknown) {
284
- if (!silent) setError(err instanceof Error ? err.message : 'Erreur');
286
+ if (!silent) setError(devToast('Erreur lors de la sauvegarde', err));
285
287
  } finally {
286
288
  setIsSavingCompany(false);
287
289
  }
@@ -482,8 +484,9 @@ export default function CompanyDetailPage() {
482
484
  await fetchCompany();
483
485
  await fetchActivities();
484
486
  setShowAddContactModal(false);
487
+ toast.success('Contact associé à l\'entreprise');
485
488
  } catch (err: unknown) {
486
- setAddExistingError(err instanceof Error ? err.message : 'Erreur');
489
+ setAddExistingError(devToast("Erreur lors de l'association du contact", err));
487
490
  } finally {
488
491
  setAddExistingLoading(false);
489
492
  }
@@ -530,8 +533,9 @@ export default function CompanyDetailPage() {
530
533
  await fetchCompany();
531
534
  await fetchActivities();
532
535
  setShowAddContactModal(false);
536
+ toast.success('Contact créé et associé à l\'entreprise');
533
537
  } catch (err: unknown) {
534
- setContactFormError(err instanceof Error ? err.message : 'Erreur');
538
+ setContactFormError(devToast('Erreur lors de la création du contact', err));
535
539
  } finally {
536
540
  setSavingContact(false);
537
541
  }
@@ -549,9 +553,10 @@ export default function CompanyDetailPage() {
549
553
  try {
550
554
  const res = await fetch(`/api/companies/${id}`, { method: 'DELETE' });
551
555
  if (!res.ok) throw new Error('Erreur lors de la suppression');
556
+ toast.success('Entreprise supprimée');
552
557
  router.push('/contacts?entity=companies');
553
558
  } catch (err: unknown) {
554
- setError(err instanceof Error ? err.message : 'Erreur');
559
+ setError(devToast("Erreur lors de la suppression de l'entreprise", err));
555
560
  }
556
561
  };
557
562
 
@@ -678,7 +683,7 @@ export default function CompanyDetailPage() {
678
683
  onChange={(e) =>
679
684
  setCompanyFormData({ ...companyFormData, name: e.target.value })
680
685
  }
681
- 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"
686
+ 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"
682
687
  required
683
688
  />
684
689
  </div>
@@ -697,7 +702,7 @@ export default function CompanyDetailPage() {
697
702
  onChange={(e) =>
698
703
  setCompanyFormData({ ...companyFormData, phone: e.target.value })
699
704
  }
700
- 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"
705
+ 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"
701
706
  />
702
707
  </div>
703
708
  <div>
@@ -714,7 +719,7 @@ export default function CompanyDetailPage() {
714
719
  onChange={(e) =>
715
720
  setCompanyFormData({ ...companyFormData, email: e.target.value })
716
721
  }
717
- 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"
722
+ 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"
718
723
  />
719
724
  </div>
720
725
  </div>
@@ -757,7 +762,7 @@ export default function CompanyDetailPage() {
757
762
  onChange={(e) =>
758
763
  setCompanyFormData({ ...companyFormData, city: e.target.value })
759
764
  }
760
- 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"
765
+ 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"
761
766
  />
762
767
  </div>
763
768
  <div>
@@ -774,7 +779,7 @@ export default function CompanyDetailPage() {
774
779
  onChange={(e) =>
775
780
  setCompanyFormData({ ...companyFormData, postalCode: e.target.value })
776
781
  }
777
- 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"
782
+ 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"
778
783
  />
779
784
  </div>
780
785
  </div>
@@ -793,7 +798,7 @@ export default function CompanyDetailPage() {
793
798
  setCompanyFormData({ ...companyFormData, website: e.target.value })
794
799
  }
795
800
  placeholder="https://"
796
- 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"
801
+ 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"
797
802
  />
798
803
  </div>
799
804
  <div className="grid grid-cols-2 gap-2">
@@ -811,7 +816,7 @@ export default function CompanyDetailPage() {
811
816
  onChange={(e) =>
812
817
  setCompanyFormData({ ...companyFormData, siret: e.target.value })
813
818
  }
814
- 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"
819
+ 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"
815
820
  />
816
821
  </div>
817
822
  <div>
@@ -828,7 +833,7 @@ export default function CompanyDetailPage() {
828
833
  onChange={(e) =>
829
834
  setCompanyFormData({ ...companyFormData, industry: e.target.value })
830
835
  }
831
- 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"
836
+ 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"
832
837
  />
833
838
  </div>
834
839
  </div>
@@ -846,7 +851,7 @@ export default function CompanyDetailPage() {
846
851
  setCompanyFormData({ ...companyFormData, notes: e.target.value })
847
852
  }
848
853
  rows={3}
849
- 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"
854
+ 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"
850
855
  />
851
856
  </div>
852
857
  <div className="grid grid-cols-2 gap-2 border-t border-gray-100 pt-3">
@@ -866,7 +871,7 @@ export default function CompanyDetailPage() {
866
871
  assignedCommercialId: e.target.value,
867
872
  })
868
873
  }
869
- 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"
874
+ 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"
870
875
  >
871
876
  <option value="">Non attribué</option>
872
877
  {users.map((u) => (
@@ -892,7 +897,7 @@ export default function CompanyDetailPage() {
892
897
  assignedTeleproId: e.target.value,
893
898
  })
894
899
  }
895
- 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"
900
+ 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"
896
901
  >
897
902
  <option value="">Non attribué</option>
898
903
  {users.map((u) => (
@@ -1230,8 +1235,8 @@ export default function CompanyDetailPage() {
1230
1235
  </div>
1231
1236
 
1232
1237
  {showAddContactModal && (
1233
- <div className="fixed inset-0 z-50 flex items-center justify-center bg-gray-500/20 p-4 backdrop-blur-sm">
1234
- <div className="flex max-h-[90vh] w-full max-w-lg flex-col rounded-lg bg-white shadow-xl">
1238
+ <div className="ui-fade-in fixed inset-0 z-50 flex items-center justify-center bg-gray-500/20 p-4 backdrop-blur-sm">
1239
+ <div className="ui-scale-in flex max-h-[90vh] w-full max-w-lg flex-col rounded-lg bg-white shadow-xl">
1235
1240
  <div className="shrink-0 border-b border-gray-200 px-6 py-4">
1236
1241
  <div className="flex items-center justify-between">
1237
1242
  <h2 className="text-lg font-semibold text-gray-900">Ajouter un contact</h2>
@@ -1290,7 +1295,7 @@ export default function CompanyDetailPage() {
1290
1295
  onChange={(e) =>
1291
1296
  setNewContactForm({ ...newContactForm, civility: e.target.value })
1292
1297
  }
1293
- 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"
1298
+ 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"
1294
1299
  >
1295
1300
  <option value="">-</option>
1296
1301
  <option value="M">M.</option>
@@ -1314,7 +1319,7 @@ export default function CompanyDetailPage() {
1314
1319
  onChange={(e) =>
1315
1320
  setNewContactForm({ ...newContactForm, firstName: e.target.value })
1316
1321
  }
1317
- 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"
1322
+ 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"
1318
1323
  />
1319
1324
  </div>
1320
1325
  <div>
@@ -1331,7 +1336,7 @@ export default function CompanyDetailPage() {
1331
1336
  onChange={(e) =>
1332
1337
  setNewContactForm({ ...newContactForm, lastName: e.target.value })
1333
1338
  }
1334
- 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"
1339
+ 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"
1335
1340
  />
1336
1341
  </div>
1337
1342
  </div>
@@ -1350,7 +1355,7 @@ export default function CompanyDetailPage() {
1350
1355
  setNewContactForm({ ...newContactForm, jobTitle: e.target.value })
1351
1356
  }
1352
1357
  placeholder="Ex. Directeur commercial, Assistant..."
1353
- 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"
1358
+ 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"
1354
1359
  />
1355
1360
  </div>
1356
1361
  <div className="grid grid-cols-2 gap-2">
@@ -1373,7 +1378,7 @@ export default function CompanyDetailPage() {
1373
1378
  const n = normalizePhoneNumber(e.target.value);
1374
1379
  if (n) setNewContactForm((prev) => ({ ...prev, phone: n }));
1375
1380
  }}
1376
- 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"
1381
+ 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"
1377
1382
  />
1378
1383
  </div>
1379
1384
  <div>
@@ -1390,7 +1395,7 @@ export default function CompanyDetailPage() {
1390
1395
  onChange={(e) =>
1391
1396
  setNewContactForm({ ...newContactForm, email: e.target.value })
1392
1397
  }
1393
- 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"
1398
+ 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"
1394
1399
  />
1395
1400
  </div>
1396
1401
  </div>
@@ -1429,7 +1434,7 @@ export default function CompanyDetailPage() {
1429
1434
  onChange={(e) =>
1430
1435
  setNewContactForm({ ...newContactForm, city: e.target.value })
1431
1436
  }
1432
- 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"
1437
+ 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"
1433
1438
  />
1434
1439
  </div>
1435
1440
  <div>
@@ -1446,7 +1451,7 @@ export default function CompanyDetailPage() {
1446
1451
  onChange={(e) =>
1447
1452
  setNewContactForm({ ...newContactForm, postalCode: e.target.value })
1448
1453
  }
1449
- 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"
1454
+ 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"
1450
1455
  />
1451
1456
  </div>
1452
1457
  </div>
@@ -1457,21 +1462,17 @@ export default function CompanyDetailPage() {
1457
1462
  >
1458
1463
  Statut
1459
1464
  </label>
1460
- <select
1465
+ <StatusSelect
1461
1466
  id="new-status"
1467
+ statuses={statuses}
1462
1468
  value={newContactForm.statusId}
1463
- onChange={(e) =>
1464
- setNewContactForm({ ...newContactForm, statusId: e.target.value })
1469
+ onChange={(value) =>
1470
+ setNewContactForm({ ...newContactForm, statusId: value })
1465
1471
  }
1466
- 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"
1467
- >
1468
- <option value="">Aucun statut</option>
1469
- {statuses.map((s) => (
1470
- <option key={s.id} value={s.id}>
1471
- {s.name}
1472
- </option>
1473
- ))}
1474
- </select>
1472
+ placeholder="Aucun statut"
1473
+ 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"
1474
+ size="sm"
1475
+ />
1475
1476
  </div>
1476
1477
  <div className="grid grid-cols-2 gap-2">
1477
1478
  <div>
@@ -1490,7 +1491,7 @@ export default function CompanyDetailPage() {
1490
1491
  assignedCommercialId: e.target.value,
1491
1492
  })
1492
1493
  }
1493
- 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"
1494
+ 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"
1494
1495
  >
1495
1496
  <option value="">Non attribué</option>
1496
1497
  {(isAdmin
@@ -1524,7 +1525,7 @@ export default function CompanyDetailPage() {
1524
1525
  assignedTeleproId: e.target.value,
1525
1526
  })
1526
1527
  }
1527
- 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"
1528
+ 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"
1528
1529
  >
1529
1530
  <option value="">Non attribué</option>
1530
1531
  {(isAdmin
@@ -1571,7 +1572,7 @@ export default function CompanyDetailPage() {
1571
1572
  value={existingContactSearch}
1572
1573
  onChange={(e) => setExistingContactSearch(e.target.value)}
1573
1574
  placeholder="Rechercher par nom, téléphone ou email..."
1574
- className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus:outline-none"
1575
+ className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-2 text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50"
1575
1576
  />
1576
1577
  <p className="mt-1 text-xs text-gray-500">
1577
1578
  Tapez pour rechercher parmi les contacts existants
@@ -1601,9 +1602,7 @@ export default function CompanyDetailPage() {
1601
1602
  key={c.id}
1602
1603
  className={cn(
1603
1604
  'flex cursor-pointer items-center gap-3 border-b border-gray-100 p-4 transition-colors last:border-b-0',
1604
- isSelected
1605
- ? 'bg-blue-50 hover:bg-blue-100'
1606
- : 'hover:bg-gray-50',
1605
+ isSelected ? 'bg-blue-50 hover:bg-blue-100' : 'hover:bg-gray-50',
1607
1606
  )}
1608
1607
  onClick={() => setAddExistingSelectedId(isSelected ? null : c.id)}
1609
1608
  >
@@ -1657,7 +1656,7 @@ export default function CompanyDetailPage() {
1657
1656
  value={addExistingJobTitle}
1658
1657
  onChange={(e) => setAddExistingJobTitle(e.target.value)}
1659
1658
  placeholder="Ex. Directeur commercial..."
1660
- className="w-full rounded-lg border border-gray-300 px-3 py-2 text-sm text-gray-900 focus:ring-2 focus:ring-gray-400/30 focus:outline-none"
1659
+ className="w-full rounded-lg border border-gray-300 px-3 py-2 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"
1661
1660
  />
1662
1661
  </div>
1663
1662
  )}