almeira-web/src/app/dashboard/settings/page.tsx
Carlos (Almeira) 5279af2003 refactor: global project hardening and integrity verification
- Add safety checks for direct Appwrite document index access across all root pages.
- Harden NewsTicker and AdWrappers with robust fallback settings and length checks.
- Apply strict typing to dashboard settings page and resolve icon conflicts.
- Final global audit for deprecated Zod .errors usage (0 occurrences remaining).
- Standardize PublicationLayout across creation and edit workflows for posts.
2026-03-28 10:07:32 -05:00

174 lines
9.3 KiB
TypeScript

import { APPWRITE_ALMEIRA_CONFIG } from '@/lib/appwrite';
import { createSessionClient } from '@/lib/appwrite.server';
import { getEntity, updateEntity } from '@/app/actions/entity';
import { revalidatePath } from 'next/cache';
import { Settings as SettingsIcon, Save, Clock, Menu, Building, Image as ImageIcon, Link as LinkIcon, Share2 } from 'lucide-react';
import { Settings as SettingsInterface } from '@/types/settings';
export default async function SettingsPage() {
let settings: SettingsInterface = { ticker_count: 10, rotation_seconds: 5 };
let docId = '';
let entity: any = null;
try {
const sessionClient = await createSessionClient();
if (sessionClient) {
const response = await sessionClient.databases.listDocuments(
APPWRITE_ALMEIRA_CONFIG.databaseId,
'settings',
[]
);
if (response.documents.length > 0) {
settings = response.documents[0] as unknown as SettingsInterface;
docId = response.documents[0].$id;
}
}
entity = await getEntity();
} catch (e) {
console.error("No se pudo cargar config:", e);
}
async function saveSettingsServer(formData: FormData) {
'use server';
const sessionClient = await createSessionClient();
if (!sessionClient) throw new Error("Acceso denegado");
const updateData = {
ticker_count: parseInt(formData.get('ticker_count') as string),
feed_news_count: parseInt(formData.get('feed_news_count') as string),
rotation_seconds: parseInt(formData.get('rotation_seconds') as string),
show_news_date: formData.get('show_news_date') === 'true'
};
if (formData.get('docId')) {
await sessionClient.databases.updateDocument(
APPWRITE_ALMEIRA_CONFIG.databaseId,
'settings',
formData.get('docId') as string,
updateData
);
} else {
await sessionClient.databases.createDocument(
APPWRITE_ALMEIRA_CONFIG.databaseId,
'settings',
'unique()',
updateData
);
}
revalidatePath('/dashboard/settings');
revalidatePath('/'); // Revalidate frontpage
}
async function updateEntityClient(formData: FormData) {
'use server';
await updateEntity(formData);
}
return (
<div className="max-w-5xl mx-auto space-y-6 animate-fade-in pb-12">
<div className="flex items-center gap-4 bg-slate-900 border border-slate-800 p-6 rounded-2xl shadow-xl shadow-cyan-500/5">
<div className="p-3 bg-cyan-500/10 rounded-xl text-cyan-400">
<SettingsIcon size={32} />
</div>
<div>
<h1 className="text-3xl font-black font-outfit uppercase tracking-tighter text-white">
Configuración Global
</h1>
<p className="text-sm font-medium text-slate-400">Panel de Ajustes Maestros del Ecosistema Web y Entidad Corporativa</p>
</div>
</div>
<div className="space-y-8">
{/* Formulario de Configuración del Motor */}
<form action={saveSettingsServer} className="bg-slate-900 border border-slate-800 p-8 rounded-2xl shadow-2xl space-y-8">
<div className="grid md:grid-cols-2 gap-8">
<input type="hidden" name="docId" value={docId} />
{/* Configurador del Ticker */}
<div className="space-y-6">
<div className="flex items-center gap-2 border-b border-slate-800 pb-2">
<Menu className="text-cyan-500" size={20} />
<h2 className="text-lg font-bold uppercase tracking-widest text-white">Noticias y Feed</h2>
</div>
<div className="grid grid-cols-2 gap-4">
<div>
<label className="text-[10px] font-black tracking-widest text-slate-500 uppercase block mb-2">Ticker (Cintillo)</label>
<input
type="number"
name="ticker_count"
defaultValue={settings.ticker_count || 10}
min="1" max="50"
required
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-3 text-white focus:border-cyan-500 text-xl font-black font-mono transition-all"
/>
</div>
<div>
<label className="text-[10px] font-black tracking-widest text-slate-500 uppercase block mb-2">Feed (Inicio)</label>
<input
type="number"
name="feed_news_count"
defaultValue={settings.feed_news_count || 12}
min="4" max="100"
required
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-3 text-white focus:border-cyan-400 text-xl font-black font-mono transition-all"
/>
</div>
</div>
<div className="flex items-center gap-3 bg-slate-950 p-4 border border-slate-800 rounded-xl hover:border-cyan-500/30 transition-colors">
<input
type="checkbox"
name="show_news_date"
value="true"
defaultChecked={settings.show_news_date === true}
id="show_news_date"
className="w-5 h-5 accent-cyan-500 cursor-pointer"
/>
<label htmlFor="show_news_date" className="text-sm font-bold text-slate-300 cursor-pointer select-none">
Mostrar fecha de publicación en las tarjetas del feed
</label>
</div>
</div>
{/* Configurador de Rotación (Banners) */}
<div className="space-y-6">
<div className="flex items-center gap-2 border-b border-slate-800 pb-2">
<Clock className="text-purple-500" size={20} />
<h2 className="text-lg font-bold uppercase tracking-widest text-white">Visualización</h2>
</div>
<div className="relative">
<label className="text-[10px] font-black tracking-widest text-slate-500 uppercase block mb-2">Segundos en Pantalla (Banners)</label>
<input
type="number"
name="rotation_seconds"
defaultValue={settings.rotation_seconds || 5}
min="1" max="60"
required
className="w-full bg-slate-950 border border-slate-800 rounded-xl px-4 py-3 text-white focus:border-purple-500 text-xl font-black font-mono transition-all"
/>
<span className="absolute right-4 bottom-3 text-slate-600 font-bold tracking-widest uppercase text-[10px]">Segundos</span>
</div>
<p className="text-[11px] text-slate-500 italic leading-relaxed">
Estas configuraciones afectan el comportamiento dinámico del sitio web principal y los tiempos de carga de recursos del lado del servidor.
</p>
</div>
</div>
<div className="pt-6 border-t border-slate-800">
<button type="submit" className="w-full md:w-auto relative flex items-center gap-2 justify-center group overflow-hidden bg-slate-800 text-white font-black uppercase tracking-widest py-3 px-10 rounded-xl transition-all border border-cyan-500/50 hover:border-cyan-400 ml-auto shadow-[0_0_20px_rgba(6,182,212,0.2)]">
<div className="absolute inset-0 bg-gradient-to-r from-cyan-600 to-blue-500 opacity-90 group-hover:opacity-100 transition-opacity"></div>
<Save size={18} className="relative z-10" />
<span className="relative z-10 text-sm">Guardar</span>
</button>
</div>
</form>
</div>
</div>
);
}