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
@@ -7,10 +7,7 @@ import { Loader2 } from 'lucide-react';
7
7
 
8
8
  export function Skeleton({ className }: { className?: string }) {
9
9
  return (
10
- <div
11
- className={cn('animate-pulse rounded bg-muted', className)}
12
- aria-label="Chargement..."
13
- />
10
+ <div className={cn('bg-muted animate-pulse rounded', className)} aria-label="Chargement..." />
14
11
  );
15
12
  }
16
13
 
@@ -27,7 +24,7 @@ interface SpinnerProps {
27
24
  }
28
25
 
29
26
  export function Spinner({ size = 'lg', className }: Readonly<SpinnerProps>) {
30
- return <Loader2 className={cn('animate-spin text-primary', SPINNER_SIZES[size], className)} />;
27
+ return <Loader2 className={cn('text-primary animate-spin', SPINNER_SIZES[size], className)} />;
31
28
  }
32
29
 
33
30
  interface PageLoaderProps {
@@ -40,7 +37,7 @@ export function PageLoader({ text = 'Chargement...', className }: Readonly<PageL
40
37
  <div className={cn('flex h-full items-center justify-center py-12', className)}>
41
38
  <div className="text-center">
42
39
  <Spinner size="lg" className="mx-auto" />
43
- <p className="mt-3 text-sm text-muted-foreground">{text}</p>
40
+ <p className="text-muted-foreground mt-3 text-sm">{text}</p>
44
41
  </div>
45
42
  </div>
46
43
  );
@@ -67,7 +64,7 @@ export function ListPageSkeleton({
67
64
  }: Readonly<ListPageSkeletonProps>) {
68
65
  return (
69
66
  <div className="h-full">
70
- <div className="flex items-center justify-between border-b border-border bg-card px-4 py-4 sm:px-6">
67
+ <div className="border-border bg-card flex items-center justify-between border-b px-4 py-4 sm:px-6">
71
68
  <div>
72
69
  <Skeleton className={cn('h-7', headerWidth)} />
73
70
  <Skeleton className={cn('mt-1 h-4', descriptionWidth)} />
@@ -101,43 +98,43 @@ export function ListPageSkeleton({
101
98
 
102
99
  export function ContactTableSkeleton() {
103
100
  return (
104
- <div className="overflow-x-auto rounded-lg border border-border bg-card shadow-(--shadow-card)">
105
- <table className="min-w-full divide-y divide-border">
101
+ <div className="border-border bg-card overflow-x-auto rounded-lg border shadow-(--shadow-card)">
102
+ <table className="divide-border min-w-full divide-y">
106
103
  <thead className="bg-muted">
107
104
  <tr>
108
105
  <th className="px-3 py-3 sm:px-6">
109
106
  <Skeleton className="h-4 w-4" />
110
107
  </th>
111
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
108
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
112
109
  Contact
113
110
  </th>
114
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
111
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
115
112
  Téléphone
116
113
  </th>
117
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
114
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
118
115
  Email
119
116
  </th>
120
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
117
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
121
118
  Statut
122
119
  </th>
123
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
120
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
124
121
  Origine
125
122
  </th>
126
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
123
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
127
124
  COMMERCIAL
128
125
  </th>
129
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
126
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
130
127
  TÉLÉPRO
131
128
  </th>
132
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
129
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
133
130
  CRÉÉ LE
134
131
  </th>
135
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
132
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
136
133
  MODIFIÉ LE
137
134
  </th>
138
135
  </tr>
139
136
  </thead>
140
- <tbody className="divide-y divide-border bg-card">
137
+ <tbody className="divide-border bg-card divide-y">
141
138
  {Array.from({ length: 8 }).map((_, i) => (
142
139
  <tr key={i} className="hover:bg-muted/70">
143
140
  <td className="px-3 py-4 whitespace-nowrap sm:px-6">
@@ -188,7 +185,7 @@ export function ContactCardsSkeleton() {
188
185
  return (
189
186
  <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
190
187
  {Array.from({ length: 9 }).map((_, i) => (
191
- <div key={i} className="rounded-lg border border-border bg-card p-6 shadow-(--shadow-card)">
188
+ <div key={i} className="border-border bg-card rounded-lg border p-6 shadow-(--shadow-card)">
192
189
  {/* En-tête */}
193
190
  <div className="mb-4 flex items-start justify-between">
194
191
  <div className="flex items-center gap-3">
@@ -226,7 +223,7 @@ export function ContactCardsSkeleton() {
226
223
  </div>
227
224
 
228
225
  {/* Pied de carte avec utilisateurs assignés */}
229
- <div className="flex items-start justify-between border-t border-border/70 pt-4">
226
+ <div className="border-border/70 flex items-start justify-between border-t pt-4">
230
227
  <div className="space-y-2">
231
228
  <div className="flex items-center gap-2">
232
229
  <Skeleton className="h-7 w-7 rounded-full" />
@@ -250,12 +247,12 @@ export function ContactCardsSkeleton() {
250
247
 
251
248
  export function AgendaMonthSkeleton() {
252
249
  return (
253
- <div className="rounded-lg border border-border bg-card shadow-(--shadow-card)">
254
- <div className="grid grid-cols-7 border-b border-border">
250
+ <div className="border-border bg-card rounded-lg border shadow-(--shadow-card)">
251
+ <div className="border-border grid grid-cols-7 border-b">
255
252
  {['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim'].map((day) => (
256
253
  <div
257
254
  key={day}
258
- className="border-r border-border p-3 text-center text-sm font-semibold text-foreground last:border-r-0"
255
+ className="border-border text-foreground border-r p-3 text-center text-sm font-semibold last:border-r-0"
259
256
  >
260
257
  {day}
261
258
  </div>
@@ -265,7 +262,7 @@ export function AgendaMonthSkeleton() {
265
262
  {Array.from({ length: 42 }).map((_, i) => (
266
263
  <div
267
264
  key={i}
268
- className="min-h-[100px] border-r border-b border-border p-2 last:border-r-0"
265
+ className="border-border min-h-[100px] border-r border-b p-2 last:border-r-0"
269
266
  >
270
267
  <Skeleton className="mb-2 h-5 w-6" />
271
268
  <div className="space-y-1">
@@ -288,31 +285,31 @@ export function AgendaWeekSkeleton() {
288
285
  };
289
286
 
290
287
  return (
291
- <div className="overflow-auto rounded-lg border border-border bg-card shadow-(--shadow-card)">
292
- <div className="grid grid-cols-8 border-b border-border bg-muted text-xs font-medium text-muted-foreground">
288
+ <div className="border-border bg-card overflow-auto rounded-lg border shadow-(--shadow-card)">
289
+ <div className="border-border bg-muted text-muted-foreground grid grid-cols-8 border-b text-xs font-medium">
293
290
  <div className="px-3 py-2 text-right">(UTC+01:00) Hr</div>
294
291
  {Array.from({ length: 7 }).map((_, i) => (
295
- <div key={i} className="border-l border-border px-3 py-2 text-center">
292
+ <div key={i} className="border-border border-l px-3 py-2 text-center">
296
293
  <Skeleton className="mx-auto h-4 w-12" />
297
294
  <Skeleton className="mx-auto mt-1 h-8 w-8 rounded-full" />
298
295
  </div>
299
296
  ))}
300
297
  </div>
301
298
  <div className="grid grid-cols-8 text-xs">
302
- <div className="border-r border-border bg-muted">
299
+ <div className="border-border bg-muted border-r">
303
300
  {HOURS.map((hour) => (
304
301
  <div
305
302
  key={hour}
306
- className="flex h-16 items-start justify-end border-b border-border pr-2"
303
+ className="border-border flex h-16 items-start justify-end border-b pr-2"
307
304
  >
308
305
  <Skeleton className="h-3 w-10" />
309
306
  </div>
310
307
  ))}
311
308
  </div>
312
309
  {Array.from({ length: 7 }).map((_, dayIndex) => (
313
- <div key={dayIndex} className="border-l border-border">
310
+ <div key={dayIndex} className="border-border border-l">
314
311
  {HOURS.map((hour, hourIndex) => (
315
- <div key={hour} className="relative h-16 border-b border-border/70 px-1.5 py-0.5">
312
+ <div key={hour} className="border-border/70 relative h-16 border-b px-1.5 py-0.5">
316
313
  {shouldShowSkeleton(dayIndex, hourIndex) && (
317
314
  <Skeleton className="h-12 w-full rounded" />
318
315
  )}
@@ -329,7 +326,7 @@ export function AgendaDaySkeleton() {
329
326
  return (
330
327
  <div className="space-y-4">
331
328
  {Array.from({ length: 5 }).map((_, i) => (
332
- <div key={i} className="rounded-lg border border-border bg-card p-4 shadow-(--shadow-card)">
329
+ <div key={i} className="border-border bg-card rounded-lg border p-4 shadow-(--shadow-card)">
333
330
  <div className="flex items-start justify-between">
334
331
  <div className="flex-1">
335
332
  <div className="flex items-center gap-2">
@@ -357,28 +354,28 @@ export function AgendaDaySkeleton() {
357
354
 
358
355
  export function UsersTableSkeleton() {
359
356
  return (
360
- <div className="overflow-x-auto rounded-lg border border-border bg-card shadow-(--shadow-card)">
361
- <table className="min-w-full divide-y divide-border">
357
+ <div className="border-border bg-card overflow-x-auto rounded-lg border shadow-(--shadow-card)">
358
+ <table className="divide-border min-w-full divide-y">
362
359
  <thead className="bg-muted">
363
360
  <tr>
364
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
361
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
365
362
  Utilisateur
366
363
  </th>
367
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
364
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
368
365
  Email
369
366
  </th>
370
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
367
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
371
368
  Rôle
372
369
  </th>
373
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
370
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
374
371
  Email vérifié
375
372
  </th>
376
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
373
+ <th className="text-muted-foreground px-3 py-3 text-left text-xs font-medium tracking-wider uppercase sm:px-6">
377
374
  Compte
378
375
  </th>
379
376
  </tr>
380
377
  </thead>
381
- <tbody className="divide-y divide-border bg-card">
378
+ <tbody className="divide-border bg-card divide-y">
382
379
  {Array.from({ length: 6 }).map((_, i) => (
383
380
  <tr key={i} className="hover:bg-muted/70">
384
381
  <td className="px-3 py-4 whitespace-nowrap sm:px-6">
@@ -416,7 +413,7 @@ export function TemplatesPageSkeleton() {
416
413
  return (
417
414
  <div className="h-full">
418
415
  {/* Header Skeleton */}
419
- <div className="border-b border-border bg-card px-4 py-4 sm:px-6 lg:px-8">
416
+ <div className="border-border bg-card border-b px-4 py-4 sm:px-6 lg:px-8">
420
417
  <div className="flex items-center justify-between">
421
418
  <div className="space-y-2">
422
419
  <Skeleton className="h-8 w-48" />
@@ -439,7 +436,7 @@ export function TemplatesPageSkeleton() {
439
436
  {Array.from({ length: 6 }).map((_, i) => (
440
437
  <div
441
438
  key={i}
442
- className="rounded-lg border border-border bg-card p-4 shadow-(--shadow-card) transition-shadow"
439
+ className="border-border bg-card rounded-lg border p-4 shadow-(--shadow-card) transition-shadow"
443
440
  >
444
441
  <div className="flex items-start justify-between">
445
442
  <div className="flex-1">
@@ -33,13 +33,13 @@ function AccordionTrigger({
33
33
  <AccordionPrimitive.Trigger
34
34
  data-slot="accordion-trigger"
35
35
  className={cn(
36
- 'focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-all outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180',
36
+ 'focus-visible:border-ring focus-visible:ring-ring/50 flex flex-1 items-start justify-between gap-4 rounded-md py-4 text-left text-sm font-medium transition-colors outline-none hover:underline focus-visible:ring-[3px] disabled:pointer-events-none disabled:opacity-50 [&[data-state=open]>svg]:rotate-180',
37
37
  className,
38
38
  )}
39
39
  {...props}
40
40
  >
41
41
  {children}
42
- <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
42
+ <ChevronDownIcon aria-hidden="true" className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
43
43
  </AccordionPrimitive.Trigger>
44
44
  </AccordionPrimitive.Header>
45
45
  );
@@ -50,7 +50,7 @@ function AlertDialogContent({
50
50
  <AlertDialogPrimitive.Content
51
51
  data-slot="alert-dialog-content"
52
52
  className={cn(
53
- 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg',
53
+ 'bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] z-50 grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg overscroll-contain',
54
54
  className,
55
55
  )}
56
56
  {...props}
@@ -1,11 +1,12 @@
1
1
  import * as React from 'react';
2
2
  import { Slot } from '@radix-ui/react-slot';
3
3
  import { cva, type VariantProps } from 'class-variance-authority';
4
+ import { motion } from 'motion/react';
4
5
 
5
6
  import { cn } from '@/lib/utils';
6
7
 
7
8
  const buttonVariants = cva(
8
- "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-[color,background-color,border-color,box-shadow,opacity] duration-(--duration-fast) ease-(--ease-standard) disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
9
10
  {
10
11
  variants: {
11
12
  variant: {
@@ -16,7 +17,7 @@ const buttonVariants = cva(
16
17
  'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
17
18
  secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
18
19
  ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
19
- link: 'text-primary underline-offset-4 hover:underline',
20
+ link: 'text-primary underline-offset-4 hover:underline active:scale-100',
20
21
  },
21
22
  size: {
22
23
  default: 'h-9 px-4 py-2 has-[>svg]:px-3',
@@ -44,15 +45,25 @@ function Button({
44
45
  VariantProps<typeof buttonVariants> & {
45
46
  asChild?: boolean;
46
47
  }) {
47
- const Comp = asChild ? Slot : 'button';
48
+ const baseClassName = cn(buttonVariants({ variant, size, className }));
49
+ const baseProps = {
50
+ 'data-slot': 'button',
51
+ 'data-variant': variant,
52
+ 'data-size': size,
53
+ className: baseClassName,
54
+ };
48
55
 
56
+ if (asChild) {
57
+ return <Slot {...baseProps} {...props} />;
58
+ }
59
+
60
+ // motion/react redéfinit des handlers (onDrag, onAnimationStart, etc.) ; cast pour compatibilité avec HTML button
49
61
  return (
50
- <Comp
51
- data-slot="button"
52
- data-variant={variant}
53
- data-size={size}
54
- className={cn(buttonVariants({ variant, size, className }))}
55
- {...props}
62
+ <motion.button
63
+ {...baseProps}
64
+ whileHover={{ scale: 1.02 }}
65
+ whileTap={{ scale: 0.98 }}
66
+ {...(props as React.ComponentPropsWithoutRef<typeof motion.button>)}
56
67
  />
57
68
  );
58
69
  }
@@ -86,7 +86,7 @@ export function Dropdown({
86
86
 
87
87
  return (
88
88
  <div className="lexkit-dropdown" ref={dropdownRef}>
89
- <div onClick={() => onOpenChange(!isOpen)}>{trigger}</div>
89
+ <button type="button" onClick={() => onOpenChange(!isOpen)}>{trigger}</button>
90
90
  {isOpen && <div className="lexkit-dropdown-content">{children}</div>}
91
91
  </div>
92
92
  );