diff --git a/src/actions/items-base.ts b/src/actions/items-base.ts new file mode 100644 index 00000000..6bb39c6c --- /dev/null +++ b/src/actions/items-base.ts @@ -0,0 +1,115 @@ +"use server"; + +import { revalidatePath } from "next/cache"; +import { z } from "zod"; +import { Prisma } from "@/generated/prisma/client"; +import { PERMS } from "@/lib/permissions"; +import { prisma } from "@/lib/prisma"; +import { adminAction } from "@/lib/safe-action"; +import { ActionError, actionOk } from "@/lib/safe-action-shared"; +import { rcon } from "@/lib/services/rcon"; +import { logStaffActivity } from "@/lib/services/staff-activity"; + +const ITEMS_BASE_FIELDS = [ + "publicName", + "itemName", + "type", + "spriteId", + "width", + "length", + "stackHeight", + "allowStack", + "allowSit", + "allowLay", + "allowWalk", + "allowGift", + "allowTrade", + "allowRecycle", + "allowMarketplaceSell", + "allowInventoryStack", + "interactionType", + "interactionModesCount", + "vendingIds", + "multiheight", + "customparams", + "effectIdMale", + "effectIdFemale", + "clothingOnWalk", +] as const; + +const updateSchema = z.object({ + id: z.coerce.number().int().positive(), + fields: z.record(z.string(), z.unknown()), +}); + +function pickAllowed( + fields: Record, + allowed: readonly string[], +): Record { + const out: Record = {}; + for (const key of allowed) { + if (Object.hasOwn(fields, key) && fields[key] !== undefined) { + out[key] = fields[key]; + } + } + return out; +} + +export const updateItemsBase = adminAction( + { permission: PERMS.CATALOG_EDIT, schema: updateSchema }, + async (ctx) => { + const { id, fields } = ctx.data; + const safe = pickAllowed(fields, ITEMS_BASE_FIELDS); + if (Object.keys(safe).length === 0) { + throw new ActionError("No valid fields to update"); + } + + const existing = await prisma.itemsBase.findUnique({ + where: { id }, + select: { id: true }, + }); + if (!existing) throw new ActionError("Item not found"); + + // Coerce common numeric / decimal fields from form strings. + const data: Record = { ...safe }; + for (const key of [ + "spriteId", + "width", + "length", + "allowStack", + "allowSit", + "allowLay", + "allowWalk", + "allowGift", + "allowTrade", + "allowRecycle", + "allowMarketplaceSell", + "allowInventoryStack", + "interactionModesCount", + "effectIdMale", + "effectIdFemale", + ]) { + if (data[key] !== undefined) data[key] = Number(data[key]); + } + if (data.stackHeight !== undefined) { + data.stackHeight = new Prisma.Decimal(String(data.stackHeight)); + } + + await prisma.itemsBase.update({ + where: { id }, + data: data as Prisma.ItemsBaseUpdateInput, + }); + await rcon.updateCatalog().catch(() => false); + await logStaffActivity({ + staffId: Number(ctx.session.user.id), + action: "items_base_update", + description: `Updated items_base #${id}`, + targetType: "items_base", + targetId: id, + }); + revalidatePath("/admin/items"); + revalidatePath(`/admin/items/${id}`); + revalidatePath("/admin/catalog"); + return actionOk({ id }); + }, +); diff --git a/src/app/admin/articles/page.tsx b/src/app/admin/articles/page.tsx index 7c030f36..8496564e 100644 --- a/src/app/admin/articles/page.tsx +++ b/src/app/admin/articles/page.tsx @@ -1,6 +1,8 @@ +import { Newspaper } from "lucide-react"; import Link from "next/link"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { ArticleCard } from "@/components/admin/article-card"; import { StatusCard } from "@/components/admin/dashboard"; import { Button } from "@/components/ui/button"; @@ -56,17 +58,18 @@ export default async function AdminArticles({ : "—"; return ( -
-
+ {t("newArticle")} -
- + } + > {error ? ( -

- Error: {error} -

+

Error: {error}

) : null}
@@ -83,7 +86,7 @@ export default async function AdminArticles({ />
-
+

{t("recentArticles")}

{articles.length === 0 ? (
{t("noArticles")}
@@ -102,6 +105,6 @@ export default async function AdminArticles({ )}
-
+ ); } diff --git a/src/app/admin/catalog/page.tsx b/src/app/admin/catalog/page.tsx index d4f4059c..653884cf 100644 --- a/src/app/admin/catalog/page.tsx +++ b/src/app/admin/catalog/page.tsx @@ -1,4 +1,7 @@ +import { Store } from "lucide-react"; import { redirect } from "next/navigation"; +import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { CatalogTree } from "@/components/admin/catalog-tree"; import { Prisma } from "@/generated/prisma/client"; import { calcPagination, parseListParams } from "@/lib/admin-helpers"; @@ -23,6 +26,7 @@ export default async function CatalogPagesPage({ } const canEdit = canAccess(permissions, PERMS.CATALOG_EDIT, session.user.rank); + const t = await getTranslations("pages.admin.catalog"); const params = await searchParams; const sp = new URLSearchParams(params); @@ -200,50 +204,52 @@ export default async function CatalogPagesPage({ } return ( -
-
-

- {totalBcPages} BC pages ({enabledBcPages} enabled) ·{" "} - {totalBcItems} items -

-
- - {canEdit && ( - ({ - id: p.id, - caption: p.caption, - iconImage: p.iconImage, - enabled: p.enabled, - orderNum: 0, - }))} - /> - )} - {canEdit && ( - - )} + +
+
+

+ {totalBcPages} BC pages ({enabledBcPages} enabled) ·{" "} + {totalBcItems} items +

+
+ + {canEdit && ( + ({ + id: p.id, + caption: p.caption, + iconImage: p.iconImage, + enabled: p.enabled, + orderNum: 0, + }))} + /> + )} + {canEdit && ( + + )} +
+ {viewMode === "tree" ? ( + + ) : ( + + )}
- {viewMode === "tree" ? ( - - ) : ( - - )} -
+ ); } @@ -396,43 +402,45 @@ export default async function CatalogPagesPage({ } return ( -
-
-

- {totalPages} pages ({enabledPages} enabled) · {totalItems}{" "} - items -

-
- - {canEdit && ( - ({ - id: p.id, - caption: p.caption, - iconImage: p.iconImage, - enabled: p.enabled, - orderNum: 0, - }))} - /> - )} - {canEdit && ( - - )} + +
+
+

