create-crm-tmp 1.1.3 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (220) hide show
  1. package/package.json +1 -1
  2. package/template/.prettierignore +2 -0
  3. package/template/README.md +1 -1
  4. package/template/components.json +22 -0
  5. package/template/exemple-contacts.csv +54 -0
  6. package/template/next.config.ts +27 -1
  7. package/template/package.json +51 -16
  8. package/template/prisma/schema.prisma +807 -58
  9. package/template/skills-lock.json +25 -0
  10. package/template/src/app/(auth)/invite/[token]/page.tsx +21 -24
  11. package/template/src/app/(auth)/reset-password/complete/page.tsx +12 -21
  12. package/template/src/app/(auth)/reset-password/page.tsx +12 -8
  13. package/template/src/app/(auth)/reset-password/verify/page.tsx +12 -8
  14. package/template/src/app/(auth)/signin/page.tsx +20 -17
  15. package/template/src/app/(dashboard)/agenda/page.tsx +2232 -2189
  16. package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +10 -7
  17. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +680 -323
  18. package/template/src/app/(dashboard)/automatisation/new/page.tsx +11 -8
  19. package/template/src/app/(dashboard)/automatisation/page.tsx +473 -180
  20. package/template/src/app/(dashboard)/closing/page.tsx +500 -468
  21. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +5049 -4110
  22. package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +1703 -0
  23. package/template/src/app/(dashboard)/contacts/loading.tsx +13 -0
  24. package/template/src/app/(dashboard)/contacts/page.tsx +3776 -2064
  25. package/template/src/app/(dashboard)/dashboard/page.tsx +37 -519
  26. package/template/src/app/(dashboard)/error.tsx +37 -0
  27. package/template/src/app/(dashboard)/layout.tsx +1 -1
  28. package/template/src/app/(dashboard)/loading.tsx +5 -0
  29. package/template/src/app/(dashboard)/settings/loading.tsx +19 -0
  30. package/template/src/app/(dashboard)/settings/page.tsx +2685 -2489
  31. package/template/src/app/(dashboard)/templates/page.tsx +500 -300
  32. package/template/src/app/(dashboard)/users/list/page.tsx +356 -350
  33. package/template/src/app/(dashboard)/users/page.tsx +279 -310
  34. package/template/src/app/(dashboard)/users/permissions/page.tsx +104 -99
  35. package/template/src/app/(dashboard)/users/roles/page.tsx +164 -137
  36. package/template/src/app/api/audit-logs/route.ts +1 -1
  37. package/template/src/app/api/auth/google/callback/route.ts +8 -5
  38. package/template/src/app/api/auth/google/disconnect/route.ts +2 -2
  39. package/template/src/app/api/companies/[id]/activities/route.ts +131 -0
  40. package/template/src/app/api/companies/[id]/route.ts +195 -0
  41. package/template/src/app/api/companies/export/route.ts +206 -0
  42. package/template/src/app/api/companies/route.ts +166 -0
  43. package/template/src/app/api/contact-views/[id]/pin/route.ts +69 -0
  44. package/template/src/app/api/contact-views/[id]/route.ts +197 -0
  45. package/template/src/app/api/contact-views/route.ts +146 -0
  46. package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +77 -0
  47. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +7 -17
  48. package/template/src/app/api/contacts/[id]/files/route.ts +83 -44
  49. package/template/src/app/api/contacts/[id]/interactions/route.ts +37 -0
  50. package/template/src/app/api/contacts/[id]/kyc/route.ts +71 -0
  51. package/template/src/app/api/contacts/[id]/meet/route.ts +38 -29
  52. package/template/src/app/api/contacts/[id]/route.ts +111 -20
  53. package/template/src/app/api/contacts/[id]/send-email/route.ts +6 -0
  54. package/template/src/app/api/contacts/[id]/workflows/run/route.ts +61 -0
  55. package/template/src/app/api/contacts/export/route.ts +13 -18
  56. package/template/src/app/api/contacts/import/route.ts +22 -19
  57. package/template/src/app/api/contacts/import-preview/route.ts +139 -0
  58. package/template/src/app/api/contacts/route.ts +202 -49
  59. package/template/src/app/api/dashboard/stats/route.ts +9 -292
  60. package/template/src/app/api/integrations/google-sheet/sync/route.ts +203 -185
  61. package/template/src/app/api/invite/complete/route.ts +20 -23
  62. package/template/src/app/api/reminders/route.ts +1 -0
  63. package/template/src/app/api/reset-password/complete/route.ts +11 -13
  64. package/template/src/app/api/send/route.ts +9 -43
  65. package/template/src/app/api/settings/closing-reasons/[id]/route.ts +10 -21
  66. package/template/src/app/api/settings/closing-reasons/route.ts +10 -21
  67. package/template/src/app/api/settings/company/route.ts +19 -26
  68. package/template/src/app/api/settings/google-ads/[id]/route.ts +20 -23
  69. package/template/src/app/api/settings/google-ads/route.ts +20 -23
  70. package/template/src/app/api/settings/google-sheet/[id]/route.ts +20 -23
  71. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +23 -32
  72. package/template/src/app/api/settings/google-sheet/preview/route.ts +104 -0
  73. package/template/src/app/api/settings/google-sheet/route.ts +20 -23
  74. package/template/src/app/api/settings/meta-leads/[id]/route.ts +20 -23
  75. package/template/src/app/api/settings/meta-leads/route.ts +20 -23
  76. package/template/src/app/api/settings/statuses/[id]/route.ts +29 -32
  77. package/template/src/app/api/settings/statuses/route.ts +24 -22
  78. package/template/src/app/api/statuses/route.ts +2 -5
  79. package/template/src/app/api/tasks/[id]/attendees/route.ts +14 -7
  80. package/template/src/app/api/tasks/[id]/route.ts +173 -137
  81. package/template/src/app/api/tasks/meet/route.ts +11 -8
  82. package/template/src/app/api/tasks/route.ts +155 -95
  83. package/template/src/app/api/templates/[id]/route.ts +22 -13
  84. package/template/src/app/api/templates/route.ts +22 -5
  85. package/template/src/app/api/users/[id]/resend-invite/route.ts +95 -0
  86. package/template/src/app/api/users/[id]/route.ts +2 -2
  87. package/template/src/app/api/users/commercials/route.ts +38 -0
  88. package/template/src/app/api/users/for-agenda/route.ts +1 -2
  89. package/template/src/app/api/users/route.ts +89 -34
  90. package/template/src/app/api/webhooks/google-ads/route.ts +20 -1
  91. package/template/src/app/api/webhooks/meta-leads/route.ts +18 -1
  92. package/template/src/app/api/workflows/[id]/route.ts +33 -6
  93. package/template/src/app/api/workflows/process/route.ts +510 -146
  94. package/template/src/app/api/workflows/route.ts +46 -4
  95. package/template/src/app/globals.css +243 -101
  96. package/template/src/app/layout.tsx +19 -8
  97. package/template/src/app/page.tsx +37 -7
  98. package/template/src/components/address-autocomplete.tsx +232 -0
  99. package/template/src/components/contacts/filter-bar.tsx +181 -0
  100. package/template/src/components/contacts/filter-builder.tsx +589 -0
  101. package/template/src/components/contacts/save-view-dialog.tsx +160 -0
  102. package/template/src/components/contacts/views-tab-bar.tsx +440 -0
  103. package/template/src/components/dashboard/activity-chart.tsx +31 -39
  104. package/template/src/components/dashboard/dashboard-content.tsx +79 -0
  105. package/template/src/components/dashboard/stat-card.tsx +40 -42
  106. package/template/src/components/dashboard/tasks-pie-chart.tsx +34 -37
  107. package/template/src/components/dashboard/upcoming-tasks-list.tsx +78 -72
  108. package/template/src/components/date-picker.tsx +396 -0
  109. package/template/src/components/editor.tsx +27 -13
  110. package/template/src/components/email-template.tsx +4 -2
  111. package/template/src/components/global-search.tsx +358 -0
  112. package/template/src/components/header.tsx +57 -62
  113. package/template/src/components/invitation-email-template.tsx +4 -2
  114. package/template/src/components/lazy-editor.tsx +11 -0
  115. package/template/src/components/meet-cancellation-email-template.tsx +11 -3
  116. package/template/src/components/meet-confirmation-email-template.tsx +10 -3
  117. package/template/src/components/meet-update-email-template.tsx +10 -3
  118. package/template/src/components/page-header.tsx +19 -15
  119. package/template/src/components/protected-page.tsx +94 -0
  120. package/template/src/components/reset-password-email-template.tsx +4 -2
  121. package/template/src/components/sidebar.tsx +92 -94
  122. package/template/src/components/skeleton.tsx +128 -42
  123. package/template/src/components/ui/accordion.tsx +64 -0
  124. package/template/src/components/ui/alert-dialog.tsx +139 -0
  125. package/template/src/components/ui/button.tsx +60 -0
  126. package/template/src/components/view-as-banner.tsx +1 -1
  127. package/template/src/components/view-as-modal.tsx +21 -16
  128. package/template/src/config/nav-pages.ts +108 -0
  129. package/template/src/contexts/app-toast-context.tsx +174 -0
  130. package/template/src/contexts/sidebar-context.tsx +16 -47
  131. package/template/src/contexts/task-reminder-context.tsx +6 -6
  132. package/template/src/contexts/view-as-context.tsx +11 -16
  133. package/template/src/hooks/use-alert.tsx +65 -0
  134. package/template/src/hooks/use-confirm.tsx +87 -0
  135. package/template/src/hooks/use-contact-views.ts +140 -0
  136. package/template/src/hooks/use-contacts.ts +69 -0
  137. package/template/src/hooks/use-fetch.ts +17 -0
  138. package/template/src/hooks/use-focus-trap.ts +73 -0
  139. package/template/src/hooks/use-statuses.ts +22 -0
  140. package/template/src/lib/address-api.ts +155 -0
  141. package/template/src/lib/cache.ts +73 -0
  142. package/template/src/lib/check-permission.ts +12 -177
  143. package/template/src/lib/contact-interactions.ts +3 -1
  144. package/template/src/lib/contact-view-filters.ts +341 -0
  145. package/template/src/lib/dashboard-stats.ts +224 -0
  146. package/template/src/lib/date-utils.ts +49 -0
  147. package/template/src/lib/get-auth-user.ts +25 -0
  148. package/template/src/lib/google-calendar.ts +54 -12
  149. package/template/src/lib/google-drive.ts +796 -75
  150. package/template/src/lib/google-fetch.ts +63 -0
  151. package/template/src/lib/local-storage.ts +34 -0
  152. package/template/src/lib/permissions.ts +245 -47
  153. package/template/src/lib/prisma.ts +11 -11
  154. package/template/src/lib/roles.ts +12 -15
  155. package/template/src/lib/template-variables.ts +67 -33
  156. package/template/src/lib/utils.ts +26 -11
  157. package/template/src/lib/workflow-executor.ts +445 -228
  158. package/template/src/proxy.ts +34 -73
  159. package/template/src/types/contact-views.ts +351 -0
  160. package/template/src/types/yousign.ts +52 -0
  161. package/template/vercel.json +12 -0
  162. package/template/WORKFLOWS_CRON.md +0 -185
  163. package/template/prisma/migrations/20251126144728_init/migration.sql +0 -78
  164. package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +0 -5
  165. package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +0 -19
  166. package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +0 -22
  167. package/template/prisma/migrations/20251128132303_add_status/migration.sql +0 -23
  168. package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +0 -75
  169. package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +0 -2
  170. package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +0 -45
  171. package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +0 -2
  172. package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +0 -27
  173. package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +0 -20
  174. package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +0 -18
  175. package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +0 -32
  176. package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +0 -20
  177. package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +0 -12
  178. package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +0 -21
  179. package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +0 -11
  180. package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +0 -12
  181. package/template/prisma/migrations/20251208094843_mg/migration.sql +0 -14
  182. package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +0 -14
  183. package/template/prisma/migrations/20251208110000_add_templates/migration.sql +0 -26
  184. package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +0 -2
  185. package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +0 -2
  186. package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +0 -2
  187. package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +0 -3
  188. package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +0 -21
  189. package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +0 -2
  190. package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +0 -10
  191. package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +0 -26
  192. package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +0 -24
  193. package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +0 -11
  194. package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +0 -12
  195. package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +0 -25
  196. package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +0 -8
  197. package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +0 -2
  198. package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +0 -80
  199. package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +0 -32
  200. package/template/prisma/migrations/20251220000000_add_task_interaction_type/migration.sql +0 -4
  201. package/template/prisma/migrations/20251221000000_add_task_type/migration.sql +0 -3
  202. package/template/prisma/migrations/20251221000001_add_event_color/migration.sql +0 -23
  203. package/template/prisma/migrations/20260210114913_add_dashboard_widget/migration.sql +0 -20
  204. package/template/prisma/migrations/20260226093949_fix_cascade_on_user_delete/migration.sql +0 -69
  205. package/template/prisma/migrations/migration_lock.toml +0 -3
  206. package/template/src/app/(dashboard)/users/layout.tsx +0 -30
  207. package/template/src/app/api/dashboard/widgets/[id]/route.ts +0 -47
  208. package/template/src/app/api/dashboard/widgets/route.ts +0 -181
  209. package/template/src/components/dashboard/add-widget-dialog.tsx +0 -161
  210. package/template/src/components/dashboard/color-picker.tsx +0 -65
  211. package/template/src/components/dashboard/contacts-chart.tsx +0 -69
  212. package/template/src/components/dashboard/interactions-by-type-chart.tsx +0 -121
  213. package/template/src/components/dashboard/recent-activity.tsx +0 -157
  214. package/template/src/components/dashboard/status-distribution-chart.tsx +0 -82
  215. package/template/src/components/dashboard/top-contacts-list.tsx +0 -119
  216. package/template/src/components/dashboard/widget-wrapper.tsx +0 -39
  217. package/template/src/contexts/dashboard-theme-context.tsx +0 -58
  218. package/template/src/lib/dashboard-themes.ts +0 -140
  219. package/template/src/lib/default-widgets.ts +0 -14
  220. package/template/src/lib/widget-registry.ts +0 -177
