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,6 +1,7 @@
1
1
  import { NextRequest, NextResponse } from 'next/server';
2
2
  import { auth } from '@/lib/auth';
3
3
  import { prisma } from '@/lib/prisma';
4
+ import { checkPermission } from '@/lib/check-permission';
4
5
 
5
6
  // GET /api/workflows - Récupérer tous les workflows de l'utilisateur
6
7
  export async function GET(request: NextRequest) {
@@ -13,6 +14,11 @@ export async function GET(request: NextRequest) {
13
14
  return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
14
15
  }
15
16
 
17
+ const canView = await checkPermission('workflows.view');
18
+ if (!canView) {
19
+ return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
20
+ }
21
+
16
22
  const workflows = await prisma.workflow.findMany({
17
23
  where: {
18
24
  userId: session.user.id,
@@ -37,7 +43,15 @@ export async function GET(request: NextRequest) {
37
43
  return NextResponse.json(workflows);
38
44
  } catch (error: any) {
39
45
  console.error('Erreur lors de la récupération des workflows:', error);
40
- return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
46
+ return NextResponse.json(
47
+ {
48
+ error:
49
+ process.env.NODE_ENV === 'development'
50
+ ? error.message || 'Erreur serveur'
51
+ : 'Erreur serveur',
52
+ },
53
+ { status: 500 },
54
+ );
41
55
  }
42
56
  }
43
57
 
@@ -52,6 +66,11 @@ export async function POST(request: NextRequest) {
52
66
  return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
53
67
  }
54
68
 
69
+ const canCreate = await checkPermission('workflows.create');
70
+ if (!canCreate) {
71
+ return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
72
+ }
73
+
55
74
  const body = await request.json();
56
75
  const {
57
76
  name,
@@ -62,10 +81,13 @@ export async function POST(request: NextRequest) {
62
81
  triggerToStatusId,
63
82
  triggerTimeDays,
64
83
  triggerTimeHours,
84
+ triggerTimeReference,
85
+ triggerTaskType,
86
+ triggerTransactionFromStatus,
87
+ triggerTransactionToStatus,
65
88
  actions = [],
66
89
  } = body;
67
90
 
68
- // Validation
69
91
  if (!name || !triggerType) {
70
92
  return NextResponse.json(
71
93
  { error: 'Le nom et le type de déclencheur sont requis' },
@@ -73,7 +95,6 @@ export async function POST(request: NextRequest) {
73
95
  );
74
96
  }
75
97
 
76
- // Créer le workflow
77
98
  const workflow = await prisma.workflow.create({
78
99
  data: {
79
100
  name,
@@ -85,6 +106,10 @@ export async function POST(request: NextRequest) {
85
106
  triggerToStatusId: triggerToStatusId || null,
86
107
  triggerTimeDays: triggerTimeDays || null,
87
108
  triggerTimeHours: triggerTimeHours || null,
109
+ triggerTimeReference: triggerTimeReference || null,
110
+ triggerTaskType: triggerTaskType || null,
111
+ triggerTransactionFromStatus: triggerTransactionFromStatus || null,
112
+ triggerTransactionToStatus: triggerTransactionToStatus || null,
88
113
  actions: {
89
114
  create: actions.map((action: any, index: number) => ({
90
115
  actionType: action.actionType,
@@ -96,8 +121,17 @@ export async function POST(request: NextRequest) {
96
121
  newStatusId: action.newStatusId || null,
97
122
  taskTitle: action.taskTitle || null,
98
123
  taskDescription: action.taskDescription || null,
124
+ taskType: action.taskType || null,
125
+ taskPriority: action.taskPriority || null,
126
+ taskAssignedUserId: action.taskAssignedUserId || null,
127
+ assignCommercialId: action.assignCommercialId || null,
128
+ assignTeleproId: action.assignTeleproId || null,
129
+ noteContent: action.noteContent || null,
130
+ notifyUserId: action.notifyUserId || null,
99
131
  conditionOperator: action.conditionOperator || null,
100
132
  conditionStatusId: action.conditionStatusId || null,
133
+ conditionOrigin: action.conditionOrigin || null,
134
+ conditionHasCompany: action.conditionHasCompany ?? null,
101
135
  })),
102
136
  },
103
137
  },
@@ -118,6 +152,14 @@ export async function POST(request: NextRequest) {
118
152
  return NextResponse.json(workflow, { status: 201 });
119
153
  } catch (error: any) {
120
154
  console.error('Erreur lors de la création du workflow:', error);
121
- return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
155
+ return NextResponse.json(
156
+ {
157
+ error:
158
+ process.env.NODE_ENV === 'development'
159
+ ? error.message || 'Erreur serveur'
160
+ : 'Erreur serveur',
161
+ },
162
+ { status: 500 },
163
+ );
122
164
  }
123
165
  }
@@ -1,22 +1,61 @@
1
1
  @import 'tailwindcss';
2
- @import 'react-grid-layout/css/styles.css';
3
2
 
4
- :root {
5
- --background: #ffffff;
6
- --foreground: #171717;
7
- }
3
+ @custom-variant dark (&:is(.dark *));
8
4
 
9
5
  @theme inline {
10
6
  --color-background: var(--background);
11
7
  --color-foreground: var(--foreground);
12
- --font-sans: var(--font-geist-sans);
13
- --font-mono: var(--font-geist-mono);
14
- }
15
-
16
- body {
17
- background: var(--background);
18
- color: var(--foreground);
19
- font-family: Arial, Helvetica, sans-serif;
8
+ --color-surface-page: var(--surface-page);
9
+ --color-brand: var(--brand);
10
+ --color-brand-foreground: var(--brand-foreground);
11
+ --font-sans: var(--font-body);
12
+ --font-mono: var(--font-mono);
13
+ --color-sidebar-ring: var(--sidebar-ring);
14
+ --color-sidebar-border: var(--sidebar-border);
15
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
16
+ --color-sidebar-accent: var(--sidebar-accent);
17
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
18
+ --color-sidebar-primary: var(--sidebar-primary);
19
+ --color-sidebar-foreground: var(--sidebar-foreground);
20
+ --color-sidebar: var(--sidebar);
21
+ --color-chart-5: var(--chart-5);
22
+ --color-chart-4: var(--chart-4);
23
+ --color-chart-3: var(--chart-3);
24
+ --color-chart-2: var(--chart-2);
25
+ --color-chart-1: var(--chart-1);
26
+ --color-ring: var(--ring);
27
+ --color-input: var(--input);
28
+ --color-border: var(--border);
29
+ --color-destructive: var(--destructive);
30
+ --color-accent-foreground: var(--accent-foreground);
31
+ --color-accent: var(--accent);
32
+ --color-muted-foreground: var(--muted-foreground);
33
+ --color-muted: var(--muted);
34
+ --color-secondary-foreground: var(--secondary-foreground);
35
+ --color-secondary: var(--secondary);
36
+ --color-primary-foreground: var(--primary-foreground);
37
+ --color-primary: var(--primary);
38
+ --color-popover-foreground: var(--popover-foreground);
39
+ --color-popover: var(--popover);
40
+ --color-card-foreground: var(--card-foreground);
41
+ --color-card: var(--card);
42
+ --radius-sm: calc(var(--radius) - 4px);
43
+ --radius-md: calc(var(--radius) - 2px);
44
+ --radius-lg: var(--radius);
45
+ --radius-xl: calc(var(--radius) + 4px);
46
+ --radius-2xl: calc(var(--radius) + 8px);
47
+ --radius-3xl: calc(var(--radius) + 12px);
48
+ --radius-4xl: calc(var(--radius) + 16px);
49
+ }
50
+
51
+ h1,
52
+ h2,
53
+ h3,
54
+ h4,
55
+ h5,
56
+ h6 {
57
+ font-family: var(--font-display), serif;
58
+ letter-spacing: -0.02em;
20
59
  }
21
60
 
22
61
  /* LexKit Editor - Clean Framework-Agnostic Styles */
@@ -968,6 +1007,36 @@ body {
968
1007
  }
969
1008
  }
970
1009
 
1010
+ @keyframes toast-slide-in {
1011
+ from {
1012
+ opacity: 0;
1013
+ transform: translateX(100%);
1014
+ }
1015
+ to {
1016
+ opacity: 1;
1017
+ transform: translateX(0);
1018
+ }
1019
+ }
1020
+
1021
+ @keyframes toast-slide-out {
1022
+ from {
1023
+ opacity: 1;
1024
+ transform: translateX(0);
1025
+ }
1026
+ to {
1027
+ opacity: 0;
1028
+ transform: translateX(100%);
1029
+ }
1030
+ }
1031
+
1032
+ .animate-toast-in {
1033
+ animation: toast-slide-in 0.35s cubic-bezier(0.21, 1.02, 0.73, 1) forwards;
1034
+ }
1035
+
1036
+ .animate-toast-out {
1037
+ animation: toast-slide-out 0.3s cubic-bezier(0.06, 0.71, 0.55, 1) forwards;
1038
+ }
1039
+
971
1040
  @keyframes subtle-area-glow {
972
1041
  0%,
973
1042
  100% {
@@ -1416,97 +1485,137 @@ table td[data-lexical-table-cell-selection] {
1416
1485
  }
1417
1486
  }
1418
1487
 
1419
- /* ===== DASHBOARD THEME UTILITIES ===== */
1420
- /* Classes utilitaires qui utilisent les CSS variables --dash-50 à --dash-700 */
1421
-
1422
- .dash-link { color: var(--dash-600); transition: color 150ms; }
1423
- .dash-link:hover { color: var(--dash-700); }
1424
-
1425
- .dash-btn { background-color: var(--dash-500); color: white; transition: background-color 150ms; }
1426
- .dash-btn:hover { background-color: var(--dash-600); }
1427
-
1428
- .dash-accent-bar { background-color: var(--dash-500); }
1429
-
1430
- .dash-icon-box { background-color: var(--dash-100); transition: background-color 150ms; }
1431
- .group:hover .dash-icon-box-gh { background-color: var(--dash-200); }
1432
-
1433
- .dash-icon-color { color: var(--dash-600); }
1434
-
1435
- .dash-avatar { background-color: var(--dash-100); color: var(--dash-700); }
1436
-
1437
- .dash-pill-active { background-color: var(--dash-500); color: white; }
1438
-
1439
- .dash-hover-bg:hover { background-color: color-mix(in srgb, var(--dash-50) 30%, transparent); }
1440
- .dash-hover-border:hover { border-color: var(--dash-200); }
1441
- .dash-hover-border-left:hover { border-left-color: var(--dash-400); background-color: color-mix(in srgb, var(--dash-50) 30%, transparent); }
1442
- .dash-hover-border-light:hover { border-color: var(--dash-100); }
1443
- .dash-hover-text:hover { color: var(--dash-600); }
1444
- .group:hover .dash-hover-text-gh { color: var(--dash-500); }
1445
-
1446
- .dash-legend-bg { background-color: color-mix(in srgb, var(--dash-50) 80%, transparent); }
1447
- .dash-legend-dot { background-color: var(--dash-500); }
1448
-
1449
- /* ===== REACT GRID LAYOUT OVERRIDES ===== */
1450
-
1451
- .react-grid-layout {
1452
- position: relative;
1453
- transition: height 200ms ease;
1454
- }
1455
-
1456
- .react-grid-item {
1457
- transition: all 200ms ease;
1458
- transition-property: left, top, width, height;
1459
- }
1460
-
1461
- .react-grid-item.cssTransforms {
1462
- transition-property: transform, width, height;
1463
- }
1464
-
1465
- .react-grid-item.resizing {
1466
- z-index: 1;
1467
- will-change: width, height;
1468
- opacity: 0.9;
1469
- }
1470
-
1471
- .react-grid-item.react-draggable-dragging {
1472
- transition: none;
1473
- z-index: 3;
1474
- will-change: transform;
1475
- opacity: 0.9;
1476
- box-shadow: 0 20px 40px rgba(0, 0, 0, 0.12);
1477
- border-radius: 16px;
1488
+ :root {
1489
+ --radius: 0.625rem;
1490
+ --duration-fast: 150ms;
1491
+ --duration-normal: 250ms;
1492
+ --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
1493
+ --shadow-card: 0 10px 30px -18px rgb(15 23 42 / 0.22);
1494
+ --shadow-dropdown: 0 18px 45px -24px rgb(15 23 42 / 0.3);
1495
+ --background: oklch(1 0 0);
1496
+ --surface-page: oklch(0.98 0.004 67);
1497
+ --foreground: oklch(0.145 0 0);
1498
+ --card: oklch(1 0 0);
1499
+ --card-foreground: oklch(0.145 0 0);
1500
+ --popover: oklch(1 0 0);
1501
+ --popover-foreground: oklch(0.145 0 0);
1502
+ --primary: oklch(0.62 0.17 259);
1503
+ --primary-foreground: oklch(0.98 0.01 255);
1504
+ --brand: oklch(0.62 0.17 259);
1505
+ --brand-foreground: oklch(0.98 0.01 255);
1506
+ --secondary: oklch(0.97 0 0);
1507
+ --secondary-foreground: oklch(0.205 0 0);
1508
+ --muted: oklch(0.97 0 0);
1509
+ --muted-foreground: oklch(0.556 0 0);
1510
+ --accent: oklch(0.97 0 0);
1511
+ --accent-foreground: oklch(0.205 0 0);
1512
+ --destructive: oklch(0.577 0.245 27.325);
1513
+ --border: oklch(0.922 0 0);
1514
+ --input: oklch(0.922 0 0);
1515
+ --ring: oklch(0.708 0 0);
1516
+ --chart-1: oklch(0.646 0.222 41.116);
1517
+ --chart-2: oklch(0.6 0.118 184.704);
1518
+ --chart-3: oklch(0.398 0.07 227.392);
1519
+ --chart-4: oklch(0.828 0.189 84.429);
1520
+ --chart-5: oklch(0.769 0.188 70.08);
1521
+ --sidebar: oklch(0.985 0 0);
1522
+ --sidebar-foreground: oklch(0.145 0 0);
1523
+ --sidebar-primary: oklch(0.62 0.17 259);
1524
+ --sidebar-primary-foreground: oklch(0.98 0.01 255);
1525
+ --sidebar-accent: oklch(0.97 0.02 255);
1526
+ --sidebar-accent-foreground: oklch(0.38 0.08 259);
1527
+ --sidebar-border: oklch(0.922 0 0);
1528
+ --sidebar-ring: oklch(0.708 0 0);
1529
+ }
1530
+
1531
+ .dark {
1532
+ --shadow-card: 0 16px 34px -22px rgb(2 6 23 / 0.9);
1533
+ --shadow-dropdown: 0 22px 40px -20px rgb(2 6 23 / 0.95);
1534
+ --background: oklch(0.145 0 0);
1535
+ --surface-page: oklch(0.19 0 0);
1536
+ --foreground: oklch(0.985 0 0);
1537
+ --card: oklch(0.205 0 0);
1538
+ --card-foreground: oklch(0.985 0 0);
1539
+ --popover: oklch(0.205 0 0);
1540
+ --popover-foreground: oklch(0.985 0 0);
1541
+ --primary: oklch(0.72 0.15 259);
1542
+ --primary-foreground: oklch(0.16 0.04 259);
1543
+ --brand: oklch(0.72 0.15 259);
1544
+ --brand-foreground: oklch(0.16 0.04 259);
1545
+ --secondary: oklch(0.269 0 0);
1546
+ --secondary-foreground: oklch(0.985 0 0);
1547
+ --muted: oklch(0.269 0 0);
1548
+ --muted-foreground: oklch(0.708 0 0);
1549
+ --accent: oklch(0.269 0 0);
1550
+ --accent-foreground: oklch(0.985 0 0);
1551
+ --destructive: oklch(0.704 0.191 22.216);
1552
+ --border: oklch(1 0 0 / 10%);
1553
+ --input: oklch(1 0 0 / 15%);
1554
+ --ring: oklch(0.556 0 0);
1555
+ --chart-1: oklch(0.488 0.243 264.376);
1556
+ --chart-2: oklch(0.696 0.17 162.48);
1557
+ --chart-3: oklch(0.769 0.188 70.08);
1558
+ --chart-4: oklch(0.627 0.265 303.9);
1559
+ --chart-5: oklch(0.645 0.246 16.439);
1560
+ --sidebar: oklch(0.205 0 0);
1561
+ --sidebar-foreground: oklch(0.985 0 0);
1562
+ --sidebar-primary: oklch(0.72 0.15 259);
1563
+ --sidebar-primary-foreground: oklch(0.16 0.04 259);
1564
+ --sidebar-accent: oklch(0.269 0 0);
1565
+ --sidebar-accent-foreground: oklch(0.985 0 0);
1566
+ --sidebar-border: oklch(1 0 0 / 10%);
1567
+ --sidebar-ring: oklch(0.556 0 0);
1568
+ }
1569
+
1570
+ @layer base {
1571
+ * {
1572
+ @apply border-border outline-ring/50;
1573
+ }
1574
+ body {
1575
+ @apply bg-background text-foreground;
1576
+ }
1478
1577
  }
1479
1578
 
1480
- .react-grid-item > .react-resizable-handle {
1481
- position: absolute;
1482
- width: 20px;
1483
- height: 20px;
1484
- bottom: 0;
1485
- right: 0;
1486
- cursor: se-resize;
1487
- opacity: 0;
1488
- transition: opacity 150ms ease;
1489
- }
1579
+ @layer utilities {
1580
+ /* Global guardrail for native buttons: pointer + fallback hover feedback */
1581
+ button:not(:disabled) {
1582
+ cursor: pointer;
1583
+ }
1490
1584
 
1491
- .react-grid-item:hover > .react-resizable-handle {
1492
- opacity: 1;
1493
- }
1585
+ button:not(:disabled):not([class*='hover:']):hover {
1586
+ opacity: 0.92;
1587
+ }
1494
1588
 
1495
- .react-grid-item > .react-resizable-handle::after {
1496
- content: '';
1497
- position: absolute;
1498
- right: 4px;
1499
- bottom: 4px;
1500
- width: 8px;
1501
- height: 8px;
1502
- border-right: 2px solid color-mix(in srgb, var(--dash-500, #f97316) 50%, transparent);
1503
- border-bottom: 2px solid color-mix(in srgb, var(--dash-500, #f97316) 50%, transparent);
1504
- border-radius: 0 0 2px 0;
1505
- }
1589
+ /* Uniform keyboard focus for key dashboard screens */
1590
+ .kb-tab-scope
1591
+ :where(
1592
+ button,
1593
+ a[href],
1594
+ input,
1595
+ select,
1596
+ textarea,
1597
+ [role='button'],
1598
+ [role='tab'],
1599
+ [role='menuitem'],
1600
+ [tabindex]:not([tabindex='-1'])
1601
+ ):focus-visible:not([class*='focus-visible:']) {
1602
+ outline: 2px solid color-mix(in oklch, var(--primary) 80%, white 20%);
1603
+ outline-offset: 2px;
1604
+ box-shadow: 0 0 0 4px color-mix(in oklch, var(--primary) 24%, transparent);
1605
+ }
1506
1606
 
1507
- .react-grid-placeholder {
1508
- background: color-mix(in srgb, var(--dash-500, #f97316) 10%, transparent) !important;
1509
- border: 2px dashed color-mix(in srgb, var(--dash-500, #f97316) 30%, transparent) !important;
1510
- border-radius: 16px !important;
1511
- opacity: 1 !important;
1607
+ .kb-tab-scope
1608
+ :where(
1609
+ button,
1610
+ a[href],
1611
+ input,
1612
+ select,
1613
+ textarea,
1614
+ [role='button'],
1615
+ [role='tab'],
1616
+ [role='menuitem'],
1617
+ [tabindex]:not([tabindex='-1'])
1618
+ ) {
1619
+ scroll-margin-top: 6rem;
1620
+ }
1512
1621
  }
@@ -1,21 +1,28 @@
1
1
  import type { Metadata } from 'next';
2
- import { Geist, Geist_Mono } from 'next/font/google';
2
+ import { DM_Sans, Playfair_Display, JetBrains_Mono } from 'next/font/google';
3
3
  import './globals.css';
4
4
  import { cn } from '@/lib/utils';
5
+ import { AppToastProvider } from '@/contexts/app-toast-context';
5
6
 
6
- const geistSans = Geist({
7
- variable: '--font-geist-sans',
7
+ const bodyFont = DM_Sans({
8
+ variable: '--font-body',
8
9
  subsets: ['latin'],
9
10
  });
10
11
 
11
- const geistMono = Geist_Mono({
12
- variable: '--font-geist-mono',
12
+ const displayFont = Playfair_Display({
13
+ variable: '--font-display',
14
+ subsets: ['latin'],
15
+ });
16
+
17
+ const monoFont = JetBrains_Mono({
18
+ variable: '--font-mono',
13
19
  subsets: ['latin'],
14
20
  });
15
21
 
16
22
  export const metadata: Metadata = {
17
- title: 'Create Next App',
18
- description: 'Generated by create next app',
23
+ title: 'Gold Blessing',
24
+ description:
25
+ 'Le CRM Gold Blessing : gérez vos campagnes, paiements et relations clients de manière efficace et intuitive.',
19
26
  };
20
27
 
21
28
  export default function RootLayout({
@@ -25,7 +32,11 @@ export default function RootLayout({
25
32
  }>) {
26
33
  return (
27
34
  <html lang="fr">
28
- <body className={cn(geistSans.variable, geistMono.variable, 'antialiased')}>{children}</body>
35
+ <body
36
+ className={cn(bodyFont.variable, displayFont.variable, monoFont.variable, 'antialiased')}
37
+ >
38
+ <AppToastProvider>{children}</AppToastProvider>
39
+ </body>
29
40
  </html>
30
41
  );
31
42
  }
@@ -2,25 +2,55 @@
2
2
 
3
3
  import { useSession } from '@/lib/auth-client';
4
4
  import { useRouter } from 'next/navigation';
5
- import { useEffect } from 'react';
5
+ import { useEffect, useState } from 'react';
6
6
 
7
7
  export default function HomePage() {
8
8
  const { data: session, isPending } = useSession();
9
9
  const router = useRouter();
10
+ const [permissionsLoaded, setPermissionsLoaded] = useState(false);
11
+ const [permissions, setPermissions] = useState<string[]>([]);
10
12
 
11
13
  useEffect(() => {
12
14
  if (isPending) return;
13
15
 
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
16
+ if (!session) {
19
17
  router.push('/signin');
18
+ return;
20
19
  }
20
+
21
+ const fetchPermissions = async () => {
22
+ try {
23
+ const response = await fetch('/api/users/me');
24
+ if (response.ok) {
25
+ const userData = await response.json();
26
+ setPermissions((userData.customRole?.permissions as string[]) || []);
27
+ }
28
+ } catch {
29
+ // Fallback to default redirect
30
+ } finally {
31
+ setPermissionsLoaded(true);
32
+ }
33
+ };
34
+
35
+ fetchPermissions();
21
36
  }, [session, isPending, router]);
22
37
 
23
- // Afficher un loader pendant la redirection
38
+ useEffect(() => {
39
+ if (!permissionsLoaded) return;
40
+
41
+ const has = (p: string) => permissions.includes(p);
42
+
43
+ if (has('dashboard.view')) {
44
+ router.push('/dashboard');
45
+ } else if (has('contacts.view_own') || has('contacts.view_all')) {
46
+ router.push('/contacts');
47
+ } else if (has('tasks.view_all') || has('tasks.view_own')) {
48
+ router.push('/agenda');
49
+ } else {
50
+ router.push('/contacts');
51
+ }
52
+ }, [permissionsLoaded, permissions, router]);
53
+
24
54
  return (
25
55
  <div className="flex min-h-screen items-center justify-center bg-gray-50">
26
56
  <div className="flex flex-col items-center gap-4">