create-crm-tmp 1.1.3 → 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 +1 -1
  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 +51 -16
  8. package/template/prisma/schema.prisma +807 -58
  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 +2232 -2189
  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 +5049 -4110
  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 +13 -18
  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 -43
  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 +29 -32
  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 +173 -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 +2 -2
  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 +89 -34
  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 +510 -146
  94. package/template/src/app/api/workflows/route.ts +46 -4
  95. package/template/src/app/globals.css +243 -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 +12 -15
  155. package/template/src/lib/template-variables.ts +67 -33
  156. package/template/src/lib/utils.ts +26 -11
  157. package/template/src/lib/workflow-executor.ts +445 -228
  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/20260226093949_fix_cascade_on_user_delete/migration.sql +0 -69
  205. package/template/prisma/migrations/migration_lock.toml +0 -3
  206. package/template/src/app/(dashboard)/users/layout.tsx +0 -30
  207. package/template/src/app/api/dashboard/widgets/[id]/route.ts +0 -47
  208. package/template/src/app/api/dashboard/widgets/route.ts +0 -181
  209. package/template/src/components/dashboard/add-widget-dialog.tsx +0 -161
  210. package/template/src/components/dashboard/color-picker.tsx +0 -65
  211. package/template/src/components/dashboard/contacts-chart.tsx +0 -69
  212. package/template/src/components/dashboard/interactions-by-type-chart.tsx +0 -121
  213. package/template/src/components/dashboard/recent-activity.tsx +0 -157
  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,157 +0,0 @@
