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,121 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { auth } from '@/lib/auth';
3
+ import nodemailer from 'nodemailer';
4
+
5
+ function htmlToText(html: string): string {
6
+ if (!html) return '';
7
+ return html
8
+ .replace(/<br\s*\/?>/gi, '\n')
9
+ .replace(/<\/p>/gi, '\n\n')
10
+ .replace(/<[^>]+>/g, '')
11
+ .replace(/\n{3,}/g, '\n\n')
12
+ .trim();
13
+ }
14
+
15
+ // POST /api/settings/smtp/test - Tester la connexion SMTP
16
+ export async function POST(request: NextRequest) {
17
+ try {
18
+ const session = await auth.api.getSession({
19
+ headers: request.headers,
20
+ });
21
+
22
+ if (!session) {
23
+ return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
24
+ }
25
+
26
+ const body = await request.json();
27
+ const { host, port, secure, username, password, fromEmail, fromName, signature } = body;
28
+
29
+ // Validation
30
+ if (!host || !port || !username || !password || !fromEmail) {
31
+ return NextResponse.json(
32
+ { error: 'Tous les champs sont requis pour tester la connexion' },
33
+ { status: 400 },
34
+ );
35
+ }
36
+
37
+ // Créer un transporteur SMTP de test
38
+ const transporter = nodemailer.createTransport({
39
+ host,
40
+ port: parseInt(port),
41
+ secure: secure === true || secure === 'true', // true pour 465, false pour les autres ports
42
+ auth: {
43
+ user: username,
44
+ pass: password,
45
+ },
46
+ // Options de test
47
+ connectionTimeout: 10000, // 10 secondes
48
+ greetingTimeout: 10000,
49
+ });
50
+
51
+ // Tester la connexion
52
+ try {
53
+ await transporter.verify();
54
+
55
+ // Construire le contenu de test avec la signature éventuelle
56
+ const baseHtml = `
57
+ <div style="font-family: Arial, sans-serif;">
58
+ <h2 style="color: #4F46E5;">Test de configuration SMTP</h2>
59
+ <p>Ceci est un email de test pour vérifier que votre configuration SMTP fonctionne correctement.</p>
60
+ <p style="color: #10B981; font-weight: bold;">✅ Votre configuration SMTP est opérationnelle !</p>
61
+ </div>
62
+ `;
63
+
64
+ const baseText =
65
+ 'Test de configuration SMTP\n\nCeci est un email de test pour vérifier que votre configuration SMTP fonctionne correctement.\n\n✅ Votre configuration SMTP est opérationnelle !';
66
+
67
+ const signatureHtml = signature ? `<br><br>${signature}` : '';
68
+ const signatureText = signature ? `\n\n${htmlToText(signature)}` : '';
69
+
70
+ const finalHtml = `${baseHtml}${signatureHtml}`;
71
+ const finalText = `${baseText}${signatureText}`;
72
+
73
+ // Si la vérification réussit, essayer d'envoyer un email de test
74
+ try {
75
+ const testEmail = await transporter.sendMail({
76
+ from: `"${fromName}" <${fromEmail}>`,
77
+ to: session.user.email, // Envoyer à l'utilisateur connecté
78
+ subject: 'Test de configuration SMTP - CRM',
79
+ text: finalText,
80
+ html: finalHtml,
81
+ });
82
+
83
+ return NextResponse.json({
84
+ success: true,
85
+ message: 'Connexion SMTP réussie ! Un email de test a été envoyé à votre adresse.',
86
+ messageId: testEmail.messageId,
87
+ });
88
+ } catch (sendError: any) {
89
+ // La connexion fonctionne mais l'envoi a échoué
90
+ return NextResponse.json(
91
+ {
92
+ success: false,
93
+ message: "Connexion SMTP réussie, mais l'envoi de l'email de test a échoué",
94
+ error: sendError.message,
95
+ },
96
+ { status: 200 },
97
+ ); // On retourne 200 car la connexion fonctionne
98
+ }
99
+ } catch (verifyError: any) {
100
+ // La connexion a échoué
101
+ return NextResponse.json(
102
+ {
103
+ success: false,
104
+ message: 'Échec de la connexion SMTP',
105
+ error: verifyError.message || 'Impossible de se connecter au serveur SMTP',
106
+ },
107
+ { status: 400 },
108
+ );
109
+ }
110
+ } catch (error: any) {
111
+ console.error('Erreur lors du test SMTP:', error);
112
+ return NextResponse.json(
113
+ {
114
+ success: false,
115
+ message: 'Erreur lors du test de connexion',
116
+ error: error.message || 'Erreur serveur',
117
+ },
118
+ { status: 500 },
119
+ );
120
+ }
121
+ }
@@ -0,0 +1,101 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { prisma } from '@/lib/prisma';
3
+ import { requireAdmin } from '@/lib/roles';
4
+
5
+ // PUT /api/settings/statuses/[id] - Mettre à jour un statut
6
+ export async function PUT(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
7
+ try {
8
+ await requireAdmin(request.headers);
9
+ const { id } = await params;
10
+
11
+ const body = await request.json();
12
+ const { name, color, order } = body;
13
+
14
+ // Validation
15
+ if (!name || !color) {
16
+ return NextResponse.json({ error: 'Le nom et la couleur sont requis' }, { status: 400 });
17
+ }
18
+
19
+ // Vérifier si le statut existe
20
+ const existing = await prisma.status.findUnique({
21
+ where: { id },
22
+ });
23
+
24
+ if (!existing) {
25
+ return NextResponse.json({ error: 'Statut non trouvé' }, { status: 404 });
26
+ }
27
+
28
+ // Vérifier si le nom existe déjà pour un autre statut
29
+ const nameConflict = await prisma.status.findFirst({
30
+ where: {
31
+ name,
32
+ NOT: { id },
33
+ },
34
+ });
35
+
36
+ if (nameConflict) {
37
+ return NextResponse.json({ error: 'Un statut avec ce nom existe déjà' }, { status: 400 });
38
+ }
39
+
40
+ const updatedStatus = await prisma.status.update({
41
+ where: { id },
42
+ data: {
43
+ name,
44
+ color,
45
+ ...(order !== undefined && { order }),
46
+ },
47
+ });
48
+
49
+ return NextResponse.json(updatedStatus);
50
+ } catch (error: any) {
51
+ console.error('Erreur lors de la mise à jour du statut:', error);
52
+
53
+ if (error.message === 'Non authentifié') {
54
+ return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
55
+ }
56
+
57
+ if (error.message === 'Permissions insuffisantes') {
58
+ return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
59
+ }
60
+
61
+ return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
62
+ }
63
+ }
64
+
65
+ // DELETE /api/settings/statuses/[id] - Supprimer un statut
66
+ export async function DELETE(
67
+ request: NextRequest,
68
+ { params }: { params: Promise<{ id: string }> },
69
+ ) {
70
+ try {
71
+ await requireAdmin(request.headers);
72
+ const { id } = await params;
73
+
74
+ // Vérifier si le statut existe
75
+ const existing = await prisma.status.findUnique({
76
+ where: { id },
77
+ });
78
+
79
+ if (!existing) {
80
+ return NextResponse.json({ error: 'Statut non trouvé' }, { status: 404 });
81
+ }
82
+
83
+ await prisma.status.delete({
84
+ where: { id },
85
+ });
86
+
87
+ return NextResponse.json({ success: true, message: 'Statut supprimé avec succès' });
88
+ } catch (error: any) {
89
+ console.error('Erreur lors de la suppression du statut:', error);
90
+
91
+ if (error.message === 'Non authentifié') {
92
+ return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
93
+ }
94
+
95
+ if (error.message === 'Permissions insuffisantes') {
96
+ return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
97
+ }
98
+
99
+ return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
100
+ }
101
+ }
@@ -0,0 +1,83 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { prisma } from '@/lib/prisma';
3
+ import { requireAdmin } from '@/lib/roles';
4
+
5
+ // GET /api/settings/statuses - Récupérer tous les statuts
6
+ export async function GET(request: NextRequest) {
7
+ try {
8
+ await requireAdmin(request.headers);
9
+
10
+ const statuses = await prisma.status.findMany({
11
+ orderBy: { order: 'asc' },
12
+ });
13
+
14
+ return NextResponse.json(statuses);
15
+ } catch (error: any) {
16
+ console.error('Erreur lors de la récupération des statuts:', error);
17
+
18
+ if (error.message === 'Non authentifié') {
19
+ return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
20
+ }
21
+
22
+ if (error.message === 'Permissions insuffisantes') {
23
+ return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
24
+ }
25
+
26
+ return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
27
+ }
28
+ }
29
+
30
+ // POST /api/settings/statuses - Créer un nouveau statut
31
+ export async function POST(request: NextRequest) {
32
+ try {
33
+ await requireAdmin(request.headers);
34
+
35
+ const body = await request.json();
36
+ const { name, color, order } = body;
37
+
38
+ // Validation
39
+ if (!name || !color) {
40
+ return NextResponse.json({ error: 'Le nom et la couleur sont requis' }, { status: 400 });
41
+ }
42
+
43
+ // Vérifier si le nom existe déjà
44
+ const existing = await prisma.status.findUnique({
45
+ where: { name },
46
+ });
47
+
48
+ if (existing) {
49
+ return NextResponse.json({ error: 'Un statut avec ce nom existe déjà' }, { status: 400 });
50
+ }
51
+
52
+ // Récupérer le dernier ordre si non fourni
53
+ let statusOrder = order;
54
+ if (statusOrder === undefined || statusOrder === null) {
55
+ const lastStatus = await prisma.status.findFirst({
56
+ orderBy: { order: 'desc' },
57
+ });
58
+ statusOrder = lastStatus ? lastStatus.order + 1 : 0;
59
+ }
60
+
61
+ const status = await prisma.status.create({
62
+ data: {
63
+ name,
64
+ color,
65
+ order: statusOrder,
66
+ },
67
+ });
68
+
69
+ return NextResponse.json(status, { status: 201 });
70
+ } catch (error: any) {
71
+ console.error('Erreur lors de la création du statut:', error);
72
+
73
+ if (error.message === 'Non authentifié') {
74
+ return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
75
+ }
76
+
77
+ if (error.message === 'Permissions insuffisantes') {
78
+ return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
79
+ }
80
+
81
+ return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
82
+ }
83
+ }
@@ -0,0 +1,25 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { auth } from '@/lib/auth';
3
+ import { prisma } from '@/lib/prisma';
4
+
5
+ // GET /api/statuses - Récupérer tous les statuts (accessible à tous les utilisateurs authentifiés)
6
+ export async function GET(request: NextRequest) {
7
+ try {
8
+ const session = await auth.api.getSession({
9
+ headers: request.headers,
10
+ });
11
+
12
+ if (!session) {
13
+ return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
14
+ }
15
+
16
+ const statuses = await prisma.status.findMany({
17
+ orderBy: { order: 'asc' },
18
+ });
19
+
20
+ return NextResponse.json(statuses);
21
+ } catch (error: any) {
22
+ console.error('Erreur lors de la récupération des statuts:', error);
23
+ return NextResponse.json({ error: 'Erreur serveur' }, { status: 500 });
24
+ }
25
+ }
@@ -0,0 +1,76 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import { auth } from '@/lib/auth';
3
+ import { prisma } from '@/lib/prisma';
4
+ import { getValidAccessToken, getGoogleCalendarEvent } from '@/lib/google-calendar';
5
+
6
+ // GET /api/tasks/[id]/attendees - Récupérer les invités d'un Google Meet
7
+ export async function GET(request: NextRequest, { params }: { params: Promise<{ id: string }> }) {
8
+ try {
9
+ const session = await auth.api.getSession({
10
+ headers: request.headers,
11
+ });
12
+
13
+ if (!session) {
14
+ return NextResponse.json({ error: 'Non authentifié' }, { status: 401 });
15
+ }
16
+
17
+ const { id } = await params;
18
+
19
+ const task = await prisma.task.findUnique({
20
+ where: { id },
21
+ select: {
22
+ id: true,
23
+ googleEventId: true,
24
+ contactId: true,
25
+ contact: {
26
+ select: {
27
+ email: true,
28
+ },
29
+ },
30
+ assignedUserId: true,
31
+ },
32
+ });
33
+
34
+ if (!task || !task.googleEventId) {
35
+ return NextResponse.json({ error: 'Tâche ou Google Event non trouvé' }, { status: 404 });
36
+ }
37
+
38
+ // Vérifier les permissions
39
+ const user = await prisma.user.findUnique({
40
+ where: { id: session.user.id },
41
+ select: { role: true },
42
+ });
43
+
44
+ if (task.assignedUserId !== session.user.id && user?.role !== 'ADMIN') {
45
+ return NextResponse.json({ error: 'Accès refusé' }, { status: 403 });
46
+ }
47
+
48
+ // Récupérer le compte Google
49
+ const googleAccount = await prisma.userGoogleAccount.findUnique({
50
+ where: { userId: session.user.id },
51
+ });
52
+
53
+ if (!googleAccount) {
54
+ return NextResponse.json({ error: 'Compte Google non connecté' }, { status: 400 });
55
+ }
56
+
57
+ // Obtenir un token valide
58
+ const accessToken = await getValidAccessToken(
59
+ googleAccount.accessToken,
60
+ googleAccount.refreshToken,
61
+ googleAccount.tokenExpiresAt,
62
+ );
63
+
64
+ // Récupérer l'événement Google Calendar
65
+ const googleEvent = await getGoogleCalendarEvent(accessToken, task.googleEventId);
66
+
67
+ // Extraire les emails des invités (inclure tous les invités, y compris le contact)
68
+ const attendees =
69
+ googleEvent.attendees?.map((attendee) => attendee.email).filter(Boolean) || [];
70
+
71
+ return NextResponse.json({ attendees });
72
+ } catch (error: any) {
73
+ console.error('Erreur lors de la récupération des invités:', error);
74
+ return NextResponse.json({ error: error.message || 'Erreur serveur' }, { status: 500 });
75
+ }
76
+ }