create-crm-tmp 1.1.3 → 2.1.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 (276) hide show
  1. package/bin/create-crm-tmp.js +56 -35
  2. package/package.json +1 -1
  3. package/template/.prettierignore +2 -0
  4. package/template/README.md +230 -115
  5. package/template/components.json +22 -0
  6. package/template/eslint.config.mjs +13 -0
  7. package/template/exemple-contacts.csv +54 -0
  8. package/template/next.config.ts +41 -1
  9. package/template/package.json +63 -15
  10. package/template/prisma/migrations/20260318095700_init_db/migration.sql +978 -0
  11. package/template/prisma/schema.prisma +311 -67
  12. package/template/src/app/(auth)/invite/[token]/page.tsx +28 -29
  13. package/template/src/app/(auth)/layout.tsx +1 -1
  14. package/template/src/app/(auth)/reset-password/complete/page.tsx +21 -27
  15. package/template/src/app/(auth)/reset-password/page.tsx +14 -10
  16. package/template/src/app/(auth)/reset-password/verify/page.tsx +14 -10
  17. package/template/src/app/(auth)/signin/page.tsx +34 -23
  18. package/template/src/app/(dashboard)/agenda/page.tsx +3655 -2357
  19. package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +10 -7
  20. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +609 -338
  21. package/template/src/app/(dashboard)/automatisation/new/page.tsx +11 -8
  22. package/template/src/app/(dashboard)/automatisation/page.tsx +463 -186
  23. package/template/src/app/(dashboard)/closing/page.tsx +517 -469
  24. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +6151 -4210
  25. package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +1702 -0
  26. package/template/src/app/(dashboard)/contacts/loading.tsx +13 -0
  27. package/template/src/app/(dashboard)/contacts/page.tsx +4124 -2130
  28. package/template/src/app/(dashboard)/dashboard/page.tsx +119 -105
  29. package/template/src/app/(dashboard)/dev/page.tsx +1291 -0
  30. package/template/src/app/(dashboard)/error.tsx +37 -0
  31. package/template/src/app/(dashboard)/layout.tsx +6 -2
  32. package/template/src/app/(dashboard)/loading.tsx +5 -0
  33. package/template/src/app/(dashboard)/settings/loading.tsx +19 -0
  34. package/template/src/app/(dashboard)/settings/page.tsx +1773 -3362
  35. package/template/src/app/(dashboard)/templates/page.tsx +504 -303
  36. package/template/src/app/(dashboard)/users/list/page.tsx +364 -355
  37. package/template/src/app/(dashboard)/users/page.tsx +279 -310
  38. package/template/src/app/(dashboard)/users/permissions/page.tsx +104 -99
  39. package/template/src/app/(dashboard)/users/roles/page.tsx +169 -140
  40. package/template/src/app/api/agenda/google-events/route.ts +92 -0
  41. package/template/src/app/api/audit-logs/route.ts +1 -1
  42. package/template/src/app/api/auth/check-active/route.ts +3 -2
  43. package/template/src/app/api/auth/google/callback/route.ts +8 -5
  44. package/template/src/app/api/auth/google/disconnect/route.ts +2 -2
  45. package/template/src/app/api/auth/google/route.ts +2 -1
  46. package/template/src/app/api/auth/google/status/route.ts +7 -31
  47. package/template/src/app/api/companies/[id]/activities/route.ts +129 -0
  48. package/template/src/app/api/companies/[id]/route.ts +194 -0
  49. package/template/src/app/api/companies/export/route.ts +206 -0
  50. package/template/src/app/api/companies/route.ts +196 -0
  51. package/template/src/app/api/contact-views/[id]/pin/route.ts +69 -0
  52. package/template/src/app/api/contact-views/[id]/route.ts +197 -0
  53. package/template/src/app/api/contact-views/route.ts +146 -0
  54. package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +55 -0
  55. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +20 -48
  56. package/template/src/app/api/contacts/[id]/files/route.ts +125 -186
  57. package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +27 -1
  58. package/template/src/app/api/contacts/[id]/interactions/route.ts +45 -8
  59. package/template/src/app/api/contacts/[id]/kyc/route.ts +81 -0
  60. package/template/src/app/api/contacts/[id]/meet/route.ts +55 -29
  61. package/template/src/app/api/contacts/[id]/route.ts +184 -21
  62. package/template/src/app/api/contacts/[id]/send-email/route.ts +33 -11
  63. package/template/src/app/api/contacts/[id]/workflows/run/route.ts +67 -0
  64. package/template/src/app/api/contacts/export/route.ts +22 -31
  65. package/template/src/app/api/contacts/import/route.ts +77 -44
  66. package/template/src/app/api/contacts/import-preview/route.ts +139 -0
  67. package/template/src/app/api/contacts/origins/route.ts +63 -0
  68. package/template/src/app/api/contacts/route.ts +322 -57
  69. package/template/src/app/api/cron/cleanup-editor-images/route.ts +166 -0
  70. package/template/src/app/api/dashboard/stats/route.ts +9 -292
  71. package/template/src/app/api/dashboard/widgets/[id]/route.ts +0 -3
  72. package/template/src/app/api/dashboard/widgets/route.ts +19 -19
  73. package/template/src/app/api/dev/reminders/test/route.ts +114 -0
  74. package/template/src/app/api/editor/upload-image/route.ts +61 -0
  75. package/template/src/app/api/integrations/google-sheet/jobs/[jobId]/route.ts +47 -0
  76. package/template/src/app/api/integrations/google-sheet/jobs/usage/route.ts +50 -0
  77. package/template/src/app/api/integrations/google-sheet/sync/route.ts +28 -542
  78. package/template/src/app/api/invite/complete/route.ts +20 -23
  79. package/template/src/app/api/jobs/google-sheet/process/route.ts +84 -0
  80. package/template/src/app/api/jobs/google-sheet/schedule/route.ts +50 -0
  81. package/template/src/app/api/reminders/clear/route.ts +120 -0
  82. package/template/src/app/api/reminders/clear/undo/route.ts +112 -0
  83. package/template/src/app/api/reminders/route.ts +165 -39
  84. package/template/src/app/api/reminders/state/route.ts +164 -0
  85. package/template/src/app/api/reset-password/complete/route.ts +11 -13
  86. package/template/src/app/api/reset-password/request/route.ts +1 -1
  87. package/template/src/app/api/reset-password/verify/route.ts +1 -1
  88. package/template/src/app/api/send/route.ts +25 -47
  89. package/template/src/app/api/settings/closing-reasons/[id]/route.ts +10 -21
  90. package/template/src/app/api/settings/closing-reasons/route.ts +10 -21
  91. package/template/src/app/api/settings/company/route.ts +19 -26
  92. package/template/src/app/api/settings/google-ads/[id]/route.ts +20 -23
  93. package/template/src/app/api/settings/google-ads/route.ts +34 -23
  94. package/template/src/app/api/settings/google-calendar/calendars/route.ts +97 -0
  95. package/template/src/app/api/settings/google-calendar/route.ts +124 -0
  96. package/template/src/app/api/settings/google-sheet/[id]/route.ts +48 -23
  97. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +56 -32
  98. package/template/src/app/api/settings/google-sheet/preview/route.ts +110 -0
  99. package/template/src/app/api/settings/google-sheet/route.ts +34 -23
  100. package/template/src/app/api/settings/integrations/logs/route.ts +93 -0
  101. package/template/src/app/api/settings/integrations/notifications/route.ts +67 -0
  102. package/template/src/app/api/settings/meta-leads/[id]/route.ts +20 -24
  103. package/template/src/app/api/settings/meta-leads/route.ts +34 -25
  104. package/template/src/app/api/settings/smtp/route.ts +53 -6
  105. package/template/src/app/api/settings/statuses/[id]/route.ts +29 -32
  106. package/template/src/app/api/settings/statuses/route.ts +24 -22
  107. package/template/src/app/api/statuses/route.ts +2 -5
  108. package/template/src/app/api/tasks/[id]/attendees/route.ts +36 -13
  109. package/template/src/app/api/tasks/[id]/route.ts +357 -145
  110. package/template/src/app/api/tasks/meet/route.ts +37 -26
  111. package/template/src/app/api/tasks/route.ts +201 -96
  112. package/template/src/app/api/templates/[id]/route.ts +22 -13
  113. package/template/src/app/api/templates/route.ts +22 -5
  114. package/template/src/app/api/users/[id]/resend-invite/route.ts +95 -0
  115. package/template/src/app/api/users/[id]/route.ts +22 -16
  116. package/template/src/app/api/users/commercials/route.ts +38 -0
  117. package/template/src/app/api/users/for-agenda/route.ts +1 -2
  118. package/template/src/app/api/users/list/route.ts +57 -19
  119. package/template/src/app/api/users/route.ts +89 -34
  120. package/template/src/app/api/webhooks/google-ads/route.ts +40 -1
  121. package/template/src/app/api/webhooks/meta-leads/route.ts +38 -1
  122. package/template/src/app/api/workflows/[id]/route.ts +29 -6
  123. package/template/src/app/api/workflows/process/route.ts +505 -170
  124. package/template/src/app/api/workflows/route.ts +42 -4
  125. package/template/src/app/globals.css +512 -32
  126. package/template/src/app/layout.tsx +28 -9
  127. package/template/src/app/page.tsx +37 -7
  128. package/template/src/components/address-autocomplete.tsx +233 -0
  129. package/template/src/components/config-error-alert.tsx +46 -0
  130. package/template/src/components/contacts/filter-bar.tsx +190 -0
  131. package/template/src/components/contacts/filter-builder.tsx +574 -0
  132. package/template/src/components/contacts/save-view-dialog.tsx +160 -0
  133. package/template/src/components/contacts/views-tab-bar.tsx +449 -0
  134. package/template/src/components/dashboard/activity-chart.tsx +6 -1
  135. package/template/src/components/dashboard/add-widget-dialog.tsx +13 -17
  136. package/template/src/components/dashboard/color-picker.tsx +7 -8
  137. package/template/src/components/dashboard/recent-activity.tsx +2 -5
  138. package/template/src/components/dashboard/stat-card.tsx +1 -3
  139. package/template/src/components/dashboard/status-distribution-chart.tsx +0 -1
  140. package/template/src/components/dashboard/top-contacts-list.tsx +7 -13
  141. package/template/src/components/dashboard/upcoming-tasks-list.tsx +2 -5
  142. package/template/src/components/dashboard/widget-wrapper.tsx +3 -6
  143. package/template/src/components/date-picker.tsx +399 -0
  144. package/template/src/components/editor/upload-editor-image.ts +42 -0
  145. package/template/src/components/editor.tsx +188 -35
  146. package/template/src/components/email-template.tsx +4 -2
  147. package/template/src/components/global-search.tsx +360 -0
  148. package/template/src/components/header.tsx +200 -107
  149. package/template/src/components/inactive-account-guard.tsx +58 -0
  150. package/template/src/components/integration-notifications-listener.tsx +12 -0
  151. package/template/src/components/invitation-email-template.tsx +4 -2
  152. package/template/src/components/lazy-editor.tsx +11 -0
  153. package/template/src/components/meet-cancellation-email-template.tsx +11 -3
  154. package/template/src/components/meet-confirmation-email-template.tsx +10 -3
  155. package/template/src/components/meet-update-email-template.tsx +10 -3
  156. package/template/src/components/page-header.tsx +19 -15
  157. package/template/src/components/protected-page.tsx +94 -0
  158. package/template/src/components/reset-password-email-template.tsx +4 -2
  159. package/template/src/components/settings/integrations/GoogleAdsIntegration.tsx +428 -0
  160. package/template/src/components/settings/integrations/GoogleSheetConfigMonitoringModal.tsx +680 -0
  161. package/template/src/components/settings/integrations/GoogleSheetIntegration.tsx +809 -0
  162. package/template/src/components/settings/integrations/ImportResultDialog.tsx +124 -0
  163. package/template/src/components/settings/integrations/IntegrationLogPanel.tsx +57 -0
  164. package/template/src/components/settings/integrations/IntegrationLogsTable.tsx +186 -0
  165. package/template/src/components/settings/integrations/MetaLeadIntegration.tsx +451 -0
  166. package/template/src/components/sidebar.tsx +117 -100
  167. package/template/src/components/skeleton.tsx +128 -45
  168. package/template/src/components/ui/accordion.tsx +64 -0
  169. package/template/src/components/ui/alert-dialog.tsx +139 -0
  170. package/template/src/components/ui/button.tsx +71 -0
  171. package/template/src/components/ui/components.tsx +1 -1
  172. package/template/src/components/ui/date-picker.tsx +422 -0
  173. package/template/src/components/ui/datetime-picker.tsx +338 -0
  174. package/template/src/components/ui/status-select.tsx +271 -0
  175. package/template/src/components/ui/tooltip.tsx +37 -0
  176. package/template/src/components/view-as-banner.tsx +1 -1
  177. package/template/src/components/view-as-modal.tsx +30 -19
  178. package/template/src/config/nav-pages.ts +108 -0
  179. package/template/src/contexts/app-toast-context.tsx +362 -0
  180. package/template/src/contexts/dashboard-theme-context.tsx +2 -7
  181. package/template/src/contexts/sidebar-context.tsx +27 -53
  182. package/template/src/contexts/task-reminder-context.tsx +134 -160
  183. package/template/src/contexts/view-as-context.tsx +32 -10
  184. package/template/src/hooks/use-alert.tsx +65 -0
  185. package/template/src/hooks/use-confirm.tsx +87 -0
  186. package/template/src/hooks/use-contact-views.ts +140 -0
  187. package/template/src/hooks/use-contacts.ts +69 -0
  188. package/template/src/hooks/use-fetch.ts +17 -0
  189. package/template/src/hooks/use-focus-trap.ts +73 -0
  190. package/template/src/hooks/use-statuses.ts +22 -0
  191. package/template/src/hooks/useIntegrationNotifications.ts +49 -0
  192. package/template/src/lib/address-api.ts +155 -0
  193. package/template/src/lib/auth.ts +8 -1
  194. package/template/src/lib/cache.ts +73 -0
  195. package/template/src/lib/check-permission.ts +12 -177
  196. package/template/src/lib/config-links.ts +14 -0
  197. package/template/src/lib/contact-duplicate.ts +79 -61
  198. package/template/src/lib/contact-interactions.ts +24 -22
  199. package/template/src/lib/contact-view-filters.ts +301 -0
  200. package/template/src/lib/contacts-list-url.ts +190 -0
  201. package/template/src/lib/dashboard-stats.ts +282 -0
  202. package/template/src/lib/dashboard-themes.ts +0 -5
  203. package/template/src/lib/date-utils.ts +176 -0
  204. package/template/src/lib/default-widgets.ts +0 -2
  205. package/template/src/lib/editor-html-image-dimensions.ts +172 -0
  206. package/template/src/lib/editor-image-limits.ts +19 -0
  207. package/template/src/lib/email-html-sanitize.ts +19 -0
  208. package/template/src/lib/encryption.ts +9 -6
  209. package/template/src/lib/fr-geography.ts +192 -0
  210. package/template/src/lib/get-auth-user.ts +25 -0
  211. package/template/src/lib/google-calendar-agenda.ts +201 -0
  212. package/template/src/lib/google-calendar.ts +309 -17
  213. package/template/src/lib/google-fetch.ts +63 -0
  214. package/template/src/lib/google-sheet-sync-jobs.ts +96 -0
  215. package/template/src/lib/google-sheet-sync-runner.ts +514 -0
  216. package/template/src/lib/integration-import-log.ts +21 -0
  217. package/template/src/lib/local-storage.ts +34 -0
  218. package/template/src/lib/permissions.ts +268 -40
  219. package/template/src/lib/prisma.ts +15 -12
  220. package/template/src/lib/qstash.ts +65 -0
  221. package/template/src/lib/reminder-state-server.ts +80 -0
  222. package/template/src/lib/reminder-state.ts +29 -0
  223. package/template/src/lib/roles.ts +12 -15
  224. package/template/src/lib/supabase-storage.ts +113 -0
  225. package/template/src/lib/template-variables.ts +204 -29
  226. package/template/src/lib/utils.ts +71 -11
  227. package/template/src/lib/widget-registry.ts +0 -4
  228. package/template/src/lib/workflow-executor.ts +391 -228
  229. package/template/src/proxy.ts +35 -73
  230. package/template/src/types/contact-views.ts +351 -0
  231. package/template/vercel.json +5 -0
  232. package/template/WORKFLOWS_CRON.md +0 -185
  233. package/template/prisma/migrations/20251126144728_init/migration.sql +0 -78
  234. package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +0 -5
  235. package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +0 -19
  236. package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +0 -22
  237. package/template/prisma/migrations/20251128132303_add_status/migration.sql +0 -23
  238. package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +0 -75
  239. package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +0 -2
  240. package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +0 -45
  241. package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +0 -2
  242. package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +0 -27
  243. package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +0 -20
  244. package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +0 -18
  245. package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +0 -32
  246. package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +0 -20
  247. package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +0 -12
  248. package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +0 -21
  249. package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +0 -11
  250. package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +0 -12
  251. package/template/prisma/migrations/20251208094843_mg/migration.sql +0 -14
  252. package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +0 -14
  253. package/template/prisma/migrations/20251208110000_add_templates/migration.sql +0 -26
  254. package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +0 -2
  255. package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +0 -2
  256. package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +0 -2
  257. package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +0 -3
  258. package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +0 -21
  259. package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +0 -2
  260. package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +0 -10
  261. package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +0 -26
  262. package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +0 -24
  263. package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +0 -11
  264. package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +0 -12
  265. package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +0 -25
  266. package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +0 -8
  267. package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +0 -2
  268. package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +0 -80
  269. package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +0 -32
  270. package/template/prisma/migrations/20251220000000_add_task_interaction_type/migration.sql +0 -4
  271. package/template/prisma/migrations/20251221000000_add_task_type/migration.sql +0 -3
  272. package/template/prisma/migrations/20251221000001_add_event_color/migration.sql +0 -23
  273. package/template/prisma/migrations/20260210114913_add_dashboard_widget/migration.sql +0 -20
  274. package/template/prisma/migrations/20260226093949_fix_cascade_on_user_delete/migration.sql +0 -69
  275. package/template/src/app/(dashboard)/users/layout.tsx +0 -30
  276. package/template/src/lib/google-drive.ts +0 -380
