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,412 @@
1
+ /**
2
+ * Système de permissions du CRM
3
+ * Chaque permission est définie par un code unique et une description
4
+ */
5
+
6
+ export interface Permission {
7
+ code: string;
8
+ name: string;
9
+ description: string;
10
+ category: string;
11
+ }
12
+
13
+ export const PERMISSION_CATEGORIES = {
14
+ ANALYTICS: 'Analytics',
15
+ CONTACTS: 'Contacts',
16
+ TASKS: 'Tâches',
17
+ TEMPLATES: 'Templates',
18
+ INTEGRATIONS: 'Intégrations',
19
+ USERS: 'Utilisateurs',
20
+ SETTINGS: 'Paramètres',
21
+ GENERAL: 'Général',
22
+ } as const;
23
+
24
+ export const PERMISSIONS: Permission[] = [
25
+ // Analytics
26
+ {
27
+ code: 'analytics.view',
28
+ name: 'Voir les analytics',
29
+ description: 'Permet de voir les statistiques et analytics',
30
+ category: PERMISSION_CATEGORIES.ANALYTICS,
31
+ },
32
+ {
33
+ code: 'analytics.export',
34
+ name: 'Exporter les analytics',
35
+ description: "Autorise l'exportation des données analytics",
36
+ category: PERMISSION_CATEGORIES.ANALYTICS,
37
+ },
38
+
39
+ // Contacts
40
+ {
41
+ code: 'contacts.view_all',
42
+ name: 'Voir tous les contacts',
43
+ description:
44
+ 'Permet de voir tous les contacts de toutes les entreprises (réservé aux administrateurs)',
45
+ category: PERMISSION_CATEGORIES.CONTACTS,
46
+ },
47
+ {
48
+ code: 'contacts.view_own',
49
+ name: 'Voir ses contacts',
50
+ description: 'Permet de voir uniquement les contacts qui lui sont assignés',
51
+ category: PERMISSION_CATEGORIES.CONTACTS,
52
+ },
53
+ {
54
+ code: 'contacts.view_unassigned',
55
+ name: 'Voir les contacts non attribués',
56
+ description: "Permet de voir les contacts qui n'ont pas encore été assignés",
57
+ category: PERMISSION_CATEGORIES.CONTACTS,
58
+ },
59
+ {
60
+ code: 'contacts.create',
61
+ name: 'Créer un contact',
62
+ description: 'Autorise la création de nouveaux contacts',
63
+ category: PERMISSION_CATEGORIES.CONTACTS,
64
+ },
65
+ {
66
+ code: 'contacts.edit_own',
67
+ name: 'Modifier ses contacts',
68
+ description: 'Autorise la modification des contacts qui lui sont assignés',
69
+ category: PERMISSION_CATEGORIES.CONTACTS,
70
+ },
71
+ {
72
+ code: 'contacts.edit_all',
73
+ name: 'Modifier tous les contacts',
74
+ description: 'Autorise la modification de tous les contacts',
75
+ category: PERMISSION_CATEGORIES.CONTACTS,
76
+ },
77
+ {
78
+ code: 'contacts.delete',
79
+ name: 'Supprimer un contact',
80
+ description: 'Autorise la suppression de contacts',
81
+ category: PERMISSION_CATEGORIES.CONTACTS,
82
+ },
83
+ {
84
+ code: 'contacts.assign',
85
+ name: 'Assigner des contacts',
86
+ description: "Permet d'assigner des contacts à d'autres utilisateurs",
87
+ category: PERMISSION_CATEGORIES.CONTACTS,
88
+ },
89
+ {
90
+ code: 'contacts.import',
91
+ name: 'Importer des contacts',
92
+ description: "Autorise l'importation de contacts via CSV/Excel",
93
+ category: PERMISSION_CATEGORIES.CONTACTS,
94
+ },
95
+ {
96
+ code: 'contacts.export',
97
+ name: 'Exporter des contacts',
98
+ description: "Autorise l'exportation de contacts",
99
+ category: PERMISSION_CATEGORIES.CONTACTS,
100
+ },
101
+ {
102
+ code: 'contacts.upload_files',
103
+ name: 'Upload de fichiers',
104
+ description: 'Permet de télécharger des fichiers pour les contacts',
105
+ category: PERMISSION_CATEGORIES.CONTACTS,
106
+ },
107
+ {
108
+ code: 'contacts.view_files',
109
+ name: 'Voir les fichiers',
110
+ description: 'Permet de voir les fichiers associés aux contacts',
111
+ category: PERMISSION_CATEGORIES.CONTACTS,
112
+ },
113
+
114
+ // Tâches
115
+ {
116
+ code: 'tasks.view_all',
117
+ name: 'Voir toutes les tâches',
118
+ description: 'Permet de voir toutes les tâches de tous les utilisateurs',
119
+ category: PERMISSION_CATEGORIES.TASKS,
120
+ },
121
+ {
122
+ code: 'tasks.view_own',
123
+ name: 'Voir ses tâches',
124
+ description: 'Permet de voir uniquement ses propres tâches',
125
+ category: PERMISSION_CATEGORIES.TASKS,
126
+ },
127
+ {
128
+ code: 'tasks.create',
129
+ name: 'Créer une tâche',
130
+ description: 'Autorise la création de nouvelles tâches',
131
+ category: PERMISSION_CATEGORIES.TASKS,
132
+ },
133
+ {
134
+ code: 'tasks.edit_own',
135
+ name: 'Modifier ses tâches',
136
+ description: 'Autorise la modification de ses propres tâches',
137
+ category: PERMISSION_CATEGORIES.TASKS,
138
+ },
139
+ {
140
+ code: 'tasks.edit_all',
141
+ name: 'Modifier toutes les tâches',
142
+ description: 'Autorise la modification de toutes les tâches',
143
+ category: PERMISSION_CATEGORIES.TASKS,
144
+ },
145
+ {
146
+ code: 'tasks.delete',
147
+ name: 'Supprimer une tâche',
148
+ description: 'Autorise la suppression de tâches',
149
+ category: PERMISSION_CATEGORIES.TASKS,
150
+ },
151
+ {
152
+ code: 'tasks.assign',
153
+ name: 'Assigner des tâches',
154
+ description: "Permet d'assigner des tâches à d'autres utilisateurs",
155
+ category: PERMISSION_CATEGORIES.TASKS,
156
+ },
157
+
158
+ // Templates
159
+ {
160
+ code: 'templates.view',
161
+ name: 'Voir les templates',
162
+ description: 'Permet de voir les templates disponibles',
163
+ category: PERMISSION_CATEGORIES.TEMPLATES,
164
+ },
165
+ {
166
+ code: 'templates.create',
167
+ name: 'Créer un template',
168
+ description: 'Autorise la création de nouveaux templates',
169
+ category: PERMISSION_CATEGORIES.TEMPLATES,
170
+ },
171
+ {
172
+ code: 'templates.edit',
173
+ name: 'Modifier un template',
174
+ description: 'Autorise la modification de templates',
175
+ category: PERMISSION_CATEGORIES.TEMPLATES,
176
+ },
177
+ {
178
+ code: 'templates.delete',
179
+ name: 'Supprimer un template',
180
+ description: 'Autorise la suppression de templates',
181
+ category: PERMISSION_CATEGORIES.TEMPLATES,
182
+ },
183
+
184
+ // Intégrations
185
+ {
186
+ code: 'integrations.view',
187
+ name: 'Voir les intégrations',
188
+ description: 'Permet de voir les intégrations configurées',
189
+ category: PERMISSION_CATEGORIES.INTEGRATIONS,
190
+ },
191
+ {
192
+ code: 'integrations.create',
193
+ name: 'Créer une intégration',
194
+ description: 'Autorise la création de nouvelles intégrations',
195
+ category: PERMISSION_CATEGORIES.INTEGRATIONS,
196
+ },
197
+ {
198
+ code: 'integrations.edit',
199
+ name: 'Modifier une intégration',
200
+ description: "Autorise la modification d'intégrations",
201
+ category: PERMISSION_CATEGORIES.INTEGRATIONS,
202
+ },
203
+ {
204
+ code: 'integrations.delete',
205
+ name: 'Supprimer une intégration',
206
+ description: "Autorise la suppression d'intégrations",
207
+ category: PERMISSION_CATEGORIES.INTEGRATIONS,
208
+ },
209
+ {
210
+ code: 'integrations.google.connect',
211
+ name: 'Connecter Google',
212
+ description: 'Permet de connecter son compte Google',
213
+ category: PERMISSION_CATEGORIES.INTEGRATIONS,
214
+ },
215
+ {
216
+ code: 'integrations.meta.manage',
217
+ name: 'Gérer les leads Meta',
218
+ description: 'Permet de configurer les intégrations Meta (Facebook)',
219
+ category: PERMISSION_CATEGORIES.INTEGRATIONS,
220
+ },
221
+ {
222
+ code: 'integrations.google_ads.manage',
223
+ name: 'Gérer les leads Google Ads',
224
+ description: 'Permet de configurer les intégrations Google Ads',
225
+ category: PERMISSION_CATEGORIES.INTEGRATIONS,
226
+ },
227
+ {
228
+ code: 'integrations.google_sheets.manage',
229
+ name: 'Gérer les Google Sheets',
230
+ description: 'Permet de configurer les synchronisations Google Sheets',
231
+ category: PERMISSION_CATEGORIES.INTEGRATIONS,
232
+ },
233
+
234
+ // Utilisateurs
235
+ {
236
+ code: 'users.view',
237
+ name: 'Voir les utilisateurs',
238
+ description: 'Permet de voir la liste des utilisateurs',
239
+ category: PERMISSION_CATEGORIES.USERS,
240
+ },
241
+ {
242
+ code: 'users.create',
243
+ name: 'Créer un utilisateur',
244
+ description: 'Autorise la création de nouveaux utilisateurs',
245
+ category: PERMISSION_CATEGORIES.USERS,
246
+ },
247
+ {
248
+ code: 'users.edit',
249
+ name: 'Modifier un utilisateur',
250
+ description: "Autorise la modification d'utilisateurs",
251
+ category: PERMISSION_CATEGORIES.USERS,
252
+ },
253
+ {
254
+ code: 'users.deactivate',
255
+ name: 'Désactiver un utilisateur',
256
+ description: "Autorise la désactivation d'utilisateurs",
257
+ category: PERMISSION_CATEGORIES.USERS,
258
+ },
259
+ {
260
+ code: 'users.delete',
261
+ name: 'Supprimer un utilisateur',
262
+ description: "Autorise la suppression définitive d'utilisateurs",
263
+ category: PERMISSION_CATEGORIES.USERS,
264
+ },
265
+ {
266
+ code: 'users.manage_roles',
267
+ name: 'Gérer les rôles',
268
+ description: 'Permet de gérer les profils et permissions',
269
+ category: PERMISSION_CATEGORIES.USERS,
270
+ },
271
+
272
+ // Paramètres
273
+ {
274
+ code: 'settings.view',
275
+ name: 'Voir les paramètres',
276
+ description: 'Permet de voir les paramètres du système',
277
+ category: PERMISSION_CATEGORIES.SETTINGS,
278
+ },
279
+ {
280
+ code: 'settings.company.edit',
281
+ name: "Modifier les infos de l'entreprise",
282
+ description: "Autorise la modification des informations de l'entreprise",
283
+ category: PERMISSION_CATEGORIES.SETTINGS,
284
+ },
285
+ {
286
+ code: 'settings.smtp.edit',
287
+ name: 'Configurer SMTP',
288
+ description: 'Permet de configurer les paramètres SMTP',
289
+ category: PERMISSION_CATEGORIES.SETTINGS,
290
+ },
291
+ {
292
+ code: 'settings.status.manage',
293
+ name: 'Gérer les statuts',
294
+ description: 'Permet de créer, modifier et supprimer des statuts',
295
+ category: PERMISSION_CATEGORIES.SETTINGS,
296
+ },
297
+
298
+ // Général
299
+ {
300
+ code: 'general.view_all_companies',
301
+ name: 'Voir les contacts de toutes les sociétés',
302
+ description:
303
+ 'Permet de voir tous les contacts de toutes les entreprises (réservé aux administrateurs)',
304
+ category: PERMISSION_CATEGORIES.GENERAL,
305
+ },
306
+ ];
307
+
308
+ // Regrouper les permissions par catégorie
309
+ export const PERMISSIONS_BY_CATEGORY = PERMISSIONS.reduce(
310
+ (acc, permission) => {
311
+ if (!acc[permission.category]) {
312
+ acc[permission.category] = [];
313
+ }
314
+ acc[permission.category].push(permission);
315
+ return acc;
316
+ },
317
+ {} as Record<string, Permission[]>,
318
+ );
319
+
320
+ // Profils par défaut avec leurs permissions
321
+ export const DEFAULT_ROLES = {
322
+ ADMIN: {
323
+ name: 'Administrateur',
324
+ description: 'Accès complet à toutes les fonctionnalités du système',
325
+ permissions: PERMISSIONS.map((p) => p.code),
326
+ },
327
+ MANAGER: {
328
+ name: 'Manager',
329
+ description: "Gestion d'équipe et accès étendu aux leads",
330
+ permissions: [
331
+ 'analytics.view',
332
+ 'contacts.view_all',
333
+ 'contacts.create',
334
+ 'contacts.edit_all',
335
+ 'contacts.assign',
336
+ 'contacts.import',
337
+ 'contacts.export',
338
+ 'contacts.view_files',
339
+ 'contacts.upload_files',
340
+ 'tasks.view_all',
341
+ 'tasks.create',
342
+ 'tasks.edit_all',
343
+ 'tasks.assign',
344
+ 'templates.view',
345
+ 'templates.create',
346
+ 'templates.edit',
347
+ 'templates.delete',
348
+ 'integrations.view',
349
+ 'users.view',
350
+ 'settings.view',
351
+ ],
352
+ },
353
+ COMMERCIAL: {
354
+ name: 'Commercial',
355
+ description: 'Accès de base pour la gestion des leads personnels',
356
+ permissions: [
357
+ 'contacts.view_own',
358
+ 'contacts.view_unassigned',
359
+ 'contacts.create',
360
+ 'contacts.edit_own',
361
+ 'contacts.view_files',
362
+ 'contacts.upload_files',
363
+ 'tasks.view_own',
364
+ 'tasks.create',
365
+ 'tasks.edit_own',
366
+ 'templates.view',
367
+ 'templates.create',
368
+ 'templates.edit',
369
+ 'integrations.view',
370
+ 'integrations.google.connect',
371
+ ],
372
+ },
373
+ TELEPRO: {
374
+ name: 'Télépro',
375
+ description: 'Accès limité pour la qualification de leads',
376
+ permissions: [
377
+ 'contacts.view_own',
378
+ 'contacts.view_unassigned',
379
+ 'contacts.create',
380
+ 'contacts.edit_own',
381
+ 'contacts.view_files',
382
+ 'tasks.view_own',
383
+ 'tasks.create',
384
+ 'tasks.edit_own',
385
+ 'templates.view',
386
+ ],
387
+ },
388
+ COMPTABLE: {
389
+ name: 'Comptable',
390
+ description: 'Accès limité aux informations financières et reporting',
391
+ permissions: [
392
+ 'analytics.view',
393
+ 'analytics.export',
394
+ 'contacts.view_all',
395
+ 'contacts.export',
396
+ 'tasks.view_all',
397
+ 'templates.view',
398
+ 'settings.view',
399
+ ],
400
+ },
401
+ };
402
+
403
+ // Helper pour vérifier si un rôle a une permission
404
+ export function hasPermission(rolePermissions: string[], requiredPermission: string): boolean {
405
+ return rolePermissions.includes(requiredPermission);
406
+ }
407
+
408
+ // Helper pour obtenir toutes les permissions d'un rôle
409
+ export function getRolePermissions(role: string): string[] {
410
+ const roleKey = role.toUpperCase() as keyof typeof DEFAULT_ROLES;
411
+ return DEFAULT_ROLES[roleKey]?.permissions || [];
412
+ }
@@ -0,0 +1,32 @@
1
+ import 'dotenv/config';
2
+ import { PrismaPg } from '@prisma/adapter-pg';
3
+ import { PrismaClient } from '../../generated/prisma/client';
4
+ import { Pool } from 'pg';
5
+
6
+ const connectionString = `${process.env.DATABASE_URL}`;
7
+
8
+ // Créer un pool de connexions PostgreSQL pour une meilleure gestion
9
+ const pool = new Pool({
10
+ connectionString,
11
+ max: 10, // Nombre maximum de connexions dans le pool
12
+ idleTimeoutMillis: 30000, // Fermer les connexions inactives après 30s
13
+ connectionTimeoutMillis: 10000, // Timeout de connexion de 10s
14
+
15
+ });
16
+
17
+ const adapter = new PrismaPg(pool);
18
+
19
+ const prisma = new PrismaClient({
20
+ adapter,
21
+ log: process.env.NODE_ENV === 'development' ? ['error', 'warn'] : ['error'],
22
+ });
23
+
24
+ // Gestion propre de la fermeture lors de l'arrêt de l'application
25
+ if (typeof window === 'undefined') {
26
+ process.on('beforeExit', async () => {
27
+ await prisma.$disconnect();
28
+ await pool.end();
29
+ });
30
+ }
31
+
32
+ export { prisma };
@@ -0,0 +1,120 @@
1
+ import { auth } from './auth';
2
+ import { prisma } from './prisma';
3
+
4
+ export enum Role {
5
+ USER = 'USER',
6
+ ADMIN = 'ADMIN',
7
+ MANAGER = 'MANAGER',
8
+ COMMERCIAL = 'COMMERCIAL',
9
+ TELEPRO = 'TELEPRO',
10
+ COMPTABLE = 'COMPTABLE',
11
+ }
12
+
13
+ /**
14
+ * Hiérarchie des rôles (du plus élevé au plus bas)
15
+ * Un rôle supérieur a automatiquement les permissions des rôles inférieurs
16
+ */
17
+ const ROLE_HIERARCHY: { [key: string]: number } = {
18
+ [Role.ADMIN]: 1, // Niveau 1 : Le plus élevé
19
+ [Role.MANAGER]: 2, // Niveau 2
20
+ [Role.COMMERCIAL]: 3, // Niveau 3
21
+ [Role.TELEPRO]: 4, // Niveau 4
22
+ [Role.COMPTABLE]: 5, // Niveau 5
23
+ [Role.USER]: 6, // Niveau 6 : Le plus bas
24
+ };
25
+
26
+ /**
27
+ * Obtient le niveau hiérarchique d'un rôle
28
+ */
29
+ function getRoleLevel(role: string): number {
30
+ return ROLE_HIERARCHY[role] || 999; // Rôle inconnu = niveau très bas
31
+ }
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
+ export function hasRole(userRole: string | undefined, requiredRole: Role): boolean {
38
+ if (!userRole) return false;
39
+
40
+ const userLevel = getRoleLevel(userRole);
41
+ const requiredLevel = getRoleLevel(requiredRole);
42
+
43
+ // L'utilisateur a le rôle requis s'il a le même rôle ou un rôle supérieur (niveau plus bas)
44
+ return userLevel <= requiredLevel;
45
+ }
46
+
47
+ /**
48
+ * Vérifie si l'utilisateur est admin
49
+ */
50
+ export function isAdmin(userRole: string | undefined): boolean {
51
+ return userRole === Role.ADMIN;
52
+ }
53
+
54
+ /**
55
+ * Vérifie si l'utilisateur est manager ou supérieur
56
+ */
57
+ export function isManagerOrAbove(userRole: string | undefined): boolean {
58
+ if (!userRole) return false;
59
+ return getRoleLevel(userRole) <= 2; // ADMIN ou MANAGER
60
+ }
61
+
62
+ /**
63
+ * Vérifie si l'utilisateur est commercial ou supérieur
64
+ */
65
+ export function isCommercialOrAbove(userRole: string | undefined): boolean {
66
+ if (!userRole) return false;
67
+ return getRoleLevel(userRole) <= 3; // ADMIN, MANAGER ou COMMERCIAL
68
+ }
69
+
70
+ /**
71
+ * Vérifie si l'utilisateur est télépro ou supérieur
72
+ */
73
+ export function isTeleproOrAbove(userRole: string | undefined): boolean {
74
+ if (!userRole) return false;
75
+ return getRoleLevel(userRole) <= 4; // ADMIN, MANAGER, COMMERCIAL ou TELEPRO
76
+ }
77
+
78
+ /**
79
+ * Vérifie si l'utilisateur est comptable ou supérieur
80
+ */
81
+ export function isComptableOrAbove(userRole: string | undefined): boolean {
82
+ if (!userRole) return false;
83
+ return getRoleLevel(userRole) <= 5; // ADMIN, MANAGER, COMMERCIAL, TELEPRO ou COMPTABLE
84
+ }
85
+
86
+ /**
87
+ * Middleware pour vérifier le rôle côté serveur
88
+ */
89
+ export async function requireRole(headers: Headers, requiredRole: Role) {
90
+ const session = await auth.api.getSession({ headers });
91
+
92
+ if (!session) {
93
+ throw new Error('Non authentifié');
94
+ }
95
+
96
+ // Récupérer le rôle depuis la session ou depuis la base de données
97
+ let userRole: string | undefined = (session.user as any).role;
98
+
99
+ // Si le rôle n'est pas dans la session, le récupérer depuis la base de données
100
+ if (!userRole && session.user?.id) {
101
+ const user = await prisma.user.findUnique({
102
+ where: { id: session.user.id },
103
+ select: { role: true },
104
+ });
105
+ userRole = user?.role || undefined;
106
+ }
107
+
108
+ if (!hasRole(userRole, requiredRole)) {
109
+ throw new Error('Permissions insuffisantes');
110
+ }
111
+
112
+ return session;
113
+ }
114
+
115
+ /**
116
+ * Middleware pour vérifier si l'utilisateur est admin
117
+ */
118
+ export async function requireAdmin(headers: Headers) {
119
+ return requireRole(headers, Role.ADMIN);
120
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Système de variables pour les templates
3
+ * Les variables sont au format {{variableName}}
4
+ */
5
+
6
+ export interface ContactVariables {
7
+ firstName?: string | null;
8
+ lastName?: string | null;
9
+ civility?: string | null;
10
+ email?: string | null;
11
+ phone?: string | null;
12
+ secondaryPhone?: string | null;
13
+ address?: string | null;
14
+ city?: string | null;
15
+ postalCode?: string | null;
16
+ companyName?: string | null;
17
+ }
18
+
19
+ /**
20
+ * Liste des variables disponibles avec leurs descriptions
21
+ */
22
+ export const AVAILABLE_VARIABLES = [
23
+ { key: '{{firstName}}', description: 'Prénom du contact' },
24
+ { key: '{{lastName}}', description: 'Nom du contact' },
25
+ { key: '{{fullName}}', description: 'Prénom et nom complets' },
26
+ { key: '{{civility}}', description: 'Civilité (M., Mme, Mlle)' },
27
+ { key: '{{email}}', description: 'Email du contact' },
28
+ { key: '{{phone}}', description: 'Téléphone principal' },
29
+ { key: '{{secondaryPhone}}', description: 'Téléphone secondaire' },
30
+ { key: '{{address}}', description: 'Adresse complète' },
31
+ { key: '{{city}}', description: 'Ville' },
32
+ { key: '{{postalCode}}', description: 'Code postal' },
33
+ { key: '{{companyName}}', description: "Nom de l'entreprise associée" },
34
+ ];
35
+
36
+ /**
37
+ * Remplace les variables dans un template par les valeurs du contact
38
+ */
39
+ export function replaceTemplateVariables(template: string, variables: ContactVariables): string {
40
+ let result = template;
41
+
42
+ // Remplacer les variables simples
43
+ result = result.replace(/\{\{firstName\}\}/g, variables.firstName || '');
44
+ result = result.replace(/\{\{lastName\}\}/g, variables.lastName || '');
45
+ result = result.replace(/\{\{civility\}\}/g, variables.civility || '');
46
+ result = result.replace(/\{\{email\}\}/g, variables.email || '');
47
+ result = result.replace(/\{\{phone\}\}/g, variables.phone || '');
48
+ result = result.replace(/\{\{secondaryPhone\}\}/g, variables.secondaryPhone || '');
49
+ result = result.replace(/\{\{address\}\}/g, variables.address || '');
50
+ result = result.replace(/\{\{city\}\}/g, variables.city || '');
51
+ result = result.replace(/\{\{postalCode\}\}/g, variables.postalCode || '');
52
+ result = result.replace(/\{\{companyName\}\}/g, variables.companyName || '');
53
+
54
+ // Variable composée : fullName
55
+ const fullName = [variables.firstName, variables.lastName].filter(Boolean).join(' ') || '';
56
+ result = result.replace(/\{\{fullName\}\}/g, fullName);
57
+
58
+ return result;
59
+ }
60
+
61
+ /**
62
+ * Extrait toutes les variables utilisées dans un template
63
+ */
64
+ export function extractVariables(template: string): string[] {
65
+ const regex = /\{\{(\w+)\}\}/g;
66
+ const variables: string[] = [];
67
+ let match;
68
+
69
+ while ((match = regex.exec(template)) !== null) {
70
+ if (!variables.includes(match[1])) {
71
+ variables.push(match[1]);
72
+ }
73
+ }
74
+
75
+ return variables;
76
+ }
@@ -0,0 +1,46 @@
1
+ import { clsx } from 'clsx';
2
+ import { twMerge } from 'tailwind-merge';
3
+
4
+ export function cn(...inputs: Array<string | false | null | undefined>) {
5
+ return twMerge(clsx(inputs));
6
+ }
7
+
8
+ /**
9
+ * Normalise un numéro de téléphone au format français : 0X XX XX XX XX
10
+ * - Supprime tous les caractères non numériques
11
+ * - Gère les numéros internationaux (+33, 0033)
12
+ * - S'assure qu'il y a un 0 au début
13
+ * - Formate avec des espaces pour la lisibilité
14
+ */
15
+ export function normalizePhoneNumber(phone: string | null | undefined): string {
16
+ if (!phone) return '';
17
+
18
+ // Supprimer tous les caractères non numériques
19
+ let digits = phone.replace(/\D/g, '');
20
+
21
+ // Gérer les numéros internationaux français
22
+ if (digits.startsWith('33')) {
23
+ // +33 ou 0033 : supprimer le préfixe et ajouter 0
24
+ digits = '0' + digits.substring(2);
25
+ } else if (digits.startsWith('0033')) {
26
+ digits = '0' + digits.substring(4);
27
+ }
28
+
29
+ // S'assurer qu'il y a un 0 au début
30
+ if (digits.length > 0 && !digits.startsWith('0')) {
31
+ digits = '0' + digits;
32
+ }
33
+
34
+ // Limiter à 10 chiffres (format français standard)
35
+ if (digits.length > 10) {
36
+ digits = digits.substring(0, 10);
37
+ }
38
+
39
+ // Formater avec des espaces : 0X XX XX XX XX
40
+ if (digits.length === 10) {
41
+ return `${digits[0]}${digits[1]} ${digits[2]}${digits[3]} ${digits[4]}${digits[5]} ${digits[6]}${digits[7]} ${digits[8]}${digits[9]}`;
42
+ }
43
+
44
+ // Si le numéro n'a pas 10 chiffres, retourner tel quel (cas d'erreur)
45
+ return digits;
46
+ }