feat(F-099): completed feature
This commit is contained in:
@@ -9,6 +9,7 @@ const TABS = [
|
||||
{ id: 'social', label: 'Redes sociales', icon: '🌐' },
|
||||
{ id: 'footer', label: 'Footer', icon: '📄' },
|
||||
{ id: 'ai', label: 'IA para SEO', icon: '✨' },
|
||||
{ id: 'smtp', label: 'SMTP / Email', icon: '✉️' },
|
||||
] as const;
|
||||
type TabId = (typeof TABS)[number]['id'];
|
||||
|
||||
@@ -32,8 +33,12 @@ export default function SettingsPage() {
|
||||
if (!form) return;
|
||||
setSaving(true); setErr(''); setMsg('');
|
||||
try {
|
||||
const { aiApiKey, ...settingsWithoutKey } = form;
|
||||
const updated = await settingsApi.update(aiApiKey ? form : settingsWithoutKey);
|
||||
const { aiApiKey, smtpPass, ...settingsWithoutSecrets } = form;
|
||||
const updated = await settingsApi.update({
|
||||
...settingsWithoutSecrets,
|
||||
...(aiApiKey ? { aiApiKey } : {}),
|
||||
...(smtpPass ? { smtpPass } : {}),
|
||||
});
|
||||
setData(updated); setForm(updated);
|
||||
setMsg('Cambios guardados correctamente');
|
||||
setTimeout(() => setMsg(''), 4000);
|
||||
@@ -44,7 +49,7 @@ export default function SettingsPage() {
|
||||
}
|
||||
};
|
||||
|
||||
const field = (key: Exclude<keyof FormData, 'aiApiKeyConfigured'>, label: string, opts?: { type?: string; placeholder?: string; rows?: number; hint?: string }) => (
|
||||
const field = (key: Exclude<keyof FormData, 'aiApiKeyConfigured' | 'smtpPassConfigured' | 'smtpSecure'>, label: string, opts?: { type?: string; placeholder?: string; rows?: number; hint?: string }) => (
|
||||
<div key={key}>
|
||||
<label className="block text-sm font-medium text-gray-700 mb-1">{label}</label>
|
||||
{opts?.rows ? (
|
||||
@@ -144,6 +149,27 @@ export default function SettingsPage() {
|
||||
{field('aiApiKey', 'API key', { type: 'password', placeholder: form?.aiApiKeyConfigured ? 'API key configurada (escribe para reemplazar)' : 'sk-...' })}
|
||||
{field('aiSeoTitlePrompt', 'Prompt para Título SEO', { rows: 4, hint: 'Usa {{name}}, {{description}} y {{brand}} como variables.' })}
|
||||
{field('aiSeoDescriptionPrompt', 'Prompt para Descripción SEO (Google)', { rows: 5, hint: 'Usa {{name}}, {{description}} y {{brand}} como variables.' })}
|
||||
{field('aiProductDescriptionPrompt', 'Prompt para Descripción del producto', { rows: 5, hint: 'Se usa solo cuando la descripción normal está vacía. Variables: {{name}}, {{description}} y {{brand}}.' })}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{tab === 'smtp' && (
|
||||
<>
|
||||
<div className="px-6 py-4 bg-gray-50 border-b border-gray-200">
|
||||
<h2 className="text-base font-semibold text-gray-800">Correo SMTP</h2>
|
||||
<p className="text-xs text-gray-400 mt-0.5">Se usa para enviar enlaces de recuperación de contraseña.</p>
|
||||
</div>
|
||||
<div className="p-6 space-y-5">
|
||||
{field('smtpHost', 'Servidor SMTP', { placeholder: 'ssl0.ovh.net' })}
|
||||
{field('smtpPort', 'Puerto', { type: 'number', placeholder: '465' })}
|
||||
<label className="flex items-center gap-2 text-sm text-gray-700">
|
||||
<input type="checkbox" checked={form?.smtpSecure ?? true} onChange={e => setForm(f => f ? { ...f, smtpSecure: e.target.checked } : f)} />
|
||||
Conexión segura SSL/TLS
|
||||
</label>
|
||||
{field('smtpUser', 'Usuario / cuenta de correo', { type: 'email', placeholder: 'info@mercadodevida.es' })}
|
||||
{field('smtpPass', 'Contraseña SMTP', { type: 'password', placeholder: form?.smtpPassConfigured ? 'Contraseña configurada (escribe para reemplazar)' : 'Contraseña del buzón' })}
|
||||
{field('smtpFrom', 'Remitente', { type: 'email', placeholder: 'info@mercadodevida.es' })}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -17,6 +17,16 @@ export async function GET(req: NextRequest) {
|
||||
const backendRes = await fetch(`${API}/${path}`, {
|
||||
headers: { Cookie: cookies },
|
||||
});
|
||||
if (path === 'admin/logs/stream') {
|
||||
return new Response(backendRes.body, {
|
||||
status: backendRes.status,
|
||||
headers: {
|
||||
'Content-Type': backendRes.headers.get('content-type') ?? 'text/event-stream',
|
||||
'Cache-Control': backendRes.headers.get('cache-control') ?? 'no-cache',
|
||||
Connection: 'keep-alive',
|
||||
},
|
||||
});
|
||||
}
|
||||
const data = await backendRes.json().catch(() => null);
|
||||
const resp = NextResponse.json(data ?? { error: 'Bad response' }, { status: backendRes.status });
|
||||
return resp;
|
||||
|
||||
Reference in New Issue
Block a user