@@ -1,91 +1,53 @@
1
- import { NextResponse } from 'next/server';
2
- import type { NextRequest } from 'next/server';
3
- import { auth } from './lib/auth';
4
- import { Role } from './lib/roles';
5
- import { prisma } from './lib/prisma';
1
+ import { NextRequest, NextResponse } from 'next/server';
6
2
 
7
- // Routes qui nécessitent une authentification
8
- const protectedRoutes = [
9
- '/dashboard',
10
- '/contacts',
11
- '/settings',
12
- '/users',
13
- '/agenda',
14
- '/automatisation',
15
- '/templates',
16
- '/closing',
17
- ];
18
-
19
- // Routes réservées aux admins
20
- const adminRoutes = ['/users'];
3
+ const SESSION_COOKIE = 'better-auth.session_token';
21
4
 
22
- // Routes d'authentification
23
- const authRoutes = ['/signin'];
24
-
25
- export async function proxy(request: NextRequest) {
26
- const { pathname } = request.nextUrl;
5
+ const PUBLIC_PAGES = new Set(['/', '/signin', '/invite', '/reset-password']);
27
6
 
28
- // Vérifier la session en utilisant Better Auth
29
- const session = await auth.api.getSession({
30
- headers: request.headers,
31
- });
32
-
33
- let isAuthenticated = !!session;
34
- let isActiveUser = true;
35
-
36
- // Récupérer le rôle depuis la session ou depuis la base de données
37
- let userRole: string | null = null;
38
- if (session && session.user?.id) {
39
- const dbUser = await prisma.user.findUnique({
40
- where: { id: session.user.id },
41
- select: { role: true, active: true },
42
- });
7
+ const PUBLIC_API_PREFIXES = [
8
+ '/api/auth',
9
+ '/api/webhooks',
10
+ '/api/cron',
11
+ '/api/jobs',
12
+ '/api/invite',
13
+ '/api/reset-password',
14
+ '/api/email/track',
15
+ '/api/workflows/process',
16
+ ];
43
17
 
44
- userRole = (session.user as any).role || dbUser?.role || null;
45
- isActiveUser = dbUser?.active ?? true;
46
- if (!isActiveUser) {
47
- isAuthenticated = false;
48
- }
18
+ function isPublicPage(pathname: string): boolean {
19
+ if (PUBLIC_PAGES.has(pathname)) return true;
20
+ for (const path of PUBLIC_PAGES) {
21
+ if (path !== '/' && pathname.startsWith(path + '/')) return true;
49
22
  }
23
+ return false;
24
+ }
50
25
 
51
- // Vérifier si la route actuelle est protégée
52
- const isProtectedRoute = protectedRoutes.some((route) => pathname.startsWith(route));
53
-
54
- // Vérifier si la route est réservée aux admins
55
- const isAdminRoute = adminRoutes.some((route) => pathname.startsWith(route));
26
+ function isPublicApi(pathname: string): boolean {
27
+ return PUBLIC_API_PREFIXES.some((prefix) => pathname.startsWith(prefix));
28
+ }
56
29
 
57
- // Vérifier si la route actuelle est une route d'auth
58
- const isAuthRoute = authRoutes.some((route) => pathname.startsWith(route));
30
+ export function proxy(request: NextRequest) {
31
+ const { pathname } = request.nextUrl;
59
32
 
60
- // Si l'utilisateur n'est pas connecté et tente d'accéder à une route protégée
61
- if (!isAuthenticated && isProtectedRoute) {
62
- const signInUrl = new URL('/signin', request.url);
63
- signInUrl.searchParams.set('callbackUrl', pathname);
64
- return NextResponse.redirect(signInUrl);
33
+ if (isPublicPage(pathname) || isPublicApi(pathname)) {
34
+ return NextResponse.next();
65
35
  }
66
36
 
67
- // Si l'utilisateur est connecté mais n'est pas admin et tente d'accéder à une route admin
68
- if (isAuthenticated && isAdminRoute && userRole !== Role.ADMIN) {
69
- return NextResponse.redirect(new URL('/dashboard', request.url));
70
- }
37
+ const hasSession = request.cookies.has(SESSION_COOKIE);
71
38
 
72
- // Si l'utilisateur est connecté et tente d'accéder aux pages d'auth
73
- if (isAuthenticated && isAuthRoute) {
74
- return NextResponse.redirect(new URL('/dashboard', request.url));
39
+ if (!hasSession) {
40
+ if (pathname.startsWith('/api/')) {
41
+ return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
42
+ }
43
+ const signinUrl = new URL('/signin', request.url);
44
+ signinUrl.searchParams.set('callbackUrl', pathname);
45
+ return NextResponse.redirect(signinUrl);
75
46
  }
76
47
 
77
48
  return NextResponse.next();
78
49
  }
79
50
 
80
51
  export const config = {
81
- matcher: [
82
- /*
83
- * Match all request paths except for the ones starting with:
84
- * - api (API routes)
85
- * - _next/static (static files)
86
- * - _next/image (image optimization files)
87
- * - favicon.ico (favicon file)
88
- */
89
- '/((?!api|_next/static|_next/image|favicon.ico).*)',
90
- ],
52
+ matcher: ['/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt|.*..*).*)'],
91
53
  };
@@ -0,0 +1,351 @@
1
+ export type FilterOperator =
2
+ | 'equals'
3
+ | 'not_equals'
4
+ | 'contains'
5
+ | 'not_contains'
6
+ | 'starts_with'
7
+ | 'ends_with'
8
+ | 'is_any_of'
9
+ | 'is_none_of'
10
+ | 'gt'
11
+ | 'gte'
12
+ | 'lt'
13
+ | 'lte'
14
+ | 'between'
15
+ | 'is_known'
16
+ | 'is_unknown'
17
+ | 'date_preset';
18
+
19
+ export type DatePreset =
20
+ | 'today'
21
+ | 'yesterday'
22
+ | 'tomorrow'
23
+ | 'this_week'
24
+ | 'this_week_so_far'
25
+ | 'last_week'
26
+ | 'next_week'
27
+ | 'this_month'
28
+ | 'this_month_so_far'
29
+ | 'last_month'
30
+ | 'next_month'
31
+ | 'this_quarter'
32
+ | 'this_quarter_so_far'
33
+ | 'last_quarter'
34
+ | 'this_year'
35
+ | 'this_year_so_far'
36
+ | 'last_year'
37
+ | 'last_7_days'
38
+ | 'last_14_days'
39
+ | 'last_30_days'
40
+ | 'last_60_days'
41
+ | 'last_90_days'
42
+ | 'last_180_days'
43
+ | 'last_365_days';
44
+
45
+ export interface ViewFilter {
46
+ field: string;
47
+ operator: FilterOperator;
48
+ value: string | string[] | null;
49
+ preset?: DatePreset;
50
+ }
51
+
52
+ export interface ViewColumn {
53
+ id: string;
54
+ visible: boolean;
55
+ order: number;
56
+ }
57
+
58
+ export interface ViewSortConfig {
59
+ field: string;
60
+ direction: 'asc' | 'desc';
61
+ }
62
+
63
+ export type FilterFieldType = 'text' | 'select' | 'date' | 'boolean' | 'select_or_text';
64
+
65
+ export interface FilterFieldDefinition {
66
+ field: string;
67
+ label: string;
68
+ type: FilterFieldType;
69
+ group: string;
70
+ operators: FilterOperator[];
71
+ optionsEndpoint?: string;
72
+ staticOptions?: { value: string; label: string }[];
73
+ clientSideOnly?: boolean;
74
+ entityTypes?: ('contacts' | 'companies')[];
75
+ }
76
+
77
+ export const DATE_PRESET_LABELS: Record<DatePreset, string> = {
78
+ today: "Aujourd'hui",
79
+ yesterday: 'Hier',
80
+ tomorrow: 'Demain',
81
+ this_week: 'Cette semaine',
82
+ this_week_so_far: "Cette semaine jusqu'à aujourd'hui",
83
+ last_week: 'La semaine dernière',
84
+ next_week: 'La semaine prochaine',
85
+ this_month: 'Ce mois-ci',
86
+ this_month_so_far: "Ce mois jusqu'à aujourd'hui",
87
+ last_month: 'Le mois dernier',
88
+ next_month: 'Le mois prochain',
89
+ this_quarter: 'Ce trimestre',
90
+ this_quarter_so_far: "Ce trimestre jusqu'à aujourd'hui",
91
+ last_quarter: 'Le trimestre dernier',
92
+ this_year: 'Cette année',
93
+ this_year_so_far: "Cette année jusqu'à aujourd'hui",
94
+ last_year: "L'année dernière",
95
+ last_7_days: 'Les 7 derniers jours',
96
+ last_14_days: 'Les 14 derniers jours',
97
+ last_30_days: 'Les 30 derniers jours',
98
+ last_60_days: 'Les 60 derniers jours',
99
+ last_90_days: 'Les 90 derniers jours',
100
+ last_180_days: 'Les 180 derniers jours',
101
+ last_365_days: 'Les 365 derniers jours',
102
+ };
103
+
104
+ export const OPERATOR_LABELS: Record<FilterOperator, string> = {
105
+ equals: 'est égal à',
106
+ not_equals: "n'est pas égal à",
107
+ contains: 'contient',
108
+ not_contains: 'ne contient pas',
109
+ starts_with: 'commence par',
110
+ ends_with: 'se termine par',
111
+ is_any_of: 'est parmi',
112
+ is_none_of: "n'est pas parmi",
113
+ gt: 'est supérieur à',
114
+ gte: 'est supérieur ou égal à',
115
+ lt: 'est inférieur à',
116
+ lte: 'est inférieur ou égal à',
117
+ between: 'est entre',
118
+ is_known: 'est connu',
119
+ is_unknown: 'est inconnu',
120
+ date_preset: 'est',
121
+ };
122
+
123
+ export const FILTER_FIELD_DEFINITIONS: FilterFieldDefinition[] = [
124
+ {
125
+ field: 'firstName',
126
+ label: 'Prénom',
127
+ type: 'text',
128
+ group: 'Informations du contact',
129
+ operators: [
130
+ 'equals',
131
+ 'not_equals',
132
+ 'contains',
133
+ 'not_contains',
134
+ 'starts_with',
135
+ 'ends_with',
136
+ 'is_known',
137
+ 'is_unknown',
138
+ ],
139
+ entityTypes: ['contacts'],
140
+ },
141
+ {
142
+ field: 'lastName',
143
+ label: 'Nom',
144
+ type: 'text',
145
+ group: 'Informations du contact',
146
+ operators: [
147
+ 'equals',
148
+ 'not_equals',
149
+ 'contains',
150
+ 'not_contains',
151
+ 'starts_with',
152
+ 'ends_with',
153
+ 'is_known',
154
+ 'is_unknown',
155
+ ],
156
+ entityTypes: ['contacts'],
157
+ },
158
+ {
159
+ field: 'phone',
160
+ label: 'Téléphone',
161
+ type: 'text',
162
+ group: 'Informations du contact',
163
+ operators: ['equals', 'contains', 'starts_with', 'is_known', 'is_unknown'],
164
+ },
165
+ {
166
+ field: 'email',
167
+ label: 'Email',
168
+ type: 'text',
169
+ group: 'Informations du contact',
170
+ operators: [
171
+ 'equals',
172
+ 'not_equals',
173
+ 'contains',
174
+ 'not_contains',
175
+ 'starts_with',
176
+ 'ends_with',
177
+ 'is_known',
178
+ 'is_unknown',
179
+ ],
180
+ },
181
+ {
182
+ field: 'address',
183
+ label: 'Adresse',
184
+ type: 'text',
185
+ group: 'Informations du contact',
186
+ operators: ['contains', 'is_known', 'is_unknown'],
187
+ },
188
+ {
189
+ field: 'city',
190
+ label: 'Ville',
191
+ type: 'text',
192
+ group: 'Informations du contact',
193
+ operators: ['equals', 'not_equals', 'contains', 'starts_with', 'is_known', 'is_unknown'],
194
+ },
195
+ {
196
+ field: 'postalCode',
197
+ label: 'Code postal',
198
+ type: 'text',
199
+ group: 'Informations du contact',
200
+ operators: ['equals', 'starts_with', 'is_known', 'is_unknown'],
201
+ },
202
+ {
203
+ field: 'civility',
204
+ label: 'Civilité',
205
+ type: 'select',
206
+ group: 'Informations du contact',
207
+ operators: ['is_any_of', 'is_none_of'],
208
+ staticOptions: [
209
+ { value: 'M', label: 'M.' },
210
+ { value: 'MME', label: 'Mme' },
211
+ { value: 'MLLE', label: 'Mlle' },
212
+ ],
213
+ entityTypes: ['contacts'],
214
+ },
215
+ {
216
+ field: 'statusId',
217
+ label: 'Statut',
218
+ type: 'select',
219
+ group: 'Statut & Assignation',
220
+ operators: ['is_any_of', 'is_none_of', 'is_known', 'is_unknown'],
221
+ optionsEndpoint: '/api/statuses',
222
+ entityTypes: ['contacts'],
223
+ },
224
+ {
225
+ field: 'origin',
226
+ label: 'Origine',
227
+ type: 'select_or_text',
228
+ group: 'Statut & Assignation',
229
+ operators: [
230
+ 'equals',
231
+ 'not_equals',
232
+ 'is_any_of',
233
+ 'is_none_of',
234
+ 'contains',
235
+ 'is_known',
236
+ 'is_unknown',
237
+ ],
238
+ entityTypes: ['contacts'],
239
+ },
240
+ {
241
+ field: 'assignedCommercialId',
242
+ label: 'Commercial',
243
+ type: 'select',
244
+ group: 'Statut & Assignation',
245
+ operators: ['is_any_of', 'is_none_of', 'is_known', 'is_unknown'],
246
+ optionsEndpoint: '/api/users/list',
247
+ },
248
+ {
249
+ field: 'assignedTeleproId',
250
+ label: 'Télépro',
251
+ type: 'select',
252
+ group: 'Statut & Assignation',
253
+ operators: ['is_any_of', 'is_none_of', 'is_known', 'is_unknown'],
254
+ optionsEndpoint: '/api/users/list',
255
+ },
256
+ {
257
+ field: 'createdById',
258
+ label: 'Créé par',
259
+ type: 'select',
260
+ group: 'Statut & Assignation',
261
+ operators: ['is_any_of', 'is_none_of'],
262
+ optionsEndpoint: '/api/users/list',
263
+ },
264
+ {
265
+ field: 'createdAt',
266
+ label: 'Date de création',
267
+ type: 'date',
268
+ group: 'Dates',
269
+ operators: ['date_preset', 'equals', 'gt', 'gte', 'lt', 'lte', 'between'],
270
+ },
271
+ {
272
+ field: 'updatedAt',
273
+ label: 'Date de modification',
274
+ type: 'date',
275
+ group: 'Dates',
276
+ operators: ['date_preset', 'equals', 'gt', 'gte', 'lt', 'lte', 'between'],
277
+ },
278
+ {
279
+ field: 'region',
280
+ label: 'Région',
281
+ type: 'select',
282
+ group: 'Géographie',
283
+ operators: ['is_any_of', 'is_none_of'],
284
+ entityTypes: ['contacts'],
285
+ },
286
+ {
287
+ field: 'department',
288
+ label: 'Département',
289
+ type: 'select',
290
+ group: 'Géographie',
291
+ operators: ['is_any_of', 'is_none_of'],
292
+ entityTypes: ['contacts'],
293
+ },
294
+ {
295
+ field: 'name',
296
+ label: 'Nom',
297
+ type: 'text',
298
+ group: "Informations de l'entreprise",
299
+ operators: [
300
+ 'equals',
301
+ 'not_equals',
302
+ 'contains',
303
+ 'not_contains',
304
+ 'starts_with',
305
+ 'ends_with',
306
+ 'is_known',
307
+ 'is_unknown',
308
+ ],
309
+ entityTypes: ['companies'],
310
+ },
311
+ {
312
+ field: 'siret',
313
+ label: 'SIRET',
314
+ type: 'text',
315
+ group: "Informations de l'entreprise",
316
+ operators: ['equals', 'contains', 'starts_with', 'is_known', 'is_unknown'],
317
+ entityTypes: ['companies'],
318
+ },
319
+ {
320
+ field: 'industry',
321
+ label: 'Secteur',
322
+ type: 'text',
323
+ group: "Informations de l'entreprise",
324
+ operators: ['equals', 'not_equals', 'contains', 'is_known', 'is_unknown'],
325
+ entityTypes: ['companies'],
326
+ },
327
+ {
328
+ field: 'website',
329
+ label: 'Site web',
330
+ type: 'text',
331
+ group: "Informations de l'entreprise",
332
+ operators: ['contains', 'is_known', 'is_unknown'],
333
+ entityTypes: ['companies'],
334
+ },
335
+ ];
336
+
337
+ export interface ContactViewData {
338
+ id: string;
339
+ name: string;
340
+ userId: string;
341
+ user?: { id: string; name: string };
342
+ isPublic: boolean;
343
+ isDefault: boolean;
344
+ entityType: 'contacts' | 'companies';
345
+ filters: ViewFilter[];
346
+ columns: ViewColumn[] | null;
347
+ sortConfig: ViewSortConfig | null;
348
+ pinOrder: number | null;
349
+ createdAt: string;
350
+ updatedAt: string;
351
+ }
@@ -1,8 +1,13 @@
1
1
  {
2
+ "buildCommand": "pnpm db:prepare:prod && pnpm build",
2
3
  "crons": [
3
4
  {
4
5
  "path": "/api/workflows/process",
5
6
  "schedule": "* * * * *"
7
+ },
8
+ {
9
+ "path": "/api/cron/cleanup-editor-images",
10
+ "schedule": "0 3 * * *"
6
11
  }
7
12
  ]
8
13
  }
@@ -1,185 +0,0 @@
1
- # Configuration des Workflows avec Vercel Cron
2
-
3
- Ce document explique comment fonctionne le système de planification des actions
4
- de workflow avec Vercel Cron.
5
-
6
- ## Fonctionnement
7
-
8
- Le système utilise une table `ScheduledWorkflowAction` dans la base de données
9
- pour stocker les actions à exécuter plus tard. Un cron job Vercel appelle
10
- l'endpoint `/api/workflows/process` toutes les minutes pour traiter les actions
11
- dont la date d'exécution est arrivée.
12
-
13
- ## Configuration
14
-
15
- ### 1. Variables d'environnement
16
-
17
- Ajoutez cette variable dans votre fichier `.env` et dans les variables
18
- d'environnement Vercel :
19
-
20
- ```env
21
- CRON_SECRET=votre_secret_aleatoire_ici
22
- ```
23
-
24
- **Important** : Utilisez un secret fort et aléatoire pour sécuriser l'endpoint
25
- cron. Vous pouvez générer un secret avec :
26
-
27
- ```bash
28
- openssl rand -base64 32
29
- ```
30
-
31
- ### 2. Configuration du service de cron (si plan Hobby)
32
-
33
- Si vous êtes sur le plan Hobby de Vercel, vous devez utiliser un service externe
34
- comme cron-job.org.
35
-
36
- #### Configuration cron-job.org
37
-
38
- 1. Créez un compte sur [cron-job.org](https://cron-job.org/)
39
- 2. Créez un nouveau cron job avec ces paramètres :
40
- - **URL** :
41
- `https://votre-domaine.vercel.app/api/workflows/process?secret=VOTRE_CRON_SECRET`
42
- - **Méthode** : `GET`
43
- - **Schedule** : `Every minute` ou `*/1 * * * *`
44
- - **Title** : "Workflow Actions Processor"
45
- - **Active** : ✓
46
-
47
- **Note** : Remplacez `VOTRE_CRON_SECRET` par la valeur de votre variable
48
- d'environnement `CRON_SECRET`.
49
-
50
- #### Autres services compatibles
51
-
52
- - **EasyCron** : Même configuration que cron-job.org
53
- - **UptimeRobot** : Utilisez l'URL avec le paramètre `secret`
54
- - **GitHub Actions** : Voir la section GitHub Actions ci-dessous
55
-
56
- ### 3. Migration Prisma
57
-
58
- Après avoir ajouté le modèle `ScheduledWorkflowAction` dans `schema.prisma`,
59
- créez et exécutez la migration :
60
-
61
- ```bash
62
- pnpm prisma migrate dev --name add_scheduled_workflow_actions
63
- ```
64
-
65
- ### 4. Configuration Vercel (optionnel - plans Pro/Enterprise uniquement)
66
-
67
- Le fichier `vercel.json` est configuré pour exécuter le cron toutes les minutes
68
- :
69
-
70
- ```json
71
- {
72
- "crons": [
73
- {
74
- "path": "/api/workflows/process",
75
- "schedule": "* * * * *"
76
- }
77
- ]
78
- }
79
- ```
80
-
81
- **Note** : Vercel Cron est disponible uniquement sur les plans Pro et
82
- Enterprise. Sur le plan Hobby, utilisez un service externe comme cron-job.org
83
- (voir section 2).
84
-
85
- ### 5. Déploiement
86
-
87
- 1. Poussez vos changements sur votre repository
88
- 2. Si vous êtes sur plan Pro/Enterprise : Vercel détectera automatiquement le
89
- fichier `vercel.json` et configurera le cron
90
- 3. Si vous êtes sur plan Hobby : Configurez cron-job.org ou un autre service
91
- externe (voir section 2)
92
- 4. Vérifiez que `CRON_SECRET` est bien configuré dans les variables
93
- d'environnement Vercel
94
-
95
- ## Fonctionnement détaillé
96
-
97
- ### Planification d'une action
98
-
99
- Quand un workflow est déclenché et qu'une action a un délai (jours/heures), le
100
- système :
101
-
102
- 1. Calcule la date d'exécution : `Date.now() + délai`
103
- 2. Sauvegarde l'action dans `ScheduledWorkflowAction` avec :
104
- - Le type d'action (SEND_EMAIL, SEND_SMS, CHANGE_STATUS, CREATE_TASK)
105
- - Les données nécessaires pour l'exécution (template, message, statut, etc.)
106
- - La date d'exécution
107
- - Le contact et le workflow concernés
108
-
109
- ### Exécution des actions
110
-
111
- Toutes les minutes, Vercel Cron appelle `/api/workflows/process` qui :
112
-
113
- 1. Récupère toutes les actions non exécutées dont `executeAt <= maintenant`
114
- 2. Pour chaque action :
115
- - Exécute l'action selon son type
116
- - Marque l'action comme exécutée (`executed: true`)
117
- - Enregistre la date d'exécution réelle
118
- - En cas d'erreur, enregistre le message d'erreur
119
-
120
- ### Types d'actions supportées
121
-
122
- - **SEND_EMAIL** : Envoie un email via SMTP avec template
123
- - **SEND_SMS** : Envoie un SMS (nécessite une API SMS)
124
- - **CHANGE_STATUS** : Change le statut du contact
125
- - **CREATE_TASK** : Crée une tâche pour le contact
126
- - **WAIT** : Action d'attente (gérée par le délai)
127
-
128
- ## Monitoring
129
-
130
- Vous pouvez surveiller les actions planifiées via la base de données :
131
-
132
- ```sql
133
- -- Actions en attente
134
- SELECT * FROM scheduled_workflow_action
135
- WHERE executed = false
136
- ORDER BY executeAt ASC;
137
-
138
- -- Actions récemment exécutées
139
- SELECT * FROM scheduled_workflow_action
140
- WHERE executed = true
141
- ORDER BY executedAt DESC
142
- LIMIT 50;
143
-
144
- -- Actions en erreur
145
- SELECT * FROM scheduled_workflow_action
146
- WHERE executed = true AND error IS NOT NULL;
147
- ```
148
-
149
- ## Limitations
150
-
151
- - **Précision** : Les actions sont exécutées avec une précision d'environ 1
152
- minute (selon la fréquence du cron)
153
- - **Limite par exécution** : Maximum 50 actions traitées par exécution du cron
154
- (pour éviter les timeouts)
155
- - **Retry** : Les actions échouées ne sont pas automatiquement réessayées (elles
156
- sont marquées comme exécutées avec une erreur)
157
-
158
- ## Dépannage
159
-
160
- ### Le cron ne s'exécute pas
161
-
162
- 1. **Si vous utilisez Vercel Cron** :
163
- - Vérifiez que vous êtes sur un plan Vercel Pro ou Enterprise
164
- - Vérifiez dans le dashboard Vercel que le cron est bien configuré (Settings
165
- → Cron Jobs)
166
- - Vérifiez les logs Vercel pour voir les erreurs éventuelles
167
-
168
- 2. **Si vous utilisez un service externe (cron-job.org, etc.)** :
169
- - Vérifiez que le job est actif dans votre service
170
- - Vérifiez l'URL utilisée (doit inclure `?secret=VOTRE_CRON_SECRET`)
171
- - Vérifiez les logs de votre service de cron
172
- - Testez manuellement l'URL dans votre navigateur (avec le secret) pour voir
173
- si elle répond
174
-
175
- ### Les actions ne s'exécutent pas
176
-
177
- 1. Vérifiez que `CRON_SECRET` est bien configuré dans Vercel
178
- 2. Vérifiez les logs de l'endpoint `/api/workflows/process`
179
- 3. Vérifiez dans la base de données que les actions sont bien créées
180
- 4. Vérifiez que `executeAt` est bien dans le passé
181
-
182
- ### Erreurs d'exécution
183
-
184
- Les erreurs sont enregistrées dans le champ `error` de
185
- `ScheduledWorkflowAction`. Consultez ce champ pour diagnostiquer les problèmes.