create-crm-tmp 1.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 (187) hide show
  1. package/bin/create-crm-tmp.js +93 -0
  2. package/package.json +25 -0
  3. package/template/.prettierignore +33 -0
  4. package/template/.prettierrc.json +25 -0
  5. package/template/README.md +173 -0
  6. package/template/eslint.config.mjs +18 -0
  7. package/template/exemple-contacts.csv +11 -0
  8. package/template/next.config.ts +8 -0
  9. package/template/package.json +64 -0
  10. package/template/postcss.config.mjs +7 -0
  11. package/template/prisma/migrations/20251126144728_init/migration.sql +78 -0
  12. package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +5 -0
  13. package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +19 -0
  14. package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +22 -0
  15. package/template/prisma/migrations/20251128132303_add_status/migration.sql +23 -0
  16. package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +75 -0
  17. package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +2 -0
  18. package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +45 -0
  19. package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +2 -0
  20. package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +27 -0
  21. package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +20 -0
  22. package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +18 -0
  23. package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +32 -0
  24. package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +20 -0
  25. package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +12 -0
  26. package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +21 -0
  27. package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +11 -0
  28. package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +12 -0
  29. package/template/prisma/migrations/20251208094843_mg/migration.sql +14 -0
  30. package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +14 -0
  31. package/template/prisma/migrations/20251208110000_add_templates/migration.sql +26 -0
  32. package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +2 -0
  33. package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +2 -0
  34. package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +2 -0
  35. package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +3 -0
  36. package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +21 -0
  37. package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +2 -0
  38. package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +10 -0
  39. package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +26 -0
  40. package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +24 -0
  41. package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +11 -0
  42. package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +12 -0
  43. package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +25 -0
  44. package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +8 -0
  45. package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +2 -0
  46. package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +80 -0
  47. package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +32 -0
  48. package/template/prisma/migrations/migration_lock.toml +3 -0
  49. package/template/prisma/schema.prisma +582 -0
  50. package/template/prisma.config.ts +14 -0
  51. package/template/src/app/(auth)/invite/[token]/page.tsx +200 -0
  52. package/template/src/app/(auth)/layout.tsx +3 -0
  53. package/template/src/app/(auth)/reset-password/complete/page.tsx +213 -0
  54. package/template/src/app/(auth)/reset-password/page.tsx +146 -0
  55. package/template/src/app/(auth)/reset-password/verify/page.tsx +183 -0
  56. package/template/src/app/(auth)/signin/page.tsx +166 -0
  57. package/template/src/app/(dashboard)/agenda/page.tsx +3051 -0
  58. package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +24 -0
  59. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +905 -0
  60. package/template/src/app/(dashboard)/automatisation/new/page.tsx +20 -0
  61. package/template/src/app/(dashboard)/automatisation/page.tsx +337 -0
  62. package/template/src/app/(dashboard)/closing/page.tsx +1052 -0
  63. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +6028 -0
  64. package/template/src/app/(dashboard)/contacts/page.tsx +3713 -0
  65. package/template/src/app/(dashboard)/dashboard/page.tsx +186 -0
  66. package/template/src/app/(dashboard)/layout.tsx +30 -0
  67. package/template/src/app/(dashboard)/settings/page.tsx +4070 -0
  68. package/template/src/app/(dashboard)/templates/page.tsx +567 -0
  69. package/template/src/app/(dashboard)/users/list/page.tsx +507 -0
  70. package/template/src/app/(dashboard)/users/page.tsx +457 -0
  71. package/template/src/app/(dashboard)/users/permissions/page.tsx +181 -0
  72. package/template/src/app/(dashboard)/users/roles/page.tsx +434 -0
  73. package/template/src/app/api/audit-logs/route.ts +57 -0
  74. package/template/src/app/api/auth/[...all]/route.ts +4 -0
  75. package/template/src/app/api/auth/check-active/route.ts +31 -0
  76. package/template/src/app/api/auth/google/callback/route.ts +94 -0
  77. package/template/src/app/api/auth/google/disconnect/route.ts +32 -0
  78. package/template/src/app/api/auth/google/route.ts +34 -0
  79. package/template/src/app/api/auth/google/status/route.ts +32 -0
  80. package/template/src/app/api/closing-reasons/route.ts +27 -0
  81. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +94 -0
  82. package/template/src/app/api/contacts/[id]/files/route.ts +269 -0
  83. package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +91 -0
  84. package/template/src/app/api/contacts/[id]/interactions/route.ts +103 -0
  85. package/template/src/app/api/contacts/[id]/meet/route.ts +296 -0
  86. package/template/src/app/api/contacts/[id]/route.ts +322 -0
  87. package/template/src/app/api/contacts/[id]/send-email/route.ts +254 -0
  88. package/template/src/app/api/contacts/export/route.ts +270 -0
  89. package/template/src/app/api/contacts/import/route.ts +381 -0
  90. package/template/src/app/api/contacts/route.ts +283 -0
  91. package/template/src/app/api/dashboard/stats/route.ts +299 -0
  92. package/template/src/app/api/email/track/[id]/route.ts +68 -0
  93. package/template/src/app/api/integrations/google-sheet/sync/route.ts +526 -0
  94. package/template/src/app/api/invite/complete/route.ts +88 -0
  95. package/template/src/app/api/invite/validate/route.ts +55 -0
  96. package/template/src/app/api/reminders/route.ts +95 -0
  97. package/template/src/app/api/reset-password/complete/route.ts +73 -0
  98. package/template/src/app/api/reset-password/request/route.ts +84 -0
  99. package/template/src/app/api/reset-password/validate/route.ts +49 -0
  100. package/template/src/app/api/reset-password/verify/route.ts +74 -0
  101. package/template/src/app/api/roles/[id]/route.ts +183 -0
  102. package/template/src/app/api/roles/route.ts +140 -0
  103. package/template/src/app/api/send/route.ts +282 -0
  104. package/template/src/app/api/settings/change-password/route.ts +95 -0
  105. package/template/src/app/api/settings/closing-reasons/[id]/route.ts +84 -0
  106. package/template/src/app/api/settings/closing-reasons/route.ts +74 -0
  107. package/template/src/app/api/settings/company/route.ts +121 -0
  108. package/template/src/app/api/settings/google-ads/[id]/route.ts +117 -0
  109. package/template/src/app/api/settings/google-ads/route.ts +122 -0
  110. package/template/src/app/api/settings/google-sheet/[id]/route.ts +230 -0
  111. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +196 -0
  112. package/template/src/app/api/settings/google-sheet/route.ts +254 -0
  113. package/template/src/app/api/settings/meta-leads/[id]/route.ts +123 -0
  114. package/template/src/app/api/settings/meta-leads/route.ts +132 -0
  115. package/template/src/app/api/settings/profile/route.ts +42 -0
  116. package/template/src/app/api/settings/smtp/route.ts +130 -0
  117. package/template/src/app/api/settings/smtp/test/route.ts +121 -0
  118. package/template/src/app/api/settings/statuses/[id]/route.ts +101 -0
  119. package/template/src/app/api/settings/statuses/route.ts +83 -0
  120. package/template/src/app/api/statuses/route.ts +25 -0
  121. package/template/src/app/api/tasks/[id]/attendees/route.ts +76 -0
  122. package/template/src/app/api/tasks/[id]/route.ts +728 -0
  123. package/template/src/app/api/tasks/meet/route.ts +240 -0
  124. package/template/src/app/api/tasks/route.ts +417 -0
  125. package/template/src/app/api/templates/[id]/route.ts +140 -0
  126. package/template/src/app/api/templates/route.ts +91 -0
  127. package/template/src/app/api/users/[id]/route.ts +168 -0
  128. package/template/src/app/api/users/list/route.ts +45 -0
  129. package/template/src/app/api/users/me/route.ts +48 -0
  130. package/template/src/app/api/users/route.ts +250 -0
  131. package/template/src/app/api/webhooks/google-ads/route.ts +208 -0
  132. package/template/src/app/api/webhooks/meta-leads/route.ts +258 -0
  133. package/template/src/app/api/workflows/[id]/route.ts +192 -0
  134. package/template/src/app/api/workflows/process/route.ts +293 -0
  135. package/template/src/app/api/workflows/route.ts +124 -0
  136. package/template/src/app/favicon.ico +0 -0
  137. package/template/src/app/globals.css +1416 -0
  138. package/template/src/app/layout.tsx +31 -0
  139. package/template/src/app/page.tsx +32 -0
  140. package/template/src/components/dashboard/activity-chart.tsx +67 -0
  141. package/template/src/components/dashboard/contacts-chart.tsx +63 -0
  142. package/template/src/components/dashboard/recent-activity.tsx +164 -0
  143. package/template/src/components/dashboard/sales-analytics-chart.tsx +81 -0
  144. package/template/src/components/dashboard/stat-card.tsx +61 -0
  145. package/template/src/components/dashboard/status-distribution-chart.tsx +45 -0
  146. package/template/src/components/dashboard/tasks-pie-chart.tsx +88 -0
  147. package/template/src/components/dashboard/top-contacts-list.tsx +129 -0
  148. package/template/src/components/dashboard/upcoming-tasks-list.tsx +126 -0
  149. package/template/src/components/editor.tsx +856 -0
  150. package/template/src/components/email-template.tsx +35 -0
  151. package/template/src/components/header.tsx +320 -0
  152. package/template/src/components/invitation-email-template.tsx +79 -0
  153. package/template/src/components/meet-cancellation-email-template.tsx +120 -0
  154. package/template/src/components/meet-confirmation-email-template.tsx +156 -0
  155. package/template/src/components/meet-update-email-template.tsx +209 -0
  156. package/template/src/components/page-header.tsx +61 -0
  157. package/template/src/components/reset-password-email-template.tsx +79 -0
  158. package/template/src/components/sidebar.tsx +294 -0
  159. package/template/src/components/skeleton.tsx +380 -0
  160. package/template/src/components/ui/commands.tsx +396 -0
  161. package/template/src/components/ui/components.tsx +150 -0
  162. package/template/src/components/ui/theme.tsx +5 -0
  163. package/template/src/components/view-as-banner.tsx +45 -0
  164. package/template/src/components/view-as-modal.tsx +186 -0
  165. package/template/src/contexts/mobile-menu-context.tsx +31 -0
  166. package/template/src/contexts/sidebar-context.tsx +107 -0
  167. package/template/src/contexts/task-reminder-context.tsx +239 -0
  168. package/template/src/contexts/view-as-context.tsx +84 -0
  169. package/template/src/hooks/use-user-role.ts +82 -0
  170. package/template/src/lib/audit-log.ts +45 -0
  171. package/template/src/lib/auth-client.ts +16 -0
  172. package/template/src/lib/auth.ts +35 -0
  173. package/template/src/lib/check-permission.ts +193 -0
  174. package/template/src/lib/contact-duplicate.ts +112 -0
  175. package/template/src/lib/contact-interactions.ts +371 -0
  176. package/template/src/lib/encryption.ts +99 -0
  177. package/template/src/lib/google-calendar.ts +300 -0
  178. package/template/src/lib/google-drive.ts +372 -0
  179. package/template/src/lib/permissions.ts +412 -0
  180. package/template/src/lib/prisma.ts +32 -0
  181. package/template/src/lib/roles.ts +120 -0
  182. package/template/src/lib/template-variables.ts +76 -0
  183. package/template/src/lib/utils.ts +46 -0
  184. package/template/src/lib/workflow-executor.ts +482 -0
  185. package/template/src/proxy.ts +91 -0
  186. package/template/tsconfig.json +34 -0
  187. package/template/vercel.json +8 -0
