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,156 @@
1
+ import React from 'react';
2
+
3
+ interface MeetConfirmationEmailTemplateProps {
4
+ contactName: string;
5
+ title: string;
6
+ scheduledAt: string;
7
+ durationMinutes: number;
8
+ meetLink?: string;
9
+ description?: string;
10
+ organizerName: string;
11
+ signature?: string;
12
+ }
13
+
14
+ export function MeetConfirmationEmailTemplate({
15
+ contactName,
16
+ title,
17
+ scheduledAt,
18
+ durationMinutes,
19
+ meetLink,
20
+ description,
21
+ organizerName,
22
+ signature,
23
+ }: MeetConfirmationEmailTemplateProps) {
24
+ const formatDate = (dateString: string) => {
25
+ const date = new Date(dateString);
26
+ return date.toLocaleDateString('fr-FR', {
27
+ weekday: 'long',
28
+ year: 'numeric',
29
+ month: 'long',
30
+ day: 'numeric',
31
+ });
32
+ };
33
+
34
+ const formatTime = (dateString: string) => {
35
+ const date = new Date(dateString);
36
+ return date.toLocaleTimeString('fr-FR', {
37
+ hour: '2-digit',
38
+ minute: '2-digit',
39
+ });
40
+ };
41
+
42
+ const formatDuration = (minutes: number) => {
43
+ if (minutes < 60) {
44
+ return `${minutes} minutes`;
45
+ }
46
+ const hours = Math.floor(minutes / 60);
47
+ const mins = minutes % 60;
48
+ if (mins === 0) {
49
+ return `${hours} heure${hours > 1 ? 's' : ''}`;
50
+ }
51
+ return `${hours} heure${hours > 1 ? 's' : ''} ${mins} minute${mins > 1 ? 's' : ''}`;
52
+ };
53
+
54
+ return (
55
+ <div style={{ fontFamily: 'Arial, sans-serif', lineHeight: '1.6', color: '#333' }}>
56
+ <div style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
57
+ <h1 style={{ color: '#1a1a1a', fontSize: '24px', marginBottom: '20px' }}>
58
+ Confirmation de rendez-vous
59
+ </h1>
60
+
61
+ <p style={{ fontSize: '16px', marginBottom: '20px' }}>Bonjour {contactName},</p>
62
+
63
+ <p style={{ fontSize: '16px', marginBottom: '20px' }}>
64
+ Votre rendez-vous a été confirmé avec succès.
65
+ </p>
66
+
67
+ <div
68
+ style={{
69
+ backgroundColor: '#f5f5f5',
70
+ padding: '20px',
71
+ borderRadius: '8px',
72
+ marginBottom: '20px',
73
+ }}
74
+ >
75
+ <h2 style={{ color: '#1a1a1a', fontSize: '20px', marginBottom: '15px' }}>{title}</h2>
76
+
77
+ <div style={{ marginBottom: '10px' }}>
78
+ <strong>Date :</strong> {formatDate(scheduledAt)}
79
+ </div>
80
+ <div style={{ marginBottom: '10px' }}>
81
+ <strong>Heure :</strong> {formatTime(scheduledAt)}
82
+ </div>
83
+ {durationMinutes > 0 && (
84
+ <div style={{ marginBottom: '10px' }}>
85
+ <strong>Durée :</strong> {formatDuration(durationMinutes)}
86
+ </div>
87
+ )}
88
+ <div style={{ marginBottom: '10px' }}>
89
+ <strong>Organisateur :</strong> {organizerName}
90
+ </div>
91
+
92
+ {description && (
93
+ <div style={{ marginTop: '15px', paddingTop: '15px', borderTop: '1px solid #ddd' }}>
94
+ <strong>Description :</strong>
95
+ <div
96
+ style={{ marginTop: '10px' }}
97
+ dangerouslySetInnerHTML={{ __html: description }}
98
+ />
99
+ </div>
100
+ )}
101
+ </div>
102
+
103
+ {meetLink && (
104
+ <>
105
+ <div style={{ marginBottom: '30px', textAlign: 'center' }}>
106
+ <a
107
+ href={meetLink}
108
+ style={{
109
+ display: 'inline-block',
110
+ backgroundColor: '#4285f4',
111
+ color: '#ffffff',
112
+ padding: '12px 24px',
113
+ textDecoration: 'none',
114
+ borderRadius: '4px',
115
+ fontSize: '16px',
116
+ fontWeight: 'bold',
117
+ }}
118
+ >
119
+ Rejoindre la réunion Google Meet
120
+ </a>
121
+ </div>
122
+
123
+ <div style={{ marginTop: '20px', paddingTop: '20px', borderTop: '1px solid #ddd' }}>
124
+ <p style={{ fontSize: '14px', color: '#666' }}>
125
+ <strong>Lien de la réunion :</strong>
126
+ <br />
127
+ <a href={meetLink} style={{ color: '#4285f4', wordBreak: 'break-all' }}>
128
+ {meetLink}
129
+ </a>
130
+ </p>
131
+ </div>
132
+ </>
133
+ )}
134
+
135
+ {!meetLink && (
136
+ <p style={{ fontSize: '14px', color: '#666', marginTop: '20px' }}>
137
+ Nous vous remercions de votre confiance et restons à votre disposition pour toute
138
+ question.
139
+ </p>
140
+ )}
141
+
142
+ {signature && (
143
+ <div
144
+ style={{
145
+ marginTop: '30px',
146
+ paddingTop: '15px',
147
+ borderTop: '1px solid #ddd',
148
+ fontSize: '14px',
149
+ }}
150
+ dangerouslySetInnerHTML={{ __html: signature }}
151
+ />
152
+ )}
153
+ </div>
154
+ </div>
155
+ );
156
+ }
@@ -0,0 +1,209 @@
1
+ import React from 'react';
2
+
3
+ interface MeetUpdateEmailTemplateProps {
4
+ contactName: string;
5
+ title: string;
6
+ oldScheduledAt: string;
7
+ newScheduledAt: string;
8
+ oldDuration?: number;
9
+ newDuration?: number;
10
+ hasDateChanged: boolean;
11
+ hasDurationChanged: boolean;
12
+ meetLink?: string;
13
+ description?: string;
14
+ organizerName: string;
15
+ signature?: string;
16
+ }
17
+
18
+ export function MeetUpdateEmailTemplate({
19
+ contactName,
20
+ title,
21
+ oldScheduledAt,
22
+ newScheduledAt,
23
+ oldDuration,
24
+ newDuration,
25
+ hasDateChanged,
26
+ hasDurationChanged,
27
+ meetLink,
28
+ description,
29
+ organizerName,
30
+ signature,
31
+ }: MeetUpdateEmailTemplateProps) {
32
+ const formatDate = (dateString: string) => {
33
+ const date = new Date(dateString);
34
+ return date.toLocaleDateString('fr-FR', {
35
+ weekday: 'long',
36
+ year: 'numeric',
37
+ month: 'long',
38
+ day: 'numeric',
39
+ });
40
+ };
41
+
42
+ const formatTime = (dateString: string) => {
43
+ const date = new Date(dateString);
44
+ return date.toLocaleTimeString('fr-FR', {
45
+ hour: '2-digit',
46
+ minute: '2-digit',
47
+ });
48
+ };
49
+
50
+ const formatDuration = (minutes: number) => {
51
+ if (minutes < 60) {
52
+ return `${minutes} minutes`;
53
+ }
54
+ const hours = Math.floor(minutes / 60);
55
+ const mins = minutes % 60;
56
+ if (mins === 0) {
57
+ return `${hours} heure${hours > 1 ? 's' : ''}`;
58
+ }
59
+ return `${hours} heure${hours > 1 ? 's' : ''} ${mins} minute${mins > 1 ? 's' : ''}`;
60
+ };
61
+
62
+ const isGoogleMeet = !!meetLink;
63
+
64
+ return (
65
+ <div style={{ fontFamily: 'Arial, sans-serif', lineHeight: '1.6', color: '#333' }}>
66
+ <div style={{ maxWidth: '600px', margin: '0 auto', padding: '20px' }}>
67
+ <h1 style={{ color: '#1a1a1a', fontSize: '24px', marginBottom: '20px' }}>
68
+ Modification de rendez-vous
69
+ </h1>
70
+
71
+ <p style={{ fontSize: '16px', marginBottom: '20px' }}>Bonjour {contactName},</p>
72
+
73
+ <p style={{ fontSize: '16px', marginBottom: '20px' }}>
74
+ Les informations de votre rendez-vous ont été modifiées.
75
+ </p>
76
+
77
+ <div
78
+ style={{
79
+ backgroundColor: '#f5f5f5',
80
+ padding: '20px',
81
+ borderRadius: '8px',
82
+ marginBottom: '20px',
83
+ }}
84
+ >
85
+ <h2 style={{ color: '#1a1a1a', fontSize: '20px', marginBottom: '15px' }}>{title}</h2>
86
+
87
+ {hasDateChanged ? (
88
+ <div
89
+ style={{
90
+ marginBottom: '15px',
91
+ paddingBottom: '15px',
92
+ borderBottom: '1px solid #ddd',
93
+ }}
94
+ >
95
+ <div style={{ marginBottom: '10px' }}>
96
+ <strong>Ancienne date :</strong>
97
+ <span style={{ textDecoration: 'line-through', color: '#999', marginLeft: '8px' }}>
98
+ {formatDate(oldScheduledAt)} à {formatTime(oldScheduledAt)}
99
+ </span>
100
+ </div>
101
+ <div style={{ marginBottom: '10px' }}>
102
+ <strong>Nouvelle date :</strong>
103
+ <span style={{ color: '#10B981', fontWeight: 'bold', marginLeft: '8px' }}>
104
+ {formatDate(newScheduledAt)} à {formatTime(newScheduledAt)}
105
+ </span>
106
+ </div>
107
+ </div>
108
+ ) : (
109
+ <>
110
+ <div style={{ marginBottom: '10px' }}>
111
+ <strong>Date :</strong> {formatDate(newScheduledAt)}
112
+ </div>
113
+ <div style={{ marginBottom: '10px' }}>
114
+ <strong>Heure :</strong> {formatTime(newScheduledAt)}
115
+ </div>
116
+ </>
117
+ )}
118
+
119
+ {isGoogleMeet && hasDurationChanged && oldDuration && newDuration && (
120
+ <div
121
+ style={{
122
+ marginBottom: '15px',
123
+ paddingBottom: '15px',
124
+ borderBottom: '1px solid #ddd',
125
+ }}
126
+ >
127
+ <div style={{ marginBottom: '10px' }}>
128
+ <strong>Ancienne durée :</strong>
129
+ <span style={{ textDecoration: 'line-through', color: '#999', marginLeft: '8px' }}>
130
+ {formatDuration(oldDuration)}
131
+ </span>
132
+ </div>
133
+ <div style={{ marginBottom: '10px' }}>
134
+ <strong>Nouvelle durée :</strong>
135
+ <span style={{ color: '#10B981', fontWeight: 'bold', marginLeft: '8px' }}>
136
+ {formatDuration(newDuration)}
137
+ </span>
138
+ </div>
139
+ </div>
140
+ )}
141
+
142
+ {isGoogleMeet && !hasDurationChanged && newDuration && (
143
+ <div style={{ marginBottom: '10px' }}>
144
+ <strong>Durée :</strong> {formatDuration(newDuration)}
145
+ </div>
146
+ )}
147
+
148
+ <div style={{ marginBottom: '10px' }}>
149
+ <strong>Organisateur :</strong> {organizerName}
150
+ </div>
151
+
152
+ {description && (
153
+ <div style={{ marginTop: '15px', paddingTop: '15px', borderTop: '1px solid #ddd' }}>
154
+ <strong>Description :</strong>
155
+ <div
156
+ style={{ marginTop: '10px' }}
157
+ dangerouslySetInnerHTML={{ __html: description }}
158
+ />
159
+ </div>
160
+ )}
161
+ </div>
162
+
163
+ {meetLink && (
164
+ <>
165
+ <div style={{ marginBottom: '30px', textAlign: 'center' }}>
166
+ <a
167
+ href={meetLink}
168
+ style={{
169
+ display: 'inline-block',
170
+ backgroundColor: '#4285f4',
171
+ color: '#ffffff',
172
+ padding: '12px 24px',
173
+ textDecoration: 'none',
174
+ borderRadius: '4px',
175
+ fontSize: '16px',
176
+ fontWeight: 'bold',
177
+ }}
178
+ >
179
+ Rejoindre la réunion Google Meet
180
+ </a>
181
+ </div>
182
+
183
+ <div style={{ marginTop: '20px', paddingTop: '20px', borderTop: '1px solid #ddd' }}>
184
+ <p style={{ fontSize: '14px', color: '#666' }}>
185
+ <strong>Lien de la réunion :</strong>
186
+ <br />
187
+ <a href={meetLink} style={{ color: '#4285f4', wordBreak: 'break-all' }}>
188
+ {meetLink}
189
+ </a>
190
+ </p>
191
+ </div>
192
+ </>
193
+ )}
194
+
195
+ {signature && (
196
+ <div
197
+ style={{
198
+ marginTop: '30px',
199
+ paddingTop: '15px',
200
+ borderTop: '1px solid #ddd',
201
+ fontSize: '14px',
202
+ }}
203
+ dangerouslySetInnerHTML={{ __html: signature }}
204
+ />
205
+ )}
206
+ </div>
207
+ </div>
208
+ );
209
+ }
@@ -0,0 +1,61 @@
1
+ 'use client';
2
+
3
+ import { useMobileMenuContext } from '@/contexts/mobile-menu-context';
4
+
5
+ interface PageHeaderProps {
6
+ title: string;
7
+ description?: string;
8
+ action?: React.ReactNode;
9
+ }
10
+
11
+ export function PageHeader({ title, description, action }: PageHeaderProps) {
12
+ const { isOpen, toggle } = useMobileMenuContext();
13
+
14
+ return (
15
+ <div className="border-b border-gray-200 bg-white px-4 py-4 sm:px-6 lg:px-8 lg:py-6">
16
+ <div className="flex items-start gap-3">
17
+ {/* Mobile menu button */}
18
+ <button
19
+ onClick={toggle}
20
+ className="mt-1 shrink-0 cursor-pointer rounded-lg p-2 text-gray-700 transition-colors hover:bg-gray-100 lg:hidden"
21
+ aria-label="Toggle menu"
22
+ >
23
+ <svg className="h-6 w-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
24
+ {isOpen ? (
25
+ <path
26
+ strokeLinecap="round"
27
+ strokeLinejoin="round"
28
+ strokeWidth={2}
29
+ d="M6 18L18 6M6 6l12 12"
30
+ />
31
+ ) : (
32
+ <path
33
+ strokeLinecap="round"
34
+ strokeLinejoin="round"
35
+ strokeWidth={2}
36
+ d="M4 6h16M4 12h16M4 18h16"
37
+ />
38
+ )}
39
+ </svg>
40
+ </button>
41
+
42
+ <div className="min-w-0 flex-1">
43
+ {action ? (
44
+ <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
45
+ <div className="min-w-0 flex-1">
46
+ <h1 className="text-xl font-bold text-gray-900 sm:text-2xl">{title}</h1>
47
+ {description && <p className="mt-1 text-sm text-gray-600">{description}</p>}
48
+ </div>
49
+ <div className="shrink-0">{action}</div>
50
+ </div>
51
+ ) : (
52
+ <>
53
+ <h1 className="text-xl font-bold text-gray-900 sm:text-2xl">{title}</h1>
54
+ {description && <p className="mt-1 text-sm text-gray-600">{description}</p>}
55
+ </>
56
+ )}
57
+ </div>
58
+ </div>
59
+ </div>
60
+ );
61
+ }
@@ -0,0 +1,79 @@
1
+ interface ResetPasswordEmailProps {
2
+ code: string;
3
+ signature?: string | null;
4
+ }
5
+
6
+ export function ResetPasswordEmailTemplate({ code, signature }: ResetPasswordEmailProps) {
7
+ return (
8
+ <div
9
+ style={{
10
+ fontFamily: 'Arial, sans-serif',
11
+ padding: '20px',
12
+ maxWidth: '600px',
13
+ margin: '0 auto',
14
+ }}
15
+ >
16
+ <h1 style={{ color: '#1F2937', fontSize: '24px', marginBottom: '16px' }}>
17
+ Réinitialisation de mot de passe
18
+ </h1>
19
+ <p style={{ color: '#4B5563', fontSize: '16px', lineHeight: '1.6', marginBottom: '16px' }}>
20
+ Vous avez demandé à réinitialiser votre mot de passe.
21
+ </p>
22
+ <p style={{ color: '#4B5563', fontSize: '16px', lineHeight: '1.6', marginBottom: '24px' }}>
23
+ Utilisez le code suivant pour continuer :
24
+ </p>
25
+ <div style={{ textAlign: 'center', marginBottom: '32px' }}>
26
+ <div
27
+ style={{
28
+ display: 'inline-block',
29
+ padding: '20px 40px',
30
+ backgroundColor: '#F3F4F6',
31
+ borderRadius: '8px',
32
+ border: '2px solid #4F46E5',
33
+ }}
34
+ >
35
+ <div
36
+ style={{
37
+ fontSize: '36px',
38
+ fontWeight: 'bold',
39
+ letterSpacing: '8px',
40
+ color: '#4F46E5',
41
+ fontFamily: 'monospace',
42
+ }}
43
+ >
44
+ {code}
45
+ </div>
46
+ </div>
47
+ </div>
48
+ <p style={{ color: '#6B7280', fontSize: '14px', lineHeight: '1.6', marginBottom: '8px' }}>
49
+ Ce code est valide pendant 15 minutes.
50
+ </p>
51
+ <p
52
+ style={{
53
+ color: '#9CA3AF',
54
+ fontSize: '12px',
55
+ marginTop: '32px',
56
+ paddingTop: '16px',
57
+ borderTop: '1px solid #E5E7EB',
58
+ }}
59
+ >
60
+ Si vous n'avez pas demandé cette réinitialisation, vous pouvez ignorer cet email. Votre mot
61
+ de passe ne sera pas modifié.
62
+ </p>
63
+
64
+ {signature && (
65
+ <div
66
+ style={{
67
+ marginTop: '24px',
68
+ paddingTop: '16px',
69
+ borderTop: '1px solid #E5E7EB',
70
+ color: '#4B5563',
71
+ fontSize: '14px',
72
+ lineHeight: '1.6',
73
+ }}
74
+ dangerouslySetInnerHTML={{ __html: signature }}
75
+ />
76
+ )}
77
+ </div>
78
+ );
79
+ }