create-crm-tmp 1.1.2 → 2.0.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 (220) hide show
  1. package/package.json +1 -1
  2. package/template/.prettierignore +2 -0
  3. package/template/README.md +53 -67
  4. package/template/components.json +22 -0
  5. package/template/exemple-contacts.csv +54 -0
  6. package/template/next.config.ts +27 -1
  7. package/template/package.json +64 -27
  8. package/template/prisma/schema.prisma +821 -72
  9. package/template/skills-lock.json +25 -0
  10. package/template/src/app/(auth)/invite/[token]/page.tsx +21 -24
  11. package/template/src/app/(auth)/reset-password/complete/page.tsx +12 -21
  12. package/template/src/app/(auth)/reset-password/page.tsx +12 -8
  13. package/template/src/app/(auth)/reset-password/verify/page.tsx +12 -8
  14. package/template/src/app/(auth)/signin/page.tsx +20 -17
  15. package/template/src/app/(dashboard)/agenda/page.tsx +2231 -2188
  16. package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +10 -7
  17. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +680 -323
  18. package/template/src/app/(dashboard)/automatisation/new/page.tsx +11 -8
  19. package/template/src/app/(dashboard)/automatisation/page.tsx +473 -180
  20. package/template/src/app/(dashboard)/closing/page.tsx +500 -468
  21. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +5035 -4126
  22. package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +1703 -0
  23. package/template/src/app/(dashboard)/contacts/loading.tsx +13 -0
  24. package/template/src/app/(dashboard)/contacts/page.tsx +3776 -2064
  25. package/template/src/app/(dashboard)/dashboard/page.tsx +37 -519
  26. package/template/src/app/(dashboard)/error.tsx +37 -0
  27. package/template/src/app/(dashboard)/layout.tsx +1 -1
  28. package/template/src/app/(dashboard)/loading.tsx +5 -0
  29. package/template/src/app/(dashboard)/settings/loading.tsx +19 -0
  30. package/template/src/app/(dashboard)/settings/page.tsx +2685 -2489
  31. package/template/src/app/(dashboard)/templates/page.tsx +500 -300
  32. package/template/src/app/(dashboard)/users/list/page.tsx +356 -350
  33. package/template/src/app/(dashboard)/users/page.tsx +279 -310
  34. package/template/src/app/(dashboard)/users/permissions/page.tsx +104 -99
  35. package/template/src/app/(dashboard)/users/roles/page.tsx +164 -137
  36. package/template/src/app/api/audit-logs/route.ts +1 -1
  37. package/template/src/app/api/auth/google/callback/route.ts +8 -5
  38. package/template/src/app/api/auth/google/disconnect/route.ts +2 -2
  39. package/template/src/app/api/companies/[id]/activities/route.ts +131 -0
  40. package/template/src/app/api/companies/[id]/route.ts +195 -0
  41. package/template/src/app/api/companies/export/route.ts +206 -0
  42. package/template/src/app/api/companies/route.ts +166 -0
  43. package/template/src/app/api/contact-views/[id]/pin/route.ts +69 -0
  44. package/template/src/app/api/contact-views/[id]/route.ts +197 -0
  45. package/template/src/app/api/contact-views/route.ts +146 -0
  46. package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +77 -0
  47. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +7 -17
  48. package/template/src/app/api/contacts/[id]/files/route.ts +83 -44
  49. package/template/src/app/api/contacts/[id]/interactions/route.ts +37 -0
  50. package/template/src/app/api/contacts/[id]/kyc/route.ts +71 -0
  51. package/template/src/app/api/contacts/[id]/meet/route.ts +38 -29
  52. package/template/src/app/api/contacts/[id]/route.ts +111 -20
  53. package/template/src/app/api/contacts/[id]/send-email/route.ts +6 -0
  54. package/template/src/app/api/contacts/[id]/workflows/run/route.ts +61 -0
  55. package/template/src/app/api/contacts/export/route.ts +12 -17
  56. package/template/src/app/api/contacts/import/route.ts +22 -19
  57. package/template/src/app/api/contacts/import-preview/route.ts +139 -0
  58. package/template/src/app/api/contacts/route.ts +202 -49
  59. package/template/src/app/api/dashboard/stats/route.ts +9 -292
  60. package/template/src/app/api/integrations/google-sheet/sync/route.ts +203 -185
  61. package/template/src/app/api/invite/complete/route.ts +20 -23
  62. package/template/src/app/api/reminders/route.ts +1 -0
  63. package/template/src/app/api/reset-password/complete/route.ts +11 -13
  64. package/template/src/app/api/send/route.ts +9 -85
  65. package/template/src/app/api/settings/closing-reasons/[id]/route.ts +10 -21
  66. package/template/src/app/api/settings/closing-reasons/route.ts +10 -21
  67. package/template/src/app/api/settings/company/route.ts +19 -26
  68. package/template/src/app/api/settings/google-ads/[id]/route.ts +20 -23
  69. package/template/src/app/api/settings/google-ads/route.ts +20 -23
  70. package/template/src/app/api/settings/google-sheet/[id]/route.ts +20 -23
  71. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +23 -32
  72. package/template/src/app/api/settings/google-sheet/preview/route.ts +104 -0
  73. package/template/src/app/api/settings/google-sheet/route.ts +20 -23
  74. package/template/src/app/api/settings/meta-leads/[id]/route.ts +20 -23
  75. package/template/src/app/api/settings/meta-leads/route.ts +20 -23
  76. package/template/src/app/api/settings/statuses/[id]/route.ts +33 -23
  77. package/template/src/app/api/settings/statuses/route.ts +24 -22
  78. package/template/src/app/api/statuses/route.ts +2 -5
  79. package/template/src/app/api/tasks/[id]/attendees/route.ts +14 -7
  80. package/template/src/app/api/tasks/[id]/route.ts +161 -137
  81. package/template/src/app/api/tasks/meet/route.ts +11 -8
  82. package/template/src/app/api/tasks/route.ts +155 -95
  83. package/template/src/app/api/templates/[id]/route.ts +22 -13
  84. package/template/src/app/api/templates/route.ts +22 -5
  85. package/template/src/app/api/users/[id]/resend-invite/route.ts +95 -0
  86. package/template/src/app/api/users/[id]/route.ts +16 -1
  87. package/template/src/app/api/users/commercials/route.ts +38 -0
  88. package/template/src/app/api/users/for-agenda/route.ts +1 -2
  89. package/template/src/app/api/users/route.ts +94 -55
  90. package/template/src/app/api/webhooks/google-ads/route.ts +20 -1
  91. package/template/src/app/api/webhooks/meta-leads/route.ts +18 -1
  92. package/template/src/app/api/workflows/[id]/route.ts +33 -6
  93. package/template/src/app/api/workflows/process/route.ts +509 -146
  94. package/template/src/app/api/workflows/route.ts +46 -4
  95. package/template/src/app/globals.css +210 -101
  96. package/template/src/app/layout.tsx +19 -8
  97. package/template/src/app/page.tsx +37 -7
  98. package/template/src/components/address-autocomplete.tsx +232 -0
  99. package/template/src/components/contacts/filter-bar.tsx +181 -0
  100. package/template/src/components/contacts/filter-builder.tsx +589 -0
  101. package/template/src/components/contacts/save-view-dialog.tsx +160 -0
  102. package/template/src/components/contacts/views-tab-bar.tsx +440 -0
  103. package/template/src/components/dashboard/activity-chart.tsx +31 -39
  104. package/template/src/components/dashboard/dashboard-content.tsx +79 -0
  105. package/template/src/components/dashboard/stat-card.tsx +40 -42
  106. package/template/src/components/dashboard/tasks-pie-chart.tsx +34 -37
  107. package/template/src/components/dashboard/upcoming-tasks-list.tsx +78 -72
  108. package/template/src/components/date-picker.tsx +396 -0
  109. package/template/src/components/editor.tsx +27 -13
  110. package/template/src/components/email-template.tsx +4 -2
  111. package/template/src/components/global-search.tsx +358 -0
  112. package/template/src/components/header.tsx +57 -62
  113. package/template/src/components/invitation-email-template.tsx +4 -2
  114. package/template/src/components/lazy-editor.tsx +11 -0
  115. package/template/src/components/meet-cancellation-email-template.tsx +11 -3
  116. package/template/src/components/meet-confirmation-email-template.tsx +10 -3
  117. package/template/src/components/meet-update-email-template.tsx +10 -3
  118. package/template/src/components/page-header.tsx +19 -15
  119. package/template/src/components/protected-page.tsx +94 -0
  120. package/template/src/components/reset-password-email-template.tsx +4 -2
  121. package/template/src/components/sidebar.tsx +92 -94
  122. package/template/src/components/skeleton.tsx +128 -42
  123. package/template/src/components/ui/accordion.tsx +64 -0
  124. package/template/src/components/ui/alert-dialog.tsx +139 -0
  125. package/template/src/components/ui/button.tsx +60 -0
  126. package/template/src/components/view-as-banner.tsx +1 -1
  127. package/template/src/components/view-as-modal.tsx +21 -16
  128. package/template/src/config/nav-pages.ts +108 -0
  129. package/template/src/contexts/app-toast-context.tsx +174 -0
  130. package/template/src/contexts/sidebar-context.tsx +16 -47
  131. package/template/src/contexts/task-reminder-context.tsx +6 -6
  132. package/template/src/contexts/view-as-context.tsx +11 -16
  133. package/template/src/hooks/use-alert.tsx +65 -0
  134. package/template/src/hooks/use-confirm.tsx +87 -0
  135. package/template/src/hooks/use-contact-views.ts +140 -0
  136. package/template/src/hooks/use-contacts.ts +69 -0
  137. package/template/src/hooks/use-fetch.ts +17 -0
  138. package/template/src/hooks/use-focus-trap.ts +73 -0
  139. package/template/src/hooks/use-statuses.ts +22 -0
  140. package/template/src/lib/address-api.ts +155 -0
  141. package/template/src/lib/cache.ts +73 -0
  142. package/template/src/lib/check-permission.ts +12 -177
  143. package/template/src/lib/contact-interactions.ts +3 -1
  144. package/template/src/lib/contact-view-filters.ts +341 -0
  145. package/template/src/lib/dashboard-stats.ts +224 -0
  146. package/template/src/lib/date-utils.ts +49 -0
  147. package/template/src/lib/get-auth-user.ts +25 -0
  148. package/template/src/lib/google-calendar.ts +54 -12
  149. package/template/src/lib/google-drive.ts +796 -75
  150. package/template/src/lib/google-fetch.ts +63 -0
  151. package/template/src/lib/local-storage.ts +34 -0
  152. package/template/src/lib/permissions.ts +245 -47
  153. package/template/src/lib/prisma.ts +11 -11
  154. package/template/src/lib/roles.ts +14 -39
  155. package/template/src/lib/template-variables.ts +67 -33
  156. package/template/src/lib/utils.ts +26 -2
  157. package/template/src/lib/workflow-executor.ts +445 -229
  158. package/template/src/proxy.ts +34 -73
  159. package/template/src/types/contact-views.ts +351 -0
  160. package/template/src/types/yousign.ts +52 -0
  161. package/template/vercel.json +12 -0
  162. package/template/WORKFLOWS_CRON.md +0 -185
  163. package/template/prisma/migrations/20251126144728_init/migration.sql +0 -78
  164. package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +0 -5
  165. package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +0 -19
  166. package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +0 -22
  167. package/template/prisma/migrations/20251128132303_add_status/migration.sql +0 -23
  168. package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +0 -75
  169. package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +0 -2
  170. package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +0 -45
  171. package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +0 -2
  172. package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +0 -27
  173. package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +0 -20
  174. package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +0 -18
  175. package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +0 -32
  176. package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +0 -20
  177. package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +0 -12
  178. package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +0 -21
  179. package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +0 -11
  180. package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +0 -12
  181. package/template/prisma/migrations/20251208094843_mg/migration.sql +0 -14
  182. package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +0 -14
  183. package/template/prisma/migrations/20251208110000_add_templates/migration.sql +0 -26
  184. package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +0 -2
  185. package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +0 -2
  186. package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +0 -2
  187. package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +0 -3
  188. package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +0 -21
  189. package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +0 -2
  190. package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +0 -10
  191. package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +0 -26
  192. package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +0 -24
  193. package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +0 -11
  194. package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +0 -12
  195. package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +0 -25
  196. package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +0 -8
  197. package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +0 -2
  198. package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +0 -80
  199. package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +0 -32
  200. package/template/prisma/migrations/20251220000000_add_task_interaction_type/migration.sql +0 -4
  201. package/template/prisma/migrations/20251221000000_add_task_type/migration.sql +0 -3
  202. package/template/prisma/migrations/20251221000001_add_event_color/migration.sql +0 -23
  203. package/template/prisma/migrations/20260210114913_add_dashboard_widget/migration.sql +0 -20
  204. package/template/prisma/migrations/migration_lock.toml +0 -3
  205. package/template/src/app/(dashboard)/users/layout.tsx +0 -30
  206. package/template/src/app/api/dashboard/widgets/[id]/route.ts +0 -47
  207. package/template/src/app/api/dashboard/widgets/route.ts +0 -181
  208. package/template/src/components/dashboard/add-widget-dialog.tsx +0 -161
  209. package/template/src/components/dashboard/color-picker.tsx +0 -65
  210. package/template/src/components/dashboard/contacts-chart.tsx +0 -69
  211. package/template/src/components/dashboard/interactions-by-type-chart.tsx +0 -121
  212. package/template/src/components/dashboard/recent-activity.tsx +0 -157
  213. package/template/src/components/dashboard/sales-analytics-chart.tsx +0 -77
  214. package/template/src/components/dashboard/status-distribution-chart.tsx +0 -82
  215. package/template/src/components/dashboard/top-contacts-list.tsx +0 -119
  216. package/template/src/components/dashboard/widget-wrapper.tsx +0 -39
  217. package/template/src/contexts/dashboard-theme-context.tsx +0 -58
  218. package/template/src/lib/dashboard-themes.ts +0 -140
  219. package/template/src/lib/default-widgets.ts +0 -14
  220. package/template/src/lib/widget-registry.ts +0 -177
