import { NextRequest, NextResponse } from 'next/server'; import { createClient } from '@/lib/supabase/server'; import { resolveActiveTenant } from '@/lib/tenant-context'; import { dualStatus, insertComment, listApprovals, listComments, partyFromUser, upsertApproval, } from '@/lib/content-collab'; import { serviceHeaders, supabaseUrl } from '@/lib/agency'; export const dynamic = 'force-dynamic'; async function gate() { const supabase = await createClient(); const { data: { user }, } = await supabase.auth.getUser(); if (!user) { return { error: NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) }; } const ctx = await resolveActiveTenant(user); if (!ctx.tenantId || ctx.portfolioWide) { return { error: NextResponse.json({ error: 'Selecciona un cliente' }, { status: 400 }), }; } return { user, ctx, party: partyFromUser(user) }; } export async function GET(req: NextRequest) { const g = await gate(); if ('error' in g && g.error) return g.error; const { ctx, party } = g as { ctx: { tenantId: string }; party: 'agency' | 'client'; }; const ids = (req.nextUrl.searchParams.get('ids') || '') .split(',') .map((s) => s.trim()) .filter(Boolean); const resourceId = req.nextUrl.searchParams.get('resourceId'); const approvals = await listApprovals(ctx.tenantId, 'asset', ids); const dual: Record> = {}; for (const id of ids) dual[id] = dualStatus(approvals[id] || []); let comments = null; if (resourceId) { comments = await listComments(ctx.tenantId, 'asset', resourceId); } return NextResponse.json({ dual, comments, party }); } export async function POST(req: NextRequest) { const g = await gate(); if ('error' in g && g.error) return g.error; const { user, ctx, party } = g as { user: { id: string; email?: string }; ctx: { tenantId: string }; party: 'agency' | 'client'; }; const body = await req.json().catch(() => ({})); const assetId = String(body.assetId || ''); if (!assetId) { return NextResponse.json({ error: 'assetId requerido' }, { status: 400 }); } if (body.action === 'approve' || body.action === 'revoke') { await upsertApproval({ tenantId: ctx.tenantId, resourceType: 'asset', resourceId: assetId, party, approved: body.action === 'approve', actorId: user.id, actorEmail: user.email || null, }); const approvals = await listApprovals(ctx.tenantId, 'asset', [assetId]); const dual = dualStatus(approvals[assetId] || []); // Mirror to asset_files columns when both OK const headers = serviceHeaders(); if (headers) { const patch: Record = { updated_at: new Date().toISOString(), }; if (party === 'agency') { patch.agency_approved_at = body.action === 'approve' ? new Date().toISOString() : null; patch.agency_approved_by = body.action === 'approve' ? user.id : null; } else { patch.client_approved_at = body.action === 'approve' ? new Date().toISOString() : null; patch.client_approved_by = body.action === 'approve' ? user.id : null; } if (dual.both) patch.review_status = 'validated'; else patch.review_status = 'in_review'; await fetch( `${supabaseUrl()}/rest/v1/asset_files?id=eq.${encodeURIComponent(assetId)}&tenant_id=eq.${encodeURIComponent(ctx.tenantId)}`, { method: 'PATCH', headers: { ...headers, 'Content-Type': 'application/json', Prefer: 'return=minimal', }, body: JSON.stringify(patch), } ); } return NextResponse.json({ ok: true, dual, party }); } if (body.action === 'comment') { const text = String(body.body || '').trim(); if (!text) { return NextResponse.json({ error: 'Comentario vacío' }, { status: 400 }); } const row = await insertComment({ tenantId: ctx.tenantId, resourceType: 'asset', resourceId: assetId, body: text.slice(0, 4000), authorId: user.id, authorEmail: user.email || null, authorParty: party, }); return NextResponse.json({ ok: true, comment: row }); } return NextResponse.json({ error: 'action inválida' }, { status: 400 }); }