create-crm-tmp 2.0.0 → 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 (190) hide show
  1. package/bin/create-crm-tmp.js +56 -35
  2. package/package.json +1 -1
  3. package/template/README.md +230 -115
  4. package/template/eslint.config.mjs +13 -0
  5. package/template/next.config.ts +14 -0
  6. package/template/package.json +15 -2
  7. package/template/prisma/migrations/20260318095700_init_db/migration.sql +978 -0
  8. package/template/prisma/migrations/migration_lock.toml +3 -0
  9. package/template/prisma/schema.prisma +132 -637
  10. package/template/src/app/(auth)/invite/[token]/page.tsx +10 -8
  11. package/template/src/app/(auth)/layout.tsx +1 -1
  12. package/template/src/app/(auth)/reset-password/complete/page.tsx +11 -8
  13. package/template/src/app/(auth)/reset-password/page.tsx +4 -4
  14. package/template/src/app/(auth)/reset-password/verify/page.tsx +4 -4
  15. package/template/src/app/(auth)/signin/page.tsx +14 -6
  16. package/template/src/app/(dashboard)/agenda/page.tsx +2243 -988
  17. package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +18 -104
  18. package/template/src/app/(dashboard)/automatisation/page.tsx +10 -26
  19. package/template/src/app/(dashboard)/closing/page.tsx +78 -62
  20. package/template/src/app/(dashboard)/contacts/[id]/page.tsx +2082 -1080
  21. package/template/src/app/(dashboard)/contacts/companies/[id]/page.tsx +46 -47
  22. package/template/src/app/(dashboard)/contacts/page.tsx +1062 -780
  23. package/template/src/app/(dashboard)/dashboard/page.tsx +533 -37
  24. package/template/src/app/(dashboard)/dev/page.tsx +1291 -0
  25. package/template/src/app/(dashboard)/layout.tsx +6 -2
  26. package/template/src/app/(dashboard)/settings/page.tsx +797 -2582
  27. package/template/src/app/(dashboard)/templates/page.tsx +55 -54
  28. package/template/src/app/(dashboard)/users/list/page.tsx +51 -48
  29. package/template/src/app/(dashboard)/users/page.tsx +1 -1
  30. package/template/src/app/(dashboard)/users/permissions/page.tsx +2 -2
  31. package/template/src/app/(dashboard)/users/roles/page.tsx +7 -5
  32. package/template/src/app/api/agenda/google-events/route.ts +92 -0
  33. package/template/src/app/api/auth/check-active/route.ts +3 -2
  34. package/template/src/app/api/auth/google/route.ts +2 -1
  35. package/template/src/app/api/auth/google/status/route.ts +7 -31
  36. package/template/src/app/api/companies/[id]/activities/route.ts +1 -3
  37. package/template/src/app/api/companies/[id]/route.ts +1 -2
  38. package/template/src/app/api/companies/route.ts +42 -12
  39. package/template/src/app/api/contacts/[id]/files/[fileId]/preview/route.ts +9 -31
  40. package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +14 -32
  41. package/template/src/app/api/contacts/[id]/files/route.ts +112 -212
  42. package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +27 -1
  43. package/template/src/app/api/contacts/[id]/interactions/route.ts +16 -16
  44. package/template/src/app/api/contacts/[id]/kyc/route.ts +21 -11
  45. package/template/src/app/api/contacts/[id]/meet/route.ts +19 -2
  46. package/template/src/app/api/contacts/[id]/route.ts +106 -34
  47. package/template/src/app/api/contacts/[id]/send-email/route.ts +27 -11
  48. package/template/src/app/api/contacts/[id]/workflows/run/route.ts +6 -0
  49. package/template/src/app/api/contacts/export/route.ts +9 -13
  50. package/template/src/app/api/contacts/import/route.ts +55 -25
  51. package/template/src/app/api/contacts/import-preview/route.ts +1 -1
  52. package/template/src/app/api/contacts/origins/route.ts +63 -0
  53. package/template/src/app/api/contacts/route.ts +153 -41
  54. package/template/src/app/api/cron/cleanup-editor-images/route.ts +166 -0
  55. package/template/src/app/api/dashboard/widgets/[id]/route.ts +44 -0
  56. package/template/src/app/api/dashboard/widgets/route.ts +181 -0
  57. package/template/src/app/api/dev/reminders/test/route.ts +114 -0
  58. package/template/src/app/api/editor/upload-image/route.ts +61 -0
  59. package/template/src/app/api/integrations/google-sheet/jobs/[jobId]/route.ts +47 -0
  60. package/template/src/app/api/integrations/google-sheet/jobs/usage/route.ts +50 -0
  61. package/template/src/app/api/integrations/google-sheet/sync/route.ts +24 -556
  62. package/template/src/app/api/jobs/google-sheet/process/route.ts +84 -0
  63. package/template/src/app/api/jobs/google-sheet/schedule/route.ts +50 -0
  64. package/template/src/app/api/reminders/clear/route.ts +120 -0
  65. package/template/src/app/api/reminders/clear/undo/route.ts +112 -0
  66. package/template/src/app/api/reminders/route.ts +164 -39
  67. package/template/src/app/api/reminders/state/route.ts +164 -0
  68. package/template/src/app/api/reset-password/request/route.ts +1 -1
  69. package/template/src/app/api/reset-password/verify/route.ts +1 -1
  70. package/template/src/app/api/send/route.ts +16 -4
  71. package/template/src/app/api/settings/google-ads/route.ts +14 -0
  72. package/template/src/app/api/settings/google-calendar/calendars/route.ts +97 -0
  73. package/template/src/app/api/settings/google-calendar/route.ts +124 -0
  74. package/template/src/app/api/settings/google-sheet/[id]/route.ts +28 -0
  75. package/template/src/app/api/settings/google-sheet/auto-map/route.ts +37 -4
  76. package/template/src/app/api/settings/google-sheet/preview/route.ts +9 -3
  77. package/template/src/app/api/settings/google-sheet/route.ts +14 -0
  78. package/template/src/app/api/settings/integrations/logs/route.ts +93 -0
  79. package/template/src/app/api/settings/integrations/notifications/route.ts +67 -0
  80. package/template/src/app/api/settings/meta-leads/[id]/route.ts +0 -1
  81. package/template/src/app/api/settings/meta-leads/route.ts +14 -2
  82. package/template/src/app/api/settings/smtp/route.ts +53 -6
  83. package/template/src/app/api/tasks/[id]/attendees/route.ts +24 -8
  84. package/template/src/app/api/tasks/[id]/route.ts +234 -58
  85. package/template/src/app/api/tasks/meet/route.ts +27 -19
  86. package/template/src/app/api/tasks/route.ts +62 -17
  87. package/template/src/app/api/users/[id]/route.ts +20 -14
  88. package/template/src/app/api/users/list/route.ts +57 -19
  89. package/template/src/app/api/webhooks/google-ads/route.ts +34 -14
  90. package/template/src/app/api/webhooks/meta-leads/route.ts +32 -12
  91. package/template/src/app/api/workflows/[id]/route.ts +0 -4
  92. package/template/src/app/api/workflows/process/route.ts +22 -51
  93. package/template/src/app/api/workflows/route.ts +0 -4
  94. package/template/src/app/globals.css +342 -4
  95. package/template/src/app/layout.tsx +11 -3
  96. package/template/src/app/page.tsx +1 -1
  97. package/template/src/components/address-autocomplete.tsx +7 -6
  98. package/template/src/components/config-error-alert.tsx +46 -0
  99. package/template/src/components/contacts/filter-bar.tsx +12 -3
  100. package/template/src/components/contacts/filter-builder.tsx +28 -43
  101. package/template/src/components/contacts/save-view-dialog.tsx +1 -1
  102. package/template/src/components/contacts/views-tab-bar.tsx +15 -6
  103. package/template/src/components/dashboard/activity-chart.tsx +41 -28
  104. package/template/src/components/dashboard/add-widget-dialog.tsx +157 -0
  105. package/template/src/components/dashboard/color-picker.tsx +64 -0
  106. package/template/src/components/dashboard/contacts-chart.tsx +69 -0
  107. package/template/src/components/dashboard/interactions-by-type-chart.tsx +121 -0
  108. package/template/src/components/dashboard/recent-activity.tsx +154 -0
  109. package/template/src/components/dashboard/stat-card.tsx +40 -40
  110. package/template/src/components/dashboard/status-distribution-chart.tsx +81 -0
  111. package/template/src/components/dashboard/tasks-pie-chart.tsx +37 -34
  112. package/template/src/components/dashboard/top-contacts-list.tsx +113 -0
  113. package/template/src/components/dashboard/upcoming-tasks-list.tsx +72 -81
  114. package/template/src/components/dashboard/widget-wrapper.tsx +36 -0
  115. package/template/src/components/date-picker.tsx +9 -6
  116. package/template/src/components/editor/upload-editor-image.ts +42 -0
  117. package/template/src/components/editor.tsx +161 -22
  118. package/template/src/components/email-template.tsx +2 -2
  119. package/template/src/components/global-search.tsx +30 -28
  120. package/template/src/components/header.tsx +178 -80
  121. package/template/src/components/inactive-account-guard.tsx +58 -0
  122. package/template/src/components/integration-notifications-listener.tsx +12 -0
  123. package/template/src/components/invitation-email-template.tsx +2 -2
  124. package/template/src/components/meet-cancellation-email-template.tsx +3 -3
  125. package/template/src/components/meet-confirmation-email-template.tsx +3 -3
  126. package/template/src/components/meet-update-email-template.tsx +3 -3
  127. package/template/src/components/page-header.tsx +5 -5
  128. package/template/src/components/protected-page.tsx +1 -1
  129. package/template/src/components/reset-password-email-template.tsx +2 -2
  130. package/template/src/components/settings/integrations/GoogleAdsIntegration.tsx +428 -0
  131. package/template/src/components/settings/integrations/GoogleSheetConfigMonitoringModal.tsx +680 -0
  132. package/template/src/components/settings/integrations/GoogleSheetIntegration.tsx +809 -0
  133. package/template/src/components/settings/integrations/ImportResultDialog.tsx +124 -0
  134. package/template/src/components/settings/integrations/IntegrationLogPanel.tsx +57 -0
  135. package/template/src/components/settings/integrations/IntegrationLogsTable.tsx +186 -0
  136. package/template/src/components/settings/integrations/MetaLeadIntegration.tsx +451 -0
  137. package/template/src/components/sidebar.tsx +45 -26
  138. package/template/src/components/skeleton.tsx +40 -43
  139. package/template/src/components/ui/accordion.tsx +2 -2
  140. package/template/src/components/ui/alert-dialog.tsx +1 -1
  141. package/template/src/components/ui/button.tsx +20 -9
  142. package/template/src/components/ui/components.tsx +1 -1
  143. package/template/src/components/ui/date-picker.tsx +422 -0
  144. package/template/src/components/ui/datetime-picker.tsx +338 -0
  145. package/template/src/components/ui/status-select.tsx +271 -0
  146. package/template/src/components/ui/tooltip.tsx +37 -0
  147. package/template/src/components/view-as-modal.tsx +13 -7
  148. package/template/src/contexts/app-toast-context.tsx +245 -57
  149. package/template/src/contexts/dashboard-theme-context.tsx +53 -0
  150. package/template/src/contexts/sidebar-context.tsx +22 -17
  151. package/template/src/contexts/task-reminder-context.tsx +134 -160
  152. package/template/src/contexts/view-as-context.tsx +33 -6
  153. package/template/src/hooks/use-focus-trap.ts +2 -2
  154. package/template/src/hooks/useIntegrationNotifications.ts +49 -0
  155. package/template/src/lib/auth.ts +8 -1
  156. package/template/src/lib/config-links.ts +14 -0
  157. package/template/src/lib/contact-duplicate.ts +79 -61
  158. package/template/src/lib/contact-interactions.ts +21 -21
  159. package/template/src/lib/contact-view-filters.ts +24 -64
  160. package/template/src/lib/contacts-list-url.ts +190 -0
  161. package/template/src/lib/dashboard-stats.ts +65 -7
  162. package/template/src/lib/dashboard-themes.ts +135 -0
  163. package/template/src/lib/date-utils.ts +127 -0
  164. package/template/src/lib/default-widgets.ts +12 -0
  165. package/template/src/lib/editor-html-image-dimensions.ts +172 -0
  166. package/template/src/lib/editor-image-limits.ts +19 -0
  167. package/template/src/lib/email-html-sanitize.ts +19 -0
  168. package/template/src/lib/encryption.ts +9 -6
  169. package/template/src/lib/fr-geography.ts +192 -0
  170. package/template/src/lib/google-calendar-agenda.ts +201 -0
  171. package/template/src/lib/google-calendar.ts +255 -5
  172. package/template/src/lib/google-sheet-sync-jobs.ts +96 -0
  173. package/template/src/lib/google-sheet-sync-runner.ts +514 -0
  174. package/template/src/lib/integration-import-log.ts +21 -0
  175. package/template/src/lib/permissions.ts +40 -10
  176. package/template/src/lib/prisma.ts +4 -1
  177. package/template/src/lib/qstash.ts +65 -0
  178. package/template/src/lib/reminder-state-server.ts +80 -0
  179. package/template/src/lib/reminder-state.ts +29 -0
  180. package/template/src/lib/supabase-storage.ts +113 -0
  181. package/template/src/lib/template-variables.ts +164 -23
  182. package/template/src/lib/utils.ts +45 -0
  183. package/template/src/lib/widget-registry.ts +173 -0
  184. package/template/src/lib/workflow-executor.ts +16 -70
  185. package/template/src/proxy.ts +1 -0
  186. package/template/vercel.json +3 -10
  187. package/template/skills-lock.json +0 -25
  188. package/template/src/components/dashboard/dashboard-content.tsx +0 -79
  189. package/template/src/lib/google-drive.ts +0 -1101
  190. package/template/src/types/yousign.ts +0 -52