@@ -1,59 +1,145 @@
1
1
  /**
2
- * Composants Skeleton pour les états de chargement
2
+ * Composants Skeleton, Spinner et PageLoader pour les états de chargement
3
3
  */
4
4
 
5
5
  import { cn } from '@/lib/utils';
6
+ import { Loader2 } from 'lucide-react';
6
7
 
7
8
  export function Skeleton({ className }: { className?: string }) {
8
9
  return (
9
10
  <div
10
- className={cn('animate-pulse rounded bg-gray-200', className)}
11
+ className={cn('animate-pulse rounded bg-muted', className)}
11
12
  aria-label="Chargement..."
12
13
  />
13
14
  );
14
15
  }
15
16
 
17
+ const SPINNER_SIZES = {
18
+ xs: 'h-3 w-3',
19
+ sm: 'h-4 w-4',
20
+ md: 'h-5 w-5',
21
+ lg: 'h-8 w-8',
22
+ } as const;
23
+
24
+ interface SpinnerProps {
25
+ size?: keyof typeof SPINNER_SIZES;
26
+ className?: string;
27
+ }
28
+
29
+ export function Spinner({ size = 'lg', className }: Readonly<SpinnerProps>) {
30
+ return <Loader2 className={cn('animate-spin text-primary', SPINNER_SIZES[size], className)} />;
31
+ }
32
+
33
+ interface PageLoaderProps {
34
+ text?: string;
35
+ className?: string;
36
+ }
37
+
38
+ export function PageLoader({ text = 'Chargement...', className }: Readonly<PageLoaderProps>) {
39
+ return (
40
+ <div className={cn('flex h-full items-center justify-center py-12', className)}>
41
+ <div className="text-center">
42
+ <Spinner size="lg" className="mx-auto" />
43
+ <p className="mt-3 text-sm text-muted-foreground">{text}</p>
44
+ </div>
45
+ </div>
46
+ );
47
+ }
48
+
49
+ interface ListPageSkeletonProps {
50
+ headerWidth?: string;
51
+ descriptionWidth?: string;
52
+ buttonWidth?: string;
53
+ itemCount?: number;
54
+ itemHeight?: string;
55
+ hasSearchBar?: boolean;
56
+ layout?: 'list' | 'grid';
57
+ }
58
+
59
+ export function ListPageSkeleton({
60
+ headerWidth = 'w-32',
61
+ descriptionWidth = 'w-48',
62
+ buttonWidth = 'w-36',
63
+ itemCount = 6,
64
+ itemHeight = 'h-16',
65
+ hasSearchBar = true,
66
+ layout = 'list',
67
+ }: Readonly<ListPageSkeletonProps>) {
68
+ return (
69
+ <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">
71
+ <div>
72
+ <Skeleton className={cn('h-7', headerWidth)} />
73
+ <Skeleton className={cn('mt-1 h-4', descriptionWidth)} />
74
+ </div>
75
+ <Skeleton className={cn('h-10 rounded-lg', buttonWidth)} />
76
+ </div>
77
+ <div className="p-4 sm:p-6">
78
+ {hasSearchBar && (
79
+ <div className="mb-4 flex gap-3">
80
+ <Skeleton className="h-10 flex-1 rounded-lg" />
81
+ <Skeleton className="h-10 w-28 rounded-lg" />
82
+ </div>
83
+ )}
84
+ {layout === 'list' ? (
85
+ <div className="space-y-3">
86
+ {Array.from({ length: itemCount }).map((_, i) => (
87
+ <Skeleton key={i} className={cn('rounded-lg', itemHeight)} />
88
+ ))}
89
+ </div>
90
+ ) : (
91
+ <div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
92
+ {Array.from({ length: itemCount }).map((_, i) => (
93
+ <Skeleton key={i} className={cn('rounded-lg', itemHeight)} />
94
+ ))}
95
+ </div>
96
+ )}
97
+ </div>
98
+ </div>
99
+ );
100
+ }
101
+
16
102
  export function ContactTableSkeleton() {
17
103
  return (
18
- <div className="overflow-x-auto rounded-lg bg-white shadow">
19
- <table className="min-w-full divide-y divide-gray-200">
20
- <thead className="bg-gray-50">
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">
106
+ <thead className="bg-muted">
21
107
  <tr>
22
108
  <th className="px-3 py-3 sm:px-6">
23
109
  <Skeleton className="h-4 w-4" />
24
110
  </th>
25
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
111
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
26
112
  Contact
27
113
  </th>
28
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
114
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
29
115
  Téléphone
30
116
  </th>
31
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
117
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
32
118
  Email
33
119
  </th>
34
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
120
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
35
121
  Statut
36
122
  </th>
37
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
123
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
38
124
  Origine
39
125
  </th>
40
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
126
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
41
127
  COMMERCIAL
42
128
  </th>
43
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
129
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
44
130
  TÉLÉPRO
45
131
  </th>
46
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
132
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
47
133
  CRÉÉ LE
48
134
  </th>
49
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
135
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
50
136
  MODIFIÉ LE
51
137
  </th>
52
138
  </tr>
53
139
  </thead>
54
- <tbody className="divide-y divide-gray-200 bg-white">
140
+ <tbody className="divide-y divide-border bg-card">
55
141
  {Array.from({ length: 8 }).map((_, i) => (
56
- <tr key={i} className="hover:bg-gray-50">
142
+ <tr key={i} className="hover:bg-muted/70">
57
143
  <td className="px-3 py-4 whitespace-nowrap sm:px-6">
58
144
  <Skeleton className="h-4 w-4" />
59
145
  </td>
@@ -102,7 +188,7 @@ export function ContactCardsSkeleton() {
102
188
  return (
103
189
  <div className="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-3">
104
190
  {Array.from({ length: 9 }).map((_, i) => (
105
- <div key={i} className="rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
191
+ <div key={i} className="rounded-lg border border-border bg-card p-6 shadow-(--shadow-card)">
106
192
  {/* En-tête */}
107
193
  <div className="mb-4 flex items-start justify-between">
108
194
  <div className="flex items-center gap-3">
@@ -140,7 +226,7 @@ export function ContactCardsSkeleton() {
140
226
  </div>
141
227
 
142
228
  {/* Pied de carte avec utilisateurs assignés */}
143
- <div className="flex items-start justify-between border-t border-gray-100 pt-4">
229
+ <div className="flex items-start justify-between border-t border-border/70 pt-4">
144
230
  <div className="space-y-2">
145
231
  <div className="flex items-center gap-2">
146
232
  <Skeleton className="h-7 w-7 rounded-full" />
@@ -164,12 +250,12 @@ export function ContactCardsSkeleton() {
164
250
 
165
251
  export function AgendaMonthSkeleton() {
166
252
  return (
167
- <div className="rounded-lg bg-white shadow">
168
- <div className="grid grid-cols-7 border-b border-gray-200">
253
+ <div className="rounded-lg border border-border bg-card shadow-(--shadow-card)">
254
+ <div className="grid grid-cols-7 border-b border-border">
169
255
  {['Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam', 'Dim'].map((day) => (
170
256
  <div
171
257
  key={day}
172
- className="border-r border-gray-200 p-3 text-center text-sm font-semibold text-gray-700 last:border-r-0"
258
+ className="border-r border-border p-3 text-center text-sm font-semibold text-foreground last:border-r-0"
173
259
  >
174
260
  {day}
175
261
  </div>
@@ -179,7 +265,7 @@ export function AgendaMonthSkeleton() {
179
265
  {Array.from({ length: 42 }).map((_, i) => (
180
266
  <div
181
267
  key={i}
182
- className="min-h-[100px] border-r border-b border-gray-200 p-2 last:border-r-0"
268
+ className="min-h-[100px] border-r border-b border-border p-2 last:border-r-0"
183
269
  >
184
270
  <Skeleton className="mb-2 h-5 w-6" />
185
271
  <div className="space-y-1">
@@ -202,31 +288,31 @@ export function AgendaWeekSkeleton() {
202
288
  };
203
289
 
204
290
  return (
205
- <div className="overflow-auto rounded-lg bg-white shadow">
206
- <div className="grid grid-cols-8 border-b border-gray-200 bg-gray-50 text-xs font-medium text-gray-500">
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">
207
293
  <div className="px-3 py-2 text-right">(UTC+01:00) Hr</div>
208
294
  {Array.from({ length: 7 }).map((_, i) => (
209
- <div key={i} className="border-l border-gray-200 px-3 py-2 text-center">
295
+ <div key={i} className="border-l border-border px-3 py-2 text-center">
210
296
  <Skeleton className="mx-auto h-4 w-12" />
211
297
  <Skeleton className="mx-auto mt-1 h-8 w-8 rounded-full" />
212
298
  </div>
213
299
  ))}
214
300
  </div>
215
301
  <div className="grid grid-cols-8 text-xs">
216
- <div className="border-r border-gray-200 bg-gray-50">
302
+ <div className="border-r border-border bg-muted">
217
303
  {HOURS.map((hour) => (
218
304
  <div
219
305
  key={hour}
220
- className="flex h-16 items-start justify-end border-b border-gray-200 pr-2"
306
+ className="flex h-16 items-start justify-end border-b border-border pr-2"
221
307
  >
222
308
  <Skeleton className="h-3 w-10" />
223
309
  </div>
224
310
  ))}
225
311
  </div>
226
312
  {Array.from({ length: 7 }).map((_, dayIndex) => (
227
- <div key={dayIndex} className="border-l border-gray-200">
313
+ <div key={dayIndex} className="border-l border-border">
228
314
  {HOURS.map((hour, hourIndex) => (
229
- <div key={hour} className="relative h-16 border-b border-gray-100 px-1.5 py-0.5">
315
+ <div key={hour} className="relative h-16 border-b border-border/70 px-1.5 py-0.5">
230
316
  {shouldShowSkeleton(dayIndex, hourIndex) && (
231
317
  <Skeleton className="h-12 w-full rounded" />
232
318
  )}
@@ -243,7 +329,7 @@ export function AgendaDaySkeleton() {
243
329
  return (
244
330
  <div className="space-y-4">
245
331
  {Array.from({ length: 5 }).map((_, i) => (
246
- <div key={i} className="rounded-lg border border-gray-200 bg-white p-4 shadow">
332
+ <div key={i} className="rounded-lg border border-border bg-card p-4 shadow-(--shadow-card)">
247
333
  <div className="flex items-start justify-between">
248
334
  <div className="flex-1">
249
335
  <div className="flex items-center gap-2">
@@ -271,30 +357,30 @@ export function AgendaDaySkeleton() {
271
357
 
272
358
  export function UsersTableSkeleton() {
273
359
  return (
274
- <div className="overflow-x-auto rounded-lg bg-white shadow">
275
- <table className="min-w-full divide-y divide-gray-200">
276
- <thead className="bg-gray-50">
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">
362
+ <thead className="bg-muted">
277
363
  <tr>
278
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
364
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
279
365
  Utilisateur
280
366
  </th>
281
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
367
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
282
368
  Email
283
369
  </th>
284
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
370
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
285
371
  Rôle
286
372
  </th>
287
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
373
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
288
374
  Email vérifié
289
375
  </th>
290
- <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-gray-500 uppercase sm:px-6">
376
+ <th className="px-3 py-3 text-left text-xs font-medium tracking-wider text-muted-foreground uppercase sm:px-6">
291
377
  Compte
292
378
  </th>
293
379
  </tr>
294
380
  </thead>
295
- <tbody className="divide-y divide-gray-200 bg-white">
381
+ <tbody className="divide-y divide-border bg-card">
296
382
  {Array.from({ length: 6 }).map((_, i) => (
297
- <tr key={i} className="hover:bg-gray-50">
383
+ <tr key={i} className="hover:bg-muted/70">
298
384
  <td className="px-3 py-4 whitespace-nowrap sm:px-6">
299
385
  <div className="flex items-center">
300
386
  <Skeleton className="h-8 w-8 rounded-full sm:h-10 sm:w-10" />
@@ -330,7 +416,7 @@ export function TemplatesPageSkeleton() {
330
416
  return (
331
417
  <div className="h-full">
332
418
  {/* Header Skeleton */}
333
- <div className="border-b border-gray-200 bg-white px-4 py-4 sm:px-6 lg:px-8">
419
+ <div className="border-b border-border bg-card px-4 py-4 sm:px-6 lg:px-8">
334
420
  <div className="flex items-center justify-between">
335
421
  <div className="space-y-2">
336
422
  <Skeleton className="h-8 w-48" />
@@ -353,7 +439,7 @@ export function TemplatesPageSkeleton() {
353
439
  {Array.from({ length: 6 }).map((_, i) => (
354
440
  <div
355
441
  key={i}
356
- className="rounded-lg border border-gray-200 bg-white p-4 shadow transition-shadow"
442
+ className="rounded-lg border border-border bg-card p-4 shadow-(--shadow-card) transition-shadow"
357
443
  >
358
444
  <div className="flex items-start justify-between">
359
445
  <div className="flex-1">
@@ -0,0 +1,64 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import * as AccordionPrimitive from '@radix-ui/react-accordion';
5
+ import { ChevronDownIcon } from 'lucide-react';
6
+
7
+ import { cn } from '@/lib/utils';
8
+
9
+ function Accordion({ ...props }: React.ComponentProps<typeof AccordionPrimitive.Root>) {
10
+ return <AccordionPrimitive.Root data-slot="accordion" {...props} />;
11
+ }
12
+
13
+ function AccordionItem({
14
+ className,
15
+ ...props
16
+ }: React.ComponentProps<typeof AccordionPrimitive.Item>) {
17
+ return (
18
+ <AccordionPrimitive.Item
19
+ data-slot="accordion-item"
20
+ className={cn('border-b last:border-b-0', className)}
21
+ {...props}
22
+ />
23
+ );
24
+ }
25
+
26
+ function AccordionTrigger({
27
+ className,
28
+ children,
29
+ ...props
30
+ }: React.ComponentProps<typeof AccordionPrimitive.Trigger>) {
31
+ return (
32
+ <AccordionPrimitive.Header className="flex">
33
+ <AccordionPrimitive.Trigger
34
+ data-slot="accordion-trigger"
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',
37
+ className,
38
+ )}
39
+ {...props}
40
+ >
41
+ {children}
42
+ <ChevronDownIcon className="text-muted-foreground pointer-events-none size-4 shrink-0 translate-y-0.5 transition-transform duration-200" />
43
+ </AccordionPrimitive.Trigger>
44
+ </AccordionPrimitive.Header>
45
+ );
46
+ }
47
+
48
+ function AccordionContent({
49
+ className,
50
+ children,
51
+ ...props
52
+ }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
53
+ return (
54
+ <AccordionPrimitive.Content
55
+ data-slot="accordion-content"
56
+ className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
57
+ {...props}
58
+ >
59
+ <div className={cn('pt-0 pb-4', className)}>{children}</div>
60
+ </AccordionPrimitive.Content>
61
+ );
62
+ }
63
+
64
+ export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
@@ -0,0 +1,139 @@
1
+ 'use client';
2
+
3
+ import * as React from 'react';
4
+ import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog';
5
+
6
+ import { cn } from '@/lib/utils';
7
+ import { buttonVariants } from '@/components/ui/button';
8
+
9
+ function AlertDialog({
10
+ ...props
11
+ }: Readonly<React.ComponentProps<typeof AlertDialogPrimitive.Root>>) {
12
+ return <AlertDialogPrimitive.Root data-slot="alert-dialog" {...props} />;
13
+ }
14
+
15
+ function AlertDialogTrigger({
16
+ ...props
17
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Trigger>) {
18
+ return <AlertDialogPrimitive.Trigger data-slot="alert-dialog-trigger" {...props} />;
19
+ }
20
+
21
+ function AlertDialogPortal({
22
+ ...props
23
+ }: Readonly<React.ComponentProps<typeof AlertDialogPrimitive.Portal>>) {
24
+ return <AlertDialogPrimitive.Portal data-slot="alert-dialog-portal" {...props} />;
25
+ }
26
+
27
+ function AlertDialogOverlay({
28
+ className,
29
+ ...props
30
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Overlay>) {
31
+ return (
32
+ <AlertDialogPrimitive.Overlay
33
+ data-slot="alert-dialog-overlay"
34
+ className={cn(
35
+ 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/50',
36
+ className,
37
+ )}
38
+ {...props}
39
+ />
40
+ );
41
+ }
42
+
43
+ function AlertDialogContent({
44
+ className,
45
+ ...props
46
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Content>) {
47
+ return (
48
+ <AlertDialogPortal>
49
+ <AlertDialogOverlay />
50
+ <AlertDialogPrimitive.Content
51
+ data-slot="alert-dialog-content"
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',
54
+ className,
55
+ )}
56
+ {...props}
57
+ />
58
+ </AlertDialogPortal>
59
+ );
60
+ }
61
+
62
+ function AlertDialogHeader({ className, ...props }: React.ComponentProps<'div'>) {
63
+ return (
64
+ <div
65
+ data-slot="alert-dialog-header"
66
+ className={cn('flex flex-col gap-2 text-center sm:text-left', className)}
67
+ {...props}
68
+ />
69
+ );
70
+ }
71
+
72
+ function AlertDialogFooter({ className, ...props }: React.ComponentProps<'div'>) {
73
+ return (
74
+ <div
75
+ data-slot="alert-dialog-footer"
76
+ className={cn('flex flex-col-reverse gap-2 sm:flex-row sm:justify-end', className)}
77
+ {...props}
78
+ />
79
+ );
80
+ }
81
+
82
+ function AlertDialogTitle({
83
+ className,
84
+ ...props
85
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Title>) {
86
+ return (
87
+ <AlertDialogPrimitive.Title
88
+ data-slot="alert-dialog-title"
89
+ className={cn('text-lg font-semibold', className)}
90
+ {...props}
91
+ />
92
+ );
93
+ }
94
+
95
+ function AlertDialogDescription({
96
+ className,
97
+ ...props
98
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Description>) {
99
+ return (
100
+ <AlertDialogPrimitive.Description
101
+ data-slot="alert-dialog-description"
102
+ className={cn('text-muted-foreground text-sm', className)}
103
+ {...props}
104
+ />
105
+ );
106
+ }
107
+
108
+ function AlertDialogAction({
109
+ className,
110
+ ...props
111
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Action>) {
112
+ return <AlertDialogPrimitive.Action className={cn(buttonVariants(), className)} {...props} />;
113
+ }
114
+
115
+ function AlertDialogCancel({
116
+ className,
117
+ ...props
118
+ }: React.ComponentProps<typeof AlertDialogPrimitive.Cancel>) {
119
+ return (
120
+ <AlertDialogPrimitive.Cancel
121
+ className={cn(buttonVariants({ variant: 'outline' }), className)}
122
+ {...props}
123
+ />
124
+ );
125
+ }
126
+
127
+ export {
128
+ AlertDialog,
129
+ AlertDialogPortal,
130
+ AlertDialogOverlay,
131
+ AlertDialogTrigger,
132
+ AlertDialogContent,
133
+ AlertDialogHeader,
134
+ AlertDialogFooter,
135
+ AlertDialogTitle,
136
+ AlertDialogDescription,
137
+ AlertDialogAction,
138
+ AlertDialogCancel,
139
+ };
@@ -0,0 +1,60 @@
1
+ import * as React from 'react';
2
+ import { Slot } from '@radix-ui/react-slot';
3
+ import { cva, type VariantProps } from 'class-variance-authority';
4
+
5
+ import { cn } from '@/lib/utils';
6
+
7
+ 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
+ {
10
+ variants: {
11
+ variant: {
12
+ default: 'bg-primary text-primary-foreground hover:bg-primary/90',
13
+ destructive:
14
+ 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60',
15
+ outline:
16
+ '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
+ secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
18
+ ghost: 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50',
19
+ link: 'text-primary underline-offset-4 hover:underline',
20
+ },
21
+ size: {
22
+ default: 'h-9 px-4 py-2 has-[>svg]:px-3',
23
+ sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5',
24
+ lg: 'h-10 rounded-md px-6 has-[>svg]:px-4',
25
+ icon: 'size-9',
26
+ 'icon-sm': 'size-8',
27
+ 'icon-lg': 'size-10',
28
+ },
29
+ },
30
+ defaultVariants: {
31
+ variant: 'default',
32
+ size: 'default',
33
+ },
34
+ },
35
+ );
36
+
37
+ function Button({
38
+ className,
39
+ variant = 'default',
40
+ size = 'default',
41
+ asChild = false,
42
+ ...props
43
+ }: React.ComponentProps<'button'> &
44
+ VariantProps<typeof buttonVariants> & {
45
+ asChild?: boolean;
46
+ }) {
47
+ const Comp = asChild ? Slot : 'button';
48
+
49
+ return (
50
+ <Comp
51
+ data-slot="button"
52
+ data-variant={variant}
53
+ data-size={size}
54
+ className={cn(buttonVariants({ variant, size, className }))}
55
+ {...props}
56
+ />
57
+ );
58
+ }
59
+
60
+ export { Button, buttonVariants };
@@ -16,7 +16,7 @@ export function ViewAsBanner() {
16
16
  };
17
17
 
18
18
  return (
19
- <div className="shrink-0 bg-indigo-600 px-4 py-3 text-white shadow-md">
19
+ <div className="shrink-0 bg-blue-600 px-4 py-3 text-white shadow-md">
20
20
  <div className="flex items-center justify-between gap-4">
21
21
  <div className="flex items-center gap-3">
22
22
  <Eye className="h-5 w-5 shrink-0" />