feat(applications): drag-to-reorder on the public site

Editors can now control the order applications appear in, the same way they
already reorder Hero slides.

- Application gains an `order Int @default(0)` column (additive migration
  20260605120000_add_application_order, IF NOT EXISTS, safe for deploy) plus
  an (isActive, order) index.
- New reorderApplications(orderedSlugs) server action — single $transaction
  renumbering, mirrors reorderHeroSlides.
- HQ applications panel: rows are now draggable by a grip handle (HTML5 DnD,
  optimistic local reorder, persisted on drop, toast feedback).
- All public-facing queries now order by [order asc, createdAt asc]: home
  ApplicationsDashboard + GlobalOperations, the footer apps list, and the
  HQ list itself. Existing rows default to 0 so current order is preserved
  until the editor drags something.

Verified: production build compiles, TypeScript clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-05 12:04:10 -05:00
parent fbfffb28d9
commit afcaf991b5
6 changed files with 72 additions and 10 deletions
@@ -18,7 +18,7 @@ export async function getApplications() {
noStore();
try {
const apps = await prisma.application.findMany({
orderBy: { createdAt: "asc" }
orderBy: [{ order: "asc" }, { createdAt: "asc" }]
});
return { success: true, apps };
} catch (error) {
@@ -161,6 +161,24 @@ export async function deleteApplication(slug: string) {
}
}
// 6b. REORDENAR APLICACIONES (drag-to-reorder, mismo patrón que HeroSlide)
// Recibe la lista de slugs en el nuevo orden y renumera el campo `order`
// en una sola transacción atómica.
export async function reorderApplications(orderedSlugs: string[]) {
try {
await prisma.$transaction(
orderedSlugs.map((slug, idx) =>
prisma.application.update({ where: { slug }, data: { order: idx } })
)
);
revalidatePath("/hq-command/dashboard/applications");
revalidatePath("/[locale]", "layout");
return { success: true };
} catch (e) {
return { error: "Failed to reorder applications." };
}
}
// 7. LA SEMILLA: AUTO-POBLAR LA BASE DE DATOS (Intacto)
export async function seedInitialApplications() {
// ... Tu código actual de la semilla se queda exactamente igual ...