@@ -4,6 +4,7 @@ import { useState, useEffect } from 'react';
4
4
  import { useRouter, useParams } from 'next/navigation';
5
5
  import { Eye, EyeOff } from 'lucide-react';
6
6
  import { useAppToast } from '@/contexts/app-toast-context';
7
+ import { devToast } from '@/lib/utils';
7
8
 
8
9
  export default function InvitePage() {
9
10
  const router = useRouter();
@@ -49,7 +50,7 @@ export default function InvitePage() {
49
50
  setValidating(false);
50
51
  } catch {
51
52
  setValidating(false);
52
- toast.error('Erreur lors de la validation du lien');
53
+ toast.error('Ce lien d\'invitation n\'est plus valide. Contactez votre administrateur.');
53
54
  router.replace('/signin');
54
55
  }
55
56
  };
@@ -93,8 +94,7 @@ export default function InvitePage() {
93
94
  '/signin?message=Mot de passe défini avec succès, vous pouvez maintenant vous connecter',
94
95
  );
95
96
  } catch (err: unknown) {
96
- const message = err instanceof Error ? err.message : 'Erreur lors de la définition du mot de passe';
97
- setError(message);
97
+ setError(devToast('Erreur lors de la définition du mot de passe', err));
98
98
  } finally {
99
99
  setLoading(false);
100
100
  }
