- 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.
174 lines
9.3 KiB
TypeScript
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>
|
|
);
|
|
}
|