fix: stabilize dashboard and harden appwrite actions against production schema mismatches

- Fix Next.js serialization error by sanitizing Appwrite documents (POJO conversion).
- Harden entity configuration actions to handle 404 (collection not found) gracefully.
- Add fallback for NewsTicker WhatsApp messages if 'status' attribute is missing.
- Prevent 'document_missing_data' error in updateEntityConfig by validating input.
This commit is contained in:
Carlos (Almeira) 2026-03-28 10:46:53 -05:00
parent 0ebe2b691a
commit 29c124580e
3 changed files with 45 additions and 18 deletions

View File

@ -26,7 +26,11 @@ export async function getEntityConfig() {
[Query.limit(1)]
);
return response.documents[0] || null;
} catch (error) {
} catch (error: any) {
// Silently handle 404 (collection not found)
if (error?.code === 404) {
return null;
}
console.error('Error fetching entity config:', error);
return null;
}
@ -40,6 +44,11 @@ export async function updateEntityConfig(data: any) {
const databases = await getAdminClient();
const existing = await getEntityConfig();
if (!data || Object.keys(data).length === 0) {
console.warn('updateEntityConfig: Data is empty, skipping update.');
return existing;
}
if (existing) {
const result = await databases.updateDocument(
DATABASE_ID,

View File

@ -40,7 +40,9 @@ async function PostsPage({
collectionId,
[Query.orderDesc('$createdAt'), Query.limit(100)]
);
posts = res.documents as unknown as Post[];
// Deep-clone/sanitize to avoid "Only plain objects..." Next.js error
posts = JSON.parse(JSON.stringify(res.documents)) as Post[];
// Pre-load relation data for the drawer/form
const fields = SCHEMA_MAP[entity] || [];
@ -63,6 +65,9 @@ async function PostsPage({
}
})
);
// Sanitize relationData too
relationData = JSON.parse(JSON.stringify(relationData));
} catch (error) {
console.error('PostsPage fetch error:', error);
}
@ -142,7 +147,7 @@ export default async function EntityPage({
);
}
const user = await getLoggedInUser();
const user = JSON.parse(JSON.stringify(await getLoggedInUser()));
const prefs = await getUserPreferences();
const hiddenColumns = prefs.hiddenColumns?.[entity] || [];
@ -183,7 +188,8 @@ export default async function EntityPage({
collectionId,
queries
);
documents = response.documents as Array<Record<string, unknown>>;
// Deep-clone/sanitize documents to avoid "Only plain objects..." Next.js error
documents = JSON.parse(JSON.stringify(response.documents));
total = response.total;
} catch (error) {
console.error(`Error fetching from ${collectionId}:`, error);

View File

@ -72,23 +72,35 @@ export default async function NewsTicker() {
// 3. Fetch Approved WhatsApp Messages
try {
const waRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'whatsapp_messages', [
Query.equal('status', 'approved'),
Query.orderDesc('timestamp'),
Query.limit(5)
]);
let waRes;
try {
waRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'whatsapp_messages', [
Query.equal('status', 'approved'),
Query.orderDesc('timestamp'),
Query.limit(5)
]);
} catch (queryError: any) {
// If 'status' attribute is missing, try fetching without it but still limited
console.warn("WhatsApp 'status' query failed, trying fallback fetch:", queryError.message);
waRes = await client.databases.listDocuments(APPWRITE_ALMEIRA_CONFIG.databaseId, 'whatsapp_messages', [
Query.orderDesc('$createdAt'),
Query.limit(5)
]);
}
waRes.documents.forEach((msg: any) => {
tickerItems.push({
type: 'whatsapp',
id: msg.$id,
titulo: `${msg.sender_name}: ${msg.content}`,
slug: null,
imagenUrl: msg.avatar || null
if (waRes && waRes.documents) {
waRes.documents.forEach((msg: any) => {
tickerItems.push({
type: 'whatsapp',
id: msg.$id,
titulo: `${msg.sender_name || 'Usuario'}: ${msg.content || ''}`,
slug: null,
imagenUrl: msg.avatar || null
});
});
});
}
} catch (waError) {
console.warn("Skipping WhatsApp messages in ticker:", waError);
console.warn("Skipping WhatsApp messages in ticker (Collection or Schema missing):", waError);
}
} catch (e) {