diff --git a/web-next/app/[locale]/page.tsx b/web-next/app/[locale]/page.tsx
index fbada07..80212b0 100644
--- a/web-next/app/[locale]/page.tsx
+++ b/web-next/app/[locale]/page.tsx
@@ -7,8 +7,8 @@ import { getRealmName } from '@/lib/realm'
// SSR por petición: los datos se leen DIRECTAMENTE de MySQL desde el servidor Next.
export const dynamic = 'force-dynamic'
-async function getData(): Promise<{ noticias: Noticia[]; status: ServerStatus | null }> {
- const [news, status] = await Promise.allSettled([getNoticias(), getServerStatus()])
+async function getData(locale: string): Promise<{ noticias: Noticia[]; status: ServerStatus | null }> {
+ const [news, status] = await Promise.allSettled([getNoticias(locale), getServerStatus()])
return {
noticias: news.status === 'fulfilled' ? news.value : [],
status: status.status === 'fulfilled' ? status.value : null,
@@ -24,11 +24,11 @@ function brandify(html: string, realm: string): string {
return html.replace(/<[^>]*>|[^<]+/g, (seg) => (seg.startsWith('<') ? seg : seg.replace(re, repl)))
}
-function formatFecha(iso: string | Date | null): string {
+function formatFecha(iso: string | Date | null, locale: string): string {
if (!iso) return ''
const d = new Date(iso)
if (isNaN(d.getTime())) return String(iso)
- return d.toLocaleDateString('es-ES', {
+ return d.toLocaleDateString(locale === 'en' ? 'en-US' : 'es-ES', {
day: 'numeric',
month: 'long',
year: 'numeric',
@@ -39,7 +39,7 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s
const { locale } = await params
setRequestLocale(locale)
const t = await getTranslations('Misc')
- const { noticias, status } = await getData()
+ const { noticias, status } = await getData(locale)
const realmName = await getRealmName()
const worldStatus = status ? status.world_status : 'Offline'
@@ -125,7 +125,7 @@ export default async function HomePage({ params }: { params: Promise<{ locale: s
{noticias.map((n) => (
{brandify(n.titulo, realmName)}
-
{formatFecha(n.fecha)}
+
{formatFecha(n.fecha, locale)}
}) {
const session = await getSession()
@@ -9,3 +9,19 @@ export async function DELETE(_request: Request, { params }: { params: Promise<{
await deleteNews(Number(id))
return Response.json({ success: true })
}
+
+export async function PUT(request: Request, { params }: { params: Promise<{ id: string }> }) {
+ const session = await getSession()
+ if (!(await isAdmin(session))) return Response.json({ success: false, error: 'forbidden' }, { status: 403 })
+ const { id } = await params
+ let b: Record = {}
+ try { b = await request.json() } catch { return Response.json({ success: false, error: 'invalidRequest' }, { status: 400 }) }
+ const titulo = String(b.titulo ?? '').trim()
+ const contenido = String(b.contenido ?? '').trim()
+ const enlace = String(b.enlace ?? '').trim() || null
+ const tituloEn = String(b.tituloEn ?? '').trim() || null
+ const contenidoEn = String(b.contenidoEn ?? '').trim() || null
+ if (!titulo || !contenido) return Response.json({ success: false, error: 'emptyError' })
+ await updateNews(Number(id), titulo, contenido, enlace, tituloEn, contenidoEn)
+ return Response.json({ success: true })
+}
diff --git a/web-next/app/api/admin/news/route.ts b/web-next/app/api/admin/news/route.ts
index 03540bf..2f2775a 100644
--- a/web-next/app/api/admin/news/route.ts
+++ b/web-next/app/api/admin/news/route.ts
@@ -10,7 +10,9 @@ export async function POST(request: Request) {
const titulo = String(b.titulo ?? '').trim()
const contenido = String(b.contenido ?? '').trim()
const enlace = String(b.enlace ?? '').trim() || null
+ const tituloEn = String(b.tituloEn ?? '').trim() || null
+ const contenidoEn = String(b.contenidoEn ?? '').trim() || null
if (!titulo || !contenido) return Response.json({ success: false, error: 'emptyError' })
- const id = await createNews(titulo, contenido, enlace)
+ const id = await createNews(titulo, contenido, enlace, tituloEn, contenidoEn)
return Response.json({ success: true, id })
}
diff --git a/web-next/components/AdminNewsManager.tsx b/web-next/components/AdminNewsManager.tsx
index 1060ae6..b0d8c1c 100644
--- a/web-next/components/AdminNewsManager.tsx
+++ b/web-next/components/AdminNewsManager.tsx
@@ -4,19 +4,22 @@ import { useState } from 'react'
import { useTranslations, useLocale } from 'next-intl'
import type { NewsItem } from '@/lib/admin-news'
+const EMPTY = { titulo: '', tituloEn: '', contenido: '', contenidoEn: '', enlace: '' }
+type Draft = typeof EMPTY
+
export function AdminNewsManager({ initialNews }: { initialNews: NewsItem[] }) {
const t = useTranslations('Admin')
const locale = useLocale()
const [news, setNews] = useState(initialNews)
- const [titulo, setTitulo] = useState('')
- const [contenido, setContenido] = useState('')
- const [enlace, setEnlace] = useState('')
+ const [draft, setDraft] = useState(EMPTY)
const [busy, setBusy] = useState(false)
const [error, setError] = useState(null)
+ const [editId, setEditId] = useState(null)
+ const [edit, setEdit] = useState(EMPTY)
async function create(e: React.FormEvent) {
e.preventDefault()
- if (busy || !titulo.trim() || !contenido.trim()) return
+ if (busy || !draft.titulo.trim() || !draft.contenido.trim()) return
setBusy(true)
setError(null)
try {
@@ -24,14 +27,41 @@ export function AdminNewsManager({ initialNews }: { initialNews: NewsItem[] }) {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
credentials: 'same-origin',
- body: JSON.stringify({ titulo, contenido, enlace }),
+ body: JSON.stringify(draft),
})
const data: { success?: boolean; id?: number } = await res.json()
if (data.success && data.id) {
- setNews([{ id: data.id, titulo, fecha: new Date().toISOString(), enlace: enlace || null }, ...news])
- setTitulo('')
- setContenido('')
- setEnlace('')
+ setNews([{ id: data.id, ...draft, fecha: new Date().toISOString(), enlace: draft.enlace || null }, ...news])
+ setDraft(EMPTY)
+ } else setError(t('genericError'))
+ } catch {
+ setError(t('genericError'))
+ } finally {
+ setBusy(false)
+ }
+ }
+
+ function startEdit(n: NewsItem) {
+ setEditId(n.id)
+ setEdit({ titulo: n.titulo, tituloEn: n.tituloEn, contenido: n.contenido, contenidoEn: n.contenidoEn, enlace: n.enlace || '' })
+ setError(null)
+ }
+
+ async function save(id: number) {
+ if (busy || !edit.titulo.trim() || !edit.contenido.trim()) return
+ setBusy(true)
+ setError(null)
+ try {
+ const res = await fetch(`/api/admin/news/${id}`, {
+ method: 'PUT',
+ headers: { 'Content-Type': 'application/json' },
+ credentials: 'same-origin',
+ body: JSON.stringify(edit),
+ })
+ const data: { success?: boolean } = await res.json()
+ if (data.success) {
+ setNews(news.map((n) => (n.id === id ? { ...n, ...edit, enlace: edit.enlace || null } : n)))
+ setEditId(null)
} else setError(t('genericError'))
} catch {
setError(t('genericError'))
@@ -50,9 +80,12 @@ export function AdminNewsManager({ initialNews }: { initialNews: NewsItem[] }) {
return (