diff --git a/src/app/api/plans/[id]/route.ts b/src/app/api/plans/[id]/route.ts index 53cb83a..5784439 100644 --- a/src/app/api/plans/[id]/route.ts +++ b/src/app/api/plans/[id]/route.ts @@ -149,3 +149,54 @@ export const PUT = withAuth(async (req, { session, prisma, params }) => { return NextResponse.json({ plan: updatedPlan, versioned: false }); } }, ['admin', 'director']); + +// DELETE: Delete a plan (restricted to admin and director, and only allowed for DRAFT plans) +export const DELETE = withAuth(async (req, { session, prisma, params }) => { + const unwrappedParams = await params; + const id = parseInt(unwrappedParams.id); + + if (isNaN(id)) { + return NextResponse.json({ error: 'ID no válido.' }, { status: 400 }); + } + + const plan = await prisma.compensationPlan.findUnique({ + where: { id }, + include: { settlements: true } + }); + + if (!plan) { + return NextResponse.json({ error: 'Plan no encontrado.' }, { status: 404 }); + } + + if (plan.status !== 'DRAFT') { + return NextResponse.json( + { error: 'Solo se pueden eliminar planes en estado BORRADOR (DRAFT).' }, + { status: 400 } + ); + } + + if (plan.settlements && plan.settlements.length > 0) { + return NextResponse.json( + { error: 'No se puede eliminar el plan porque tiene liquidaciones asociadas.' }, + { status: 400 } + ); + } + + await prisma.$transaction(async (tx: any) => { + await createAuditLog(tx, { + userId: session.userId, + action: 'DELETE', + targetTable: 'compensation_plans', + targetId: id, + previousValue: plan, + newValue: null, + ipAddress: req.headers.get('x-forwarded-for') || '127.0.0.1' + }); + + await tx.compensationPlan.delete({ + where: { id } + }); + }); + + return NextResponse.json({ success: true }); +}, ['admin', 'director']); diff --git a/src/app/plans/page.module.css b/src/app/plans/page.module.css index 5468d02..16ca3e9 100644 --- a/src/app/plans/page.module.css +++ b/src/app/plans/page.module.css @@ -230,6 +230,7 @@ .cardFooter { display: flex; + flex-wrap: wrap; gap: var(--space-2); margin-top: auto; } diff --git a/src/app/plans/page.tsx b/src/app/plans/page.tsx index 4ede086..1d092b5 100644 --- a/src/app/plans/page.tsx +++ b/src/app/plans/page.tsx @@ -43,6 +43,22 @@ export default function PlansPage() { const [maxCap, setMaxCap] = useState(''); const [status, setStatus] = useState('DRAFT'); const [error, setError] = useState(null); + const [editPlanId, setEditPlanId] = useState(null); + + const handleCloseModal = () => { + setName(''); + setCode(''); + setValidityStart(''); + setValidityEnd(''); + setType('PERCENTAGE'); + setMetaAmount(''); + setPercentageRate(''); + setMaxCap(''); + setStatus('DRAFT'); + setEditPlanId(null); + setError(null); + setShowModal(false); + }; const fetchPlans = async () => { try { @@ -80,8 +96,10 @@ export default function PlansPage() { } try { - const res = await fetch('/api/plans', { - method: 'POST', + const url = editPlanId ? `/api/plans/${editPlanId}` : '/api/plans'; + const method = editPlanId ? 'PUT' : 'POST'; + const res = await fetch(url, { + method, headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ name, @@ -99,27 +117,53 @@ export default function PlansPage() { const data = await res.json(); if (!res.ok) { - setError(data.error || 'Failed to create plan'); + setError(data.error || 'Failed to submit plan'); return; } - // Reset form & reload - setName(''); - setCode(''); - setValidityStart(''); - setValidityEnd(''); - setType('PERCENTAGE'); - setMetaAmount(''); - setPercentageRate(''); - setMaxCap(''); - setStatus('DRAFT'); - setShowModal(false); + handleCloseModal(); fetchPlans(); } catch (err) { setError('An error occurred during submission.'); } }; + const handleEditClick = (plan: Plan) => { + setName(plan.name); + setCode(plan.code); + setValidityStart(plan.validityStart ? plan.validityStart.substring(0, 10) : ''); + setValidityEnd(plan.validityEnd ? plan.validityEnd.substring(0, 10) : ''); + setType(plan.type); + setMetaAmount(plan.metaAmount ? plan.metaAmount.toString() : ''); + setPercentageRate(plan.percentageRate ? plan.percentageRate.toString() : ''); + setMaxCap(plan.maxCap ? plan.maxCap.toString() : ''); + setStatus(plan.status); + setEditPlanId(plan.id); + setShowModal(true); + }; + + const handleDeletePlan = async (planId: number) => { + if (!confirm(t('plans.confirmDelete'))) { + return; + } + + try { + const res = await fetch(`/api/plans/${planId}`, { + method: 'DELETE' + }); + + if (!res.ok) { + const data = await res.json(); + alert(data.error || 'Failed to delete plan'); + return; + } + + fetchPlans(); + } catch (err) { + alert('An error occurred while deleting the plan.'); + } + }; + const handleToggleStatus = async (plan: Plan) => { const nextStatus = plan.status === 'ACTIVE' ? 'INACTIVE' : 'ACTIVE'; try { @@ -207,6 +251,23 @@ export default function PlansPage() { > {plan.status === 'ACTIVE' ? t('plans.deactivateVersion') : t('plans.activate')} + + {plan.status === 'DRAFT' && ( + + )} ) : ( @@ -224,7 +285,7 @@ export default function PlansPage() { {showModal && (
-

Crear {t('plans.newPlan')}

+

{editPlanId ? t('plans.modalEditTitle') : t('plans.modalCreateTitle')}

{error &&
{error}
} @@ -332,7 +393,7 @@ export default function PlansPage() {
-