@@ -131,15 +131,16 @@ export default function InvitePage() {
131
131
 
132
132
  <form onSubmit={handleSubmit} className="space-y-6">
133
133
  <div>
134
- <label className="block text-sm font-medium text-gray-700">Mot de passe</label>
134
+ <label htmlFor="password" className="block text-sm font-medium text-gray-700">Mot de passe</label>
135
135
  <div className="relative mt-1">
136
136
  <input
137
+ id="password"
137
138
  type={showPassword ? 'text' : 'password'}
138
139
  required
139
140
  minLength={6}
140
141
  value={password}
141
142
  onChange={(e) => setPassword(e.target.value)}
142
- className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
143
+ className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:border-indigo-500"
143
144
  placeholder="••••••••"
144
145
  />
145
146
  <button
@@ -155,17 +156,18 @@ export default function InvitePage() {
155
156
  </div>
156
157
 
157
158
  <div>
158
- <label className="block text-sm font-medium text-gray-700">
159
+ <label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700">
159
160
  Confirmer le mot de passe
160
161
  </label>
161
162
  <div className="relative mt-1">
162
163
  <input
164
+ id="confirmPassword"
163
165
  type={showConfirmPassword ? 'text' : 'password'}
164
166
  required
165
167
  minLength={6}
166
168
  value={confirmPassword}
167
169
  onChange={(e) => setConfirmPassword(e.target.value)}
168
- className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
170
+ className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:border-indigo-500"
169
171
  placeholder="••••••••"
170
172
  />
171
173
  <button
@@ -186,7 +188,7 @@ export default function InvitePage() {
186
188
  <button
187
189
  type="submit"
188
190
  disabled={loading}
189
- className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
191
+ className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
190
192
  >
191
193
  {loading ? 'Création du compte...' : 'Créer mon compte'}
192
194
  </button>
@@ -1,3 +1,3 @@
1
1
  export default function AuthLayout({ children }: { children: React.ReactNode }) {
2
- return <div className="bg-lienar-to-br min-h-screen from-blue-50 to-indigo-100">{children}</div>;
2
+ return <div className="bg-gradient-to-br min-h-screen from-blue-50 to-indigo-100">{children}</div>;
3
3
  }
@@ -4,6 +4,7 @@ import { useState, useEffect, Suspense } from 'react';
4
4
  import { useRouter, useSearchParams } from 'next/navigation';
5
5
  import { Eye, EyeOff } from 'lucide-react';
6
6
  import { useAppToast } from '@/contexts/app-toast-context';
7
+ import { devToast } from '@/lib/utils';
7
8
 
8
9
  function ResetPasswordCompleteContent() {
9
10
  const router = useRouter();
@@ -39,7 +40,7 @@ function ResetPasswordCompleteContent() {
39
40
  setValidating(false);
40
41
  } catch (err) {
41
42
  setValidating(false);
42
- toast.error('Erreur lors de la validation du lien');
43
+ toast.error(devToast('Ce lien de réinitialisation n\'est plus valide. Veuillez en demander un nouveau.', err));
43
44
  router.replace('/signin');
44
45
  }
45
46
  };
@@ -48,7 +49,7 @@ function ResetPasswordCompleteContent() {
48
49
  validateToken();
49
50
  } else {
50
51
  setValidating(false);
51
- toast.error('Token manquant');
52
+ toast.error('Ce lien est incomplet. Veuillez réessayer depuis votre email.');
52
53
  router.replace('/signin');
53
54
  }
54
55
  }, [token, toast, router]);
@@ -87,7 +88,7 @@ function ResetPasswordCompleteContent() {
87
88
  '/signin?message=Mot de passe réinitialisé avec succès, vous pouvez maintenant vous connecter',
88
89
  );
89
90
  } catch (err: any) {
90
- setError(err.message);
91
+ setError(devToast('Erreur lors de la réinitialisation du mot de passe', err));
91
92
  } finally {
92
93
  setLoading(false);
93
94
  }
