diff --git a/src/app/dashboard/[entity]/page.tsx b/src/app/dashboard/[entity]/page.tsx
index 0f224bb..0ca24a6 100644
--- a/src/app/dashboard/[entity]/page.tsx
+++ b/src/app/dashboard/[entity]/page.tsx
@@ -1,14 +1,25 @@
import { APPWRITE_ALMEIRA_CONFIG } from '@/lib/appwrite';
import { createSessionClient } from '@/lib/appwrite.server';
+import { Query } from 'node-appwrite';
import Link from 'next/link';
import { Pencil } from 'lucide-react';
import { DeleteButton } from '@/components/DeleteButton';
import { collectionMap, SCHEMA_MAP } from '@/lib/schema';
-export default async function EntityPage({ params }: { params: { entity: string } }) {
- // Await the params object before using its properties
+export default async function EntityPage({
+ params,
+ searchParams
+}: {
+ params: { entity: string },
+ searchParams: { q?: string, page?: string }
+}) {
const { entity } = await params;
+ const sParams = await searchParams;
+ const q = sParams.q || '';
+ const page = Number(sParams.page) || 1;
+ const limit = 15;
+ const offset = (page - 1) * limit;
const collectionId = collectionMap[entity];
const fieldsConfig = SCHEMA_MAP[entity] || [];
@@ -31,18 +42,39 @@ export default async function EntityPage({ params }: { params: { entity: string
}
let documents: any[] = [];
+ let total = 0;
try {
const sessionClient = await createSessionClient();
if (!sessionClient) {
throw new Error("No sesión activa");
}
+ const queries = [
+ Query.limit(limit),
+ Query.offset(offset),
+ Query.orderDesc('$createdAt')
+ ];
+
+ if (q) {
+ const searchableFields = fieldsConfig
+ .filter(f => f.type === 'text' || f.type === 'textarea')
+ .map(f => f.name)
+ .slice(0, 2); // Limit to first two text fields for performance
+
+ if (searchableFields.length > 0) {
+ // Note: Appwrite multiple contains are AND-ed by default.
+ // For a better search we might need something else but this is a good start.
+ queries.push(Query.contains(searchableFields[0], q));
+ }
+ }
+
const response = await sessionClient.databases.listDocuments(
APPWRITE_ALMEIRA_CONFIG.databaseId,
collectionId,
- []
+ queries
);
documents = response.documents;
+ total = response.total;
} catch (error) {
console.error(`Error fetching from ${collectionId}:`, error);
}
@@ -60,16 +92,30 @@ export default async function EntityPage({ params }: { params: { entity: string
};
return (
-
-
+
+
{entityName}
- Total almacenado: {documents.length}
+ Total en sistema: {total}
+
+
+
+ Nuevo
@@ -78,8 +124,8 @@ export default async function EntityPage({ params }: { params: { entity: string
{documents.length === 0 ? (
-
AÚN NO HAY DATOS
- Para comenzar, haz clic en el botón superior derecho llamado "Añadir Nuevo".
+ NO SE ENCONTRARON REGISTROS
+ Prueba ajustando tu búsqueda o crea un nuevo elemento.
) : (
@@ -99,9 +145,17 @@ export default async function EntityPage({ params }: { params: { entity: string
{rawHeaders.map(h => {
let value = doc[h];
+ const field = fieldsConfig.find(f => f.name === h);
+
+ // Resolve Relations
if (typeof value === 'object' && value !== null) {
- value = Array.isArray(value) ? `[Lista (${value.length})]` : value.$id || '[Vinculado]';
+ if (field?.type === 'relation' && field.relationLabel) {
+ value = value[field.relationLabel] || value.$id || '[Vinculado]';
+ } else {
+ value = Array.isArray(value) ? `[Lista (${value.length})]` : value.$id || '[Vinculado]';
+ }
}
+
if (typeof value === 'boolean') {
value = value ? 'Sí' : 'No';
}
@@ -128,6 +182,27 @@ export default async function EntityPage({ params }: { params: { entity: string
)}
+
+ {/* Pagination */}
+ {total > limit && (
+
+
+ ANTERIOR
+
+
+ Página {page} de {Math.ceil(total / limit)}
+
+ = Math.ceil(total / limit) ? 'opacity-30 pointer-events-none bg-slate-900' : 'bg-slate-900 text-white hover:border-cyan-500'}`}
+ >
+ SIGUIENTE
+
+
+ )}
);
}
diff --git a/src/app/dashboard/media/page.tsx b/src/app/dashboard/media/page.tsx
index 5fed7a5..91614d2 100644
--- a/src/app/dashboard/media/page.tsx
+++ b/src/app/dashboard/media/page.tsx
@@ -4,18 +4,42 @@ import Link from 'next/link';
import { Image as ImageIcon, Video, Plus, Trash2 } from 'lucide-react';
import { DeleteButton } from '@/components/DeleteButton';
-export default async function MediaLibraryPage() {
+import { Query } from 'node-appwrite';
+
+export default async function MediaLibraryPage({
+ searchParams
+}: {
+ searchParams: { q?: string, page?: string }
+}) {
+ const sParams = await searchParams;
+ const q = sParams.q || '';
+ const page = Number(sParams.page) || 1;
+ const limit = 12;
+ const offset = (page - 1) * limit;
+
let media: any[] = [];
+ let total = 0;
try {
const sessionClient = await createSessionClient();
if (!sessionClient) throw new Error("No sesión activa");
+ const queries = [
+ Query.limit(limit),
+ Query.offset(offset),
+ Query.orderDesc('$createdAt')
+ ];
+
+ if (q) {
+ queries.push(Query.contains('titulo', q));
+ }
+
const response = await sessionClient.databases.listDocuments(
APPWRITE_ALMEIRA_CONFIG.databaseId,
'media',
- []
+ queries
);
media = response.documents;
+ total = response.total;
} catch (error) {
console.error(`Error fetching media:`, error);
}
@@ -31,15 +55,29 @@ export default async function MediaLibraryPage() {
return (
-
+
Biblioteca de Medios
- Total almacenado: {media.length}
+ Total en sistema: {total}
+
+
+
Subir Archivo
@@ -49,54 +87,77 @@ export default async function MediaLibraryPage() {
{media.length === 0 ? (
-
BIBLIOTECA VACÍA
- Para comenzar, sube y optimiza tu primer recurso multimedia con el botón superior.
+ NO SE ENCONTRARON RECURSOS
+ Prueba ajustando tu búsqueda o sube un nuevo archivo.
) : (
-
- {media.map((item) => {
- const fileUrl = getFilePreviewUrl(item.bucket_id, item.file_id);
- return (
-
-
- {item.tipo === 'video' ? (
- <>
-
-
-
-
-
+ <>
+
+ {media.map((item) => {
+ const fileUrl = getFilePreviewUrl(item.bucket_id, item.file_id);
+ return (
+
+
+ {item.tipo === 'video' ? (
+ <>
+
+
+ >
+ ) : (
+ // eslint-disable-next-line @next/next/no-img-element
+

+ )}
+
+
+
+
+
+
+
+ {item.tipo === 'video' ? : }
+ {item.tipo}
+
+
{item.titulo}
+
+ {(item.alt_text || item.descripcion_seo) ? (
+
+ {item.alt_text &&
ALT: {item.alt_text}
}
+ {item.descripcion_seo &&
SEO: {item.descripcion_seo}
}
- >
- ) : (
- // eslint-disable-next-line @next/next/no-img-element
-

- )}
-
-
-
-
-
-
-
- {item.tipo === 'video' ? : }
- {item.tipo}
-
-
{item.titulo}
-
- {(item.alt_text || item.descripcion_seo) ? (
-
- {item.alt_text &&
ALT: {item.alt_text}
}
- {item.descripcion_seo &&
SEO: {item.descripcion_seo}
}
-
- ) : (
-
Falta SEO
- )}
-
-
- );
- })}
-
+ ) : (
+
Falta SEO
+ )}
+
+
+ );
+ })}
+
+
+ {/* Pagination */}
+ {total > limit && (
+
+
+ ANTERIOR
+
+
+ Página {page} de {Math.ceil(total / limit)}
+
+ = Math.ceil(total / limit) ? 'opacity-30 pointer-events-none bg-slate-900' : 'bg-slate-900 text-white hover:border-pink-500'}`}
+ >
+ SIGUIENTE
+
+
+ )}
+ >
)}
);