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.
- package/bin/create-crm-tmp.js +93 -0
- package/package.json +25 -0
- package/template/.prettierignore +33 -0
- package/template/.prettierrc.json +25 -0
- package/template/README.md +173 -0
- package/template/eslint.config.mjs +18 -0
- package/template/exemple-contacts.csv +11 -0
- package/template/next.config.ts +8 -0
- package/template/package.json +64 -0
- package/template/postcss.config.mjs +7 -0
- package/template/prisma/migrations/20251126144728_init/migration.sql +78 -0
- package/template/prisma/migrations/20251126155204_add_user_roles/migration.sql +5 -0
- package/template/prisma/migrations/20251128095126_add_company_info/migration.sql +19 -0
- package/template/prisma/migrations/20251128123321_add_smtp_config/migration.sql +22 -0
- package/template/prisma/migrations/20251128132303_add_status/migration.sql +23 -0
- package/template/prisma/migrations/20251201102207_add_user_active/migration.sql +75 -0
- package/template/prisma/migrations/20251201105507_add_email_signature/migration.sql +2 -0
- package/template/prisma/migrations/20251201151122_add_tasks/migration.sql +45 -0
- package/template/prisma/migrations/20251202111854_add_task_reminder/migration.sql +2 -0
- package/template/prisma/migrations/20251202135859_add_google_meet_integration/migration.sql +27 -0
- package/template/prisma/migrations/20251203103317_add_meta_lead_integration/migration.sql +20 -0
- package/template/prisma/migrations/20251203104002_add_google_ads_integration/migration.sql +18 -0
- package/template/prisma/migrations/20251203112122_add_google_sheet_integration/migration.sql +32 -0
- package/template/prisma/migrations/20251203153853_allow_multiple_integration_configs/migration.sql +20 -0
- package/template/prisma/migrations/20251205141705_update_user_roles/migration.sql +12 -0
- package/template/prisma/migrations/20251205150000_add_commercial_and_telepro_assignment/migration.sql +21 -0
- package/template/prisma/migrations/20251205160000_add_interaction_logging/migration.sql +11 -0
- package/template/prisma/migrations/20251208090314_add_automatic_interaction_types/migration.sql +12 -0
- package/template/prisma/migrations/20251208094843_mg/migration.sql +14 -0
- package/template/prisma/migrations/20251208100000_add_company_support/migration.sql +14 -0
- package/template/prisma/migrations/20251208110000_add_templates/migration.sql +26 -0
- package/template/prisma/migrations/20251208141304_add_video_conference_task_type/migration.sql +2 -0
- package/template/prisma/migrations/20251209104759_add_internal_note_to_task/migration.sql +2 -0
- package/template/prisma/migrations/20251209134803_add_company_field/migration.sql +2 -0
- package/template/prisma/migrations/20251209150000_rename_company_to_company_name/migration.sql +3 -0
- package/template/prisma/migrations/20251209150016_add_email_tracking/migration.sql +21 -0
- package/template/prisma/migrations/20251209155908_add_notify_contact_to_task/migration.sql +2 -0
- package/template/prisma/migrations/20251210110019_add_appointment_types/migration.sql +10 -0
- package/template/prisma/migrations/20251210113928_add_contact_files/migration.sql +26 -0
- package/template/prisma/migrations/20251212132339_add_custom_roles/migration.sql +24 -0
- package/template/prisma/migrations/20251215104448_add_file_interaction_types/migration.sql +11 -0
- package/template/prisma/migrations/20251215145616_add_closing_reasons/migration.sql +12 -0
- package/template/prisma/migrations/20251216140850_add_log_users/migration.sql +25 -0
- package/template/prisma/migrations/20251216151000_rename_perdu_to_ferme/migration.sql +8 -0
- package/template/prisma/migrations/20251216162318_add_column_mappings_to_google_sheet/migration.sql +2 -0
- package/template/prisma/migrations/20251216185127_add_workflows/migration.sql +80 -0
- package/template/prisma/migrations/20251216192237_add_scheduled_workflow_actions/migration.sql +32 -0
- package/template/prisma/migrations/migration_lock.toml +3 -0
- package/template/prisma/schema.prisma +582 -0
- package/template/prisma.config.ts +14 -0
- package/template/src/app/(auth)/invite/[token]/page.tsx +200 -0
- package/template/src/app/(auth)/layout.tsx +3 -0
- package/template/src/app/(auth)/reset-password/complete/page.tsx +213 -0
- package/template/src/app/(auth)/reset-password/page.tsx +146 -0
- package/template/src/app/(auth)/reset-password/verify/page.tsx +183 -0
- package/template/src/app/(auth)/signin/page.tsx +166 -0
- package/template/src/app/(dashboard)/agenda/page.tsx +3051 -0
- package/template/src/app/(dashboard)/automatisation/[id]/page.tsx +24 -0
- package/template/src/app/(dashboard)/automatisation/_components/workflow-editor.tsx +905 -0
- package/template/src/app/(dashboard)/automatisation/new/page.tsx +20 -0
- package/template/src/app/(dashboard)/automatisation/page.tsx +337 -0
- package/template/src/app/(dashboard)/closing/page.tsx +1052 -0
- package/template/src/app/(dashboard)/contacts/[id]/page.tsx +6028 -0
- package/template/src/app/(dashboard)/contacts/page.tsx +3713 -0
- package/template/src/app/(dashboard)/dashboard/page.tsx +186 -0
- package/template/src/app/(dashboard)/layout.tsx +30 -0
- package/template/src/app/(dashboard)/settings/page.tsx +4070 -0
- package/template/src/app/(dashboard)/templates/page.tsx +567 -0
- package/template/src/app/(dashboard)/users/list/page.tsx +507 -0
- package/template/src/app/(dashboard)/users/page.tsx +457 -0
- package/template/src/app/(dashboard)/users/permissions/page.tsx +181 -0
- package/template/src/app/(dashboard)/users/roles/page.tsx +434 -0
- package/template/src/app/api/audit-logs/route.ts +57 -0
- package/template/src/app/api/auth/[...all]/route.ts +4 -0
- package/template/src/app/api/auth/check-active/route.ts +31 -0
- package/template/src/app/api/auth/google/callback/route.ts +94 -0
- package/template/src/app/api/auth/google/disconnect/route.ts +32 -0
- package/template/src/app/api/auth/google/route.ts +34 -0
- package/template/src/app/api/auth/google/status/route.ts +32 -0
- package/template/src/app/api/closing-reasons/route.ts +27 -0
- package/template/src/app/api/contacts/[id]/files/[fileId]/route.ts +94 -0
- package/template/src/app/api/contacts/[id]/files/route.ts +269 -0
- package/template/src/app/api/contacts/[id]/interactions/[interactionId]/route.ts +91 -0
- package/template/src/app/api/contacts/[id]/interactions/route.ts +103 -0
- package/template/src/app/api/contacts/[id]/meet/route.ts +296 -0
- package/template/src/app/api/contacts/[id]/route.ts +322 -0
- package/template/src/app/api/contacts/[id]/send-email/route.ts +254 -0
- package/template/src/app/api/contacts/export/route.ts +270 -0
- package/template/src/app/api/contacts/import/route.ts +381 -0
- package/template/src/app/api/contacts/route.ts +283 -0
- package/template/src/app/api/dashboard/stats/route.ts +299 -0
- package/template/src/app/api/email/track/[id]/route.ts +68 -0
- package/template/src/app/api/integrations/google-sheet/sync/route.ts +526 -0
- package/template/src/app/api/invite/complete/route.ts +88 -0
- package/template/src/app/api/invite/validate/route.ts +55 -0
- package/template/src/app/api/reminders/route.ts +95 -0
- package/template/src/app/api/reset-password/complete/route.ts +73 -0
- package/template/src/app/api/reset-password/request/route.ts +84 -0
- package/template/src/app/api/reset-password/validate/route.ts +49 -0
- package/template/src/app/api/reset-password/verify/route.ts +74 -0
- package/template/src/app/api/roles/[id]/route.ts +183 -0
- package/template/src/app/api/roles/route.ts +140 -0
- package/template/src/app/api/send/route.ts +282 -0
- package/template/src/app/api/settings/change-password/route.ts +95 -0
- package/template/src/app/api/settings/closing-reasons/[id]/route.ts +84 -0
- package/template/src/app/api/settings/closing-reasons/route.ts +74 -0
- package/template/src/app/api/settings/company/route.ts +121 -0
- package/template/src/app/api/settings/google-ads/[id]/route.ts +117 -0
- package/template/src/app/api/settings/google-ads/route.ts +122 -0
- package/template/src/app/api/settings/google-sheet/[id]/route.ts +230 -0
- package/template/src/app/api/settings/google-sheet/auto-map/route.ts +196 -0
- package/template/src/app/api/settings/google-sheet/route.ts +254 -0
- package/template/src/app/api/settings/meta-leads/[id]/route.ts +123 -0
- package/template/src/app/api/settings/meta-leads/route.ts +132 -0
- package/template/src/app/api/settings/profile/route.ts +42 -0
- package/template/src/app/api/settings/smtp/route.ts +130 -0
- package/template/src/app/api/settings/smtp/test/route.ts +121 -0
- package/template/src/app/api/settings/statuses/[id]/route.ts +101 -0
- package/template/src/app/api/settings/statuses/route.ts +83 -0
- package/template/src/app/api/statuses/route.ts +25 -0
- package/template/src/app/api/tasks/[id]/attendees/route.ts +76 -0
- package/template/src/app/api/tasks/[id]/route.ts +728 -0
- package/template/src/app/api/tasks/meet/route.ts +240 -0
- package/template/src/app/api/tasks/route.ts +417 -0
- package/template/src/app/api/templates/[id]/route.ts +140 -0
- package/template/src/app/api/templates/route.ts +91 -0
- package/template/src/app/api/users/[id]/route.ts +168 -0
- package/template/src/app/api/users/list/route.ts +45 -0
- package/template/src/app/api/users/me/route.ts +48 -0
- package/template/src/app/api/users/route.ts +250 -0
- package/template/src/app/api/webhooks/google-ads/route.ts +208 -0
- package/template/src/app/api/webhooks/meta-leads/route.ts +258 -0
- package/template/src/app/api/workflows/[id]/route.ts +192 -0
- package/template/src/app/api/workflows/process/route.ts +293 -0
- package/template/src/app/api/workflows/route.ts +124 -0
- package/template/src/app/favicon.ico +0 -0
- package/template/src/app/globals.css +1416 -0
- package/template/src/app/layout.tsx +31 -0
- package/template/src/app/page.tsx +32 -0
- package/template/src/components/dashboard/activity-chart.tsx +67 -0
- package/template/src/components/dashboard/contacts-chart.tsx +63 -0
- package/template/src/components/dashboard/recent-activity.tsx +164 -0
- package/template/src/components/dashboard/sales-analytics-chart.tsx +81 -0
- package/template/src/components/dashboard/stat-card.tsx +61 -0
- package/template/src/components/dashboard/status-distribution-chart.tsx +45 -0
- package/template/src/components/dashboard/tasks-pie-chart.tsx +88 -0
- package/template/src/components/dashboard/top-contacts-list.tsx +129 -0
- package/template/src/components/dashboard/upcoming-tasks-list.tsx +126 -0
- package/template/src/components/editor.tsx +856 -0
- package/template/src/components/email-template.tsx +35 -0
- package/template/src/components/header.tsx +320 -0
- package/template/src/components/invitation-email-template.tsx +79 -0
- package/template/src/components/meet-cancellation-email-template.tsx +120 -0
- package/template/src/components/meet-confirmation-email-template.tsx +156 -0
- package/template/src/components/meet-update-email-template.tsx +209 -0
- package/template/src/components/page-header.tsx +61 -0
- package/template/src/components/reset-password-email-template.tsx +79 -0
- package/template/src/components/sidebar.tsx +294 -0
- package/template/src/components/skeleton.tsx +380 -0
- package/template/src/components/ui/commands.tsx +396 -0
- package/template/src/components/ui/components.tsx +150 -0
- package/template/src/components/ui/theme.tsx +5 -0
- package/template/src/components/view-as-banner.tsx +45 -0
- package/template/src/components/view-as-modal.tsx +186 -0
- package/template/src/contexts/mobile-menu-context.tsx +31 -0
- package/template/src/contexts/sidebar-context.tsx +107 -0
- package/template/src/contexts/task-reminder-context.tsx +239 -0
- package/template/src/contexts/view-as-context.tsx +84 -0
- package/template/src/hooks/use-user-role.ts +82 -0
- package/template/src/lib/audit-log.ts +45 -0
- package/template/src/lib/auth-client.ts +16 -0
- package/template/src/lib/auth.ts +35 -0
- package/template/src/lib/check-permission.ts +193 -0
- package/template/src/lib/contact-duplicate.ts +112 -0
- package/template/src/lib/contact-interactions.ts +371 -0
- package/template/src/lib/encryption.ts +99 -0
- package/template/src/lib/google-calendar.ts +300 -0
- package/template/src/lib/google-drive.ts +372 -0
- package/template/src/lib/permissions.ts +412 -0
- package/template/src/lib/prisma.ts +32 -0
- package/template/src/lib/roles.ts +120 -0
- package/template/src/lib/template-variables.ts +76 -0
- package/template/src/lib/utils.ts +46 -0
- package/template/src/lib/workflow-executor.ts +482 -0
- package/template/src/proxy.ts +91 -0
- package/template/tsconfig.json +34 -0
- 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
|
+
}
|