@@ -122,15 +123,16 @@ function ResetPasswordCompleteContent() {
122
123
 
123
124
  <form onSubmit={handleSubmit} className="space-y-6">
124
125
  <div>
125
- <label className="block text-sm font-medium text-gray-700">Nouveau mot de passe</label>
126
+ <label htmlFor="password" className="block text-sm font-medium text-gray-700">Nouveau mot de passe</label>
126
127
  <div className="relative mt-1">
127
128
  <input
129
+ id="password"
128
130
  type={showPassword ? 'text' : 'password'}
129
131
  required
130
132
  minLength={6}
131
133
  value={password}
132
134
  onChange={(e) => setPassword(e.target.value)}
133
- className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
135
+ className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:border-indigo-500"
134
136
  placeholder="••••••••"
135
137
  />
136
138
  <button
@@ -146,17 +148,18 @@ function ResetPasswordCompleteContent() {
146
148
  </div>
147
149
 
148
150
  <div>
149
- <label className="block text-sm font-medium text-gray-700">
151
+ <label htmlFor="confirmPassword" className="block text-sm font-medium text-gray-700">
150
152
  Confirmer le mot de passe
151
153
  </label>
152
154
  <div className="relative mt-1">
153
155
  <input
156
+ id="confirmPassword"
154
157
  type={showConfirmPassword ? 'text' : 'password'}
155
158
  required
156
159
  minLength={6}
157
160
  value={confirmPassword}
158
161
  onChange={(e) => setConfirmPassword(e.target.value)}
159
- className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
162
+ className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:border-indigo-500"
160
163
  placeholder="••••••••"
161
164
  />
162
165
  <button
@@ -177,7 +180,7 @@ function ResetPasswordCompleteContent() {
177
180
  <button
178
181
  type="submit"
179
182
  disabled={loading}
180
- className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
183
+ className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
181
184
  >
182
185
  {loading ? 'Réinitialisation...' : 'Réinitialiser le mot de passe'}
183
186
  </button>
@@ -4,6 +4,7 @@ import { useEffect, useState } from 'react';
4
4
  import { useRouter } from 'next/navigation';
5
5
  import Link from 'next/link';
6
6
  import { useAppToast } from '@/contexts/app-toast-context';
7
+ import { devToast } from '@/lib/utils';
7
8
 
8
9
  export default function ResetPasswordPage() {
9
10
  const router = useRouter();
@@ -39,8 +40,7 @@ export default function ResetPasswordPage() {
39
40
 
40
41
  setSuccess(true);
41
42
  } catch (err: unknown) {
42
- const message = err instanceof Error ? err.message : "Erreur lors de l'envoi du code";
43
- setError(message);
43
+ setError(devToast("Erreur lors de l'envoi du code", err));
44
44
  } finally {
45
45
  setLoading(false);
46
46
  }
@@ -123,7 +123,7 @@ export default function ResetPasswordPage() {
123
123
  required
124
124
  value={email}
125
125
  onChange={(e) => setEmail(e.target.value)}
126
- className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-3 text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
126
+ className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-3 text-gray-900 placeholder-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:border-indigo-500"
127
127
  placeholder="vous@exemple.com"
128
128
  />
129
129
  </div>
@@ -131,7 +131,7 @@ export default function ResetPasswordPage() {
131
131
  <button
132
132
  type="submit"
133
133
  disabled={loading}
134
- className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
134
+ className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
135
135
  >
136
136
  {loading ? 'Envoi...' : 'Envoyer le code'}
137
137
  </button>
@@ -4,6 +4,7 @@ import { useState, useEffect, Suspense } from 'react';
4
4
  import { useRouter, useSearchParams } from 'next/navigation';
5
5
  import Link from 'next/link';
6
6
  import { useAppToast } from '@/contexts/app-toast-context';
7
+ import { devToast } from '@/lib/utils';
7
8
 
8
9
  function VerifyResetCodeContent() {
9
10
  const router = useRouter();
@@ -88,8 +89,7 @@ function VerifyResetCodeContent() {
88
89
  // Rediriger vers la page de définition du nouveau mot de passe
89
90
  router.push(`/reset-password/complete?token=${data.token}`);
90
91
  } catch (err: unknown) {
91
- const message = err instanceof Error ? err.message : 'Code invalide';
92
- setError(message);
92
+ setError(devToast('Code invalide', err));
93
93
  setCode(['', '', '', '', '', '']);
94
94
  const firstInput = document.getElementById('code-0') as HTMLInputElement;
95
95
  firstInput?.focus();
@@ -123,7 +123,7 @@ function VerifyResetCodeContent() {
123
123
  onChange={(e) => handleCodeChange(index, e.target.value)}
124
124
  onKeyDown={(e) => handleKeyDown(index, e)}
125
125
  onPaste={index === 0 ? handlePaste : undefined}
126
- className="h-12 w-12 rounded-lg border-2 border-gray-300 text-center text-xl font-bold text-gray-900 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none sm:h-14 sm:w-14 sm:text-2xl"
126
+ className="h-12 w-12 rounded-lg border-2 border-gray-300 text-center text-xl font-bold text-gray-900 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:border-indigo-500 sm:h-14 sm:w-14 sm:text-2xl"
127
127
  autoFocus={index === 0}
128
128
  />
129
129
  ))}
@@ -132,7 +132,7 @@ function VerifyResetCodeContent() {
132
132
  <button
133
133
  type="submit"
134
134
  disabled={loading || code.join('').length !== 6}
135
- className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
135
+ className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
136
136
  >
137
137
  {loading ? 'Vérification...' : 'Vérifier le code'}
138
138
  </button>
@@ -6,6 +6,7 @@ import Link from 'next/link';
6
6
  import { signIn, signOut } from '@/lib/auth-client';
7
7
  import { Eye, EyeOff } from 'lucide-react';
8
8
  import { useAppToast } from '@/contexts/app-toast-context';
9
+ import { devToast } from '@/lib/utils';
9
10
 
10
11
  function SignInContent() {
11
12
  const router = useRouter();
@@ -19,13 +20,21 @@ function SignInContent() {
19
20
  const [showPassword, setShowPassword] = useState(false);
20
21
 
21
22
  useEffect(() => {
23
+ const inactive = searchParams.get('inactive');
24
+ if (inactive === '1') {
25
+ toast.warning(
26
+ 'Votre compte a été désactivé. Contactez un administrateur si vous pensez que c’est une erreur.',
27
+ );
28
+ router.replace('/signin', { scroll: false });
29
+ return;
30
+ }
22
31
  const message = searchParams.get('message');
23
32
  if (message) {
24
33
  setSuccessMessage(message);
25
34
  // Nettoyer l'URL
26
35
  router.replace('/signin', { scroll: false });
27
36
  }
28
- }, [searchParams, router]);
37
+ }, [searchParams, router, toast]);
29
38
 
30
39
  useEffect(() => {
31
40
  if (!successMessage) return;
@@ -72,8 +81,7 @@ function SignInContent() {
72
81
  // Étape 3 : Rediriger vers le dashboard si tout est OK
73
82
  router.push('/dashboard');
74
83
  } catch (err) {
75
- setError('Email ou mot de passe incorrect');
76
- console.error(err);
84
+ setError(devToast('Email ou mot de passe incorrect', err));
77
85
  } finally {
78
86
  setLoading(false);
79
87
  }
@@ -101,7 +109,7 @@ function SignInContent() {
101
109
  required
102
110
  value={email}
103
111
  onChange={(e) => setEmail(e.target.value)}
104
- className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-3 text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
112
+ className="mt-1 block w-full rounded-lg border border-gray-300 px-4 py-3 text-gray-900 placeholder-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:border-indigo-500"
105
113
  placeholder="vous@exemple.com"
106
114
  />
107
115
  </div>
@@ -117,7 +125,7 @@ function SignInContent() {
117
125
  required
118
126
  value={password}
119
127
  onChange={(e) => setPassword(e.target.value)}
120
- className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus:border-indigo-500 focus:ring-2 focus:ring-indigo-500 focus:outline-none"
128
+ className="block w-full rounded-lg border border-gray-300 px-4 py-3 pr-10 text-gray-900 placeholder-gray-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:border-indigo-500"
121
129
  placeholder="••••••••"
122
130
  />
123
131
  <button
@@ -134,7 +142,7 @@ function SignInContent() {
134
142
  <button
135
143
  type="submit"
136
144
  disabled={loading}
137
- className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-700 focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 focus:outline-none disabled:cursor-not-allowed disabled:opacity-50"
145
+ className="w-full cursor-pointer rounded-lg bg-indigo-600 px-4 py-3 font-semibold text-white transition-colors hover:bg-indigo-700 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring/50 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50"
138
146
  >
139
147
  {loading ? 'Connexion...' : 'Se connecter'}
140
148
  </button>