From b840172946be507e736011093d43d9696bb1b5a4 Mon Sep 17 00:00:00 2001 From: Julio Ruiz Date: Fri, 14 Aug 2026 01:25:49 -0500 Subject: [PATCH] Merge row edit/delete into a dropdown menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New deletePastor server action (also removes the pastor's letter to avoid orphaned uploads) - Replace the per-row edit icon with a "⋮" actions dropdown containing Editar and Eliminar (Eliminar confirms before deleting) - Uses the popover API + CSS anchor positioning so the menu isn't clipped by the table's horizontal-scroll container Co-Authored-By: Claude Opus 4.8 --- src/app/(frontend)/PastorsTable.tsx | 110 +++++++++++++++++++++++----- src/app/(frontend)/actions.ts | 25 +++++++ 2 files changed, 117 insertions(+), 18 deletions(-) diff --git a/src/app/(frontend)/PastorsTable.tsx b/src/app/(frontend)/PastorsTable.tsx index d9228b6..a369402 100644 --- a/src/app/(frontend)/PastorsTable.tsx +++ b/src/app/(frontend)/PastorsTable.tsx @@ -3,7 +3,7 @@ import React, { useRef, useState, useTransition } from 'react' import { useRouter } from 'next/navigation' -import { createPastor, deleteLetter, updatePastor, uploadLetter } from './actions' +import { createPastor, deleteLetter, deletePastor, updatePastor, uploadLetter } from './actions' export type PastorRow = { id: number @@ -243,23 +243,7 @@ export function PastorsTable({ rows }: { rows: PastorRow[] }) { ))} - + setEditing(p)} /> ))} @@ -283,6 +267,96 @@ export function PastorsTable({ rows }: { rows: PastorRow[] }) { ) } +function RowActions({ pastor, onEdit }: { pastor: PastorRow; onEdit: () => void }) { + const router = useRouter() + const [pending, startTransition] = useTransition() + const menuId = `row-menu-${pastor.id}` + const anchor = `--row-anchor-${pastor.id}` + + const close = () => document.getElementById(menuId)?.hidePopover?.() + + const onDelete = () => { + close() + if (!window.confirm(`¿Eliminar a ${pastor.name}? Esta acción no se puede deshacer.`)) return + startTransition(async () => { + await deletePastor(pastor.id) + router.refresh() + }) + } + + return ( + <> + +
    +
  • + +
  • +
  • + +
  • +
+ + ) +} + function LetterActions({ pastor, onUpload }: { pastor: PastorRow; onUpload: () => void }) { const router = useRouter() const [pending, startTransition] = useTransition() diff --git a/src/app/(frontend)/actions.ts b/src/app/(frontend)/actions.ts index c2e8eb0..312ede9 100644 --- a/src/app/(frontend)/actions.ts +++ b/src/app/(frontend)/actions.ts @@ -88,6 +88,31 @@ export async function updatePastor(id: number, data: PastorUpdateInput) { return { ok: true } } +/** + * Elimina un pastor por completo. También borra su carta asociada (si existe) + * para no dejar archivos huérfanos. + */ +export async function deletePastor(id: number) { + const payload = await getPayload({ config: await config }) + + const pastor = await payload.findByID({ collection: 'pastors', id, depth: 0 }) + const letterId = + pastor.letter && typeof pastor.letter === 'object' ? pastor.letter.id : pastor.letter + + await payload.delete({ collection: 'pastors', id }) + + if (letterId) { + try { + await payload.delete({ collection: 'pastor-letters', id: letterId }) + } catch { + // The upload doc may already be gone; deleting the pastor is enough. + } + } + + revalidatePath('/') + return { ok: true } +} + /** * Elimina la carta asociada a un pastor: desvincula el campo `letter` y borra * el documento de subida, de modo que se pueda subir uno nuevo en su lugar.