From 44947d0d29c15b36fa0d00f792408c578600f1e8 Mon Sep 17 00:00:00 2001 From: "Carlos (Almeira)" Date: Fri, 27 Mar 2026 18:42:07 -0500 Subject: [PATCH] fix: resolve production UnrecognizedActionError by externalizing entity server actions into stable top-level exports --- src/app/actions/entities.ts | 73 ++++++++++++++++++++++++ src/app/dashboard/[entity]/[id]/page.tsx | 29 +--------- src/app/dashboard/[entity]/new/page.tsx | 29 +--------- 3 files changed, 79 insertions(+), 52 deletions(-) create mode 100644 src/app/actions/entities.ts diff --git a/src/app/actions/entities.ts b/src/app/actions/entities.ts new file mode 100644 index 0000000..55b9830 --- /dev/null +++ b/src/app/actions/entities.ts @@ -0,0 +1,73 @@ +'use server'; + +import { collectionMap, SCHEMA_MAP } from '@/lib/schema'; +import { createDocumentServer, updateDocumentServer } from './appwrite'; +import { redirect } from 'next/navigation'; + +/** + * Centralized action to create a new entity document + */ +export async function saveEntityAction(entity: string, formData: FormData) { + const collectionId = collectionMap[entity]; + const fields = SCHEMA_MAP[entity] || []; + + if (!collectionId) throw new Error(`Colección no encontrada para ${entity}`); + + const data: Record = {}; + for (const [key, value] of formData.entries()) { + if (key.startsWith('$ACTION')) continue; + data[key] = value; + } + + // Type conversions based on schema + const numericFields = fields.filter(f => f.type === 'number').map(f => f.name); + for (const nKey of numericFields) { + if (data[nKey]) data[nKey] = parseFloat(data[nKey]); + } + + // Normalization + if (data.estado && typeof data.estado === 'string') { + data.estado = data.estado.toLowerCase(); + } + + const res = await createDocumentServer(collectionId, data); + if (res.success) { + redirect(`/dashboard/${entity}`); + } else { + throw new Error(res.error || "Error desconocido al crear documento"); + } +} + +/** + * Centralized action to update an existing entity document + */ +export async function updateEntityAction(entity: string, id: string, formData: FormData) { + const collectionId = collectionMap[entity]; + const fields = SCHEMA_MAP[entity] || []; + + if (!collectionId) throw new Error(`Colección no encontrada para ${entity}`); + + const data: Record = {}; + for (const [key, value] of formData.entries()) { + if (key.startsWith('$ACTION')) continue; + data[key] = value; + } + + // Type conversions based on schema + const numericFields = fields.filter(f => f.type === 'number').map(f => f.name); + for (const nKey of numericFields) { + if (data[nKey]) data[nKey] = parseFloat(data[nKey]); + } + + // Normalization + if (data.estado && typeof data.estado === 'string') { + data.estado = data.estado.toLowerCase(); + } + + const res = await updateDocumentServer(collectionId, id, data); + if (res.success) { + redirect(`/dashboard/${entity}`); + } else { + throw new Error(res.error || "Error desconocido al actualizar documento"); + } +} diff --git a/src/app/dashboard/[entity]/[id]/page.tsx b/src/app/dashboard/[entity]/[id]/page.tsx index 54ad110..e26304e 100644 --- a/src/app/dashboard/[entity]/[id]/page.tsx +++ b/src/app/dashboard/[entity]/[id]/page.tsx @@ -2,6 +2,7 @@ import { APPWRITE_ALMEIRA_CONFIG, projectId, endpoint } from '@/lib/appwrite'; import { createSessionClient } from '@/lib/appwrite.server'; import { collectionMap, SCHEMA_MAP } from '@/lib/schema'; import { updateDocumentServer } from '@/app/actions/appwrite'; +import { updateEntityAction } from '@/app/actions/entities'; import { redirect } from 'next/navigation'; import { EntityForm } from '@/components/dashboard/EntityForm'; @@ -58,31 +59,7 @@ export default async function EditEntityPage({ params }: { params: Promise<{ ent }) ); - async function updateAction(formData: FormData) { - 'use server'; - - const data: Record = {}; - for (const [key, value] of formData.entries()) { - if (key.startsWith('$ACTION')) continue; - data[key] = value; - } - - const numericFields = fields.filter(f => f.type === 'number').map(f => f.name); - for (const nKey of numericFields) { - if(data[nKey]) data[nKey] = parseFloat(data[nKey]); - } - - if (data.estado && typeof data.estado === 'string') { - data.estado = data.estado.toLowerCase(); - } - - const res = await updateDocumentServer(collectionId, id, data); - if(res.success) { - redirect(`/dashboard/${entity}`); - } else { - console.error("Server Action res error", res.error); - } - } + const boundUpdate = updateEntityAction.bind(null, entity, id); return ( ); diff --git a/src/app/dashboard/[entity]/new/page.tsx b/src/app/dashboard/[entity]/new/page.tsx index cc14a40..5b278a1 100644 --- a/src/app/dashboard/[entity]/new/page.tsx +++ b/src/app/dashboard/[entity]/new/page.tsx @@ -2,6 +2,7 @@ import { APPWRITE_ALMEIRA_CONFIG, projectId, endpoint } from '@/lib/appwrite'; import { createSessionClient } from '@/lib/appwrite.server'; import { collectionMap, SCHEMA_MAP } from '@/lib/schema'; import { createDocumentServer } from '@/app/actions/appwrite'; +import { saveEntityAction } from '@/app/actions/entities'; import { redirect } from 'next/navigation'; import { EntityForm } from '@/components/dashboard/EntityForm'; @@ -41,31 +42,7 @@ export default async function NewEntityPage({ params }: { params: Promise<{ enti }) ); - async function submitAction(formData: FormData) { - 'use server'; - - const data: Record = {}; - for (const [key, value] of formData.entries()) { - if (key.startsWith('$ACTION')) continue; - data[key] = value; - } - - const numericFields = fields.filter(f => f.type === 'number').map(f => f.name); - for (const nKey of numericFields) { - if(data[nKey]) data[nKey] = parseFloat(data[nKey]); - } - - if (data.estado && typeof data.estado === 'string') { - data.estado = data.estado.toLowerCase(); - } - - const res = await createDocumentServer(collectionId, data); - if(res.success) { - redirect(`/dashboard/${entity}`); - } else { - console.error("Server Action res error", res.error); - } - } + const boundSave = saveEntityAction.bind(null, entity); return ( );