@@ -0,0 +1,63 @@
1
+ const MAX_RETRIES = 3;
2
+ const BASE_DELAY_MS = 1000;
3
+
4
+ interface GoogleFetchOptions extends RequestInit {
5
+ maxRetries?: number;
6
+ }
7
+
8
+ /**
9
+ * Wrapper autour de fetch pour les APIs Google avec retry automatique,
10
+ * backoff exponentiel et gestion des erreurs de quota (429) et serveur (5xx).
11
+ */
12
+ export async function googleFetch(
13
+ url: string,
14
+ options: GoogleFetchOptions = {},
15
+ ): Promise<Response> {
16
+ const { maxRetries = MAX_RETRIES, ...fetchOptions } = options;
17
+
18
+ let lastError: Error | null = null;
19
+
20
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
21
+ try {
22
+ const response = await fetch(url, fetchOptions);
23
+
24
+ if (response.ok) {
25
+ return response;
26
+ }
27
+
28
+ const status = response.status;
29
+
30
+ // Ne pas retry les erreurs client (sauf 429 rate limit)
31
+ if (status >= 400 && status < 500 && status !== 429) {
32
+ return response;
33
+ }
34
+
35
+ // Retry sur 429 (rate limit) et 5xx (erreur serveur)
36
+ if (attempt < maxRetries && (status === 429 || status >= 500)) {
37
+ const retryAfter = response.headers.get('Retry-After');
38
+ const delayMs = retryAfter
39
+ ? Number.parseInt(retryAfter, 10) * 1000
40
+ : BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 500;
41
+
42
+ await sleep(delayMs);
43
+ continue;
44
+ }
45
+
46
+ return response;
47
+ } catch (error) {
48
+ lastError = error instanceof Error ? error : new Error(String(error));
49
+
50
+ if (attempt < maxRetries) {
51
+ const delayMs = BASE_DELAY_MS * Math.pow(2, attempt) + Math.random() * 500;
52
+ await sleep(delayMs);
53
+ continue;
54
+ }
55
+ }
56
+ }
57
+
58
+ throw lastError || new Error(`Google API request failed after ${maxRetries + 1} attempts`);
59
+ }
60
+
61
+ function sleep(ms: number): Promise<void> {
62
+ return new Promise((resolve) => setTimeout(resolve, ms));
63
+ }
@@ -0,0 +1,34 @@
1
+ const APP_STORAGE_VERSION = 'v1';
2
+
3
+ function getVersionedKey(key: string) {
4
+ return `${key}:${APP_STORAGE_VERSION}`;
5
+ }
6
+
7
+ export function safeLocalStorageGet<T>(key: string, fallback: T): T {
8
+ if (typeof window === 'undefined') return fallback;
9
+ try {
10
+ const raw = window.localStorage.getItem(getVersionedKey(key));
11
+ if (!raw) return fallback;
12
+ return JSON.parse(raw) as T;
13
+ } catch {
14
+ return fallback;
15
+ }
16
+ }
17
+
18
+ export function safeLocalStorageSet<T>(key: string, value: T) {
19
+ if (typeof window === 'undefined') return;
20
+ try {
21
+ window.localStorage.setItem(getVersionedKey(key), JSON.stringify(value));
22
+ } catch {
23
+ // Ignore private mode / quota errors.
24
+ }
25
+ }
26
+
27
+ export function safeLocalStorageRemove(key: string) {
28
+ if (typeof window === 'undefined') return;
29
+ try {
30
+ window.localStorage.removeItem(getVersionedKey(key));
31
+ } catch {
32
+ // Ignore private mode / quota errors.
33
+ }
34
+ }
@@ -11,35 +11,25 @@ export interface Permission {
11
11
  }