+ {totalPages} pages ({enabledPages} enabled) · {totalItems}{" "} + items +

+
+ + {canEdit && ( + ({ + id: p.id, + caption: p.caption, + iconImage: p.iconImage, + enabled: p.enabled, + orderNum: 0, + }))} + /> + )} + {canEdit && ( + + )} +
-
- {viewMode === "tree" ? ( - - ) : ( - - )} -
+ {viewMode === "tree" ? ( + + ) : ( + + )} +
+ ); } diff --git a/src/app/admin/commandocentrum/page.tsx b/src/app/admin/commandocentrum/page.tsx index 652910fb..c4672edf 100644 --- a/src/app/admin/commandocentrum/page.tsx +++ b/src/app/admin/commandocentrum/page.tsx @@ -1,6 +1,8 @@ import os from "node:os"; +import { Terminal } from "lucide-react"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { DiagnosticRow, InfoItem, @@ -87,205 +89,219 @@ export default async function CommandoCentrum() { const rconSet = !!process.env.RCON_HOST; return ( -
- {/* ── Live status ────────────────────────────────────────── */} -
- = 0 - ? t("totalAccounts", { count: totalUsers }) - : t("dbUnavailable") - } - state="neutral" - icon="👤" - /> - - - -
+ +
+ {/* ── Live status ────────────────────────────────────────── */} +
+ = 0 + ? t("totalAccounts", { count: totalUsers }) + : t("dbUnavailable") + } + state="neutral" + icon="👤" + /> + + + +
- {/* ── Online users widget ───────────────────────────────── */} -
-

{t("onlineUsers")}

- -
- - {/* ── Server info + diagnostics ──────────────────────────── */} -
-
-

{t("serverInfo")}

-
- - - - - - - - -
+ {/* ── Online users widget ───────────────────────────────── */} +
+

{t("onlineUsers")}

+
-
-

{t("systemDiagnostics")}

-
- - - - - - -
-
-
+ {/* ── Server info + diagnostics ──────────────────────────── */} +
+
+

{t("serverInfo")}

+
+ + + + + + + + +
+
- +
+

{t("systemDiagnostics")}

+
+ + + + + + +
+
+
- {/* ── Staff activity ─────────────────────────────────────── */} -
-

{t("recentStaffActivity")}

- {activity.length === 0 ? ( -
{t("noStaffActivity")}
- ) : ( -
- - - - - - - - - - - {activity.map((a) => ( - - - - - + + + {/* ── Staff activity ─────────────────────────────────────── */} +
+

{t("recentStaffActivity")}

+ {activity.length === 0 ? ( +
{t("noStaffActivity")}
+ ) : ( +
+
{t("colWhen")}{t("colStaff")}{t("colAction")}{t("colDescription")}
- {formatDate(a.createdAt, "datetime-seconds")} - {nameById.get(Number(a.userId)) ?? `#${a.userId}`} - - {a.action} - - - {a.description} -
+ + + + + + - ))} - -
{t("colWhen")}{t("colStaff")}{t("colAction")}{t("colDescription")}
-
- )} -
- - {/* ── Emulator errors ────────────────────────────────────── */} -
-

{t("recentEmulatorErrors")}

- {errors.length === 0 ? ( -
{t("noEmulatorErrors")}
- ) : ( -
- - - - - - - - - - - {errors.map((e) => { - const trace = decodeStacktrace(e.stacktrace); - const snippet = trace - ? trace.split("\n").slice(0, 20).join("\n") + - (trace.split("\n").length > 20 - ? `\n… ${t("truncated")}` - : "") - : t("empty"); - return ( - + + + {activity.map((a) => ( + - - + + - ); - })} - -
{t("colWhen")}{t("colType")}{t("colVersion")}{t("colStacktrace")}
- {formatDate( - e.timestamp ? new Date(e.timestamp * 1000) : null, - "datetime-seconds", - )} - {e.type} - {e.version} + {formatDate(a.createdAt, "datetime-seconds")} -
-													{snippet}
-												
+ {nameById.get(Number(a.userId)) ?? `#${a.userId}`} +
+ + {a.action} + + + {a.description}
-
- )} -
-
+ ))} + + +
+ )} + + + {/* ── Emulator errors ────────────────────────────────────── */} +
+

{t("recentEmulatorErrors")}

+ {errors.length === 0 ? ( +
{t("noEmulatorErrors")}
+ ) : ( +
+ + + + + + + + + + + {errors.map((e) => { + const trace = decodeStacktrace(e.stacktrace); + const snippet = trace + ? trace.split("\n").slice(0, 20).join("\n") + + (trace.split("\n").length > 20 + ? `\n… ${t("truncated")}` + : "") + : t("empty"); + return ( + + + + + + + ); + })} + +
{t("colWhen")}{t("colType")}{t("colVersion")}{t("colStacktrace")}
+ {formatDate( + e.timestamp ? new Date(e.timestamp * 1000) : null, + "datetime-seconds", + )} + {e.type} + {e.version} + +
+														{snippet}
+													
+
+
+ )} +
+ + ); } diff --git a/src/app/admin/guilds/page.tsx b/src/app/admin/guilds/page.tsx index c09729e2..eedd7515 100644 --- a/src/app/admin/guilds/page.tsx +++ b/src/app/admin/guilds/page.tsx @@ -1,6 +1,8 @@ +import { Shield } from "lucide-react"; import Link from "next/link"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { AdminSimplePager } from "@/components/admin/admin-simple-pager"; import { StatusCard } from "@/components/admin/dashboard"; import { Button } from "@/components/ui/button"; @@ -104,103 +106,105 @@ export default async function AdminGuildsPage({ ); return ( -
- {loadError ? ( -
- {t("loadError")} -
- ) : null} - -
- - -
- -
- -
- -
-

- {t("title")} ({total}) -

- {rows.length === 0 ? ( -
{t("empty")}
- ) : ( -
- - - - - - - - - - - - - {rows.map((g) => ( - - - - - - - - - ))} - -
{t("colId")}{t("colBadge")}{t("colName")}{t("colOwner")}{t("colMembers")}{t("colCreated")}
{g.id} - {g.badge ? ( - // eslint-disable-next-line @next/next/no-img-element - - ) : ( - — - )} - - - {g.name || `#${g.id}`} - - {g.description ? ( -
- {g.description} -
- ) : null} -
- - {ownerById.get(g.userId) ?? `#${g.userId}`} - - {membersByGuild.get(g.id) ?? 0} - {formatDate(new Date(g.dateCreated * 1000), "date")} -
+ +
+ {loadError ? ( +
+ {t("loadError")}
- )} - -
-
+ ) : null} + +
+ + +
+ +
+ +
+ +
+

+ {t("listHeading", { count: total })} +

+ {rows.length === 0 ? ( +
{t("empty")}
+ ) : ( +
+ + + + + + + + + + + + + {rows.map((g) => ( + + + + + + + + + ))} + +
{t("colId")}{t("colBadge")}{t("colName")}{t("colOwner")}{t("colMembers")}{t("colCreated")}
{g.id} + {g.badge ? ( + // eslint-disable-next-line @next/next/no-img-element + + ) : ( + — + )} + + + {g.name || `#${g.id}`} + + {g.description ? ( +
+ {g.description} +
+ ) : null} +
+ + {ownerById.get(g.userId) ?? `#${g.userId}`} + + {membersByGuild.get(g.id) ?? 0} + {formatDate(new Date(g.dateCreated * 1000), "date")} +
+
+ )} + +
+ + ); } diff --git a/src/app/admin/items/[id]/items-base-edit-form.tsx b/src/app/admin/items/[id]/items-base-edit-form.tsx new file mode 100644 index 00000000..71edd895 --- /dev/null +++ b/src/app/admin/items/[id]/items-base-edit-form.tsx @@ -0,0 +1,273 @@ +"use client"; + +import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { useTranslations } from "next-intl"; +import { useState } from "react"; +import { updateItemsBase } from "@/actions/items-base"; +import { Button } from "@/components/ui/button"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { useServerAction } from "@/hooks/use-server-action"; + +export type ItemsBaseEditValues = { + id: number; + spriteId: number; + publicName: string; + itemName: string; + type: string; + width: number; + length: number; + stackHeight: string; + allowStack: number; + allowSit: number; + allowLay: number; + allowWalk: number; + allowGift: number; + allowTrade: number; + allowRecycle: number; + allowMarketplaceSell: number; + allowInventoryStack: number; + interactionType: string; + interactionModesCount: number; + vendingIds: string; + multiheight: string; + customparams: string; +}; + +const ALLOW_FLAGS: Array<{ key: keyof ItemsBaseEditValues; label: string }> = [ + { key: "allowStack", label: "Stack" }, + { key: "allowSit", label: "Sit" }, + { key: "allowLay", label: "Lay" }, + { key: "allowWalk", label: "Walk" }, + { key: "allowGift", label: "Gift" }, + { key: "allowTrade", label: "Trade" }, + { key: "allowRecycle", label: "Recycle" }, + { key: "allowMarketplaceSell", label: "Marketplace" }, + { key: "allowInventoryStack", label: "Inv. stack" }, +]; + +export function ItemsBaseEditForm({ + initial, + canEdit, +}: { + initial: ItemsBaseEditValues; + canEdit: boolean; +}) { + const t = useTranslations("pages.admin.items"); + const router = useRouter(); + const { run, isPending } = useServerAction(); + const [form, setForm] = useState(initial); + + function setField( + key: K, + value: ItemsBaseEditValues[K], + ) { + setForm((prev) => ({ ...prev, [key]: value })); + } + + function handleSave() { + if (!canEdit || isPending) return; + const { id, ...fields } = form; + run(() => updateItemsBase({ id, fields }), { + successMessage: t("saved"), + onSuccess: () => router.refresh(), + }); + } + + return ( +
+
+
+

+ #{form.id} +

+

+ {form.publicName || form.itemName || t("untitled")} +

+
+
+ + {canEdit ? ( + + ) : null} +
+
+ +
+ + + {t("sectionIdentity")} + + +
+ + setField("publicName", e.target.value)} + /> +
+
+ + setField("itemName", e.target.value)} + /> +
+
+
+ + setField("type", e.target.value)} + /> +
+
+ + setField("spriteId", Number(e.target.value))} + /> +
+
+
+
+ + + + {t("sectionDims")} + + +
+
+ + setField("width", Number(e.target.value))} + /> +
+
+ + setField("length", Number(e.target.value))} + /> +
+
+ + setField("stackHeight", e.target.value)} + /> +
+
+
+ + setField("interactionType", e.target.value)} + /> +
+
+ + + setField("interactionModesCount", Number(e.target.value)) + } + /> +
+
+
+ + + + {t("sectionFlags")} + + + {ALLOW_FLAGS.map(({ key, label }) => ( + + ))} + + + + + + {t("sectionExtra")} + + +
+ + setField("vendingIds", e.target.value)} + /> +
+
+ + setField("multiheight", e.target.value)} + /> +
+
+ + setField("customparams", e.target.value)} + /> +
+
+
+
+
+ ); +} diff --git a/src/app/admin/items/[id]/page.tsx b/src/app/admin/items/[id]/page.tsx new file mode 100644 index 00000000..5bd24ead --- /dev/null +++ b/src/app/admin/items/[id]/page.tsx @@ -0,0 +1,66 @@ +import { Package } from "lucide-react"; +import { notFound, redirect } from "next/navigation"; +import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; +import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; +import { prisma } from "@/lib/prisma"; +import { ItemsBaseEditForm } from "./items-base-edit-form"; + +export const dynamic = "force-dynamic"; + +export default async function ItemsBaseDetailPage({ + params, +}: { + params: Promise<{ id: string }>; +}) { + const { session, permissions } = await getAdminContext(); + if (!canAccess(permissions, PERMS.CATALOG_VIEW, session.user.rank)) { + redirect("/admin"); + } + + const t = await getTranslations("pages.admin.items"); + const { id: rawId } = await params; + const id = Number(rawId); + if (!Number.isFinite(id) || id <= 0) notFound(); + + const item = await prisma.itemsBase.findUnique({ where: { id } }); + if (!item) notFound(); + + const canEdit = canAccess(permissions, PERMS.CATALOG_EDIT, session.user.rank); + + return ( + + + + ); +} diff --git a/src/app/admin/items/items-table.tsx b/src/app/admin/items/items-table.tsx new file mode 100644 index 00000000..39f99dad --- /dev/null +++ b/src/app/admin/items/items-table.tsx @@ -0,0 +1,118 @@ +"use client"; + +import { Eye, Package, Pencil } from "lucide-react"; +import Link from "next/link"; +import { DataTable } from "@/components/admin/data-table"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import type { DataTableColumn, PaginatedResult } from "@/types/common"; + +export interface ItemsBaseRow { + id: number; + spriteId: number; + publicName: string; + itemName: string; + type: string; + interactionType: string; + width: number; + length: number; +} + +function getColumns(canEdit: boolean): DataTableColumn[] { + return [ + { key: "id", label: "ID", sortable: true }, + { + key: "publicName", + label: "Name", + sortable: true, + render: (_, row) => ( + + +
+ + {row.publicName || "—"} + + + {row.itemName} + +
+ + ), + }, + { + key: "spriteId", + label: "Sprite", + sortable: true, + render: (v) => {String(v)}, + }, + { + key: "type", + label: "Type", + sortable: true, + filterKey: "filter_type", + filterOptions: [ + { label: "Floor (s)", value: "s" }, + { label: "Wall (i)", value: "i" }, + { label: "Effect (e)", value: "e" }, + { label: "Badge (b)", value: "b" }, + { label: "Robot (r)", value: "r" }, + ], + render: (v) => {String(v)}, + }, + { + key: "interactionType", + label: "Interaction", + sortable: true, + render: (v) => ( + + {String(v || "default")} + + ), + }, + { + key: "width", + label: "Size", + render: (_, row) => ( + + {row.width}×{row.length} + + ), + }, + { + key: "actions", + label: "", + render: (_, row) => ( +
+ +
+ ), + }, + ]; +} + +export function ItemsTable({ + data, + canEdit, +}: { + data: PaginatedResult; + canEdit: boolean; +}) { + return ( + + ); +} diff --git a/src/app/admin/items/page.tsx b/src/app/admin/items/page.tsx new file mode 100644 index 00000000..d7a90a79 --- /dev/null +++ b/src/app/admin/items/page.tsx @@ -0,0 +1,73 @@ +import { Package } from "lucide-react"; +import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; +import { fetchAdminList } from "@/lib/admin-list"; +import { canAccess, PERMS } from "@/lib/permissions"; +import { type ItemsBaseRow, ItemsTable } from "./items-table"; + +export default async function ItemsBasePage({ + params: paramsPromise, + searchParams, +}: { + params: Promise<{ locale: string }>; + searchParams: Promise>; +}) { + const result = await fetchAdminList( + { + permission: PERMS.CATALOG_VIEW, + model: "itemsBase", + searchFields: ["itemName", "publicName"], + defaultSort: { id: "desc" }, + select: { + id: true, + spriteId: true, + publicName: true, + itemName: true, + type: true, + interactionType: true, + width: true, + length: true, + }, + filterMap: { + filter_type: (v) => ({ type: v }), + }, + mapRow: (r) => ({ + id: r.id as number, + spriteId: r.spriteId as number, + publicName: (r.publicName as string) || "", + itemName: (r.itemName as string) || "", + type: (r.type as string) || "s", + interactionType: (r.interactionType as string) || "default", + width: r.width as number, + length: r.length as number, + }), + }, + paramsPromise, + searchParams, + ); + + const t = await getTranslations("pages.admin.items"); + const canEdit = canAccess( + result.permissions, + PERMS.CATALOG_EDIT, + result.rank, + ); + + return ( + +

+ {t("total", { count: result.total })} +

+ +
+ ); +} diff --git a/src/app/admin/logs/page.tsx b/src/app/admin/logs/page.tsx index 465e40d9..7c938e1d 100644 --- a/src/app/admin/logs/page.tsx +++ b/src/app/admin/logs/page.tsx @@ -1,5 +1,7 @@ +import { ClipboardList } from "lucide-react"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { StatusCard } from "@/components/admin/dashboard"; import { buildStaffActivityWhere } from "@/lib/admin/log-filters"; import { calcPagination, parseListParams } from "@/lib/admin-helpers"; @@ -97,31 +99,37 @@ export default async function AdminStaffActivities({ })); return ( -
-
- - - 0 ? "ok" : "neutral"} - icon="🟢" + +
+
+ + + 0 ? "ok" : "neutral"} + icon="🟢" + /> +
+ +
- - -
+ ); } diff --git a/src/app/admin/marketplace/page.tsx b/src/app/admin/marketplace/page.tsx index 6ddf2cd3..243a854f 100644 --- a/src/app/admin/marketplace/page.tsx +++ b/src/app/admin/marketplace/page.tsx @@ -1,7 +1,9 @@ +import { Store } from "lucide-react"; import Link from "next/link"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; import { cancelMarketplaceListing } from "@/actions/admin-marketplace"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { AdminSimplePager } from "@/components/admin/admin-simple-pager"; import { StatusCard } from "@/components/admin/dashboard"; import { Button } from "@/components/ui/button"; @@ -69,109 +71,115 @@ export default async function AdminMarketplacePage({ const usernameById = new Map(users.map((u) => [u.id, u.username])); return ( -
- {loadError ? ( -
- {t("loadError")} + +
+ {loadError ? ( +
+ {t("loadError")} +
+ ) : null} + +
+ +
- ) : null} -
- - -
+
+ + +
-
- - -
- -
-

- {filter === "sold" ? t("soldListings") : t("activeListings")} ({total} - ) -

- {rows.length === 0 ? ( -
{t("empty")}
- ) : ( -
- - - - - - - - - {filter === "sold" ? : null} - {canEdit && filter === "active" ? ( - - ) : null} - - - - {rows.map((row) => ( - - - - - - - {filter === "sold" ? ( - - ) : null} +
+

+ {filter === "sold" ? t("soldListings") : t("activeListings")} ( + {total}) +

+ {rows.length === 0 ? ( +
{t("empty")}
+ ) : ( +
+
{t("colId")}{t("colItem")}{t("colSeller")}{t("colPrice")}{t("colListed")}{t("colSold")}{t("colActions")}
{row.id} - {row.itemId} - - - {usernameById.get(row.userId) ?? `#${row.userId}`} - - {row.price} - {formatDate(new Date(row.timestamp * 1000))} - - {formatDate( - row.soldTimestamp - ? new Date(row.soldTimestamp * 1000) - : null, - )} -
+ + + + + + + + {filter === "sold" ? : null} {canEdit && filter === "active" ? ( - + ) : null} - ))} - -
{t("colId")}{t("colItem")}{t("colSeller")}{t("colPrice")}{t("colListed")}{t("colSold")} -
- - -
-
{t("colActions")}
-
- )} - -
-
+ + + {rows.map((row) => ( + + {row.id} + + {row.itemId} + + + + {usernameById.get(row.userId) ?? `#${row.userId}`} + + + {row.price} + + {formatDate(new Date(row.timestamp * 1000))} + + {filter === "sold" ? ( + + {formatDate( + row.soldTimestamp + ? new Date(row.soldTimestamp * 1000) + : null, + )} + + ) : null} + {canEdit && filter === "active" ? ( + +
+ + +
+ + ) : null} + + ))} + + +
+ )} + + +
+ ); } diff --git a/src/app/admin/online/page.tsx b/src/app/admin/online/page.tsx index d9d8b540..ddd19dfd 100644 --- a/src/app/admin/online/page.tsx +++ b/src/app/admin/online/page.tsx @@ -1,3 +1,6 @@ +import { Wifi } from "lucide-react"; +import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { fetchAdminList } from "@/lib/admin-list"; import { PERMS } from "@/lib/permissions"; import { OnlineTable } from "./online-table"; @@ -27,10 +30,12 @@ export default async function OnlineUsersPage({ searchParams, ); + const t = await getTranslations("pages.admin.online"); + return ( -
-

- Currently online users ({result.total} connected) + +

+ {t("connected", { count: result.total })}

-
+ ); } diff --git a/src/app/admin/rooms/page.tsx b/src/app/admin/rooms/page.tsx index a99a60ad..3d512fff 100644 --- a/src/app/admin/rooms/page.tsx +++ b/src/app/admin/rooms/page.tsx @@ -1,3 +1,6 @@ +import { DoorOpen } from "lucide-react"; +import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { fetchAdminList } from "@/lib/admin-list"; import { canAccess, PERMS } from "@/lib/permissions"; import { RoomsTable } from "./rooms-table"; @@ -44,11 +47,12 @@ export default async function RoomsPage({ searchParams, ); + const t = await getTranslations("pages.admin.rooms"); const canEdit = canAccess(result.permissions, PERMS.ROOMS_EDIT, result.rank); return ( -
-

+ +

{result.total.toLocaleString()} rooms total

@@ -62,6 +66,6 @@ export default async function RoomsPage({ }} canEdit={canEdit} /> -
+ ); } diff --git a/src/app/admin/settings/page.tsx b/src/app/admin/settings/page.tsx index 90424ec3..8bc9dc72 100644 --- a/src/app/admin/settings/page.tsx +++ b/src/app/admin/settings/page.tsx @@ -1,5 +1,7 @@ +import { Settings } from "lucide-react"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { AdvancedSettingsPanel } from "./advanced-settings-panel"; @@ -41,29 +43,31 @@ export default async function AdminSettings() { const documented = settings.filter((s) => !!s.comment).length; return ( -
-
- - {t("keysInDatabase", { count: settings.length })} - - - {t("managedInForm", { count: MANAGED_SETTING_KEYS.length })} - - - {t("documented", { count: documented })} - + +
+
+ + {t("keysInDatabase", { count: settings.length })} + + + {t("managedInForm", { count: MANAGED_SETTING_KEYS.length })} + + + {t("documented", { count: documented })} + +
+ + + + ({ + key: s.key, + value: s.value, + comment: s.comment, + }))} + canEdit={canEdit} + />
- - - - ({ - key: s.key, - value: s.value, - comment: s.comment, - }))} - canEdit={canEdit} - /> -
+ ); } diff --git a/src/app/admin/shop/page.tsx b/src/app/admin/shop/page.tsx index 118a4b37..1365e0a1 100644 --- a/src/app/admin/shop/page.tsx +++ b/src/app/admin/shop/page.tsx @@ -1,6 +1,8 @@ +import { ShoppingCart } from "lucide-react"; import Link from "next/link"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { AdminSimplePager } from "@/components/admin/admin-simple-pager"; import { StatusCard } from "@/components/admin/dashboard"; import { CurrencyIcon } from "@/components/shared/currency-icon"; @@ -111,142 +113,152 @@ export default async function AdminShop({ ).length; return ( -
- {loadError ? ( -
- Could not load some shop data (database unavailable). -
- ) : null} -
+ {t("newPackage")} -
+ } + > +
+ {loadError ? ( +
+ Could not load some shop data (database unavailable). +
+ ) : null} -
- - - 0 ? "ok" : "neutral"} - icon="💳" - /> -
+
+ + + 0 ? "ok" : "neutral"} + icon="💳" + /> +
-
-