@@ -0,0 +1,31 @@
1
+ import type { Metadata } from 'next';
2
+ import { Geist, Geist_Mono } from 'next/font/google';
3
+ import './globals.css';
4
+ import { cn } from '@/lib/utils';
5
+
6
+ const geistSans = Geist({
7
+ variable: '--font-geist-sans',
8
+ subsets: ['latin'],
9
+ });
10
+
11
+ const geistMono = Geist_Mono({
12
+ variable: '--font-geist-mono',
13
+ subsets: ['latin'],
14
+ });
15
+
16
+ export const metadata: Metadata = {
17
+ title: 'Create Next App',
18
+ description: 'Generated by create next app',
19
+ };
20
+
21
+ export default function RootLayout({
22
+ children,
23
+ }: Readonly<{
24
+ children: React.ReactNode;
25
+ }>) {
26
+ return (
27
+ <html lang="fr">
28
+ <body className={cn(geistSans.variable, geistMono.variable, 'antialiased')}>{children}</body>
29
+ </html>
30
+ );
31
+ }
@@ -0,0 +1,32 @@
1
+ 'use client';
2
+
3
+ import { useSession } from '@/lib/auth-client';
4
+ import { useRouter } from 'next/navigation';
5
+ import { useEffect } from 'react';
6
+
7
+ export default function HomePage() {
8
+ const { data: session, isPending } = useSession();
9
+ const router = useRouter();
10
+
11
+ useEffect(() => {
12
+ if (isPending) return;
13
+
14
+ if (session) {
15
+ // Utilisateur connecté -> rediriger vers le dashboard
16
+ router.push('/dashboard');
17
+ } else {
18
+ // Utilisateur non connecté -> rediriger vers la page de connexion
19
+ router.push('/signin');
20
+ }
21
+ }, [session, isPending, router]);
22
+
23
+ // Afficher un loader pendant la redirection
24
+ return (
25
+ <div className="flex min-h-screen items-center justify-center bg-gray-50">
26
+ <div className="flex flex-col items-center gap-4">
27
+ <div className="h-12 w-12 animate-spin rounded-full border-4 border-gray-200 border-t-indigo-600"></div>
28
+ <p className="text-sm text-gray-600">Redirection...</p>
29
+ </div>
30
+ </div>
31
+ );
32
+ }
@@ -0,0 +1,67 @@
1
+ 'use client';
2
+
3
+ import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis, Legend } from 'recharts';
4
+
5
+ interface ActivityChartProps {
6
+ data: Array<{ date: string; interactions: number; tasks: number }>;
7
+ }
8
+
9
+ export function ActivityChart({ data }: ActivityChartProps) {
10
+ return (
11
+ <div className="rounded-xl border border-gray-200/50 bg-white p-6 shadow-lg transition-shadow duration-300 hover:shadow-xl">
12
+ <div className="mb-4">
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>
15
+ </div>
16
+ <div className="h-[300px]">
17
+ <ResponsiveContainer width="100%" height="100%">
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>
29
+ <XAxis
30
+ dataKey="date"
31
+ stroke="#9ca3af"
32
+ fontSize={12}
33
+ tickLine={false}
34
+ axisLine={false}
35
+ />
36
+ <YAxis stroke="#9ca3af" fontSize={12} tickLine={false} axisLine={false} />
37
+ <Tooltip
38
+ contentStyle={{
39
+ backgroundColor: 'white',
40
+ border: '1px solid #e5e7eb',
41
+ borderRadius: '12px',
42
+ boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
43
+ }}
44
+ labelStyle={{ color: '#374151', fontWeight: 600 }}
45
+ />
46
+ <Legend
47
+ wrapperStyle={{ paddingTop: '20px' }}
48
+ iconType="circle"
49
+ />
50
+ <Bar
51
+ dataKey="interactions"
52
+ fill="url(#interactionsGradient)"
53
+ radius={[8, 8, 0, 0]}
54
+ name="Interactions"
55
+ />
56
+ <Bar
57
+ dataKey="tasks"
58
+ fill="url(#tasksGradient)"
59
+ radius={[8, 8, 0, 0]}
60
+ name="Tâches"
61
+ />
62
+ </BarChart>
63
+ </ResponsiveContainer>
64
+ </div>
65
+ </div>
66
+ );
67
+ }
@@ -0,0 +1,63 @@
1
+ 'use client';
2
+
3
+ import { Area, AreaChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
4
+
5
+ interface ContactsChartProps {
6
+ data: Array<{ month: string; count: number }>;
7
+ }
8
+
9
+ export function ContactsChart({ data }: ContactsChartProps) {
10
+ return (
11
+ <div className="rounded-xl border border-gray-200/50 bg-white p-6 shadow-lg transition-shadow duration-300 hover:shadow-xl">
12
+ <div className="mb-4 flex items-center justify-between">
13
+ <div>
14
+ <h3 className="text-lg font-bold text-gray-900">Évolution des Contacts</h3>
15
+ <p className="mt-1 text-sm text-gray-500">Nombre de contacts créés par mois</p>
16
+ </div>
17
+ </div>
18
+ <div className="h-[300px]">
19
+ <ResponsiveContainer width="100%" height="100%">
20
+ <AreaChart data={data}>
21
+ <defs>
22
+ <linearGradient id="colorCount" x1="0" y1="0" x2="0" y2="1">
23
+ <stop offset="5%" stopColor="#8b5cf6" stopOpacity={0.4} />
24
+ <stop offset="50%" stopColor="#6366f1" stopOpacity={0.3} />
25
+ <stop offset="95%" stopColor="#6366f1" stopOpacity={0} />
26
+ </linearGradient>
27
+ </defs>
28
+ <XAxis
29
+ dataKey="month"
30
+ stroke="#9ca3af"
31
+ fontSize={12}
32
+ tickLine={false}
33
+ axisLine={false}
34
+ />
35
+ <YAxis
36
+ stroke="#9ca3af"
37
+ fontSize={12}
38
+ tickLine={false}
39
+ axisLine={false}
40
+ tickFormatter={(value) => `${value}`}
41
+ />
42
+ <Tooltip
43
+ contentStyle={{
44
+ backgroundColor: 'white',
45
+ border: '1px solid #e5e7eb',
46
+ borderRadius: '12px',
47
+ boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
48
+ }}
49
+ labelStyle={{ color: '#374151', fontWeight: 600 }}
50
+ />
51
+ <Area
52
+ type="monotone"
53
+ dataKey="count"
54
+ stroke="#8b5cf6"
55
+ strokeWidth={3}
56
+ fill="url(#colorCount)"
57
+ />
58
+ </AreaChart>
59
+ </ResponsiveContainer>
60
+ </div>
61
+ </div>
62
+ );
63
+ }
@@ -0,0 +1,164 @@
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
+ interactions: Interaction[];
33
+ }
34
+
35
+ const interactionIcons = {
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 = {
50
+ CALL: 'bg-blue-100 text-blue-600',
51
+ SMS: 'bg-green-100 text-green-600',
52
+ EMAIL: 'bg-purple-100 text-purple-600',
53
+ MEETING: 'bg-indigo-100 text-indigo-600',
54
+ NOTE: 'bg-gray-100 text-gray-600',
55
+ STATUS_CHANGE: 'bg-amber-100 text-amber-600',
56
+ CONTACT_UPDATE: 'bg-cyan-100 text-cyan-600',
57
+ APPOINTMENT_CREATED: 'bg-emerald-100 text-emerald-600',
58
+ APPOINTMENT_DELETED: 'bg-red-100 text-red-600',
59
+ APPOINTMENT_CHANGED: 'bg-orange-100 text-orange-600',
60
+ ASSIGNMENT_CHANGE: 'bg-pink-100 text-pink-600',
61
+ };
62
+
63
+ const interactionLabels = {
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
+ if (interactions.length === 0) {
79
+ return (
80
+ <div className="rounded-lg border border-gray-200 bg-white p-6 shadow-sm">
81
+ <div className="flex items-center justify-between">
82
+ <h3 className="text-lg font-semibold text-gray-900">Activité Récente</h3>
83
+ </div>
84
+ <div className="mt-6 text-center text-sm text-gray-500">
85
+ <FileText className="mx-auto h-12 w-12 text-gray-400" />
86
+ <p className="mt-2">Aucune activité récente</p>
87
+ </div>
88
+ </div>
89
+ );
90
+ }
91
+
92
+ return (
93
+ <div className="rounded-xl border border-gray-200/50 bg-white p-6 shadow-lg transition-shadow duration-300 hover:shadow-xl">
94
+ <div className="mb-4 flex items-center justify-between">
95
+ <h3 className="text-lg font-bold text-gray-900">Activité Récente</h3>
96
+ <Link
97
+ href="/contacts"
98
+ className="text-sm font-semibold text-indigo-600 transition-colors hover:text-indigo-700"
99
+ >
100
+ Voir tout →
101
+ </Link>
102
+ </div>
103
+ <div className="space-y-3">
104
+ {interactions.map((interaction) => {
105
+ const Icon =
106
+ interactionIcons[interaction.type as keyof typeof interactionIcons] || FileText;
107
+ const color =
108
+ interactionColors[interaction.type as keyof typeof interactionColors] ||
109
+ 'bg-gray-100 text-gray-600';
110
+ const label =
111
+ interactionLabels[interaction.type as keyof typeof interactionLabels] ||
112
+ interaction.type;
113
+ const date = new Date(interaction.date);
114
+ const now = new Date();
115
+ const diffMs = now.getTime() - date.getTime();
116
+ const diffHours = Math.floor(diffMs / (1000 * 60 * 60));
117
+ const diffMinutes = Math.floor(diffMs / (1000 * 60));
118
+
119
+ let timeAgo = '';
120
+ if (diffMinutes < 1) {
121
+ timeAgo = "À l'instant";
122
+ } else if (diffMinutes < 60) {
123
+ timeAgo = `Il y a ${diffMinutes} min`;
124
+ } else if (diffHours < 24) {
125
+ timeAgo = `Il y a ${diffHours}h`;
126
+ } else {
127
+ timeAgo = date.toLocaleDateString('fr-FR', {
128
+ day: 'numeric',
129
+ month: 'short',
130
+ });
131
+ }
132
+
133
+ return (
134
+ <div
135
+ key={interaction.id}
136
+ className="group flex items-start gap-3 rounded-lg border-l-4 border-transparent bg-gray-50/50 p-3 transition-all duration-200 hover:border-indigo-400 hover:bg-indigo-50/30"
137
+ >
138
+ <div className={cn('rounded-lg p-2 shadow-sm', color)}>
139
+ <Icon className="h-4 w-4" />
140
+ </div>
141
+ <div className="flex-1">
142
+ <div className="flex items-start justify-between">
143
+ <div>
144
+ <p className="text-sm font-semibold text-gray-900">{label}</p>
145
+ <Link
146
+ href={`/contacts/${interaction.contact.id}`}
147
+ className="text-sm text-gray-600 transition-colors hover:text-indigo-600"
148
+ >
149
+ {interaction.contact.name}
150
+ </Link>
151
+ {interaction.title && (
152
+ <p className="mt-1 text-xs text-gray-500">{interaction.title}</p>
153
+ )}
154
+ </div>
155
+ <span className="text-xs font-medium text-gray-500">{timeAgo}</span>
156
+ </div>
157
+ </div>
158
+ </div>
159
+ );
160
+ })}
161
+ </div>
162
+ </div>
163
+ );
164
+ }
@@ -0,0 +1,81 @@
1
+ 'use client';
2
+
3
+ import { Bar, BarChart, ResponsiveContainer, Tooltip, XAxis, YAxis } from 'recharts';
4
+
5
+ interface SalesAnalyticsChartProps {
6
+ data: Array<{ month: string; count: number }>;
7
+ }
8
+
9
+ export function SalesAnalyticsChart({ data }: SalesAnalyticsChartProps) {
10
+ // Calculer le total et la croissance
11
+ const total = data.reduce((sum, item) => sum + item.count, 0);
12
+ const previousTotal = data.length > 1 ? data.slice(0, -1).reduce((sum, item) => sum + item.count, 0) : 0;
13
+ const growth = previousTotal > 0 ? ((total - previousTotal) / previousTotal) * 100 : 0;
14
+
15
+ return (
16
+ <div className="rounded-xl border border-gray-200/50 bg-white p-6 shadow-lg transition-shadow duration-300 hover:shadow-xl">
17
+ <div className="mb-4 flex items-center justify-between">
18
+ <div>
19
+ <h3 className="text-lg font-bold text-gray-900">Analytiques des Ventes</h3>
20
+ </div>
21
+ <select className="rounded-lg border border-gray-300 bg-white px-3 py-1.5 text-sm font-medium text-gray-700 focus:border-indigo-500 focus:outline-none focus:ring-2 focus:ring-indigo-500">
22
+ <option>Mensuel</option>
23
+ <option>Hebdomadaire</option>
24
+ <option>Annuel</option>
25
+ </select>
26
+ </div>
27
+ <div className="mb-4 flex items-baseline gap-4">
28
+ <div>
29
+ <p className="text-2xl font-bold text-gray-900">
30
+ {total.toLocaleString('fr-FR')} contacts ce mois
31
+ </p>
32
+ <p className="mt-1 text-sm font-semibold text-emerald-600">
33
+ Augmentation ce mois : +{growth.toFixed(1)}%
34
+ </p>
35
+ </div>
36
+ </div>
37
+ <div className="h-[280px]">
38
+ <ResponsiveContainer width="100%" height="100%">
39
+ <BarChart data={data} barCategoryGap="20%">
40
+ <defs>
41
+ <linearGradient id="barGradient" x1="0" y1="0" x2="0" y2="1">
42
+ <stop offset="0%" stopColor="#8b5cf6" stopOpacity={1} />
43
+ <stop offset="100%" stopColor="#6366f1" stopOpacity={0.8} />
44
+ </linearGradient>
45
+ </defs>
46
+ <XAxis
47
+ dataKey="month"
48
+ stroke="#9ca3af"
49
+ fontSize={12}
50
+ tickLine={false}
51
+ axisLine={false}
52
+ />
53
+ <YAxis
54
+ stroke="#9ca3af"
55
+ fontSize={12}
56
+ tickLine={false}
57
+ axisLine={false}
58
+ tickFormatter={(value) => `${value}`}
59
+ />
60
+ <Tooltip
61
+ contentStyle={{
62
+ backgroundColor: 'white',
63
+ border: '1px solid #e5e7eb',
64
+ borderRadius: '12px',
65
+ boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
66
+ }}
67
+ labelStyle={{ color: '#374151', fontWeight: 600 }}
68
+ formatter={(value: number) => [`${value.toLocaleString('fr-FR')}`, 'Contacts']}
69
+ />
70
+ <Bar
71
+ dataKey="count"
72
+ fill="url(#barGradient)"
73
+ radius={[8, 8, 0, 0]}
74
+ />
75
+ </BarChart>
76
+ </ResponsiveContainer>
77
+ </div>
78
+ </div>
79
+ );
80
+ }
81
+
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+
3
+ import { LucideIcon } from 'lucide-react';
4
+ import { cn } from '@/lib/utils';
5
+
6
+ interface StatCardProps {
7
+ title: string;
8
+ value: string | number;
9
+ icon: LucideIcon;
10
+ trend?: {
11
+ value: number;
12
+ label: string;
13
+ };
14
+ iconColor?: string;
15
+ iconBgColor?: string;
16
+ }
17
+
18
+ export function StatCard({
19
+ title,
20
+ value,
21
+ icon: Icon,
22
+ trend,
23
+ iconColor = 'text-indigo-600',
24
+ iconBgColor = 'bg-indigo-100',
25
+ }: StatCardProps) {
26
+ return (
27
+ <div className="group relative overflow-hidden rounded-xl border border-gray-200/50 bg-white p-6 shadow-lg transition-all duration-300 hover:shadow-xl hover:-translate-y-1">
28
+ <div className="absolute inset-0 bg-gradient-to-br from-white via-purple-50/30 to-indigo-50/30 opacity-0 transition-opacity duration-300 group-hover:opacity-100" />
29
+ <div className="relative flex items-center justify-between">
30
+ <div className="flex-1">
31
+ <p className="text-sm font-medium text-gray-600">{title}</p>
32
+ <p className="mt-2 bg-gradient-to-r from-gray-900 to-gray-700 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-gray-500">{trend.label}</span>
47
+ </p>
48
+ )}
49
+ </div>
50
+ <div
51
+ className={cn(
52
+ 'rounded-xl bg-gradient-to-br p-4 shadow-md transition-transform duration-300 group-hover:scale-110 group-hover:shadow-lg',
53
+ iconBgColor,
54
+ )}
55
+ >
56
+ <Icon className={cn('h-6 w-6', iconColor)} />
57
+ </div>
58
+ </div>
59
+ </div>
60
+ );
61
+ }
@@ -0,0 +1,45 @@
1
+ 'use client';
2
+
3
+ import { PolarAngleAxis, PolarGrid, Radar, RadarChart, ResponsiveContainer } from 'recharts';
4
+
5
+ interface StatusDistributionChartProps {
6
+ data: Array<{ name: string; value: number }>;
7
+ }
8
+
9
+ export function StatusDistributionChart({ data }: StatusDistributionChartProps) {
10
+ return (
11
+ <div className="rounded-xl border border-gray-200/50 bg-white p-6 shadow-lg transition-shadow duration-300 hover:shadow-xl">
12
+ <div className="mb-4">
13
+ <h3 className="text-lg font-bold text-gray-900">Répartition par Statut</h3>
14
+ <p className="mt-1 text-sm text-gray-500">Distribution des contacts selon leur statut</p>
15
+ </div>
16
+ <div className="h-[300px]">
17
+ <ResponsiveContainer width="100%" height="100%">
18
+ <RadarChart data={data}>
19
+ <defs>
20
+ <linearGradient id="radarGradient" x1="0" y1="0" x2="0" y2="1">
21
+ <stop offset="0%" stopColor="#8b5cf6" stopOpacity={0.8} />
22
+ <stop offset="100%" stopColor="#6366f1" stopOpacity={0.4} />
23
+ </linearGradient>
24
+ </defs>
25
+ <PolarGrid stroke="#e5e7eb" />
26
+ <PolarAngleAxis
27
+ dataKey="name"
28
+ stroke="#6b7280"
29
+ fontSize={12}
30
+ tick={{ fill: '#6b7280' }}
31
+ />
32
+ <Radar
33
+ name="Contacts"
34
+ dataKey="value"
35
+ stroke="#8b5cf6"
36
+ fill="url(#radarGradient)"
37
+ fillOpacity={0.6}
38
+ strokeWidth={2}
39
+ />
40
+ </RadarChart>
41
+ </ResponsiveContainer>
42
+ </div>
43
+ </div>
44
+ );
45
+ }
@@ -0,0 +1,88 @@
1
+ 'use client';
2
+
3
+ import { Cell, Pie, PieChart, ResponsiveContainer, Tooltip } from 'recharts';
4
+
5
+ interface TasksPieChartProps {
6
+ completed: number;
7
+ pending: number;
8
+ }
9
+
10
+ const COLORS = {
11
+ completed: '#10b981',
12
+ pending: '#f59e0b',
13
+ };
14
+
15
+ export function TasksPieChart({ completed, pending }: TasksPieChartProps) {
16
+ const data = [
17
+ { name: 'Complétées', value: completed },
18
+ { name: 'En attente', value: pending },
19
+ ];
20
+
21
+ const total = completed + pending;
22
+ const completionRate = total > 0 ? Math.round((completed / total) * 100) : 0;
23
+
24
+ return (
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>
29
+ </div>
30
+ <div className="flex items-center justify-center">
31
+ <div className="relative h-[200px] w-[200px]">
32
+ <ResponsiveContainer width="100%" height="100%">
33
+ <PieChart>
34
+ <Pie
35
+ data={data}
36
+ cx="50%"
37
+ cy="50%"
38
+ innerRadius={60}
39
+ outerRadius={80}
40
+ paddingAngle={2}
41
+ dataKey="value"
42
+ >
43
+ {data.map((entry, index) => (
44
+ <Cell
45
+ key={`cell-${index}`}
46
+ fill={entry.name === 'Complétées' ? COLORS.completed : COLORS.pending}
47
+ />
48
+ ))}
49
+ </Pie>
50
+ <Tooltip
51
+ contentStyle={{
52
+ backgroundColor: 'white',
53
+ border: '1px solid #e5e7eb',
54
+ borderRadius: '12px',
55
+ boxShadow: '0 4px 6px -1px rgba(0, 0, 0, 0.1)',
56
+ }}
57
+ />
58
+ </PieChart>
59
+ </ResponsiveContainer>
60
+ <div className="absolute inset-0 flex items-center justify-center">
61
+ <div className="text-center">
62
+ <p className="text-3xl font-bold bg-gradient-to-r from-gray-900 to-gray-700 bg-clip-text text-transparent">
63
+ {completionRate}%
64
+ </p>
65
+ <p className="text-xs text-gray-500">Complétées</p>
66
+ </div>
67
+ </div>
68
+ </div>
69
+ </div>
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" />
73
+ <div>
74
+ <p className="text-xs text-gray-500">Complétées</p>
75
+ <p className="font-semibold text-gray-900">{completed}</p>
76
+ </div>
77
+ </div>
78
+ <div className="flex items-center gap-2 rounded-lg bg-amber-50 p-2">
79
+ <div className="h-3 w-3 rounded-full bg-amber-500" />
80
+ <div>
81
+ <p className="text-xs text-gray-500">En attente</p>
82
+ <p className="font-semibold text-gray-900">{pending}</p>
83
+ </div>
84
+ </div>
85
+ </div>
86
+ </div>
87
+ );
88
+ }