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,64 +1,56 @@
1
1
  'use client';
2
2
 
3
- import {
4
- Bar,
5
- BarChart,
6
- ResponsiveContainer,
7
- Tooltip,
8
- XAxis,
9
- YAxis,
10
- CartesianGrid,
11
- Legend,
12
- } from 'recharts';
13
- import { useDashboardTheme } from '@/contexts/dashboard-theme-context';
3
+ import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis, Legend } from 'recharts';
14
4
 
15
5
  interface ActivityChartProps {
16
- readonly data: Array<{ date: string; interactions: number; tasks: number }>;
6
+ data: Array<{ date: string; interactions: number; tasks: number }>;
17
7
  }
18
8
 
19
- export function ActivityChart({ data }: Readonly<ActivityChartProps>) {
20
- const { theme } = useDashboardTheme();
21
-
9
+ export function ActivityChart({ data }: ActivityChartProps) {
22
10
  return (
23
- <div className="flex h-full flex-col rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
11
+ <div className="rounded-xl border border-gray-200/50 bg-white p-6 shadow-lg transition-shadow duration-300 hover:shadow-xl">
24
12
  <div className="mb-4">
25
- <h3 className="text-base font-semibold text-gray-900">Activité des 7 Derniers Jours</h3>
26
- <p className="mt-0.5 text-xs text-gray-400">Interactions et tâches créées</p>
13
+ <h3 className="text-lg font-bold text-gray-900">Activité des 7 Derniers Jours</h3>
14
+ <p className="mt-1 text-sm text-gray-500">Interactions et tâches créées</p>
27
15
  </div>
28
- <div className="min-h-0 flex-1">
16
+ <div className="h-[300px]">
29
17
  <ResponsiveContainer width="100%" height="100%">
30
- <BarChart
31
- data={data}
32
- barCategoryGap="25%"
33
- margin={{ top: 5, right: 5, left: -20, bottom: 0 }}
34
- >
35
- <CartesianGrid strokeDasharray="3 3" stroke="#f3f4f6" vertical={false} />
18
+ <BarChart data={data} barCategoryGap="20%">
19
+ <defs>
20
+ <linearGradient id="interactionsGradient" x1="0" y1="0" x2="0" y2="1">
21
+ <stop offset="0%" stopColor="#6366f1" stopOpacity={1} />
22
+ <stop offset="100%" stopColor="#4f46e5" stopOpacity={0.8} />
23
+ </linearGradient>
24
+ <linearGradient id="tasksGradient" x1="0" y1="0" x2="0" y2="1">
25
+ <stop offset="0%" stopColor="#8b5cf6" stopOpacity={1} />
26
+ <stop offset="100%" stopColor="#7c3aed" stopOpacity={0.8} />
27
+ </linearGradient>
28
+ </defs>
36
29
  <XAxis
37
30
  dataKey="date"
38
- stroke="#d1d5db"
39
- fontSize={11}
31
+ stroke="#9ca3af"
32
+ fontSize={12}
40
33
  tickLine={false}
41
34
  axisLine={false}
42
- dy={8}
43
35
  />
44
- <YAxis stroke="#d1d5db" fontSize={11} tickLine={false} axisLine={false} width={40} />
36
+ <YAxis stroke="#9ca3af" fontSize={12} tickLine={false} axisLine={false} />
45
37
  <Tooltip
46
38
  contentStyle={{
47
- backgroundColor: '#fff',
48
- border: '1px solid #f3f4f6',
39
+ backgroundColor: 'white',
40
+ border: '1px solid #e5e7eb',
49
41
  borderRadius: '12px',
50
- boxShadow: '0 4px 12px rgba(0,0,0,0.08)',
51
- fontSize: '13px',
42
+ boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
52
43
  }}
53
44
  labelStyle={{ color: '#374151', fontWeight: 600 }}
54
45
  />
55
- <Legend
56
- wrapperStyle={{ paddingTop: '12px', fontSize: '12px' }}
57
- iconType="circle"
58
- iconSize={8}
46
+ <Legend wrapperStyle={{ paddingTop: '20px' }} iconType="circle" />
47
+ <Bar
48
+ dataKey="interactions"
49
+ fill="url(#interactionsGradient)"
50
+ radius={[8, 8, 0, 0]}
51
+ name="Interactions"
59
52
  />
60
- <Bar dataKey="interactions" fill={theme.hex[500]} radius={[6, 6, 0, 0]} name="Interactions" />
61
- <Bar dataKey="tasks" fill={theme.hex[300]} radius={[6, 6, 0, 0]} name="Tâches" />
53
+ <Bar dataKey="tasks" fill="url(#tasksGradient)" radius={[8, 8, 0, 0]} name="Tâches" />
62
54
  </BarChart>
63
55
  </ResponsiveContainer>
64
56
  </div>
@@ -0,0 +1,79 @@
1
+ 'use client';
2
+
3
+ import dynamic from 'next/dynamic';
4
+ import { Users, CheckCircle2, Clock, TrendingUp } from 'lucide-react';
5
+ import { StatCard } from '@/components/dashboard/stat-card';
6
+ import { UpcomingTasksList } from '@/components/dashboard/upcoming-tasks-list';
7
+ import { PageHeader } from '@/components/page-header';
8
+ import { ProtectedPage } from '@/components/protected-page';
9
+ import { Skeleton } from '@/components/skeleton';
10
+ import type { DashboardStats } from '@/lib/dashboard-stats';
11
+
12
+ const ActivityChart = dynamic(
13
+ () => import('@/components/dashboard/activity-chart').then((m) => m.ActivityChart),
14
+ { ssr: false, loading: () => <Skeleton className="h-80 rounded-lg" /> },
15
+ );
16
+ const TasksPieChart = dynamic(
17
+ () => import('@/components/dashboard/tasks-pie-chart').then((m) => m.TasksPieChart),
18
+ { ssr: false, loading: () => <Skeleton className="h-80 rounded-lg" /> },
19
+ );
20
+
21
+ interface DashboardContentProps {
22
+ stats: DashboardStats;
23
+ }
24
+
25
+ export function DashboardContent({ stats }: Readonly<DashboardContentProps>) {
26
+ return (
27
+ <ProtectedPage requiredPermission="analytics.view">
28
+ <div className="h-full">
29
+ <PageHeader title="Tableau de Bord" description="Vue d'ensemble de votre activité" />
30
+
31
+ <div className="space-y-6 p-4 sm:p-6">
32
+ <div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4">
33
+ <StatCard
34
+ title="Total Contacts"
35
+ value={stats.overview.totalContacts.toLocaleString('fr-FR')}
36
+ icon={Users}
37
+ trend={{
38
+ value: stats.overview.contactsGrowth,
39
+ label: 'ce mois',
40
+ }}
41
+ iconColor="text-primary"
42
+ iconBgColor="bg-primary/15"
43
+ />
44
+ <StatCard
45
+ title="Nouveaux ce Mois"
46
+ value={stats.overview.contactsThisMonth.toLocaleString('fr-FR')}
47
+ icon={TrendingUp}
48
+ iconColor="text-green-600"
49
+ iconBgColor="bg-green-100"
50
+ />
51
+ <StatCard
52
+ title="Tâches Complétées"
53
+ value={stats.tasks.completed.toLocaleString('fr-FR')}
54
+ icon={CheckCircle2}
55
+ iconColor="text-emerald-600"
56
+ iconBgColor="bg-emerald-100"
57
+ />
58
+ <StatCard
59
+ title="Tâches en Attente"
60
+ value={stats.tasks.pending.toLocaleString('fr-FR')}
61
+ icon={Clock}
62
+ iconColor="text-blue-600"
63
+ iconBgColor="bg-blue-100"
64
+ />
65
+ </div>
66
+
67
+ <div className="grid gap-6 lg:grid-cols-3">
68
+ <div className="lg:col-span-2">
69
+ <ActivityChart data={stats.activity.last7Days} />
70
+ </div>
71
+ <TasksPieChart completed={stats.tasks.completed} pending={stats.tasks.pending} />
72
+ </div>
73
+
74
+ <UpcomingTasksList tasks={stats.tasks.upcoming} />
75
+ </div>
76
+ </div>
77
+ </ProtectedPage>
78
+ );
79
+ }
@@ -1,63 +1,61 @@
1
1
  'use client';
2
2
 
3
+ import { LucideIcon } from 'lucide-react';
3
4
  import { cn } from '@/lib/utils';
4
5
 
5
6
  interface StatCardProps {
6
- readonly title: string;
7
- readonly value: string | number;
8
- readonly trend?: {
9
- readonly value: number;
10
- readonly label: string;
7
+ title: string;
8
+ value: string | number;
9
+ icon: LucideIcon;
10
+ trend?: {
11
+ value: number;
12
+ label: string;
11
13
  };
12
- readonly subtitle?: string;
13
- readonly accentColor?: string;
14
+ iconColor?: string;
15
+ iconBgColor?: string;
14
16
  }
15
17
 
16
18
  export function StatCard({
17
19
  title,
18
20
  value,
21
+ icon: Icon,
19
22
  trend,
20
- subtitle,
21
- accentColor = 'dash-accent-bar',
23
+ iconColor = 'text-primary',
24
+ iconBgColor = 'bg-primary/15',
22
25
  }: StatCardProps) {
23
26
  return (
24
- <div className="group relative flex h-full flex-col justify-between overflow-hidden rounded-2xl border border-gray-100 bg-white p-5 shadow-sm transition-all duration-200 hover:shadow-md">
25
- {/* Barre d'accent en haut */}
26
- <div className={cn('absolute top-0 left-0 h-1 w-full', accentColor)} />
27
-
28
- <div className="flex items-start justify-between">
27
+ <div className="group relative overflow-hidden rounded-xl border border-border bg-card p-6 shadow-(--shadow-card) transition-all duration-300 hover:-translate-y-1">
28
+ <div className="absolute inset-0 bg-linear-to-br from-card via-card to-muted/40 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
29
+ <div className="relative flex items-center justify-between">
29
30
  <div className="flex-1">
30
- <p className="text-sm font-medium text-gray-500">{title}</p>
31
- <p className="mt-2 text-3xl font-bold tracking-tight text-gray-900">{value}</p>
31
+ <p className="text-sm font-medium text-muted-foreground">{title}</p>
32
+ <p className="mt-2 bg-linear-to-r from-foreground to-foreground/80 bg-clip-text text-3xl font-bold text-transparent">
33
+ {value}
34
+ </p>
35
+ {trend && (
36
+ <p className="mt-2 flex items-center gap-1 text-sm">
37
+ <span
38
+ className={cn(
39
+ 'font-semibold',
40
+ trend.value >= 0 ? 'text-emerald-600' : 'text-red-600',
41
+ )}
42
+ >
43
+ {trend.value >= 0 ? '+' : ''}
44
+ {trend.value}%
45
+ </span>
46
+ <span className="text-muted-foreground">{trend.label}</span>
47
+ </p>
48
+ )}
32
49
  </div>
33
-
34
- {/* Mini bar chart décoratif */}
35
- <div className="flex items-end gap-[3px] opacity-60">
36
- {[40, 65, 45, 80, 55, 70, 90].map((barH) => (
37
- <div
38
- key={`bar-${barH}`}
39
- className={cn('w-[4px] rounded-full', accentColor)}
40
- style={{ height: `${barH * 0.3}px`, opacity: 0.4 + (barH / 90) * 0.6 }}
41
- />
42
- ))}
50
+ <div
51
+ className={cn(
52
+ 'rounded-xl bg-linear-to-br p-4 shadow-sm transition-transform duration-300 group-hover:scale-110',
53
+ iconBgColor,
54
+ )}
55
+ >
56
+ <Icon className={cn('h-6 w-6', iconColor)} />
43
57
  </div>
44
58
  </div>
45
-
46
- <div className="mt-3 flex items-center gap-2">
47
- {trend && (
48
- <span
49
- className={cn(
50
- 'inline-flex items-center gap-0.5 rounded-full px-2 py-0.5 text-xs font-semibold',
51
- trend.value >= 0 ? 'bg-emerald-50 text-emerald-600' : 'bg-red-50 text-red-600',
52
- )}
53
- >
54
- {trend.value >= 0 ? '↑' : '↓'} {Math.abs(trend.value)}%
55
- </span>
56
- )}
57
- {(subtitle || trend?.label) && (
58
- <span className="text-xs text-gray-400">{subtitle || trend?.label}</span>
59
- )}
60
- </div>
61
59
  </div>
62
60
  );
63
61
  }
@@ -1,21 +1,18 @@
1
1
  'use client';
2
2
 
3
3
  import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from 'recharts';
4
- import { useDashboardTheme } from '@/contexts/dashboard-theme-context';
5
4
 
6
5
  interface TasksPieChartProps {
7
- readonly completed: number;
8
- readonly pending: number;
6
+ completed: number;
7
+ pending: number;
9
8
  }
10
9
 
11
- export function TasksPieChart({ completed, pending }: Readonly<TasksPieChartProps>) {
12
- const { theme } = useDashboardTheme();
13
-
14
- const COLORS = {
15
- completed: '#10b981',
16
- pending: theme.hex[500],
17
- };
10
+ const COLORS = {
11
+ completed: '#10b981',
12
+ pending: '#f59e0b',
13
+ };
18
14
 
15
+ export function TasksPieChart({ completed, pending }: TasksPieChartProps) {
19
16
  const data = [
20
17
  { name: 'Complétées', value: completed },
21
18
  { name: 'En attente', value: pending },
@@ -25,64 +22,64 @@ export function TasksPieChart({ completed, pending }: Readonly<TasksPieChartProp
25
22
  const completionRate = total > 0 ? Math.round((completed / total) * 100) : 0;
26
23
 
27
24
  return (
28
- <div className="flex h-full flex-col rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
29
- <div className="mb-2">
30
- <h3 className="text-base font-semibold text-gray-900">Statut des Tâches</h3>
31
- <p className="mt-0.5 text-xs text-gray-400">Répartition des tâches</p>
25
+ <div className="rounded-xl border border-gray-200/50 bg-white p-6 shadow-lg transition-shadow duration-300 hover:shadow-xl">
26
+ <div className="mb-4">
27
+ <h3 className="text-lg font-bold text-gray-900">Statut des Tâches</h3>
28
+ <p className="mt-1 text-sm text-gray-500">Répartition des tâches</p>
32
29
  </div>
33
- <div className="flex min-h-0 flex-1 items-center justify-center">
34
- <div className="relative h-[160px] w-[160px]">
30
+ <div className="flex items-center justify-center">
31
+ <div className="relative h-[200px] w-[200px]">
35
32
  <ResponsiveContainer width="100%" height="100%">
36
33
  <PieChart>
37
34
  <Pie
38
35
  data={data}
39
36
  cx="50%"
40
37
  cy="50%"
41
- innerRadius={50}
42
- outerRadius={70}
43
- paddingAngle={3}
38
+ innerRadius={60}
39
+ outerRadius={80}
40
+ paddingAngle={2}
44
41
  dataKey="value"
45
- strokeWidth={0}
46
42
  >
47
- {data.map((entry) => (
43
+ {data.map((entry, index) => (
48
44
  <Cell
49
- key={entry.name}
45
+ key={`cell-${index}`}
50
46
  fill={entry.name === 'Complétées' ? COLORS.completed : COLORS.pending}
51
47
  />
52
48
  ))}
53
49
  </Pie>
54
50
  <Tooltip
55
51
  contentStyle={{
56
- backgroundColor: '#fff',
57
- border: '1px solid #f3f4f6',
52
+ backgroundColor: 'white',
53
+ border: '1px solid #e5e7eb',
58
54
  borderRadius: '12px',
59
- boxShadow: '0 4px 12px rgba(0,0,0,0.08)',
60
- fontSize: '13px',
55
+ boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
61
56
  }}
62
57
  />
63
58
  </PieChart>
64
59
  </ResponsiveContainer>
65
60
  <div className="absolute inset-0 flex items-center justify-center">
66
61
  <div className="text-center">
67
- <p className="text-2xl font-bold text-gray-900">{completionRate}%</p>
68
- <p className="text-[10px] text-gray-400">Complétées</p>
62
+ <p className="bg-linear-to-r from-gray-900 to-gray-700 bg-clip-text text-3xl font-bold text-transparent">
63
+ {completionRate}%
64
+ </p>
65
+ <p className="text-xs text-gray-500">Complétées</p>
69
66
  </div>
70
67
  </div>
71
68
  </div>
72
69
  </div>
73
- <div className="mt-2 grid grid-cols-2 gap-2">
74
- <div className="flex items-center gap-2 rounded-xl bg-emerald-50/80 px-3 py-2">
75
- <div className="h-2.5 w-2.5 rounded-full bg-emerald-500" />
70
+ <div className="mt-4 grid grid-cols-2 gap-4">
71
+ <div className="flex items-center gap-2 rounded-lg bg-emerald-50 p-2">
72
+ <div className="h-3 w-3 rounded-full bg-emerald-500" />
76
73
  <div>
77
- <p className="text-[10px] text-gray-500">Complétées</p>
78
- <p className="text-sm font-semibold text-gray-900">{completed}</p>
74
+ <p className="text-xs text-gray-500">Complétées</p>
75
+ <p className="font-semibold text-gray-900">{completed}</p>
79
76
  </div>
80
77
  </div>
81
- <div className="dash-legend-bg flex items-center gap-2 rounded-xl px-3 py-2">
82
- <div className="dash-legend-dot h-2.5 w-2.5 rounded-full" />
78
+ <div className="flex items-center gap-2 rounded-lg bg-blue-50 p-2">
79
+ <div className="h-3 w-3 rounded-full bg-blue-500" />
83
80
  <div>
84
- <p className="text-[10px] text-gray-500">En attente</p>
85
- <p className="text-sm font-semibold text-gray-900">{pending}</p>
81
+ <p className="text-xs text-gray-500">En attente</p>
82
+ <p className="font-semibold text-gray-900">{pending}</p>
86
83
  </div>
87
84
  </div>
88
85
  </div>
@@ -1,8 +1,7 @@
1
1
  'use client';
2
2
 
3
3
  import Link from 'next/link';
4
- import { Calendar, Phone, Video, Mail, CheckCircle2, Clock, ListTodo } from 'lucide-react';
5
- import { cn } from '@/lib/utils';
4
+ import { Calendar, Phone, Video, Mail, CheckCircle2, Clock } from 'lucide-react';
6
5
 
7
6
  interface Task {
8
7
  id: string;
@@ -14,26 +13,25 @@ interface Task {
14
13
  }
15
14
 
16
15
  interface UpcomingTasksListProps {
17
- readonly tasks: Task[];
16
+ tasks: Task[];
18
17
  }
19
18
 
20
- const taskIcons: Record<string, typeof Phone> = {
19
+ const taskIcons = {
21
20
  CALL: Phone,
22
21
  MEETING: Calendar,
23
22
  EMAIL: Mail,
24
23
  VIDEO_CONFERENCE: Video,
25
- TASK: ListTodo,
26
24
  OTHER: CheckCircle2,
27
25
  };
28
26
 
29
- const priorityStyles: Record<string, string> = {
30
- LOW: 'bg-gray-100 text-gray-600',
31
- MEDIUM: 'bg-blue-50 text-blue-600',
32
- HIGH: 'bg-orange-50 text-orange-600',
33
- URGENT: 'bg-red-50 text-red-600',
27
+ const priorityColors = {
28
+ LOW: 'text-gray-500 bg-gray-100',
29
+ MEDIUM: 'text-blue-600 bg-blue-100',
30
+ HIGH: 'text-orange-600 bg-orange-100',
31
+ URGENT: 'text-red-600 bg-red-100',
34
32
  };
35
33
 
36
- const priorityLabels: Record<string, string> = {
34
+ const priorityLabels = {
37
35
  LOW: 'Basse',
38
36
  MEDIUM: 'Moyenne',
39
37
  HIGH: 'Haute',
@@ -41,79 +39,87 @@ const priorityLabels: Record<string, string> = {
41
39
  };
42
40
 
43
41
  export function UpcomingTasksList({ tasks }: UpcomingTasksListProps) {
42
+ if (tasks.length === 0) {
43
+ return (
44
+ <div className="rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
45
+ <div className="flex items-center justify-between">
46
+ <h3 className="text-lg font-semibold text-gray-900">Tâches à Venir</h3>
47
+ <Link
48
+ href="/agenda"
49
+ className="text-sm font-medium text-blue-600 hover:text-blue-700"
50
+ >
51
+ Voir tout
52
+ </Link>
53
+ </div>
54
+ <div className="mt-6 text-center text-sm text-gray-500">
55
+ <Clock className="mx-auto h-12 w-12 text-gray-400" />
56
+ <p className="mt-2">Aucune tâche à venir</p>
57
+ </div>
58
+ </div>
59
+ );
60
+ }
61
+
44
62
  return (
45
- <div className="flex h-full flex-col rounded-2xl border border-gray-100 bg-white p-5 shadow-sm">
63
+ <div className="rounded-xl border border-gray-200/50 bg-white p-6 shadow-lg transition-shadow duration-300 hover:shadow-xl">
46
64
  <div className="mb-4 flex items-center justify-between">
47
- <div>
48
- <h3 className="text-base font-semibold text-gray-900">Tâches à Venir</h3>
49
- <p className="mt-0.5 text-xs text-gray-400">Prochains rendez-vous et tâches</p>
50
- </div>
65
+ <h3 className="text-lg font-bold text-gray-900">Liste des Tâches</h3>
51
66
  <Link
52
67
  href="/agenda"
53
- className="dash-link cursor-pointer text-xs font-medium"
68
+ className="text-sm font-semibold text-blue-600 transition-colors hover:text-blue-700"
54
69
  >
55
70
  Voir tout →
56
71
  </Link>
57
72
  </div>
73
+ <div className="space-y-3">
74
+ {tasks.map((task) => {
75
+ const Icon = taskIcons[task.type as keyof typeof taskIcons] || CheckCircle2;
76
+ const scheduledDate = new Date(task.scheduledAt);
58
77
 
59
- {tasks.length === 0 ? (
60
- <div className="flex flex-1 items-center justify-center">
61
- <div className="text-center">
62
- <Clock className="mx-auto h-10 w-10 text-gray-200" />
63
- <p className="mt-2 text-sm text-gray-400">Aucune tâche à venir</p>
64
- </div>
65
- </div>
66
- ) : (
67
- <div className="flex-1 space-y-2 overflow-auto">
68
- {tasks.map((task) => {
69
- const Icon = taskIcons[task.type] || CheckCircle2;
70
- const scheduledDate = new Date(task.scheduledAt);
71
-
72
- return (
73
- <div
74
- key={task.id}
75
- className="group flex items-start gap-3 rounded-xl border border-gray-50 bg-gray-50/50 p-3 transition-all duration-150 dash-hover-border-light dash-hover-bg"
76
- >
77
- <div className="dash-icon-box flex h-8 w-8 shrink-0 items-center justify-center rounded-lg">
78
- <Icon className="dash-icon-color h-3.5 w-3.5" />
79
- </div>
80
- <div className="min-w-0 flex-1">
81
- <div className="flex items-start justify-between gap-2">
82
- <p className="truncate text-sm font-medium text-gray-900">{task.title}</p>
83
- <span
84
- className={cn(
85
- 'shrink-0 rounded-full px-2 py-0.5 text-[10px] font-medium',
86
- priorityStyles[task.priority] || 'bg-gray-100 text-gray-600',
87
- )}
88
- >
89
- {priorityLabels[task.priority] || task.priority}
90
- </span>
78
+ return (
79
+ <div
80
+ key={task.id}
81
+ className="group flex items-start gap-3 rounded-lg border border-gray-100 bg-gray-50/50 p-3 transition-all duration-200 hover:border-blue-200 hover:bg-blue-50/30"
82
+ >
83
+ <input
84
+ type="checkbox"
85
+ className="mt-1 h-4 w-4 cursor-pointer rounded border-gray-300 text-blue-600 focus:ring-2 focus:ring-gray-400/30"
86
+ />
87
+ <div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-linear-to-br from-blue-100 to-purple-100">
88
+ <Icon className="h-4 w-4 text-blue-600" />
89
+ </div>
90
+ <div className="flex-1">
91
+ <div className="flex items-start justify-between">
92
+ <div>
93
+ <p className="font-semibold text-gray-900">{task.title}</p>
94
+ {task.contact && (
95
+ <Link
96
+ href={`/contacts/${task.contact.id}`}
97
+ className="text-sm text-gray-600 transition-colors hover:text-blue-600"
98
+ >
99
+ {task.contact.name}
100
+ </Link>
101
+ )}
91
102
  </div>
92
- {task.contact && (
93
- <Link
94
- href={`/contacts/${task.contact.id}`}
95
- className="dash-hover-text cursor-pointer text-xs text-gray-500 transition-colors"
96
- >
97
- {task.contact.name}
98
- </Link>
99
- )}
100
- <p className="mt-1 text-[11px] text-gray-400">
101
- {scheduledDate.toLocaleDateString('fr-FR', {
102
- day: 'numeric',
103
- month: 'long',
104
- })}{' '}
105
- à{' '}
106
- {scheduledDate.toLocaleTimeString('fr-FR', {
107
- hour: '2-digit',
108
- minute: '2-digit',
109
- })}
110
- </p>
103
+ <button className="cursor-pointer opacity-0 transition-opacity group-hover:opacity-100 hover:opacity-100">
104
+ <span className="text-gray-400">⋯</span>
105
+ </button>
111
106
  </div>
107
+ <p className="mt-1 text-xs text-gray-500">
108
+ {scheduledDate.toLocaleDateString('fr-FR', {
109
+ day: 'numeric',
110
+ month: 'long',
111
+ })}{' '}
112
+ à{' '}
113
+ {scheduledDate.toLocaleTimeString('fr-FR', {
114
+ hour: '2-digit',
115
+ minute: '2-digit',
116
+ })}
117
+ </p>
112
118
  </div>
113
- );
114
- })}
115
- </div>
116
- )}
119
+ </div>
120
+ );
121
+ })}
122
+ </div>
117
123
  </div>
118
124
  );
119
125
  }