- {t("packagesTotal", { count: packageTotal })} -

- {packages.length === 0 ? ( -
{t("noPackages")}
- ) : ( -
- - - - - - - - - - - - - - - {packages.map((p) => ( - - - - - - - - - +
+

+ {t("packagesTotal", { count: packageTotal })} +

+ {packages.length === 0 ? ( +
{t("noPackages")}
+ ) : ( +
+
#{t("colName")}{t("colPrice")}{t("colCredits")}{t("colDuckets")}{t("colDiamonds")}{t("colRank")}{t("colPosition")}
{String(p.id)} - {p.name} - {p.costs} - - - - - - {p.giveRank ?? "—"}{p.position}
+ + + + + + + + + + - ))} - -
#{t("colName")}{t("colPrice")}{t("colCredits")}{t("colDuckets")}{t("colDiamonds")}{t("colRank")}{t("colPosition")}
-
- )} - -
- -
-

- {t("recentOrders")} ({orders.length}) -

-

{t("ordersHint")}

- {orders.length === 0 ? ( -
{t("noOrders")}
- ) : ( -
- - - - - - - - - - - - - {orders.map((o) => { - const state = statusState(o.status); - return ( - + + + {packages.map((p) => ( + + - - + - + + - ); - })} - -
{t("colTransaction")}{t("colUser")}{t("colDescription")}{t("colAmount")}{t("colStatus")}{t("colDate")}
{String(p.id)} - {o.transactionId} + {p.name} - - #{o.userId} - - {o.description ?? "—"}{p.costs} - {o.amount} {o.currency} + - {o.status ? ( - - {o.status} - - ) : ( - — - )} + - {formatDate(o.createdAt, "date")} + + {p.giveRank ?? "—"}{p.position}
-
- )} -
-
+ ))} + + +
+ )} + + + +
+