12
12
 
13
13
  export const PERMISSION_CATEGORIES = {
14
- DASHBOARD: 'Tableau de bord',
14
+ DASHBOARD: 'Dashboard',
15
15
  ANALYTICS: 'Analytics',
16
16
  CONTACTS: 'Contacts',
17
17
  TASKS: 'Tâches',
18
+ WORKFLOWS: 'Workflows',
18
19
  TEMPLATES: 'Templates',
19
20
  INTEGRATIONS: 'Intégrations',
20
21
  USERS: 'Utilisateurs',
21
22
  SETTINGS: 'Paramètres',
23
+ AUDIT: 'Audit',
22
24
  GENERAL: 'Général',
23
25
  } as const;
24
26
 
25
27
  export const PERMISSIONS: Permission[] = [
26
- // Tableau de bord
28
+ // Dashboard
27
29
  {
28
30
  code: 'dashboard.view',
29
- name: 'Voir le tableau de bord',
30
- description: 'Permet d\'accéder au tableau de bord et de visualiser les widgets',
31
- category: PERMISSION_CATEGORIES.DASHBOARD,
32
- },
33
- {
34
- code: 'dashboard.widgets.manage',
35
- name: 'Gérer les widgets',
36
- description: 'Permet d\'ajouter, supprimer et réorganiser les widgets du tableau de bord',
37
- category: PERMISSION_CATEGORIES.DASHBOARD,
38
- },
39
- {
40
- code: 'dashboard.widgets.reset',
41
- name: 'Réinitialiser le tableau de bord',
42
- description: 'Permet de réinitialiser la disposition des widgets aux valeurs par défaut',
31
+ name: 'Accéder au dashboard',
32
+ description: 'Permet de voir le tableau de bord principal',
43
33
  category: PERMISSION_CATEGORIES.DASHBOARD,
44
34
  },
45
35
 
@@ -61,8 +51,7 @@ export const PERMISSIONS: Permission[] = [
61
51
  {
62
52
  code: 'contacts.view_all',
63
53
  name: 'Voir tous les contacts',
64
- description:
65
- 'Permet de voir tous les contacts de toutes les entreprises (réservé aux administrateurs)',
54
+ description: 'Permet de voir tous les contacts de toutes les entreprises',
66
55
  category: PERMISSION_CATEGORIES.CONTACTS,
67
56
  },
68
57
  {
@@ -131,6 +120,110 @@ export const PERMISSIONS: Permission[] = [
131
120
  description: 'Permet de voir les fichiers associés aux contacts',
132
121
  category: PERMISSION_CATEGORIES.CONTACTS,
133
122
  },
123
+ {
124
+ code: 'contacts.view_closing_pipeline',
125
+ name: 'Voir le pipeline de clôture',
126
+ description: 'Permet de visualiser le pipeline kanban de clôture des contacts',
127
+ category: PERMISSION_CATEGORIES.CONTACTS,
128
+ },
129
+ {
130
+ code: 'contacts.manage_pipeline',
131
+ name: 'Gérer le pipeline de clôture',
132
+ description: 'Permet de déplacer les contacts dans le pipeline kanban',
133
+ category: PERMISSION_CATEGORIES.CONTACTS,
134
+ },
135
+ {
136
+ code: 'contacts.send_email',
137
+ name: 'Envoyer des emails',
138
+ description: "Permet d'envoyer des emails aux contacts depuis leur fiche",
139
+ category: PERMISSION_CATEGORIES.CONTACTS,
140
+ },
141
+ {
142
+ code: 'contacts.delete_files',
143
+ name: 'Supprimer des fichiers',
144
+ description: 'Permet de supprimer les fichiers associés aux contacts',
145
+ category: PERMISSION_CATEGORIES.CONTACTS,
146
+ },
147
+ {
148
+ code: 'contacts.views.create',
149
+ name: 'Créer des vues de contacts',
150
+ description: 'Permet de créer des vues personnalisées de contacts',
151
+ category: PERMISSION_CATEGORIES.CONTACTS,
152
+ },
153
+ {
154
+ code: 'contacts.views.edit_own',
155
+ name: 'Modifier ses vues',
156
+ description: 'Permet de modifier ses propres vues de contacts',
157
+ category: PERMISSION_CATEGORIES.CONTACTS,
158
+ },
159
+ {
160
+ code: 'contacts.views.edit_all',
161
+ name: 'Modifier toutes les vues',
162
+ description: 'Permet de modifier les vues de tous les utilisateurs',
163
+ category: PERMISSION_CATEGORIES.CONTACTS,
164
+ },
165
+ {
166
+ code: 'contacts.views.delete_own',
167
+ name: 'Supprimer ses vues',
168
+ description: 'Permet de supprimer ses propres vues de contacts',
169
+ category: PERMISSION_CATEGORIES.CONTACTS,
170
+ },
171
+ {
172
+ code: 'contacts.views.delete_all',
173
+ name: 'Supprimer toutes les vues',
174
+ description: 'Permet de supprimer les vues de tous les utilisateurs',
175
+ category: PERMISSION_CATEGORIES.CONTACTS,
176
+ },
177
+ {
178
+ code: 'contacts.views.share',
179
+ name: 'Partager des vues',
180
+ description: 'Permet de rendre des vues publiques ou privées',
181
+ category: PERMISSION_CATEGORIES.CONTACTS,
182
+ },
183
+
184
+ // Entreprises (fiches entreprise, liste, activités)
185
+ {
186
+ code: 'companies.view_all',
187
+ name: 'Voir toutes les entreprises',
188
+ description: 'Permet de voir toutes les entreprises (vue Entreprises)',
189
+ category: PERMISSION_CATEGORIES.CONTACTS,
190
+ },
191
+ {
192
+ code: 'companies.view_own',
193
+ name: 'Voir ses entreprises',
194
+ description: 'Permet de voir uniquement les entreprises assignées ou créées',
195
+ category: PERMISSION_CATEGORIES.CONTACTS,
196
+ },
197
+ {
198
+ code: 'companies.create',
199
+ name: 'Créer une entreprise',
200
+ description: 'Autorise la création de nouvelles entreprises',
201
+ category: PERMISSION_CATEGORIES.CONTACTS,
202
+ },
203
+ {
204
+ code: 'companies.edit',
205
+ name: 'Modifier une entreprise',
206
+ description: "Autorise la modification des informations d'une entreprise",
207
+ category: PERMISSION_CATEGORIES.CONTACTS,
208
+ },
209
+ {
210
+ code: 'companies.delete',
211
+ name: 'Supprimer une entreprise',
212
+ description: "Autorise la suppression d'une entreprise (les contacts liés sont déliés)",
213
+ category: PERMISSION_CATEGORIES.CONTACTS,
214
+ },
215
+ {
216
+ code: 'companies.view_activities',
217
+ name: "Voir les activités d'une entreprise",
218
+ description: "Permet de voir l'historique des activités (modifications, contacts ajoutés)",
219
+ category: PERMISSION_CATEGORIES.CONTACTS,
220
+ },
221
+ {
222
+ code: 'companies.add_activity',
223
+ name: 'Ajouter une activité entreprise',
224
+ description: "Permet d'enregistrer une activité sur une fiche entreprise (notes, etc.)",
225
+ category: PERMISSION_CATEGORIES.CONTACTS,
226
+ },
134
227
 
135
228
  // Tâches
136
229
  {
@@ -183,6 +276,38 @@ export const PERMISSIONS: Permission[] = [
183
276
  category: PERMISSION_CATEGORIES.TASKS,
184
277
  },
185
278
 
279
+ // Workflows
280
+ {
281
+ code: 'workflows.view',
282
+ name: 'Voir les workflows',
283
+ description: 'Permet de voir les workflows automatisés',
284
+ category: PERMISSION_CATEGORIES.WORKFLOWS,
285
+ },
286
+ {
287
+ code: 'workflows.create',
288
+ name: 'Créer un workflow',
289
+ description: 'Autorise la création de nouveaux workflows',
290
+ category: PERMISSION_CATEGORIES.WORKFLOWS,
291
+ },
292
+ {
293
+ code: 'workflows.edit',
294
+ name: 'Modifier un workflow',
295
+ description: 'Autorise la modification de workflows',
296
+ category: PERMISSION_CATEGORIES.WORKFLOWS,
297
+ },
298
+ {
299
+ code: 'workflows.delete',
300
+ name: 'Supprimer un workflow',
301
+ description: 'Autorise la suppression de workflows',
302
+ category: PERMISSION_CATEGORIES.WORKFLOWS,
303
+ },
304
+ {
305
+ code: 'workflows.activate',
306
+ name: 'Activer/désactiver des workflows',
307
+ description: "Permet d'activer ou désactiver des workflows",
308
+ category: PERMISSION_CATEGORIES.WORKFLOWS,
309
+ },
310
+
186
311
  // Templates
187
312
  {
188
313
  code: 'templates.view',
@@ -258,13 +383,6 @@ export const PERMISSIONS: Permission[] = [
258
383
  description: 'Permet de configurer les synchronisations Google Sheets',
259
384
  category: PERMISSION_CATEGORIES.INTEGRATIONS,
260
385
  },
261
- {
262
- code: 'integrations.google_drive.manage',
263
- name: 'Gérer Google Drive partagé',
264
- description:
265
- 'Permet de configurer le compte Google Drive administrateur utilisé pour le stockage des fichiers',
266
- category: PERMISSION_CATEGORIES.INTEGRATIONS,
267
- },
268
386
 
269
387
  // Utilisateurs
270
388
  {
@@ -331,16 +449,35 @@ export const PERMISSIONS: Permission[] = [
331
449
  },
332
450
  {
333
451
  code: 'settings.closing_reasons.manage',
334
- name: 'Gérer les motifs de fermeture',
335
- description: 'Permet de créer, modifier et supprimer des motifs de fermeture de contacts',
452
+ name: 'Gérer les motifs de clôture',
453
+ description: 'Permet de créer, modifier et supprimer des motifs de clôture',
336
454
  category: PERMISSION_CATEGORIES.SETTINGS,
337
455
  },
338
456
  {
339
457
  code: 'settings.workflows.manage',
340
- name: 'Gérer les automatisations',
341
- description: 'Permet de créer, modifier, activer et supprimer des workflows (automatisations)',
458
+ name: 'Gérer les paramètres de workflows',
459
+ description: 'Permet de configurer les paramètres globaux des workflows',
342
460
  category: PERMISSION_CATEGORIES.SETTINGS,
343
461
  },
462
+ // Audit
463
+ {
464
+ code: 'audit.view_all',
465
+ name: "Voir tous les logs d'audit",
466
+ description: "Permet de consulter l'historique complet des actions du système",
467
+ category: PERMISSION_CATEGORIES.AUDIT,
468
+ },
469
+ {
470
+ code: 'audit.view_own',
471
+ name: "Voir ses logs d'audit",
472
+ description: "Permet de consulter uniquement son propre historique d'actions",
473
+ category: PERMISSION_CATEGORIES.AUDIT,
474
+ },
475
+ {
476
+ code: 'audit.export',
477
+ name: "Exporter les logs d'audit",
478
+ description: "Autorise l'exportation des logs d'audit",
479
+ category: PERMISSION_CATEGORIES.AUDIT,
480
+ },
344
481
 
345
482
  // Général
346
483
  {
@@ -350,13 +487,6 @@ export const PERMISSIONS: Permission[] = [
350
487
  'Permet de voir tous les contacts de toutes les entreprises (réservé aux administrateurs)',
351
488
  category: PERMISSION_CATEGORIES.GENERAL,
352
489
  },
353
- {
354
- code: 'general.audit_logs.view',
355
- name: 'Voir les logs d’audit',
356
- description:
357
- 'Permet de consulter les journaux d’audit (actions des utilisateurs, historique des modifications)',
358
- category: PERMISSION_CATEGORIES.GENERAL,
359
- },
360
490
  ];
361
491
 
362
492
  // Regrouper les permissions par catégorie
@@ -371,7 +501,6 @@ export const PERMISSIONS_BY_CATEGORY = PERMISSIONS.reduce(
371
501
  {} as Record<string, Permission[]>,
372
502
  );
373
503
 
374
- // Profils par défaut avec leurs permissions
375
504
  export const DEFAULT_ROLES = {
376
505
  ADMIN: {
377
506
  name: 'Administrateur',
@@ -383,9 +512,8 @@ export const DEFAULT_ROLES = {
383
512
  description: "Gestion d'équipe et accès étendu aux leads",
384
513
  permissions: [
385
514
  'dashboard.view',
386
- 'dashboard.widgets.manage',
387
- 'dashboard.widgets.reset',
388
515
  'analytics.view',
516
+ 'general.view_all_companies',
389
517
  'contacts.view_all',
390
518
  'contacts.create',
391
519
  'contacts.edit_all',
@@ -394,17 +522,54 @@ export const DEFAULT_ROLES = {
394
522
  'contacts.export',
395
523
  'contacts.view_files',
396
524
  'contacts.upload_files',
525
+ 'contacts.view_closing_pipeline',
526
+ 'contacts.manage_pipeline',
527
+ 'contacts.send_email',
528
+ 'contacts.delete_files',
529
+ 'contacts.views.create',
530
+ 'contacts.views.edit_own',
531
+ 'contacts.views.edit_all',
532
+ 'contacts.views.delete_own',
533
+ 'contacts.views.delete_all',
534
+ 'contacts.views.share',
535
+ 'companies.view_all',
536
+ 'companies.create',
537
+ 'companies.edit',
538
+ 'companies.delete',
539
+ 'companies.view_activities',
540
+ 'companies.add_activity',
397
541
  'tasks.view_all',
398
542
  'tasks.create',
399
543
  'tasks.edit_all',
400
544
  'tasks.assign',
545
+ 'tasks.view_other_users_events',
546
+ 'workflows.view',
547
+ 'workflows.create',
548
+ 'workflows.edit',
549
+ 'workflows.activate',
550
+ 'settings.workflows.manage',
551
+ 'workflows.view',
552
+ 'workflows.create',
553
+ 'workflows.edit',
554
+ 'workflows.activate',
401
555
  'templates.view',
402
556
  'templates.create',
403
557
  'templates.edit',
404
558
  'templates.delete',
405
559
  'integrations.view',
560
+ 'integrations.create',
561
+ 'integrations.edit',
562
+ 'integrations.delete',
563
+ 'integrations.meta.manage',
564
+ 'integrations.google_ads.manage',
565
+ 'integrations.google_sheets.manage',
406
566
  'users.view',
407
567
  'settings.view',
568
+ 'settings.status.manage',
569
+ 'settings.closing_reasons.manage',
570
+ 'audit.view_all',
571
+ 'audit.view_own',
572
+ 'audit.export',
408
573
  ],
409
574
  },
410
575
  COMMERCIAL: {
@@ -412,21 +577,37 @@ export const DEFAULT_ROLES = {
412
577
  description: 'Accès de base pour la gestion des leads personnels',
413
578
  permissions: [
414
579
  'dashboard.view',
415
- 'dashboard.widgets.manage',
416
580
  'contacts.view_own',
417
581
  'contacts.view_unassigned',
418
582
  'contacts.create',
419
583
  'contacts.edit_own',
584
+ 'contacts.export',
420
585
  'contacts.view_files',
421
586
  'contacts.upload_files',
587
+ 'contacts.view_closing_pipeline',
588
+ 'contacts.manage_pipeline',
589
+ 'contacts.send_email',
590
+ 'contacts.views.create',
591
+ 'contacts.views.edit_own',
592
+ 'contacts.views.delete_own',
593
+ 'contacts.views.share',
594
+ 'companies.view_own',
595
+ 'companies.create',
596
+ 'companies.edit',
597
+ 'companies.view_activities',
598
+ 'companies.add_activity',
422
599
  'tasks.view_own',
423
600
  'tasks.create',
424
601
  'tasks.edit_own',
602
+ 'tasks.assign',
603
+ 'audit.view_own',
425
604
  'templates.view',
426
605
  'templates.create',
427
606
  'templates.edit',
428
607
  'integrations.view',
429
608
  'integrations.google.connect',
609
+ 'settings.view',
610
+ 'settings.smtp.edit',
430
611
  ],
431
612
  },
432
613
  TELEPRO: {
@@ -439,10 +620,28 @@ export const DEFAULT_ROLES = {
439
620
  'contacts.create',
440
621
  'contacts.edit_own',
441
622
  'contacts.view_files',
623
+ 'contacts.upload_files',
624
+ 'contacts.view_closing_pipeline',
625
+ 'contacts.manage_pipeline',
626
+ 'contacts.send_email',
627
+ 'contacts.views.create',
628
+ 'contacts.views.edit_own',
629
+ 'contacts.views.delete_own',
630
+ 'contacts.views.share',
631
+ 'companies.view_own',
632
+ 'companies.create',
633
+ 'companies.edit',
634
+ 'companies.view_activities',
635
+ 'companies.add_activity',
442
636
  'tasks.view_own',
443
637
  'tasks.create',
444
638
  'tasks.edit_own',
639
+ 'tasks.assign',
445
640
  'templates.view',
641
+ 'templates.create',
642
+ 'templates.edit',
643
+ 'settings.view',
644
+ 'settings.smtp.edit',
446
645
  ],
447
646
  },
448
647
  COMPTABLE: {
@@ -454,20 +653,19 @@ export const DEFAULT_ROLES = {
454
653
  'analytics.export',
455
654
  'contacts.view_all',
456
655
  'contacts.export',
656
+ 'companies.view_all',
657
+ 'companies.view_activities',
457
658
  'tasks.view_all',
659
+ 'general.view_all_companies',
458
660
  'templates.view',
459
661
  'settings.view',
662
+ 'audit.view_all',
663
+ 'audit.view_own',
664
+ 'audit.export',
460
665
  ],
461
666
  },
462
667
  };
463
668
 
464
- // Helper pour vérifier si un rôle a une permission
465
669
  export function hasPermission(rolePermissions: string[], requiredPermission: string): boolean {
466
670
  return rolePermissions.includes(requiredPermission);
467
671
  }
468
-
469
- // Helper pour obtenir toutes les permissions d'un rôle
470
- export function getRolePermissions(role: string): string[] {
471
- const roleKey = role.toUpperCase() as keyof typeof DEFAULT_ROLES;
472
- return DEFAULT_ROLES[roleKey]?.permissions || [];
473
- }
@@ -15,17 +15,17 @@ const pool = new Pool({
15
15
 
16
16
  const adapter = new PrismaPg(pool);
17
17
 
18
- const prisma = new PrismaClient({
19
- adapter,
20
- log: process.env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'],
21
- });
18
+ const globalForPrisma = globalThis as unknown as {
19
+ prisma?: PrismaClient;
20
+ };
22
21
 
23
- // Gestion propre de la fermeture lors de l'arrêt de l'application
24
- if (typeof window === 'undefined') {
25
- process.on('beforeExit', async () => {
26
- await prisma.$disconnect();
27
- await pool.end();
22
+ export const prisma =
23
+ globalForPrisma.prisma ??
24
+ new PrismaClient({
25
+ adapter,
26
+ log: process.env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'],
28
27
  });
29
- }
30
28
 
31
- export { prisma };
29
+ if (process.env.NODE_ENV !== 'production') {
30
+ globalForPrisma.prisma = prisma;
31
+ }
@@ -30,10 +30,6 @@ function getRoleLevel(role: string): number {
30
30
  return ROLE_HIERARCHY[role] || 999; // Rôle inconnu = niveau très bas
31
31
  }
32
32
 
33
- /**
34
- * Vérifie si l'utilisateur a le rôle requis ou un rôle supérieur dans la hiérarchie
35
- * Un rôle supérieur a automatiquement les permissions des rôles inférieurs
36
- */
37
33
  function hasRole(userRole: string | undefined, requiredRole: Role): boolean {
38
34
  if (!userRole) return false;
39
35
 
@@ -44,27 +40,28 @@ function hasRole(userRole: string | undefined, requiredRole: Role): boolean {
44
40
  return userLevel <= requiredLevel;
45
41
  }
46
42
 
43
+ export function isAdmin(userRole: string | undefined): boolean {
44
+ return userRole === Role.ADMIN;
45
+ }
46
+
47
47
  const CUSTOM_ROLE_NAME_TO_ENUM: Record<string, Role> = {
48
- 'Administrateur': Role.ADMIN,
49
- 'Manager': Role.MANAGER,
50
- 'Commercial': Role.COMMERCIAL,
51
- 'Télépro': Role.TELEPRO,
52
- 'Comptable': Role.COMPTABLE,
48
+ Administrateur: Role.ADMIN,
49
+ Manager: Role.MANAGER,
50
+ Commercial: Role.COMMERCIAL,
51
+ Télépro: Role.TELEPRO,
52
+ Comptable: Role.COMPTABLE,
53
53
  };
54
54
 
55
55
  /**
56
- * Résout la valeur de l'enum Role à partir du nom d'un CustomRole.
57
- * Retourne Role.USER pour les profils personnalisés non reconnus.
56
+ * Résout le Role enum à partir du nom d'un CustomRole.
57
+ * Retourne USER si aucune correspondance n'est trouvée.
58
58
  */
59
59
  export function resolveRoleFromCustomRoleName(customRoleName: string | null | undefined): Role {
60
60
  if (!customRoleName) return Role.USER;
61
61
  return CUSTOM_ROLE_NAME_TO_ENUM[customRoleName] ?? Role.USER;
62
62
  }
63
63
 
64
- /**
65
- * Middleware pour vérifier le rôle côté serveur
66
- */
67
- export async function requireRole(headers: Headers, requiredRole: Role) {
64
+ async function requireRole(headers: Headers, requiredRole: Role) {
68
65
  const session = await auth.api.getSession({ headers });
69
66
 
70
67
  if (!session) {