114 lines
3.9 KiB
TypeScript
114 lines
3.9 KiB
TypeScript
import { ExternalLink, Server } from 'lucide-react'
|
|
import Link from 'next/link'
|
|
import { EmulatorSettingsForm } from '@/app/[locale]/admin/settings/emulator/emulator-settings-form'
|
|
import { Button } from '@/components/ui/button'
|
|
import type { Prisma } from '@/generated/prisma/client'
|
|
import { calcPagination } from '@/lib/admin-helpers'
|
|
import { prisma } from '@/lib/prisma'
|
|
|
|
// SQL `LIKE` patterns that capture the user-facing strings inside
|
|
// `emulator_settings`. Anything not matching is config (timeouts, ports, IDs)
|
|
// and lives in /admin/settings/emulator instead.
|
|
const TRANSLATION_KEY_PATTERNS = [
|
|
'commands.description.%',
|
|
'commands.keys.%',
|
|
'commands.cmd_%',
|
|
'wordfilter.%',
|
|
'pet.%message%',
|
|
'matchmaking.%message%',
|
|
'message.%',
|
|
'notification.%',
|
|
] as const
|
|
|
|
export default async function EmulatorTranslationsPage({
|
|
searchParams,
|
|
}: {
|
|
searchParams: Promise<Record<string, string | undefined>>
|
|
}) {
|
|
const sp = await searchParams
|
|
const search = sp.search || ''
|
|
const group = sp.group || ''
|
|
const perPage = Math.min(Math.max(parseInt(sp.perPage || '50', 10), 10), 200)
|
|
const page = Math.max(parseInt(sp.page || '1', 10), 1)
|
|
|
|
const patternFilters: Prisma.EmulatorSettingWhereInput[] = TRANSLATION_KEY_PATTERNS.map((p) => ({
|
|
key: { contains: p.replace(/%/g, '') },
|
|
}))
|
|
|
|
const baseFilter: Prisma.EmulatorSettingWhereInput = { OR: patternFilters }
|
|
|
|
const conditions: Prisma.EmulatorSettingWhereInput[] = [baseFilter]
|
|
if (group) conditions.push({ key: { startsWith: `${group}.` } })
|
|
if (search) {
|
|
conditions.push({
|
|
OR: [{ key: { contains: search } }, { value: { contains: search } }],
|
|
})
|
|
}
|
|
const where: Prisma.EmulatorSettingWhereInput = { AND: conditions }
|
|
|
|
const [settings, total, allKeys] = await Promise.all([
|
|
prisma.emulatorSetting.findMany({
|
|
where,
|
|
orderBy: { key: 'asc' },
|
|
skip: (page - 1) * perPage,
|
|
take: perPage,
|
|
}),
|
|
prisma.emulatorSetting.count({ where }),
|
|
// Group chips reflect the WHOLE translation-pattern subset (so they don't
|
|
// disappear when you click one); we ignore the group/search filters here.
|
|
prisma.emulatorSetting.findMany({
|
|
where: baseFilter,
|
|
select: { key: true },
|
|
}),
|
|
])
|
|
|
|
const groupCounts = new Map<string, number>()
|
|
for (const { key } of allKeys) {
|
|
const g = key.includes('.') ? key.split('.')[0] : 'general'
|
|
groupCounts.set(g, (groupCounts.get(g) ?? 0) + 1)
|
|
}
|
|
const allGroups = Array.from(groupCounts.entries())
|
|
.map(([name, count]) => ({ name, count }))
|
|
.sort((a, b) => a.name.localeCompare(b.name))
|
|
|
|
const pagination = calcPagination(total, page, perPage)
|
|
const settingsMap = settings.map((s) => ({ key: s.key, value: s.value }))
|
|
|
|
return (
|
|
<div className="space-y-4">
|
|
<div className="flex items-start justify-between flex-wrap gap-2">
|
|
<div>
|
|
<h1 className="text-2xl font-bold flex items-center gap-2">
|
|
<Server className="w-6 h-6" />
|
|
Emulator
|
|
</h1>
|
|
<p className="text-sm text-muted-foreground">
|
|
Sottoinsieme di <code className="text-xs">emulator_settings</code> con stringhe utente
|
|
(comandi, wordfilter, messaggi pet/matchmaking).
|
|
</p>
|
|
</div>
|
|
<Button asChild variant="outline" size="sm">
|
|
<Link href="/admin/settings/emulator">
|
|
<ExternalLink className="w-4 h-4 mr-1" />
|
|
Impostazioni complete
|
|
</Link>
|
|
</Button>
|
|
</div>
|
|
|
|
<div className="rounded-md border bg-muted/40 px-3 py-2 text-xs text-muted-foreground">
|
|
Le impostazioni di configurazione (porte, timeout, ID) non compaiono qui — usa il link
|
|
«Impostazioni complete». Le modifiche salvate sono sincronizzate con l'emulatore via
|
|
RCON.
|
|
</div>
|
|
|
|
<EmulatorSettingsForm
|
|
settings={settingsMap}
|
|
pagination={pagination}
|
|
search={search}
|
|
allGroups={allGroups}
|
|
activeGroup={group}
|
|
/>
|
|
</div>
|
|
)
|
|
}
|