+ {t("recentOrders")} ({orders.length}) +

+

{t("ordersHint")}

+ {orders.length === 0 ? ( +
{t("noOrders")}
+ ) : ( +
+ + + + + + + + + + + + + {orders.map((o) => { + const state = statusState(o.status); + return ( + + + + + + + + + ); + })} + +
{t("colTransaction")}{t("colUser")}{t("colDescription")}{t("colAmount")}{t("colStatus")}{t("colDate")}
+ {o.transactionId} + + + #{o.userId} + + {o.description ?? "—"} + {o.amount} {o.currency} + + {o.status ? ( + + {o.status} + + ) : ( + — + )} + + {formatDate(o.createdAt, "date")} +
+
+ )} +
+ + ); } diff --git a/src/app/admin/wordfilter/page.tsx b/src/app/admin/wordfilter/page.tsx index af4efe74..813c22ea 100644 --- a/src/app/admin/wordfilter/page.tsx +++ b/src/app/admin/wordfilter/page.tsx @@ -1,4 +1,7 @@ +import { Filter } from "lucide-react"; import { redirect } from "next/navigation"; +import { getTranslations } from "next-intl/server"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { WordFilterClient } from "./wordfilter-client"; @@ -9,6 +12,7 @@ export default async function AdminWordFilter() { redirect("/admin"); } + const t = await getTranslations("pages.admin.wordfilter"); const canEdit = canAccess( permissions, PERMS.WORDFILTER_EDIT, @@ -27,5 +31,9 @@ export default async function AdminWordFilter() { words = []; } - return ; + return ( + + + + ); } diff --git a/src/lib/admin-list.ts b/src/lib/admin-list.ts index 7b517f5a..5d1b8f38 100644 --- a/src/lib/admin-list.ts +++ b/src/lib/admin-list.ts @@ -102,14 +102,18 @@ export async function fetchAdminList( const { search, page, sort, order } = parsed; // Build WHERE clause - const searchWhere = + const searchOr = search && config.searchFields.length > 0 - ? { - OR: config.searchFields.map((field) => - buildFieldCondition(field, search), - ), - } - : {}; + ? config.searchFields.map((field) => buildFieldCondition(field, search)) + : []; + // Exact numeric id match (furni / rooms / items lookup by id). + if (search && /^\d+$/.test(search)) { + const asId = Number(search); + if (Number.isFinite(asId) && asId > 0) { + searchOr.push({ id: asId }); + } + } + const searchWhere = searchOr.length > 0 ? { OR: searchOr } : {}; // Apply URL filter_* params via filterMap const filterWhere: Record = {}; diff --git a/src/lib/admin-nav.ts b/src/lib/admin-nav.ts index 8ee70b03..c99b9594 100644 --- a/src/lib/admin-nav.ts +++ b/src/lib/admin-nav.ts @@ -435,6 +435,13 @@ export const ADMIN_NAV_GROUPS: AdminNavGroup[] = [ permission: PERMS.CATALOG_VIEW, matchPrefixes: ["/admin/catalog"], }, + { + href: "/admin/items", + labelKey: "itemsBase", + icon: Package, + permission: PERMS.CATALOG_VIEW, + matchPrefixes: ["/admin/items"], + }, { href: "/admin/rare-values", labelKey: "rareValues", diff --git a/src/lib/admin-operations-contract.test.ts b/src/lib/admin-operations-contract.test.ts index 81d2228e..66094646 100644 --- a/src/lib/admin-operations-contract.test.ts +++ b/src/lib/admin-operations-contract.test.ts @@ -40,6 +40,8 @@ const ROUTES: Array<[string, string]> = [ ["housekeeping", "PERMS.SETTINGS_VIEW"], ["logs", "PERMS.LOGS_VIEW"], ["catalog", "PERMS.CATALOG_VIEW"], + ["items", "PERMS.CATALOG_VIEW"], + ["items/[id]", "PERMS.CATALOG_VIEW"], ["rooms/edit/[id]", "PERMS.ROOMS_EDIT"], ]; @@ -64,6 +66,7 @@ const ACTION_GATES: Array<[string, string]> = [ ["admin-shop.ts", "PERMS.SHOP_EDIT"], ["admin-radio-autodj.ts", "PERMS.RADIO_EDIT"], ["catalog.ts", "PERMS.CATALOG_EDIT"], + ["items-base.ts", "PERMS.CATALOG_EDIT"], ["rooms.ts", "PERMS.ROOMS_EDIT"], ["admin-vpn.ts", "PERMS.SETTINGS_EDIT"], ["admin-ads.ts", "PERMS.PAGES_EDIT"], diff --git a/src/lib/staff-smoke-contract.test.ts b/src/lib/staff-smoke-contract.test.ts index 482cb6a9..70bc7967 100644 --- a/src/lib/staff-smoke-contract.test.ts +++ b/src/lib/staff-smoke-contract.test.ts @@ -82,4 +82,15 @@ describe("staff smoke contract", () => { expect(readFileSync(path, "utf8"), path).toContain("TicketQueueBanner"); } }); + + it("ships items_base browser", () => { + expect(existsSync("src/app/admin/items/page.tsx")).toBe(true); + expect(existsSync("src/app/admin/items/[id]/page.tsx")).toBe(true); + expect(readFileSync("src/actions/items-base.ts", "utf8")).toContain( + "updateItemsBase", + ); + expect(readFileSync("src/lib/admin-nav.ts", "utf8")).toContain( + "/admin/items", + ); + }); }); diff --git a/src/messages/bg.json b/src/messages/bg.json index 754fc6c9..84576ad0 100644 --- a/src/messages/bg.json +++ b/src/messages/bg.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Табло за управление", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/cs.json b/src/messages/cs.json index 19a8eae3..eec8a426 100644 --- a/src/messages/cs.json +++ b/src/messages/cs.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Dashboard", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/da.json b/src/messages/da.json index 3c02b804..8039c1c5 100644 --- a/src/messages/da.json +++ b/src/messages/da.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Dashboard", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/de.json b/src/messages/de.json index 49b2ea0b..24e76de9 100644 --- a/src/messages/de.json +++ b/src/messages/de.json @@ -879,7 +879,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Dashboard", @@ -2835,7 +2836,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "radioRequests": { diff --git a/src/messages/el.json b/src/messages/el.json index f35cb140..79c23041 100644 --- a/src/messages/el.json +++ b/src/messages/el.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Ταμπλό", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/en.json b/src/messages/en.json index 5401ee72..964c04e9 100644 --- a/src/messages/en.json +++ b/src/messages/en.json @@ -1004,6 +1004,7 @@ "hubCatalog": "Catalog & rares", "hubShop": "Shop & payments", "catalog": "Catalog", + "itemsBase": "Items base", "shop": "Shop", "marketplace": "Marketplace", "transactions": "Transactions", @@ -1662,6 +1663,38 @@ "cancel": "Cancel" } }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, "catalog": { "title": "Catalog", "subtitle": "Browse and manage catalog pages and items", @@ -1805,6 +1838,7 @@ "title": "Guilds", "subtitle": "Hotel groups and membership", "totalGuilds": "Guilds", + "listHeading": "Guilds ({count})", "searchPlaceholder": "Search by name, description or ID…", "search": "Search", "empty": "No guilds found.", diff --git a/src/messages/es.json b/src/messages/es.json index 06668011..07e5b748 100644 --- a/src/messages/es.json +++ b/src/messages/es.json @@ -879,7 +879,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Panel", @@ -2835,7 +2836,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "radioRequests": { diff --git a/src/messages/fr.json b/src/messages/fr.json index dadc4a70..49bf16c4 100644 --- a/src/messages/fr.json +++ b/src/messages/fr.json @@ -879,7 +879,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Tableau de bord", @@ -2835,7 +2836,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "radioRequests": { diff --git a/src/messages/hr.json b/src/messages/hr.json index df35eb95..cacb1bad 100644 --- a/src/messages/hr.json +++ b/src/messages/hr.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Nadzorna ploča", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/hu.json b/src/messages/hu.json index a6da3bb1..a4fd8cbf 100644 --- a/src/messages/hu.json +++ b/src/messages/hu.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Irányítópult", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/it.json b/src/messages/it.json index 079352de..7c15d3ca 100644 --- a/src/messages/it.json +++ b/src/messages/it.json @@ -1032,7 +1032,8 @@ "commandLog": "Log comandi", "tradeLog": "Log trade", "tools": "Strumenti", - "guilds": "Gruppi" + "guilds": "Gruppi", + "itemsBase": "Items base" }, "dashboard": { "title": "Dashboard", @@ -1799,7 +1800,8 @@ "noMembers": "Nessun membro.", "back": "Torna ai gruppi", "viewPublic": "Apri pagina pubblica", - "disband": "Sciogli gruppo" + "disband": "Sciogli gruppo", + "listHeading": "Guilds ({count})" }, "transactions": { "title": "Transazioni", @@ -2989,6 +2991,38 @@ "less": "Meno", "more": "Più" } + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "radioRequests": { diff --git a/src/messages/nl.json b/src/messages/nl.json index 8c1768be..4a4dfcd1 100644 --- a/src/messages/nl.json +++ b/src/messages/nl.json @@ -1062,7 +1062,8 @@ "chatLog": "Chatlog", "commandLog": "Commandolog", "tradeLog": "Tradelog", - "tools": "Tools" + "tools": "Tools", + "itemsBase": "Items base" }, "dashboard": { "title": "Dashboard", @@ -1751,7 +1752,8 @@ "noMembers": "Geen leden.", "back": "Terug naar gildes", "viewPublic": "Openbare pagina", - "disband": "Gilde ontbinden" + "disband": "Gilde ontbinden", + "listHeading": "Guilds ({count})" }, "transactions": { "title": "Transacties", @@ -3017,6 +3019,38 @@ "less": "Minder", "more": "Meer" } + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "mod": { diff --git a/src/messages/no.json b/src/messages/no.json index a489ca4d..a23f50ce 100644 --- a/src/messages/no.json +++ b/src/messages/no.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Dashbord", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/pl.json b/src/messages/pl.json index 4d7fdf0d..fee62c8d 100644 --- a/src/messages/pl.json +++ b/src/messages/pl.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Pulpit nawigacyjny", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/pt.json b/src/messages/pt.json index c461b6bb..22558d8f 100644 --- a/src/messages/pt.json +++ b/src/messages/pt.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Painel", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/ro.json b/src/messages/ro.json index 82ef40bd..d9f0827d 100644 --- a/src/messages/ro.json +++ b/src/messages/ro.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Tabloul de bord", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/ru.json b/src/messages/ru.json index d561dcf9..4b73a456 100644 --- a/src/messages/ru.json +++ b/src/messages/ru.json @@ -907,7 +907,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Панель управления", @@ -2863,7 +2864,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/sk.json b/src/messages/sk.json index b0dcbdcc..bdbc9d2f 100644 --- a/src/messages/sk.json +++ b/src/messages/sk.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Dashboard", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/sr.json b/src/messages/sr.json index 1befdf44..bf300e82 100644 --- a/src/messages/sr.json +++ b/src/messages/sr.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Дасхбоард", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/sv.json b/src/messages/sv.json index 0bfceda0..566cbbed 100644 --- a/src/messages/sv.json +++ b/src/messages/sv.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Instrumentbräda", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/tr.json b/src/messages/tr.json index 1222b806..0c3697c2 100644 --- a/src/messages/tr.json +++ b/src/messages/tr.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Kontrol Paneli", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": { diff --git a/src/messages/uk.json b/src/messages/uk.json index 4b75d047..5c5eb001 100644 --- a/src/messages/uk.json +++ b/src/messages/uk.json @@ -909,7 +909,8 @@ "commandLog": "Command log", "tradeLog": "Trade log", "tools": "Tools", - "guilds": "Guilds" + "guilds": "Guilds", + "itemsBase": "Items base" }, "dashboard": { "title": "Приладова панель", @@ -2865,7 +2866,40 @@ "noMembers": "No members.", "back": "Back to guilds", "viewPublic": "Open public page", - "disband": "Disband guild" + "disband": "Disband guild", + "listHeading": "Guilds ({count})" + }, + "items": { + "title": "Items base", + "subtitle": "Browse and edit furni definitions in items_base (standalone from catalog pages)", + "total": "{count, plural, one {# item} other {# items}}", + "editTitle": "Edit {name}", + "editSubtitle": "items_base #{id}", + "back": "Back to list", + "save": "Save", + "saved": "Item saved", + "untitled": "Untitled item", + "sectionIdentity": "Identity", + "sectionDims": "Dimensions & interaction", + "sectionFlags": "Allow flags", + "sectionExtra": "Extra", + "publicName": "Public name", + "itemName": "Classname", + "type": "Type", + "spriteId": "Sprite ID", + "width": "Width", + "length": "Length", + "stackHeight": "Stack height", + "interactionType": "Interaction", + "interactionModes": "Modes", + "vendingIds": "Vending IDs", + "multiheight": "Multiheight", + "customparams": "Custom params" + }, + "online": { + "title": "Online users", + "subtitle": "Players currently connected", + "connected": "{count, plural, one {# player connected} other {# players connected}}" } }, "error": {