1
- 'use client';
2
-
3
- import Link from 'next/link';
4
- import {
5
- Phone,
6
- Mail,
7
- Calendar,
8
- MessageSquare,
9
- FileText,
10
- TrendingUp,
11
- RefreshCw,
12
- CalendarCheck,
13
- CalendarX,
14
- CalendarClock,
15
- UserCheck,
16
- } from 'lucide-react';
17
- import { cn } from '@/lib/utils';
18
-
19
- interface Interaction {
20
- id: string;
21
- type: string;
22
- title: string | null;
23
- content: string;
24
- date: string;
25
- contact: {
26
- id: string;
27
- name: string;
28
- };
29
- }
30
-
31
- interface RecentActivityProps {
32
- readonly interactions: Interaction[];
33
- }
34
-
35
- const interactionIcons: Record<string, typeof Phone> = {
36
- CALL: Phone,
37
- SMS: MessageSquare,
38
- EMAIL: Mail,
39
- MEETING: Calendar,
40
- NOTE: FileText,
41
- STATUS_CHANGE: TrendingUp,
42
- CONTACT_UPDATE: RefreshCw,
43
- APPOINTMENT_CREATED: CalendarCheck,
44
- APPOINTMENT_DELETED: CalendarX,
45
- APPOINTMENT_CHANGED: CalendarClock,
46
- ASSIGNMENT_CHANGE: UserCheck,
47
- };
48
-
49
- const interactionColors: Record<string, string> = {
50
- CALL: 'bg-blue-50 text-blue-500',
51
- SMS: 'bg-emerald-50 text-emerald-500',
52
- EMAIL: 'bg-orange-50 text-orange-500',
53
- MEETING: 'bg-purple-50 text-purple-500',
54
- NOTE: 'bg-gray-50 text-gray-500',
55
- STATUS_CHANGE: 'bg-amber-50 text-amber-500',
56
- CONTACT_UPDATE: 'bg-cyan-50 text-cyan-500',
57
- APPOINTMENT_CREATED: 'bg-emerald-50 text-emerald-500',
58
- APPOINTMENT_DELETED: 'bg-red-50 text-red-500',
59
- APPOINTMENT_CHANGED: 'bg-orange-50 text-orange-500',
60
- ASSIGNMENT_CHANGE: 'bg-pink-50 text-pink-500',
61
- };
62
-
63
- const interactionLabels: Record<string, string> = {
64
- CALL: 'Appel',
65
- SMS: 'SMS',
66
- EMAIL: 'Email',
67
- MEETING: 'Réunion',
68
- NOTE: 'Note',
69
- STATUS_CHANGE: 'Changement de statut',
70
- CONTACT_UPDATE: 'Mise à jour',
71
- APPOINTMENT_CREATED: 'RDV créé',
72
- APPOINTMENT_DELETED: 'RDV supprimé',
73
- APPOINTMENT_CHANGED: 'RDV modifié',
74
- ASSIGNMENT_CHANGE: 'Assignation',
75
- };
76
-
77
- export function RecentActivity({ interactions }: RecentActivityProps) {
78
- return (
79
- <div className="flex h-full flex-col rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
80
- <div className="mb-4 flex items-center justify-between">
81
- <div>
82
- <h3 className="text-base font-semibold text-gray-900">Activité Récente</h3>
83
- <p className="mt-0.5 text-xs text-gray-400">Dernières interactions</p>
84
- </div>
85
- <Link
86
- href="/contacts"
87
- className="dash-link cursor-pointer text-xs font-medium"
88
- >
89
- Voir tout →
90
- </Link>
91
- </div>
92
-
93
- {interactions.length === 0 ? (
94
- <div className="flex flex-1 items-center justify-center">
95
- <div className="text-center">
96
- <FileText className="mx-auto h-10 w-10 text-gray-200" />
97
- <p className="mt-2 text-sm text-gray-400">Aucune activité récente</p>
98
- </div>
99
- </div>
100
- ) : (
101
- <div className="flex-1 space-y-1 overflow-auto">
102
- {interactions.map((interaction) => {
103
- const Icon = interactionIcons[interaction.type] || FileText;
104
- const color = interactionColors[interaction.type] || 'bg-gray-50 text-gray-500';
105
- const label = interactionLabels[interaction.type] || interaction.type;
106
- const date = new Date(interaction.date);
107
- const now = new Date();
108
- const diffMs = now.getTime() - date.getTime();
109
- const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
110
- const diffMinutes = Math.floor(diffMs / (1000 * 60));
111
-
112
- let timeAgo = '';
113
- if (diffMinutes < 1) {
114
- timeAgo = "À l'instant";
115
- } else if (diffMinutes < 60) {
116
- timeAgo = `Il y a ${diffMinutes} min`;
117
- } else if (diffHours < 24) {
118
- timeAgo = `Il y a ${diffHours}h`;
119
- } else {
120
- timeAgo = date.toLocaleDateString('fr-FR', {
121
- day: 'numeric',
122
- month: 'short',
123
- });
124
- }
125
-
126
- return (
127
- <div
128
- key={interaction.id}
129
- className="group flex items-start gap-3 rounded-xl border-l-2 border-transparent px-3 py-2.5 transition-all duration-150 dash-hover-border-left"
130
- >
131
- <div className={cn('rounded-lg p-1.5', color)}>
132
- <Icon className="h-3.5 w-3.5" />
133
- </div>
134
- <div className="min-w-0 flex-1">
135
- <div className="flex items-start justify-between gap-2">
136
- <div className="min-w-0">
137
- <p className="text-sm font-medium text-gray-900">{label}</p>
138
- <Link
139
- href={`/contacts/${interaction.contact.id}`}
140
- className="dash-hover-text cursor-pointer text-xs text-gray-500 transition-colors"
141
- >
142
- {interaction.contact.name}
143
- </Link>
144
- </div>
145
- <span className="shrink-0 text-[10px] font-medium text-gray-400">
146
- {timeAgo}
147
- </span>
148
- </div>
149
- </div>
150
- </div>
151
- );
152
- })}
153
- </div>
154
- )}
155
- </div>
156
- );
157
- }
@@ -1,82 +0,0 @@
1
- 'use client';
2
-
3
- import {
4
- Bar,
5
- BarChart,
6
- ResponsiveContainer,
7
- Tooltip,
8
- XAxis,
9
- YAxis,
10
- CartesianGrid,
11
- Cell,
12
- } from 'recharts';
13
- import { useDashboardTheme } from '@/contexts/dashboard-theme-context';
14
-
15
- interface StatusDistributionChartProps {
16
- readonly data: Array<{ name: string; value: number }>;
17
- }
18
-
19
- export function StatusDistributionChart({ data }: Readonly<StatusDistributionChartProps>) {
20
- const { theme } = useDashboardTheme();
21
-
22
- // Générer une palette basée sur le thème actif + couleurs complémentaires
23
- const statusColors = [
24
- theme.hex[500],
25
- theme.hex[400],
26
- theme.hex[300],
27
- theme.hex[200],
28
- '#10b981',
29
- '#34d399',
30
- '#6ee7b7',
31
- '#3b82f6',
32
- '#60a5fa',
33
- '#93c5fd',
34
- ];
35
-
36
- return (
37
- <div className="flex h-full flex-col rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
38
- <div className="mb-4">
39
- <h3 className="text-base font-semibold text-gray-900">Répartition par Statut</h3>
40
- <p className="mt-0.5 text-xs text-gray-400">Distribution des contacts</p>
41
- </div>
42
- <div className="min-h-0 flex-1">
43
- <ResponsiveContainer width="100%" height="100%">
44
- <BarChart
45
- data={data}
46
- layout="vertical"
47
- margin={{ top: 0, right: 20, left: 0, bottom: 0 }}
48
- >
49
- <CartesianGrid strokeDasharray="3 3" stroke="#f3f4f6" horizontal={false} />
50
- <XAxis type="number" stroke="#d1d5db" fontSize={11} tickLine={false} axisLine={false} />
51
- <YAxis
52
- dataKey="name"
53
- type="category"
54
- stroke="#d1d5db"
55
- fontSize={11}
56
- tickLine={false}
57
- axisLine={false}
58
- width={90}
59
- />
60
- <Tooltip
61
- contentStyle={{
62
- backgroundColor: '#fff',
63
- border: '1px solid #f3f4f6',
64
- borderRadius: '12px',
65
- boxShadow: '0 4px 12px rgba(0,0,0,0.08)',
66
- fontSize: '13px',
67
- }}
68
- />
69
- <Bar dataKey="value" radius={[0, 6, 6, 0]} name="Contacts" barSize={20}>
70
- {data.map((entry, index) => (
71
- <Cell
72
- key={`status-${entry.name}`}
73
- fill={statusColors[index % statusColors.length]}
74
- />
75
- ))}
76
- </Bar>
77
- </BarChart>
78
- </ResponsiveContainer>
79
- </div>
80
- </div>
81
- );
82
- }
@@ -1,119 +0,0 @@
1
- 'use client';
2
-
3
- import Link from 'next/link';
4
- import { Users } from 'lucide-react';
5
-
6
- interface Contact {
7
- id: string;
8
- name: string;
9
- phone: string;
10
- email: string | null;
11
- status: string;
12
- interactionsCount: number;
13
- assignedCommercial?: string;
14
- assignedTelepro?: string;
15
- }
16
-
17
- interface TopContactsListProps {
18
- readonly contacts: Contact[];
19
- }
20
-
21
- export function TopContactsList({ contacts }: TopContactsListProps) {
22
- if (contacts.length === 0) {
23
- return (
24
- <div className="flex h-full flex-col rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
25
- <div className="flex items-center justify-between">
26
- <div>
27
- <h3 className="text-base font-semibold text-gray-900">Derniers Prospects</h3>
28
- <p className="mt-0.5 text-xs text-gray-400">Contacts récemment ajoutés</p>
29
- </div>
30
- <Link
31
- href="/contacts"
32
- className="dash-link cursor-pointer text-xs font-medium"
33
- >
34
- Voir tout →
35
- </Link>
36
- </div>
37
- <div className="flex flex-1 items-center justify-center">
38
- <div className="text-center">
39
- <Users className="mx-auto h-10 w-10 text-gray-200" />
40
- <p className="mt-2 text-sm text-gray-400">Aucun contact</p>
41
- </div>
42
- </div>
43
- </div>
44
- );
45
- }
46
-
47
- return (
48
- <div className="flex h-full flex-col rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
49
- <div className="mb-4 flex items-center justify-between">
50
- <div>
51
- <h3 className="text-base font-semibold text-gray-900">Derniers Prospects</h3>
52
- <p className="mt-0.5 text-xs text-gray-400">Contacts récemment ajoutés</p>
53
- </div>
54
- <Link
55
- href="/contacts"
56
- className="dash-link cursor-pointer text-xs font-medium"
57
- >
58
- Voir tout →
59
- </Link>
60
- </div>
61
- <div className="flex-1 overflow-auto">
62
- <table className="w-full">
63
- <thead>
64
- <tr className="border-b border-gray-100">
65
- <th className="pb-2.5 text-left text-[11px] font-medium tracking-wider text-gray-400 uppercase">
66
- Prospect
67
- </th>
68
- <th className="pb-2.5 text-left text-[11px] font-medium tracking-wider text-gray-400 uppercase">
69
- Email
70
- </th>
71
- <th className="pb-2.5 text-left text-[11px] font-medium tracking-wider text-gray-400 uppercase">
72
- Télépro
73
- </th>
74
- <th className="pb-2.5 text-left text-[11px] font-medium tracking-wider text-gray-400 uppercase">
75
- Statut
76
- </th>
77
- </tr>
78
- </thead>
79
- <tbody>
80
- {contacts.map((contact) => {
81
- const nameParts = contact.name.split(' ');
82
- const firstName = nameParts[0] || '';
83
- const lastName = nameParts.slice(1).join(' ') || '';
84
- const initials = `${firstName[0] || ''}${lastName[0] || ''}`.toUpperCase();
85
-
86
- return (
87
- <tr
88
- key={contact.id}
89
- className="border-b border-gray-50 transition-colors dash-hover-bg"
90
- >
91
- <td className="py-3">
92
- <div className="flex items-center gap-2.5">
93
- <div className="dash-avatar flex h-7 w-7 shrink-0 items-center justify-center rounded-full text-[10px] font-semibold">
94
- {initials || '?'}
95
- </div>
96
- <Link
97
- href={`/contacts/${contact.id}`}
98
- className="dash-hover-text cursor-pointer text-sm font-medium text-gray-900 transition-colors"
99
- >
100
- {contact.name}
101
- </Link>
102
- </div>
103
- </td>
104
- <td className="py-3 text-xs text-gray-500">{contact.email || '-'}</td>
105
- <td className="py-3 text-xs text-gray-500">{contact.assignedTelepro || '-'}</td>
106
- <td className="py-3">
107
- <span className="inline-flex rounded-full bg-gray-100 px-2 py-0.5 text-[10px] font-medium text-gray-700">
108
- {contact.status}
109
- </span>
110
- </td>
111
- </tr>
112
- );
113
- })}
114
- </tbody>
115
- </table>
116
- </div>
117
- </div>
118
- );
119
- }
@@ -1,39 +0,0 @@
1
- 'use client';
2
-
3
- import { X, GripVertical } from 'lucide-react';
4
- import { cn } from '@/lib/utils';
5
-
6
- interface WidgetWrapperProps {
7
- readonly children: React.ReactNode;
8
- readonly onRemove?: () => void;
9
- readonly className?: string;
10
- }
11
-
12
- export function WidgetWrapper({ children, onRemove, className }: WidgetWrapperProps) {
13
- return (
14
- <div className={cn('group/widget relative h-full w-full', className)}>
15
- {/* Barre d'outils au hover (visible uniquement si des actions sont disponibles) */}
16
- {onRemove && (
17
- <div className="absolute top-0 right-0 z-10 flex items-center gap-1 opacity-0 transition-opacity duration-150 group-hover/widget:opacity-100">
18
- {/* Poignée de drag */}
19
- <div className="drag-handle flex h-7 w-7 cursor-grab items-center justify-center rounded-lg bg-white/90 shadow-sm ring-1 ring-gray-200/60 backdrop-blur-sm transition-colors hover:bg-gray-50 active:cursor-grabbing">
20
- <GripVertical className="h-3.5 w-3.5 text-gray-400" />
21
- </div>
22
- {/* Bouton supprimer */}
23
- <button
24
- onClick={(e) => {
25
- e.stopPropagation();
26
- onRemove();
27
- }}
28
- className="flex h-7 w-7 cursor-pointer items-center justify-center rounded-lg bg-white/90 shadow-sm ring-1 ring-gray-200/60 backdrop-blur-sm transition-colors hover:bg-red-50 hover:text-red-600"
29
- >
30
- <X className="h-3.5 w-3.5" />
31
- </button>
32
- </div>
33
- )}
34
-
35
- {/* Contenu du widget */}
36
- {children}
37
- </div>
38
- );
39
- }
@@ -1,58 +0,0 @@
1
- 'use client';
2
-
3
- import { createContext, useContext, useEffect, useMemo, useState, type ReactNode } from 'react';
4
- import {
5
- type DashboardTheme,
6
- DASHBOARD_THEMES,
7
- DEFAULT_THEME_KEY,
8
- getThemeByKey,
9
- } from '@/lib/dashboard-themes';
10
-
11
- interface DashboardThemeContextType {
12
- theme: DashboardTheme;
13
- setThemeKey: (key: string) => void;
14
- themes: DashboardTheme[];
15
- }
16
-
17
- const DashboardThemeContext = createContext<DashboardThemeContextType | undefined>(undefined);
18
-
19
- const STORAGE_KEY = 'dashboard_theme';
20
-
21
- export function DashboardThemeProvider({ children }: Readonly<{ children: ReactNode }>) {
22
- const [themeKey, setThemeKeyState] = useState(DEFAULT_THEME_KEY);
23
-
24
- // Charger le thème depuis le localStorage
25
- useEffect(() => {
26
- const stored = localStorage.getItem(STORAGE_KEY);
27
- if (stored && DASHBOARD_THEMES.some((t) => t.key === stored)) {
28
- setThemeKeyState(stored);
29
- }
30
- }, []);
31
-
32
- const setThemeKey = (key: string) => {
33
- setThemeKeyState(key);
34
- localStorage.setItem(STORAGE_KEY, key);
35
- };
36
-
37
- const theme = getThemeByKey(themeKey);
38
-
39
- const value = useMemo(
40
- () => ({ theme, setThemeKey, themes: DASHBOARD_THEMES }),
41
- // eslint-disable-next-line react-hooks/exhaustive-deps
42
- [theme],
43
- );
44
-
45
- return (
46
- <DashboardThemeContext.Provider value={value}>
47
- {children}
48
- </DashboardThemeContext.Provider>
49
- );
50
- }
51
-
52
- export function useDashboardTheme() {
53
- const context = useContext(DashboardThemeContext);
54
- if (!context) {
55
- throw new Error('useDashboardTheme doit être utilisé dans un DashboardThemeProvider');
56
- }
57
- return context;
58
- }
@@ -1,140 +0,0 @@
1
- /**
2
- * Thèmes de couleur pour le tableau de bord
3
- * Chaque thème fournit des valeurs hex pour toutes les nuances nécessaires
4
- */
5
-
6
- export interface DashboardTheme {
7
- key: string;
8
- label: string;
9
- hex: {
10
- 50: string;
11
- 100: string;
12
- 200: string;
13
- 300: string;
14
- 400: string;
15
- 500: string;
16
- 600: string;
17
- 700: string;
18
- };
19
- }
20
-
21
- export const DASHBOARD_THEMES: DashboardTheme[] = [
22
- {
23
- key: 'orange',
24
- label: 'Orange',
25
- hex: {
26
- 50: '#fff7ed',
27
- 100: '#ffedd5',
28
- 200: '#fed7aa',
29
- 300: '#fdba74',
30
- 400: '#fb923c',
31
- 500: '#f97316',
32
- 600: '#ea580c',
33
- 700: '#c2410c',
34
- },
35
- },
36
- {
37
- key: 'blue',
38
- label: 'Bleu',
39
- hex: {
40
- 50: '#eff6ff',
41
- 100: '#dbeafe',
42
- 200: '#bfdbfe',
43
- 300: '#93c5fd',
44
- 400: '#60a5fa',
45
- 500: '#3b82f6',
46
- 600: '#2563eb',
47
- 700: '#1d4ed8',
48
- },
49
- },
50
- {
51
- key: 'violet',
52
- label: 'Violet',
53
- hex: {
54
- 50: '#f5f3ff',
55
- 100: '#ede9fe',
56
- 200: '#ddd6fe',
57
- 300: '#c4b5fd',
58
- 400: '#a78bfa',
59
- 500: '#8b5cf6',
60
- 600: '#7c3aed',
61
- 700: '#6d28d9',
62
- },
63
- },
64
- {
65
- key: 'emerald',
66
- label: 'Émeraude',
67
- hex: {
68
- 50: '#ecfdf5',
69
- 100: '#d1fae5',
70
- 200: '#a7f3d0',
71
- 300: '#6ee7b7',
72
- 400: '#34d399',
73
- 500: '#10b981',
74
- 600: '#059669',
75
- 700: '#047857',
76
- },
77
- },
78
- {
79
- key: 'rose',
80
- label: 'Rose',
81
- hex: {
82
- 50: '#fff1f2',
83
- 100: '#ffe4e6',
84
- 200: '#fecdd3',
85
- 300: '#fda4af',
86
- 400: '#fb7185',
87
- 500: '#f43f5e',
88
- 600: '#e11d48',
89
- 700: '#be123c',
90
- },
91
- },
92
- {
93
- key: 'cyan',
94
- label: 'Cyan',
95
- hex: {
96
- 50: '#ecfeff',
97
- 100: '#cffafe',
98
- 200: '#a5f3fc',
99
- 300: '#67e8f9',
100
- 400: '#22d3ee',
101
- 500: '#06b6d4',
102
- 600: '#0891b2',
103
- 700: '#0e7490',
104
- },
105
- },
106
- {
107
- key: 'amber',
108
- label: 'Ambre',
109
- hex: {
110
- 50: '#fffbeb',
111
- 100: '#fef3c7',
112
- 200: '#fde68a',
113
- 300: '#fcd34d',
114
- 400: '#fbbf24',
115
- 500: '#f59e0b',
116
- 600: '#d97706',
117
- 700: '#b45309',
118
- },
119
- },
120
- {
121
- key: 'indigo',
122
- label: 'Indigo',
123
- hex: {
124
- 50: '#eef2ff',
125
- 100: '#e0e7ff',
126
- 200: '#c7d2fe',
127
- 300: '#a5b4fc',
128
- 400: '#818cf8',
129
- 500: '#6366f1',
130
- 600: '#4f46e5',
131
- 700: '#4338ca',
132
- },
133
- },
134
- ];
135
-
136
- export const DEFAULT_THEME_KEY = 'orange';
137
-
138
- export function getThemeByKey(key: string): DashboardTheme {
139
- return DASHBOARD_THEMES.find((t) => t.key === key) || DASHBOARD_THEMES[0];
140
- }
@@ -1,14 +0,0 @@
1
- // Layout par défaut pour les nouveaux utilisateurs
2
- // Ce fichier est séparé du widget-registry pour être importable côté serveur
3
- export const DEFAULT_WIDGETS = [
4
- { type: 'stat_total_contacts', x: 0, y: 0, w: 3, h: 2 },
5
- { type: 'stat_new_contacts', x: 3, y: 0, w: 3, h: 2 },
6
- { type: 'stat_completed_tasks', x: 6, y: 0, w: 3, h: 2 },
7
- { type: 'stat_pending_tasks', x: 9, y: 0, w: 3, h: 2 },
8
- { type: 'contacts_chart', x: 0, y: 2, w: 6, h: 4 },
9
- { type: 'top_contacts', x: 6, y: 2, w: 6, h: 4 },
10
- { type: 'activity_chart', x: 0, y: 6, w: 8, h: 4 },
11
- { type: 'tasks_pie', x: 8, y: 6, w: 4, h: 4 },
12
- { type: 'upcoming_tasks', x: 0, y: 10, w: 6, h: 5 },
13
- { type: 'recent_activity', x: 6, y: 10, w: 6, h: 5 },
14
- ];