espace-paie-odentas/app/api/tickets/route.ts

148 lines
5.3 KiB
TypeScript

import { NextResponse } from "next/server";
import { cookies } from "next/headers";
import { createRouteHandlerClient } from "@supabase/auth-helpers-nextjs";
import { sendInternalTicketCreatedEmail } from "@/lib/emailMigrationHelpers";
import { createSbServer, createSbServiceRole } from "@/lib/supabaseServer";
export const dynamic = "force-dynamic";
export async function GET(req: Request) {
const sb = createRouteHandlerClient({ cookies });
const url = new URL(req.url);
const status = url.searchParams.get("status");
const orgId = url.searchParams.get("org_id");
const limit = Math.min(parseInt(url.searchParams.get("limit") || "50", 10) || 50, 100);
const offset = parseInt(url.searchParams.get("offset") || "0", 10) || 0;
// Optional filters
let query = sb.from("tickets")
.select("id, org_id, subject, status, priority, created_by, assigned_to, source, last_message_at, message_count, unread_by_client, unread_by_staff, created_at, updated_at")
.order("last_message_at", { ascending: false })
.range(offset, offset + limit - 1);
if (status) query = query.eq("status", status);
if (orgId) query = query.eq("org_id", orgId);
const { data, error } = await query;
if (error) return NextResponse.json({ error: error.message }, { status: 400 });
return NextResponse.json({ items: data || [] });
}
export async function POST(req: Request) {
const sb = createRouteHandlerClient({ cookies });
const { data: { user } } = await sb.auth.getUser();
if (!user) return new NextResponse("Unauthorized", { status: 401 });
const body = await req.json().catch(() => ({}));
let { org_id, subject, message, priority } = body || {} as any;
subject = String(subject || "").trim();
message = String(message || "").trim();
priority = (priority || "normal").toString();
if (!subject || !message) {
return NextResponse.json({ error: "Sujet et message requis" }, { status: 400 });
}
// Determine if staff
let isStaff = false;
try {
const { data: s } = await sb.from("staff_users").select("is_staff").eq("user_id", user.id).maybeSingle();
isStaff = !!s?.is_staff;
} catch {}
// Determine org_id if missing
if (!org_id) {
if (isStaff) {
const c = cookies();
org_id = c.get("active_org_id")?.value || null;
} else {
const { data: m } = await sb
.from("organization_members")
.select("org_id")
.eq("user_id", user.id)
.eq("revoked", false)
.maybeSingle();
org_id = m?.org_id || null;
}
}
if (!org_id) return NextResponse.json({ error: "Impossible de déterminer l'organisation" }, { status: 400 });
const { data: ticket, error: tErr } = await sb
.from("tickets")
.insert({
org_id,
subject,
priority,
created_by: user.id,
source: isStaff ? "staff" : "web",
message_count: 1, // Initialiser à 1 puisqu'il y aura forcément le message initial
last_message_at: new Date().toISOString(),
unread_by_client: isStaff ? 1 : 0,
unread_by_staff: isStaff ? 0 : 1
})
.select("*")
.single();
if (tErr) return NextResponse.json({ error: tErr.message }, { status: 400 });
const { error: mErr } = await sb
.from("ticket_messages")
.insert({ ticket_id: ticket.id, author_id: user.id, body: message, internal: false, via: isStaff ? "staff" : "web" });
if (mErr) return NextResponse.json({ error: mErr.message }, { status: 400 });
// Corriger le message_count si le trigger l'a incrémenté (il devrait rester à 1)
await sb
.from("tickets")
.update({ message_count: 1 })
.eq("id", ticket.id);
// Envoyer une notification interne par email à paie@odentas.fr si le ticket n'est pas créé par le staff
if (!isStaff) {
try {
console.log('[TICKET CREATE] Sending internal notification email...');
// Récupérer les infos de l'utilisateur
const sbAdmin = createSbServiceRole();
const { data: userData } = await sbAdmin.auth.admin.getUserById(user.id);
const userName = userData?.user?.user_metadata?.display_name
|| userData?.user?.user_metadata?.first_name
|| 'Utilisateur inconnu';
// Récupérer les infos de l'organisation
const { data: organization } = await sb
.from('organizations')
.select('name')
.eq('id', org_id)
.single();
// Récupérer le code employeur
const { data: orgDetails } = await sb
.from('organization_details')
.select('code_employeur')
.eq('org_id', org_id)
.single();
// Déterminer la catégorie du ticket (basée sur le sujet pour l'instant)
const category = 'Support général';
await sendInternalTicketCreatedEmail({
ticketId: ticket.id,
ticketSubject: subject,
ticketCategory: category,
ticketMessage: message,
userName,
userEmail: user.email || 'Email non disponible',
organizationName: organization?.name,
employerCode: orgDetails?.code_employeur,
});
console.log('[TICKET CREATE] Internal notification email sent successfully');
} catch (emailError) {
// Ne pas bloquer la création du ticket si l'email échoue
console.error('[TICKET CREATE] Failed to send internal notification email:', emailError);
}
}
return NextResponse.json(ticket, { status: 201 });
}