create-crm-tmp 1.1.3 → 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 (276) hide show
  1. package/bin/create-crm-tmp.js +56 -35
  2. package/package.json +1 -1
  3. package/template/.prettierignore +2 -0
  4. package/template/README.md +230 -115
  5. package/template/components.json +22 -0
  6. package/template/eslint.config.mjs +13 -0
  7. package/template/exemple-contacts.csv +54 -0
  8. package/template/next.config.ts +41 -1
  9. package/template/package.json +63 -15
  10. package/template/prisma/migrations/20260318095700_init_db/migration.sql +978 -0
  11. package/template/prisma/schema.prisma +311 -67
  12. package/template/src/app/(auth)/invite/[token]/page.tsx +28 -29
  13. package/template/src/app/(auth)/layout.tsx +1 -1
  14. package/template/src/app/(auth)/reset-password/complete/page.tsx +21 -27
  15. package/template/src/app/(auth)/reset-password/page.tsx +14 -10
  16. package/template/src/app/(auth)/reset-password/verify/page.tsx +14 -10
  17. package/template/src/app/(auth)/signin/page.tsx +34 -23
  18. package/template/src/app/(dashboard)/agenda/page.tsx +3655 -2357
  19. package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +10 -7
  20. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +609 -338
  21. package/template/src/app/(dashboard)/automatisation/new/page.tsx +11 -8
  22. package/template/src/app/(dashboard)/automatisation/page.tsx +463 -186
  23. package/template/src/app/(dashboard)/closing/page.tsx +517 -469
  24. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +6151 -4210
  25. package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +1702 -0
  26. package/template/src/app/(dashboard)/contacts/loading.tsx +13 -0
  27. package/template/src/app/(dashboard)/contacts/page.tsx +4124 -2130
  28. package/template/src/app/(dashboard)/dashboard/page.tsx +119 -105
  29. package/template/src/app/(dashboard)/dev/page.tsx +1291 -0
  30. package/template/src/app/(dashboard)/error.tsx +37 -0
  31. package/template/src/app/(dashboard)/layout.tsx +6 -2
  32. package/template/src/app/(dashboard)/loading.tsx +5 -0
  33. package/template/src/app/(dashboard)/settings/loading.tsx +19 -0
  34. package/template/src/app/(dashboard)/settings/page.tsx +1773 -3362
  35. package/template/src/app/(dashboard)/templates/page.tsx +504 -303
  36. package/template/src/app/(dashboard)/users/list/page.tsx +364 -355
  37. package/template/src/app/(dashboard)/users/page.tsx +279 -310
  38. package/template/src/app/(dashboard)/users/permissions/page.tsx +104 -99
  39. package/template/src/app/(dashboard)/users/roles/page.tsx +169 -140
  40. package/template/src/app/api/agenda/google-events/route.ts +92 -0
  41. package/template/src/app/api/audit-logs/route.ts +1 -1
  42. package/template/src/app/api/auth/check-active/route.ts +3 -2
  43. package/template/src/app/api/auth/google/callback/route.ts +8 -5
  44. package/template/src/app/api/auth/google/disconnect/route.ts +2 -2
  45. package/template/src/app/api/auth/google/route.ts +2 -1
  46. package/template/src/app/api/auth/google/status/route.ts +7 -31
  47. package/template/src/app/api/companies/[id]/activities/route.ts +129 -0
  48. package/template/src/app/api/companies/[id]/route.ts +194 -0
  49. package/template/src/app/api/companies/export/route.ts +206 -0
  50. package/template/src/app/api/companies/route.ts +196 -0
  51. package/template/src/app/api/contact-views/[id]/pin/route.ts +69 -0
  52. package/template/src/app/api/contact-views/[id]/route.ts +197 -0
  53. package/template/src/app/api/contact-views/route.ts +146 -0
  54. package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +55 -0
  55. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +20 -48
  56. package/template/src/app/api/contacts/[id]/files/route.ts +125 -186
  57. package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +27 -1
  58. package/template/src/app/api/contacts/[id]/interactions/route.ts +45 -8
  59. package/template/src/app/api/contacts/[id]/kyc/route.ts +81 -0
  60. package/template/src/app/api/contacts/[id]/meet/route.ts +55 -29
  61. package/template/src/app/api/contacts/[id]/route.ts +184 -21
  62. package/template/src/app/api/contacts/[id]/send-email/route.ts +33 -11
  63. package/template/src/app/api/contacts/[id]/workflows/run/route.ts +67 -0
  64. package/template/src/app/api/contacts/export/route.ts +22 -31
  65. package/template/src/app/api/contacts/import/route.ts +77 -44
  66. package/template/src/app/api/contacts/import-preview/route.ts +139 -0
  67. package/template/src/app/api/contacts/origins/route.ts +63 -0
  68. package/template/src/app/api/contacts/route.ts +322 -57
  69. package/template/src/app/api/cron/cleanup-editor-images/route.ts +166 -0
  70. package/template/src/app/api/dashboard/stats/route.ts +9 -292
  71. package/template/src/app/api/dashboard/widgets/[id]/route.ts +0 -3
  72. package/template/src/app/api/dashboard/widgets/route.ts +19 -19
  73. package/template/src/app/api/dev/reminders/test/route.ts +114 -0
  74. package/template/src/app/api/editor/upload-image/route.ts +61 -0
  75. package/template/src/app/api/integrations/google-sheet/jobs/[jobId]/route.ts +47 -0
  76. package/template/src/app/api/integrations/google-sheet/jobs/usage/route.ts +50 -0
  77. package/template/src/app/api/integrations/google-sheet/sync/route.ts +28 -542
  78. package/template/src/app/api/invite/complete/route.ts +20 -23
  79. package/template/src/app/api/jobs/google-sheet/process/route.ts +84 -0
  80. package/template/src/app/api/jobs/google-sheet/schedule/route.ts +50 -0
  81. package/template/src/app/api/reminders/clear/route.ts +120 -0
  82. package/template/src/app/api/reminders/clear/undo/route.ts +112 -0
  83. package/template/src/app/api/reminders/route.ts +165 -39
  84. package/template/src/app/api/reminders/state/route.ts +164 -0
  85. package/template/src/app/api/reset-password/complete/route.ts +11 -13
  86. package/template/src/app/api/reset-password/request/route.ts +1 -1
  87. package/template/src/app/api/reset-password/verify/route.ts +1 -1
  88. package/template/src/app/api/send/route.ts +25 -47
  89. package/template/src/app/api/settings/closing-reasons/[id]/route.ts +10 -21
  90. package/template/src/app/api/settings/closing-reasons/route.ts +10 -21
  91. package/template/src/app/api/settings/company/route.ts +19 -26
  92. package/template/src/app/api/settings/google-ads/[id]/route.ts +20 -23
  93. package/template/src/app/api/settings/google-ads/route.ts +34 -23
  94. package/template/src/app/api/settings/google-calendar/calendars/route.ts +97 -0
  95. package/template/src/app/api/settings/google-calendar/route.ts +124 -0
  96. package/template/src/app/api/settings/google-sheet/[id]/route.ts +48 -23
  97. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +56 -32
  98. package/template/src/app/api/settings/google-sheet/preview/route.ts +110 -0
  99. package/template/src/app/api/settings/google-sheet/route.ts +34 -23
  100. package/template/src/app/api/settings/integrations/logs/route.ts +93 -0
  101. package/template/src/app/api/settings/integrations/notifications/route.ts +67 -0
  102. package/template/src/app/api/settings/meta-leads/[id]/route.ts +20 -24
  103. package/template/src/app/api/settings/meta-leads/route.ts +34 -25
  104. package/template/src/app/api/settings/smtp/route.ts +53 -6
  105. package/template/src/app/api/settings/statuses/[id]/route.ts +29 -32
  106. package/template/src/app/api/settings/statuses/route.ts +24 -22
  107. package/template/src/app/api/statuses/route.ts +2 -5
  108. package/template/src/app/api/tasks/[id]/attendees/route.ts +36 -13
  109. package/template/src/app/api/tasks/[id]/route.ts +357 -145
  110. package/template/src/app/api/tasks/meet/route.ts +37 -26
  111. package/template/src/app/api/tasks/route.ts +201 -96
  112. package/template/src/app/api/templates/[id]/route.ts +22 -13
  113. package/template/src/app/api/templates/route.ts +22 -5
  114. package/template/src/app/api/users/[id]/resend-invite/route.ts +95 -0
  115. package/template/src/app/api/users/[id]/route.ts +22 -16
  116. package/template/src/app/api/users/commercials/route.ts +38 -0
  117. package/template/src/app/api/users/for-agenda/route.ts +1 -2
  118. package/template/src/app/api/users/list/route.ts +57 -19
  119. package/template/src/app/api/users/route.ts +89 -34
  120. package/template/src/app/api/webhooks/google-ads/route.ts +40 -1
  121. package/template/src/app/api/webhooks/meta-leads/route.ts +38 -1
  122. package/template/src/app/api/workflows/[id]/route.ts +29 -6
  123. package/template/src/app/api/workflows/process/route.ts +505 -170
  124. package/template/src/app/api/workflows/route.ts +42 -4
  125. package/template/src/app/globals.css +512 -32
  126. package/template/src/app/layout.tsx +28 -9
  127. package/template/src/app/page.tsx +37 -7
  128. package/template/src/components/address-autocomplete.tsx +233 -0
  129. package/template/src/components/config-error-alert.tsx +46 -0
  130. package/template/src/components/contacts/filter-bar.tsx +190 -0
  131. package/template/src/components/contacts/filter-builder.tsx +574 -0
  132. package/template/src/components/contacts/save-view-dialog.tsx +160 -0
  133. package/template/src/components/contacts/views-tab-bar.tsx +449 -0
  134. package/template/src/components/dashboard/activity-chart.tsx +6 -1
  135. package/template/src/components/dashboard/add-widget-dialog.tsx +13 -17
  136. package/template/src/components/dashboard/color-picker.tsx +7 -8
  137. package/template/src/components/dashboard/recent-activity.tsx +2 -5
  138. package/template/src/components/dashboard/stat-card.tsx +1 -3
  139. package/template/src/components/dashboard/status-distribution-chart.tsx +0 -1
  140. package/template/src/components/dashboard/top-contacts-list.tsx +7 -13
  141. package/template/src/components/dashboard/upcoming-tasks-list.tsx +2 -5
  142. package/template/src/components/dashboard/widget-wrapper.tsx +3 -6
  143. package/template/src/components/date-picker.tsx +399 -0
  144. package/template/src/components/editor/upload-editor-image.ts +42 -0
  145. package/template/src/components/editor.tsx +188 -35
  146. package/template/src/components/email-template.tsx +4 -2
  147. package/template/src/components/global-search.tsx +360 -0
  148. package/template/src/components/header.tsx +200 -107
  149. package/template/src/components/inactive-account-guard.tsx +58 -0
  150. package/template/src/components/integration-notifications-listener.tsx +12 -0
  151. package/template/src/components/invitation-email-template.tsx +4 -2
  152. package/template/src/components/lazy-editor.tsx +11 -0
  153. package/template/src/components/meet-cancellation-email-template.tsx +11 -3
  154. package/template/src/components/meet-confirmation-email-template.tsx +10 -3
  155. package/template/src/components/meet-update-email-template.tsx +10 -3
  156. package/template/src/components/page-header.tsx +19 -15
  157. package/template/src/components/protected-page.tsx +94 -0
  158. package/template/src/components/reset-password-email-template.tsx +4 -2
  159. package/template/src/components/settings/integrations/GoogleAdsIntegration.tsx +428 -0
  160. package/template/src/components/settings/integrations/GoogleSheetConfigMonitoringModal.tsx +680 -0
  161. package/template/src/components/settings/integrations/GoogleSheetIntegration.tsx +809 -0
  162. package/template/src/components/settings/integrations/ImportResultDialog.tsx +124 -0
  163. package/template/src/components/settings/integrations/IntegrationLogPanel.tsx +57 -0
  164. package/template/src/components/settings/integrations/IntegrationLogsTable.tsx +186 -0
  165. package/template/src/components/settings/integrations/MetaLeadIntegration.tsx +451 -0
  166. package/template/src/components/sidebar.tsx +117 -100
  167. package/template/src/components/skeleton.tsx +128 -45
  168. package/template/src/components/ui/accordion.tsx +64 -0
  169. package/template/src/components/ui/alert-dialog.tsx +139 -0
  170. package/template/src/components/ui/button.tsx +71 -0
  171. package/template/src/components/ui/components.tsx +1 -1
  172. package/template/src/components/ui/date-picker.tsx +422 -0
  173. package/template/src/components/ui/datetime-picker.tsx +338 -0
  174. package/template/src/components/ui/status-select.tsx +271 -0
  175. package/template/src/components/ui/tooltip.tsx +37 -0
  176. package/template/src/components/view-as-banner.tsx +1 -1
  177. package/template/src/components/view-as-modal.tsx +30 -19
  178. package/template/src/config/nav-pages.ts +108 -0
  179. package/template/src/contexts/app-toast-context.tsx +362 -0
  180. package/template/src/contexts/dashboard-theme-context.tsx +2 -7
  181. package/template/src/contexts/sidebar-context.tsx +27 -53
  182. package/template/src/contexts/task-reminder-context.tsx +134 -160
  183. package/template/src/contexts/view-as-context.tsx +32 -10
  184. package/template/src/hooks/use-alert.tsx +65 -0
  185. package/template/src/hooks/use-confirm.tsx +87 -0
  186. package/template/src/hooks/use-contact-views.ts +140 -0
  187. package/template/src/hooks/use-contacts.ts +69 -0
  188. package/template/src/hooks/use-fetch.ts +17 -0
  189. package/template/src/hooks/use-focus-trap.ts +73 -0
  190. package/template/src/hooks/use-statuses.ts +22 -0
  191. package/template/src/hooks/useIntegrationNotifications.ts +49 -0
  192. package/template/src/lib/address-api.ts +155 -0
  193. package/template/src/lib/auth.ts +8 -1
  194. package/template/src/lib/cache.ts +73 -0
  195. package/template/src/lib/check-permission.ts +12 -177
  196. package/template/src/lib/config-links.ts +14 -0
  197. package/template/src/lib/contact-duplicate.ts +79 -61
  198. package/template/src/lib/contact-interactions.ts +24 -22
  199. package/template/src/lib/contact-view-filters.ts +301 -0
  200. package/template/src/lib/contacts-list-url.ts +190 -0
  201. package/template/src/lib/dashboard-stats.ts +282 -0
  202. package/template/src/lib/dashboard-themes.ts +0 -5
  203. package/template/src/lib/date-utils.ts +176 -0
  204. package/template/src/lib/default-widgets.ts +0 -2
  205. package/template/src/lib/editor-html-image-dimensions.ts +172 -0
  206. package/template/src/lib/editor-image-limits.ts +19 -0
  207. package/template/src/lib/email-html-sanitize.ts +19 -0
  208. package/template/src/lib/encryption.ts +9 -6
  209. package/template/src/lib/fr-geography.ts +192 -0
  210. package/template/src/lib/get-auth-user.ts +25 -0
  211. package/template/src/lib/google-calendar-agenda.ts +201 -0
  212. package/template/src/lib/google-calendar.ts +309 -17
  213. package/template/src/lib/google-fetch.ts +63 -0
  214. package/template/src/lib/google-sheet-sync-jobs.ts +96 -0
  215. package/template/src/lib/google-sheet-sync-runner.ts +514 -0
  216. package/template/src/lib/integration-import-log.ts +21 -0
  217. package/template/src/lib/local-storage.ts +34 -0
  218. package/template/src/lib/permissions.ts +268 -40
  219. package/template/src/lib/prisma.ts +15 -12
  220. package/template/src/lib/qstash.ts +65 -0
  221. package/template/src/lib/reminder-state-server.ts +80 -0
  222. package/template/src/lib/reminder-state.ts +29 -0
  223. package/template/src/lib/roles.ts +12 -15
  224. package/template/src/lib/supabase-storage.ts +113 -0
  225. package/template/src/lib/template-variables.ts +204 -29
  226. package/template/src/lib/utils.ts +71 -11
  227. package/template/src/lib/widget-registry.ts +0 -4
  228. package/template/src/lib/workflow-executor.ts +391 -228
  229. package/template/src/proxy.ts +35 -73
  230. package/template/src/types/contact-views.ts +351 -0
  231. package/template/vercel.json +5 -0
  232. package/template/WORKFLOWS_CRON.md +0 -185
  233. package/template/prisma/migrations/20251126144728_init/migration.sql +0 -78
  234. package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +0 -5
  235. package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +0 -19
  236. package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +0 -22
  237. package/template/prisma/migrations/20251128132303_add_status/migration.sql +0 -23
  238. package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +0 -75
  239. package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +0 -2
  240. package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +0 -45
  241. package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +0 -2
  242. package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +0 -27
  243. package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +0 -20
  244. package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +0 -18
  245. package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +0 -32
  246. package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +0 -20
  247. package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +0 -12
  248. package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +0 -21
  249. package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +0 -11
  250. package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +0 -12
  251. package/template/prisma/migrations/20251208094843_mg/migration.sql +0 -14
  252. package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +0 -14
  253. package/template/prisma/migrations/20251208110000_add_templates/migration.sql +0 -26
  254. package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +0 -2
  255. package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +0 -2
  256. package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +0 -2
  257. package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +0 -3
  258. package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +0 -21
  259. package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +0 -2
  260. package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +0 -10
  261. package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +0 -26
  262. package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +0 -24
  263. package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +0 -11
  264. package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +0 -12
  265. package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +0 -25
  266. package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +0 -8
  267. package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +0 -2
  268. package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +0 -80
  269. package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +0 -32
  270. package/template/prisma/migrations/20251220000000_add_task_interaction_type/migration.sql +0 -4
  271. package/template/prisma/migrations/20251221000000_add_task_type/migration.sql +0 -3
  272. package/template/prisma/migrations/20251221000001_add_event_color/migration.sql +0 -23
  273. package/template/prisma/migrations/20260210114913_add_dashboard_widget/migration.sql +0 -20
  274. package/template/prisma/migrations/20260226093949_fix_cascade_on_user_delete/migration.sql +0 -69
  275. package/template/src/app/(dashboard)/users/layout.tsx +0 -30
  276. package/template/src/lib/google-drive.ts +0 -380
@@ -45,7 +45,6 @@ model User {
45
45
  role Role @default(USER)
46
46
  customRoleId String? // Profil personnalisé optionnel
47
47
  customRole CustomRole? @relation(fields: [customRoleId], references: [id], onDelete: SetNull)
48
- eventColor String? // Couleur pour les événements de l'utilisateur
49
48
  createdAt DateTime @default(now())
50
49
  updatedAt DateTime @updatedAt
51
50
  sessions Session[]
@@ -67,13 +66,33 @@ model User {
67
66
  auditLogsAsActor AuditLog[] @relation("AuditActor")
68
67
  auditLogsAsTargetUser AuditLog[] @relation("AuditTargetUser")
69
68
  workflows Workflow[] @relation("WorkflowOwner")
69
+ contactViews ContactView[]
70
+ contactViewPins ContactViewPin[]
71
+ companiesAssignedCommercial Company[] @relation("CompanyAssignedCommercial")
72
+ companiesAssignedTelepro Company[] @relation("CompanyAssignedTelepro")
73
+ companiesCreatedBy Company[] @relation("CompanyCreatedBy")
74
+ companyActivities CompanyActivity[]
75
+ workflowActionsTaskAssigned WorkflowAction[] @relation("WorkflowActionTaskAssignedUser")
76
+ workflowActionsAssignCommercial WorkflowAction[] @relation("WorkflowActionAssignCommercial")
77
+ workflowActionsAssignTelepro WorkflowAction[] @relation("WorkflowActionAssignTelepro")
78
+ workflowActionsNotifyUser WorkflowAction[] @relation("WorkflowActionNotifyUser")
70
79
  dashboardWidgets DashboardWidget[]
80
+ integrationImportLogs IntegrationImportLog[]
81
+ googleSheetSyncJobs GoogleSheetSyncJob[]
82
+ reminderStates UserReminderState[]
71
83
 
72
84
  @@unique([email])
73
85
  @@index([customRoleId])
74
86
  @@map("user")
75
87
  }
76
88
 
89
+ enum ReminderStateStatus {
90
+ UNREAD
91
+ READ
92
+ DISMISSED
93
+ CLEARED
94
+ }
95
+
77
96
  model AuditLog {
78
97
  id String @id @default(cuid())
79
98
  actorId String?
@@ -136,26 +155,80 @@ model Verification {
136
155
  updatedAt DateTime @updatedAt
137
156
 
138
157
  @@index([identifier])
158
+ @@index([value])
139
159
  @@map("verification")
140
160
  }
141
161
 
162
+ model Organization {
163
+ id String @id @default("company")
164
+ name String?
165
+ legalRepresentative String? // Nom du représentant légal ou statutaire
166
+ address String?
167
+ city String?
168
+ postalCode String?
169
+ country String?
170
+ phone String?
171
+ email String?
172
+ website String?
173
+ siret String?
174
+ vatNumber String?
175
+ logo String?
176
+ cerfaTemplateFileId String?
177
+ contractTemplateFileId String?
178
+ signaturePosition Json? // Position de la signature électronique {page, x, y, width, height}
179
+ retractionPosition Json? // Position pour le formulaire de rétractation
180
+ createdAt DateTime @default(now())
181
+ updatedAt DateTime @updatedAt
182
+
183
+ @@map("company")
184
+ }
185
+
142
186
  model Company {
143
- id String @id @default("company")
144
- name String?
145
- address String?
146
- city String?
147
- postalCode String?
148
- country String?
149
- phone String?
150
- email String?
151
- website String?
152
- siret String?
153
- vatNumber String?
154
- logo String?
187
+ id String @id @default(cuid())
188
+ name String
189
+ phone String?
190
+ email String?
191
+ address String?
192
+ city String?
193
+ postalCode String?
194
+ website String?
195
+ siret String?
196
+ industry String?
197
+ notes String? @db.Text
198
+ assignedCommercialId String?
199
+ assignedCommercial User? @relation("CompanyAssignedCommercial", fields: [assignedCommercialId], references: [id], onDelete: SetNull)
200
+ assignedTeleproId String?
201
+ assignedTelepro User? @relation("CompanyAssignedTelepro", fields: [assignedTeleproId], references: [id], onDelete: SetNull)
202
+ createdById String?
203
+ createdBy User? @relation("CompanyCreatedBy", fields: [createdById], references: [id], onDelete: SetNull)
204
+ createdAt DateTime @default(now())
205
+ updatedAt DateTime @updatedAt
206
+ contacts Contact[]
207
+ activities CompanyActivity[]
208
+
209
+ @@index([assignedCommercialId])
210
+ @@index([assignedTeleproId])
211
+ @@index([createdById])
212
+ @@index([name])
213
+ @@map("companies")
214
+ }
215
+
216
+ model CompanyActivity {
217
+ id String @id @default(cuid())
218
+ companyId String
219
+ company Company @relation(fields: [companyId], references: [id], onDelete: Cascade)
220
+ type String // COMPANY_UPDATE, CONTACT_ADDED, NOTE
221
+ title String?
222
+ content String @db.Text
223
+ metadata Json? // Détails des changements (champs modifiés, etc.)
224
+ userId String?
225
+ user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
155
226
  createdAt DateTime @default(now())
156
- updatedAt DateTime @updatedAt
157
227
 
158
- @@map("company")
228
+ @@index([companyId])
229
+ @@index([companyId, createdAt])
230
+ @@index([userId])
231
+ @@map("company_activity")
159
232
  }
160
233
 
161
234
  model SmtpConfig {
@@ -181,6 +254,8 @@ model Status {
181
254
  name String
182
255
  color String // Couleur au format hexadécimal (ex: #3B82F6)
183
256
  order Int @default(0) // Ordre d'affichage
257
+ isSystem Boolean @default(false)
258
+ requiresClosingReason Boolean @default(false)
184
259
  createdAt DateTime @default(now())
185
260
  updatedAt DateTime @updatedAt
186
261
  contacts Contact[]
@@ -222,11 +297,12 @@ enum InteractionType {
222
297
 
223
298
  enum TaskType {
224
299
  CALL
225
- MEETING
300
+ MEETING // Rendez-vous physique
226
301
  EMAIL
227
302
  VIDEO_CONFERENCE
228
303
  OTHER
229
304
  TASK
305
+ PHYSICAL_APPOINTMENT // Rendez-vous physique avec localisation
230
306
  }
231
307
 
232
308
  enum TaskPriority {
@@ -248,11 +324,12 @@ model Contact {
248
324
  city String?
249
325
  postalCode String?
250
326
  origin String? // Origine du contact
251
- companyName String? // Nom de l'entreprise (texte brut)
252
- isCompany Boolean @default(false) // Indique si le contact est une entreprise
253
- companyId String? // ID de l'entreprise à laquelle ce contact est lié
254
- companyRelation Contact? @relation("ContactCompany", fields: [companyId], references: [id], onDelete: SetNull)
255
- contacts Contact[] @relation("ContactCompany") // Contacts liés à cette entreprise (via companyId)
327
+ companyName String? // Nom de société en texte libre (sans liaison entreprise)
328
+ companyId String?
329
+ company Company? @relation(fields: [companyId], references: [id], onDelete: SetNull)
330
+ jobTitle String? // Intitulé du poste (pour hiérarchie dans l'entreprise)
331
+ website String? // Site internet (URL cliquable)
332
+ socialNetworks Json? // Réseaux sociaux : [{ platform: string, url: string }]
256
333
  statusId String?
257
334
  status Status? @relation(fields: [statusId], references: [id], onDelete: SetNull)
258
335
  closingReason String? // Motif de fermeture (si statut Fermé)
@@ -268,13 +345,24 @@ model Contact {
268
345
  tasks Task[]
269
346
  files ContactFile[]
270
347
  scheduledWorkflowActions ScheduledWorkflowAction[]
348
+ // KYC fields
349
+ placeOfBirth String? // Lieu de naissance
350
+ dateOfBirth DateTime? // Date de naissance
351
+ idType String? // Type de pièce d'identité (texte libre)
352
+ idNumber String? // Numéro de pièce d'identité
353
+ idExpiryDate DateTime? // Date d'expiration de la pièce d'identité
354
+ idDocumentUrl String?
355
+ idVerifiedByAdmin Boolean @default(false) // Vérifié par un admin
271
356
 
272
357
  @@index([statusId])
273
358
  @@index([assignedCommercialId])
274
359
  @@index([assignedTeleproId])
275
360
  @@index([createdById])
276
361
  @@index([companyId])
277
- @@index([isCompany])
362
+ @@index([phone])
363
+ @@index([createdAt])
364
+ @@index([updatedAt])
365
+ @@index([statusId, updatedAt])
278
366
  @@map("contact")
279
367
  }
280
368
 
@@ -295,6 +383,7 @@ model Interaction {
295
383
 
296
384
  @@index([contactId])
297
385
  @@index([userId])
386
+ @@index([contactId, createdAt])
298
387
  @@map("interaction")
299
388
  }
300
389
 
@@ -329,10 +418,17 @@ model Task {
329
418
  completed Boolean @default(false) // Tâche terminée ou non
330
419
  completedAt DateTime? // Date de complétion
331
420
  googleEventId String? // ID de l'évènement Google Calendar
421
+ googleCalendarId String? // Calendrier Google contenant l'évènement (null = primary pour les anciennes lignes)
332
422
  googleMeetLink String? // URL du Google Meet
333
423
  durationMinutes Int? // Durée de la réunion en minutes (pour Google Meet)
334
424
  internalNote String? // Note personnelle (non partagée dans les emails)
335
425
  notifyContact Boolean? // Si le contact a été prévenu lors de la création
426
+ // Champs pour les rendez-vous physiques
427
+ location String? // Adresse complète du lieu
428
+ locationAddress String? // Rue
429
+ locationCity String? // Ville
430
+ locationPostalCode String? // Code postal
431
+ isAtHome Boolean @default(false) // Rendez-vous au domicile du contact
336
432
  createdAt DateTime @default(now())
337
433
  updatedAt DateTime @updatedAt
338
434
 
@@ -341,35 +437,62 @@ model Task {
341
437
  @@index([createdById])
342
438
  @@index([scheduledAt])
343
439
  @@index([googleEventId])
440
+ @@index([assignedUserId, completed, scheduledAt])
441
+ @@index([assignedUserId, createdAt])
344
442
  @@map("task")
345
443
  }
346
444
 
445
+ model UserReminderState {
446
+ id String @id @default(cuid())
447
+ userId String
448
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
449
+ reminderId String
450
+ status ReminderStateStatus
451
+ createdAt DateTime @default(now())
452
+ updatedAt DateTime @updatedAt
453
+ /** Expiration logique de la ligne (TTL rétention). */
454
+ expiresAt DateTime?
455
+ /** Pour `reminderId === __all__` + CLEARED : instant de coupure affichage (indépendant de updatedAt). */
456
+ clearedCutoffAt DateTime?
457
+ /** Métadonnées (ex. fenêtre undo après clear-all). */
458
+ metadata Json?
459
+
460
+ @@unique([userId, reminderId])
461
+ @@index([userId, status])
462
+ @@index([updatedAt])
463
+ @@index([userId, expiresAt])
464
+ @@map("user_reminder_state")
465
+ }
466
+
347
467
  model UserGoogleAccount {
348
- id String @id @default(cuid())
349
- userId String @unique
350
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
351
- accessToken String
352
- refreshToken String
353
- tokenExpiresAt DateTime
354
- email String? // Email du compte Google connecté
355
- createdAt DateTime @default(now())
356
- updatedAt DateTime @updatedAt
468
+ id String @id @default(cuid())
469
+ userId String @unique
470
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
471
+ accessToken String
472
+ refreshToken String
473
+ tokenExpiresAt DateTime
474
+ email String? // Email du compte Google connecté
475
+ defaultGoogleCalendarId String? // Calendrier par défaut pour créer des événements (sinon primary)
476
+ agendaVisibleGoogleCalendarIds Json? // IDs des calendriers dont les événements sont affichés dans l'agenda CRM
477
+ agendaGoogleEventColor String? // Couleur d'affichage (#RRGGBB) des événements Google dans l'agenda CRM
478
+ createdAt DateTime @default(now())
479
+ updatedAt DateTime @updatedAt
357
480
 
358
481
  @@map("user_google_account")
359
482
  }
360
483
 
361
484
  model ContactFile {
362
- id String @id @default(cuid())
363
- contactId String
364
- contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade)
365
- fileName String // Nom original du fichier
366
- fileSize Int // Taille en octets
367
- mimeType String // Type MIME du fichier
368
- googleDriveFileId String // ID du fichier dans Google Drive
369
- uploadedById String? // Utilisateur qui a uploadé le fichier
370
- uploadedBy User? @relation("ContactFileUploadedBy", fields: [uploadedById], references: [id], onDelete: SetNull)
371
- createdAt DateTime @default(now())
372
- updatedAt DateTime @updatedAt
485
+ id String @id @default(cuid())
486
+ contactId String
487
+ contact Contact @relation(fields: [contactId], references: [id], onDelete: Cascade)
488
+ fileName String
489
+ fileSize Int
490
+ mimeType String
491
+ storagePath String
492
+ uploadedById String?
493
+ uploadedBy User? @relation("ContactFileUploadedBy", fields: [uploadedById], references: [id], onDelete: SetNull)
494
+ createdAt DateTime @default(now())
495
+ updatedAt DateTime @updatedAt
373
496
 
374
497
  @@index([contactId])
375
498
  @@index([uploadedById])
@@ -436,6 +559,60 @@ model GoogleSheetSyncConfig {
436
559
  @@map("google_sheet_sync_config")
437
560
  }
438
561
 
562
+ model IntegrationImportLog {
563
+ id String @id @default(cuid())
564
+ integrationType String // "google_sheet" | "meta_lead" | "google_ads"
565
+ configId String
566
+ configName String
567
+ action String // "created" | "updated" | "synced" | "deleted"
568
+ actorId String?
569
+ actor User? @relation(fields: [actorId], references: [id], onDelete: SetNull)
570
+ totalImported Int @default(0)
571
+ totalDuplicates Int @default(0)
572
+ totalUpdated Int @default(0)
573
+ totalErrors Int @default(0)
574
+ errorDetails Json?
575
+ createdAt DateTime @default(now())
576
+
577
+ @@index([integrationType, configId])
578
+ @@index([createdAt])
579
+ @@map("integration_import_log")
580
+ }
581
+
582
+ enum GoogleSheetSyncJobStatus {
583
+ QUEUED
584
+ RUNNING
585
+ SUCCEEDED
586
+ FAILED
587
+ }
588
+
589
+ enum GoogleSheetSyncJobTriggerType {
590
+ MANUAL
591
+ SCHEDULED
592
+ }
593
+
594
+ model GoogleSheetSyncJob {
595
+ id String @id @default(cuid())
596
+ status GoogleSheetSyncJobStatus @default(QUEUED)
597
+ triggerType GoogleSheetSyncJobTriggerType @default(MANUAL)
598
+ requestedByUserId String?
599
+ requestedByUser User? @relation(fields: [requestedByUserId], references: [id], onDelete: SetNull)
600
+ configId String?
601
+ payload Json?
602
+ result Json?
603
+ error String?
604
+ startedAt DateTime?
605
+ finishedAt DateTime?
606
+ createdAt DateTime @default(now())
607
+ updatedAt DateTime @updatedAt
608
+
609
+ @@index([status])
610
+ @@index([createdAt])
611
+ @@index([requestedByUserId])
612
+ @@index([triggerType, createdAt])
613
+ @@map("google_sheet_sync_job")
614
+ }
615
+
439
616
  model ClosingReason {
440
617
  id String @id @default(cuid())
441
618
  name String // Libellé du motif de fermeture (ex: Faux numéro, Pas intéressé, etc.)
@@ -469,18 +646,29 @@ model Template {
469
646
  }
470
647
 
471
648
  enum WorkflowTriggerType {
472
- CONTACT_CREATED // Nouveau contact créé
473
- STATUS_CHANGED // Changement de statut
474
- TIME_BASED // Basé sur le temps
475
- MANUAL // Déclencheur manuel
649
+ CONTACT_CREATED // Nouveau contact créé
650
+ STATUS_CHANGED // Changement de statut
651
+ TIME_BASED // Basé sur le temps
652
+ MANUAL // Déclencheur manuel
653
+ TASK_COMPLETED // Tâche complétée
654
+ CONTACT_ASSIGNMENT_CHANGED // Changement d'assignation du contact
655
+ }
656
+
657
+ enum WorkflowTimeReference {
658
+ CONTACT_CREATED_DATE // Date de création du contact
659
+ LAST_STATUS_CHANGE // Dernier changement de statut
660
+ LAST_INTERACTION // Dernière interaction
476
661
  }
477
662
 
478
663
  enum WorkflowActionType {
479
664
  SEND_EMAIL // Envoyer un email
480
665
  SEND_SMS // Envoyer un SMS
481
- CHANGE_STATUS // Changer le statut
482
- CREATE_TASK // Créer une tâche
483
- WAIT // Attendre
666
+ CHANGE_STATUS // Changer le statut
667
+ CREATE_TASK // Créer une tâche
668
+ WAIT // Attendre
669
+ ASSIGN_CONTACT // Assigner le contact à un commercial/télépro
670
+ ADD_NOTE // Ajouter une note au contact
671
+ NOTIFY_USER // Notifier un utilisateur (créer une tâche pour lui)
484
672
  }
485
673
 
486
674
  enum WorkflowConditionOperator {
@@ -506,8 +694,12 @@ model Workflow {
506
694
  triggerToStatus Status? @relation("WorkflowTriggerToStatus", fields: [triggerToStatusId], references: [id], onDelete: SetNull)
507
695
 
508
696
  // Pour TIME_BASED
509
- triggerTimeDays Int? // Nombre de jours après un événement
510
- triggerTimeHours Int? // Nombre d'heures après un événement
697
+ triggerTimeDays Int? // Nombre de jours après un événement
698
+ triggerTimeHours Int? // Nombre d'heures après un événement
699
+ triggerTimeReference WorkflowTimeReference? // Événement de référence pour TIME_BASED
700
+
701
+ // Pour TASK_COMPLETED
702
+ triggerTaskType TaskType? // Type de tâche (optionnel, null = toutes)
511
703
 
512
704
  actions WorkflowAction[]
513
705
  scheduledActions ScheduledWorkflowAction[]
@@ -515,7 +707,7 @@ model Workflow {
515
707
  updatedAt DateTime @updatedAt
516
708
 
517
709
  @@index([userId])
518
- @@index([active])
710
+ @@index([active, triggerType])
519
711
  @@map("workflow")
520
712
  }
521
713
 
@@ -547,13 +739,32 @@ model WorkflowAction {
547
739
  newStatus Status? @relation("WorkflowActionNewStatus", fields: [newStatusId], references: [id], onDelete: SetNull)
548
740
 
549
741
  // Pour CREATE_TASK
550
- taskTitle String? // Titre de la tâche
551
- taskDescription String? // Description de la tâche
742
+ taskTitle String? // Titre de la tâche
743
+ taskDescription String? // Description de la tâche
744
+ taskType TaskType? // Type de tâche (CALL, MEETING, etc.) — défaut OTHER
745
+ taskPriority TaskPriority? // Priorité (LOW, MEDIUM, HIGH, URGENT) — défaut MEDIUM
746
+ taskAssignedUserId String? // Utilisateur assigné à la tâche
747
+ taskAssignedUser User? @relation("WorkflowActionTaskAssignedUser", fields: [taskAssignedUserId], references: [id], onDelete: SetNull)
748
+
749
+ // Pour ASSIGN_CONTACT
750
+ assignCommercialId String? // Commercial à assigner
751
+ assignCommercial User? @relation("WorkflowActionAssignCommercial", fields: [assignCommercialId], references: [id], onDelete: SetNull)
752
+ assignTeleproId String? // Télépro à assigner
753
+ assignTelepro User? @relation("WorkflowActionAssignTelepro", fields: [assignTeleproId], references: [id], onDelete: SetNull)
754
+
755
+ // Pour ADD_NOTE
756
+ noteContent String? @db.Text // Contenu de la note (supporte les variables de template)
757
+
758
+ // Pour NOTIFY_USER
759
+ notifyUserId String? // Utilisateur à notifier
760
+ notifyUser User? @relation("WorkflowActionNotifyUser", fields: [notifyUserId], references: [id], onDelete: SetNull)
552
761
 
553
762
  // Condition (optionnelle)
554
- conditionOperator WorkflowConditionOperator? // Opérateur de condition
555
- conditionStatusId String? // Statut pour la condition
556
- conditionStatus Status? @relation("WorkflowActionConditionStatus", fields: [conditionStatusId], references: [id], onDelete: SetNull)
763
+ conditionOperator WorkflowConditionOperator? // Opérateur de condition
764
+ conditionStatusId String? // Statut pour la condition
765
+ conditionStatus Status? @relation("WorkflowActionConditionStatus", fields: [conditionStatusId], references: [id], onDelete: SetNull)
766
+ conditionOrigin String? // Origine du contact pour la condition
767
+ conditionHasCompany Boolean? // true = contact doit avoir une société, false = ne doit pas
557
768
 
558
769
  createdAt DateTime @default(now())
559
770
  updatedAt DateTime @updatedAt
@@ -585,17 +796,50 @@ model ScheduledWorkflowAction {
585
796
  @@map("scheduled_workflow_action")
586
797
  }
587
798
 
799
+ model ContactView {
800
+ id String @id @default(cuid())
801
+ name String
802
+ userId String
803
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
804
+ isPublic Boolean @default(false)
805
+ isDefault Boolean @default(false)
806
+ entityType String @default("contacts") // "contacts" | "companies"
807
+ filters Json @default("[]")
808
+ columns Json?
809
+ sortConfig Json?
810
+ createdAt DateTime @default(now())
811
+ updatedAt DateTime @updatedAt
812
+ pins ContactViewPin[]
813
+
814
+ @@index([userId])
815
+ @@index([isPublic])
816
+ @@index([entityType, isPublic])
817
+ @@map("contact_view")
818
+ }
819
+
820
+ model ContactViewPin {
821
+ id String @id @default(cuid())
822
+ userId String
823
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
824
+ viewId String
825
+ view ContactView @relation(fields: [viewId], references: [id], onDelete: Cascade)
826
+ pinOrder Int @default(0)
827
+
828
+ @@unique([userId, viewId])
829
+ @@index([userId])
830
+ @@map("contact_view_pin")
831
+ }
832
+
588
833
  model DashboardWidget {
589
- id String @id @default(cuid())
590
- userId String
591
- user User @relation(fields: [userId], references: [id], onDelete: Cascade)
592
- type String // Identifiant du type de widget (ex: stat_total_contacts, contacts_chart, etc.)
593
- x Int @default(0)
594
- y Int @default(0)
595
- w Int @default(6) // Largeur sur grille de 12
596
- h Int @default(4) // Hauteur en unités
597
- createdAt DateTime @default(now())
598
- updatedAt DateTime @updatedAt
834
+ id String @id @default(cuid())
835
+ userId String
836
+ user User @relation(fields: [userId], references: [id], onDelete: Cascade)
837
+ type String
838
+ x Int @default(0)
839
+ y Int @default(0)
840
+ w Int @default(4)
841
+ h Int @default(3)
842
+ settings Json @default("{}")
599
843
 
600
844
  @@index([userId])
601
845
  @@map("dashboard_widget")
@@ -3,10 +3,13 @@
3
3
  import { useState, useEffect } from 'react';
4
4
  import { useRouter, useParams } from 'next/navigation';
5
5
  import { Eye, EyeOff } from 'lucide-react';
6
+ import { useAppToast } from '@/contexts/app-toast-context';
7
+ import { devToast } from '@/lib/utils';
6
8
 
7
9
  export default function InvitePage() {
8
10
  const router = useRouter();
9
11
  const params = useParams();
12
+ const toast = useAppToast();
10
13
  const token = params.token as string;
11
14
 
12
15
  const [password, setPassword] = useState('');
@@ -19,6 +22,14 @@ export default function InvitePage() {
19
22
  const [showPassword, setShowPassword] = useState(false);
20
23
  const [showConfirmPassword, setShowConfirmPassword] = useState(false);
21
24
 
25
+ useEffect(() => {
26
+ if (!error) return;
27
+ toast.error(error);
28
+ if (userEmail) {
29
+ setError('');
30
+ }
31
+ }, [error, toast, userEmail]);
32
+
22
33
  // Vérifier que le token est valide au chargement
23
34
  useEffect(() => {
24
35
  const validateToken = async () => {
@@ -27,17 +38,20 @@ export default function InvitePage() {
27
38
  const data = await response.json();
28
39
 
29
40
  if (!response.ok) {
30
- setError(data.error || 'Lien invalide ou expiré');
41
+ const msg = data.error || 'Lien invalide ou expiré';
31
42
  setValidating(false);
43
+ toast.error(msg);
44
+ router.replace('/signin');
32
45
  return;
33
46
  }
34
47
 
35
48
  setUserEmail(data.email);
36
49
  setUserName(data.name || '');
37
50
  setValidating(false);
38
- } catch (err) {
39
- setError('Erreur lors de la validation du lien');
51
+ } catch {
40
52
  setValidating(false);
53
+ toast.error('Ce lien d\'invitation n\'est plus valide. Contactez votre administrateur.');
54
+ router.replace('/signin');
41
55
  }
42
56
  };
43
57
 
@@ -79,8 +93,8 @@ export default function InvitePage() {
79
93
  router.push(
80
94
  '/signin?message=Mot de passe défini avec succès, vous pouvez maintenant vous connecter',
81
95
  );
82
- } catch (err: any) {
83
- setError(err.message);
96
+ } catch (err: unknown) {
97
+ setError(devToast('Erreur lors de la définition du mot de passe', err));
84
98
  } finally {
85
99
  setLoading(false);
86
100
  }
@@ -96,23 +110,8 @@ export default function InvitePage() {
96
110
  );
97
111
  }
98
112
 
99
- if (error && !userEmail) {
100
- return (
101
- <div className="flex min-h-screen items-center justify-center px-4">
102
- <div className="w-full max-w-md rounded-2xl bg-white p-8 shadow-xl">
103
- <div className="text-center">
104
- <h1 className="text-2xl font-bold text-red-600">Lien invalide</h1>
105
- <p className="mt-4 text-gray-600">{error}</p>
106
- <button
107
- onClick={() => router.push('/signin')}
108
- className="mt-6 cursor-pointer rounded-lg bg-indigo-600 px-4 py-2 text-white hover:bg-indigo-700"
109
- >
110
- Retour à la connexion
111
- </button>
112
- </div>
113
- </div>
114
- </div>
115
- );
113
+ if (!userEmail && !validating) {
114
+ return null;
116
115
  }
117
116
 
118
117
  return (
@@ -130,19 +129,18 @@ export default function InvitePage() {
130
129
  <p className="mt-1 text-sm break-all text-gray-500">{userEmail}</p>
131
130
  </div>
132
131
 
133
- {error && <div className="mb-4 rounded-lg bg-red-50 p-4 text-sm text-red-600">{error}</div>}
134
-
135
132
  <form onSubmit={handleSubmit} className="space-y-6">
136
133
  <div>
137
- <label className="block text-sm font-medium text-gray-700">Mot de passe</label>
134
+ <label htmlFor="password" className="block text-sm font-medium text-gray-700">Mot de passe</label>
138
135
  <div className="relative mt-1">
139
136
  <input
137
+ id="password"
140
138
  type={showPassword ? 'text' : 'password'}
141
139
  required
142
140
  minLength={6}
143
141
  value={password}
144
142
  onChange={(e) => setPassword(e.target.value)}
145
- className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
143
+ className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:border-indigo-500"
146
144
  placeholder="••••••••"
147
145
  />
148
146
  <button
@@ -158,17 +156,18 @@ export default function InvitePage() {
158
156
  </div>
159
157
 
160
158
  <div>
161
- <label className="block text-sm font-medium text-gray-700">
159
+ <label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700">
162
160
  Confirmer le mot de passe
163
161
  </label>
164
162
  <div className="relative mt-1">
165
163
  <input
164
+ id="confirmPassword"
166
165
  type={showConfirmPassword ? 'text' : 'password'}
167
166
  required
168
167
  minLength={6}
169
168
  value={confirmPassword}
170
169
  onChange={(e) => setConfirmPassword(e.target.value)}
171
- className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
170
+ className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:border-indigo-500"
172
171
  placeholder="••••••••"
173
172
  />
174
173
  <button
@@ -189,7 +188,7 @@ export default function InvitePage() {
189
188
  <button
190
189
  type="submit"
191
190
  disabled={loading}
192
- className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white 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:opacity-50"
191
+ className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
193
192
  >
194
193
  {loading ? 'Création du compte...' : 'Créer mon compte'}
195
194
  </button>
@@ -1,3 +1,3 @@
1
1
  export default function AuthLayout({ children }: { children: React.ReactNode }) {
2
- return <div className="bg-lienar-to-br min-h-screen from-blue-50 to-indigo-100">{children}</div>;
2
+ return <div className="bg-gradient-to-br min-h-screen from-blue-50 to-indigo-100">{children}</div>;
3
3
  }