From 3ad3f9512cd75e4c5dd89c075ed21d1cf48a2c05 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Thu, 30 Jul 2026 20:15:07 +0200 Subject: [PATCH] feat(admin): ban appeals, photos polish, economy adjust, staff smoke Co-authored-by: Cursor --- src/actions/admin-help-tickets.ts | 47 ++++ src/actions/admin-photos.ts | 31 ++- src/actions/bulk-users.ts | 136 ++++++++++++ src/actions/help-tickets.ts | 51 ++++- src/app/(site)/help/tickets/page.tsx | 11 + src/app/admin/bans/page.tsx | 208 +++++++++--------- .../[id]/admin-help-ticket-detail.tsx | 40 +++- src/app/admin/help-tickets/[id]/page.tsx | 28 ++- src/app/admin/photos/page.tsx | 158 ++++++++----- .../admin/users/[id]/user-quick-actions.tsx | 35 +++ src/app/admin/users/page.tsx | 24 +- src/app/admin/users/show/[id]/page.tsx | 14 +- src/app/admin/users/users-table.tsx | 22 +- src/lib/staff-smoke-contract.test.ts | 85 +++++++ src/messages/bg.json | 27 ++- src/messages/cs.json | 27 ++- src/messages/da.json | 27 ++- src/messages/de.json | 27 ++- src/messages/el.json | 27 ++- src/messages/en.json | 21 +- src/messages/es.json | 27 ++- src/messages/fr.json | 27 ++- src/messages/hr.json | 27 ++- src/messages/hu.json | 27 ++- src/messages/it.json | 27 ++- src/messages/nl.json | 27 ++- src/messages/no.json | 27 ++- src/messages/pl.json | 27 ++- src/messages/pt.json | 27 ++- src/messages/ro.json | 27 ++- src/messages/ru.json | 27 ++- src/messages/sk.json | 27 ++- src/messages/sr.json | 27 ++- src/messages/sv.json | 27 ++- src/messages/tr.json | 27 ++- src/messages/uk.json | 27 ++- 36 files changed, 1185 insertions(+), 293 deletions(-) create mode 100644 src/lib/staff-smoke-contract.test.ts diff --git a/src/actions/admin-help-tickets.ts b/src/actions/admin-help-tickets.ts index 5973520a..43bc3e26 100644 --- a/src/actions/admin-help-tickets.ts +++ b/src/actions/admin-help-tickets.ts @@ -31,6 +31,53 @@ function revalidateHelpCenterTicketPaths(ticketId: bigint) { revalidatePath(`/help/tickets/${id}`); } +export const liftBanFromHelpTicket = adminAction( + { + permission: PERMS.USERS_BAN, + schema: helpCenterTicketIdSchema, + }, + async (ctx) => { + const ticketId = ctx.data.ticketId; + const ticket = await prisma.websiteHelpCenterTickets.findUnique({ + where: { id: ticketId }, + select: { id: true, userId: true, open: true, title: true }, + }); + if (!ticket) throw new ActionError("Ticket not found"); + if (ticket.userId == null) { + throw new ActionError("Ticket has no requester to unban"); + } + + const removed = await prisma.ban.deleteMany({ + where: { userId: ticket.userId }, + }); + + const now = new Date(); + if (ticket.open) { + await prisma.websiteHelpCenterTickets.update({ + where: { id: ticketId }, + data: { open: false, updatedAt: now }, + }); + } + + logAudit({ + userId: ctx.session.user.id, + action: "unban_via_help_ticket", + target: "User", + targetId: ticket.userId, + after: { + ticketId: String(ticketId), + removedBans: removed.count, + title: ticket.title, + }, + }); + + revalidateHelpCenterTicketPaths(ticketId); + revalidatePath("/admin/bans"); + revalidatePath(`/admin/users/show/${ticket.userId}`); + return actionOk({ removed: removed.count, userId: ticket.userId }); + }, +); + const HELP_TICKET_EDIT = [PERMS.TICKETS_EDIT, PERMS.MOD_TICKETS_EDIT] as const; export const replyHelpCenterTicket = adminAction( diff --git a/src/actions/admin-photos.ts b/src/actions/admin-photos.ts index 1b7e5773..da1d8b19 100644 --- a/src/actions/admin-photos.ts +++ b/src/actions/admin-photos.ts @@ -4,17 +4,46 @@ import { revalidatePath } from "next/cache"; import { requirePermission } from "@/lib/admin/guard"; import { PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; +import { logStaffActivity } from "@/lib/services/staff-activity"; export async function deletePhoto(formData: FormData): Promise { - await requirePermission(PERMS.PAGES_EDIT); + const staff = await requirePermission(PERMS.PAGES_EDIT); const id = Number(formData.get("id")); if (!(id > 0)) return; try { await prisma.cameraWeb.delete({ where: { id } }); + await logStaffActivity({ + staffId: staff.id, + action: "photo_delete", + description: `Deleted camera photo #${id}`, + targetType: "camera_web", + targetId: id, + }); } catch { // Record may have already been removed; ignore. } revalidatePath("/admin/photos"); } + +export async function bulkDeletePhotos(formData: FormData): Promise { + const staff = await requirePermission(PERMS.PAGES_EDIT); + const raw = String(formData.get("ids") ?? ""); + const ids = raw + .split(",") + .map((s) => Number(s.trim())) + .filter((n) => Number.isFinite(n) && n > 0); + if (ids.length === 0) return; + + const result = await prisma.cameraWeb.deleteMany({ + where: { id: { in: ids } }, + }); + await logStaffActivity({ + staffId: staff.id, + action: "photo_bulk_delete", + description: `Deleted ${result.count} camera photo(s)`, + targetType: "camera_web", + }); + revalidatePath("/admin/photos"); +} diff --git a/src/actions/bulk-users.ts b/src/actions/bulk-users.ts index a6c99215..d03bf746 100644 --- a/src/actions/bulk-users.ts +++ b/src/actions/bulk-users.ts @@ -181,3 +181,139 @@ export async function bulkGiveBadge({ data: { given, total: userIds.length, failedIds }, }; } + +export async function bulkAdjustCurrency({ + userIds, + amount, + type, +}: { + userIds: number[]; + /** Positive = give, negative = take. Balances clamped at 0. */ + amount: number; + type: "credits" | "pixels" | "points"; +}): Promise< + ActionResult<{ + adjusted: number; + total: number; + failedIds: Array<{ userId: number; reason: string }>; + }> +> { + const staff = await requirePermission(PERMS.USERS_EDIT); + if (!Number.isFinite(amount) || amount === 0) { + return { ok: false as const, error: "Amount must be a non-zero number" }; + } + + if (amount > 0) { + const given = await bulkGiveCurrency({ userIds, amount, type }); + if (!given.ok) return given; + if (!given.data) { + return { ok: false as const, error: "Currency adjustment failed" }; + } + return { + ok: true as const, + data: { + adjusted: given.data.given, + total: given.data.total, + failedIds: given.data.failedIds, + }, + }; + } + + const take = Math.abs(Math.trunc(amount)); + let adjusted = 0; + const failedIds: Array<{ userId: number; reason: string }> = []; + + for (const userId of userIds) { + try { + if (type === "credits") { + const user = await prisma.user.findUnique({ + where: { id: userId }, + select: { credits: true }, + }); + if (!user) { + failedIds.push({ userId, reason: "Not found" }); + continue; + } + const next = Math.max(0, user.credits - take); + await prisma.user.update({ + where: { id: userId }, + data: { credits: next }, + }); + } else { + const currencyType = type === "pixels" ? 0 : 101; + const row = await prisma.usersCurrency.findUnique({ + where: { userId_type: { userId, type: currencyType } }, + }); + const current = row?.amount ?? 0; + const next = Math.max(0, current - take); + await prisma.usersCurrency.upsert({ + where: { userId_type: { userId, type: currencyType } }, + update: { amount: next }, + create: { userId, type: currencyType, amount: next }, + }); + } + adjusted++; + } catch { + failedIds.push({ userId, reason: "Database error" }); + } + } + + await logStaffActivity({ + staffId: staff.id, + action: "bulk_adjust_currency", + description: `Adjusted ${amount} ${type} for ${adjusted} user(s) (DB-only take; no RCON debit)`, + targetType: "user", + }); + return { + ok: true as const, + data: { adjusted, total: userIds.length, failedIds }, + }; +} + +/** + * Persist trade lock on `sanctions.trade_locked_until`. + * No first-class RCON trade-lock helper in this CMS — DB only. + */ +export async function setTradeLock({ + userId, + untilUnix, +}: { + userId: number; + /** Unix seconds; 0 clears the lock. */ + untilUnix: number; +}): Promise> { + const staff = await requirePermission(PERMS.USERS_EDIT); + const until = Math.max(0, Math.trunc(untilUnix)); + + const existing = await prisma.sanctions.findFirst({ + where: { habboId: userId }, + select: { id: true }, + }); + if (existing) { + await prisma.sanctions.update({ + where: { id: existing.id }, + data: { tradeLockedUntil: until }, + }); + } else { + await prisma.sanctions.create({ + data: { + habboId: userId, + tradeLockedUntil: until, + reason: until > 0 ? "Trade lock (CMS)" : "", + }, + }); + } + + await logStaffActivity({ + staffId: staff.id, + action: until > 0 ? "trade_lock" : "trade_unlock", + description: + until > 0 + ? `Trade-locked user #${userId} until ${until}` + : `Cleared trade lock for user #${userId}`, + targetType: "user", + targetId: userId, + }); + + return { ok: true as const, data: { userId, untilUnix: until } }; +} diff --git a/src/actions/help-tickets.ts b/src/actions/help-tickets.ts index 0a09444d..25968a85 100644 --- a/src/actions/help-tickets.ts +++ b/src/actions/help-tickets.ts @@ -99,12 +99,61 @@ export async function createTicket(formData: FormData): Promise { } if (!moderated) { + const banAppeal = + String(formData.get("banAppeal") ?? "") === "1" || + String(formData.get("banAppeal") ?? "") === "on"; + let ticketTitle = title; + let categoryId: bigint | null = null; + + if (banAppeal) { + if (!/ban\s*appeal/i.test(ticketTitle)) { + ticketTitle = `[Ban appeal] ${ticketTitle}`.slice(0, 255); + } + const existing = await prisma.websiteHelpCenterCategories.findFirst( + { + where: { name: { equals: "Ban appeal" } }, + select: { id: true }, + }, + ); + if (existing) { + categoryId = existing.id; + } else { + try { + const created = await prisma.websiteHelpCenterCategories.create( + { + data: { + name: "Ban appeal", + content: + "Appeals for account bans. Staff can lift bans from the ticket.", + position: 0, + }, + select: { id: true }, + }, + ); + categoryId = created.id; + } catch { + const again = + await prisma.websiteHelpCenterCategories.findFirst({ + where: { name: { equals: "Ban appeal" } }, + select: { id: true }, + }); + categoryId = again?.id ?? null; + } + } + } else { + const rawCat = String(formData.get("categoryId") ?? "").trim(); + if (/^\d+$/.test(rawCat)) { + categoryId = BigInt(rawCat); + } + } + const now = new Date(); await prisma.websiteHelpCenterTickets.create({ data: { userId, - title, + title: ticketTitle, content, + categoryId, open: true, createdAt: now, updatedAt: now, diff --git a/src/app/(site)/help/tickets/page.tsx b/src/app/(site)/help/tickets/page.tsx index a78496bd..62cd436a 100644 --- a/src/app/(site)/help/tickets/page.tsx +++ b/src/app/(site)/help/tickets/page.tsx @@ -107,6 +107,17 @@ export default async function HelpTicketsPage({ maxLength={5000} required /> +
+
- -
-

- {t("activeBans")} ({bans.length}) -

- {bans.length === 0 ? ( -
{t("noBans")}
- ) : ( -
- - - - - - - - - - - {bans.map((b) => ( - - - - - - - - ))} - -
{t("colUserId")}{t("colType")}{t("colReason")}{t("colExpires")} -
{b.userId} - - {b.type} - - {b.banReason || "—"} - {b.banExpire === 0 ? ( - - {t("permanent")} - - ) : ( - - {formatDate(new Date(b.banExpire * 1000))} - - )} - -
- - -
-
+
+

+ {t("newBan")} +

+
+ + + + +
- )} -
- + + +
+

+ {t("activeBans")} ({bans.length}) +

+ {bans.length === 0 ? ( +
{t("noBans")}
+ ) : ( +
+ + + + + + + + + + + {bans.map((b) => ( + + + + + + + + ))} + +
{t("colUserId")}{t("colType")}{t("colReason")}{t("colExpires")} +
{b.userId} + + {b.type} + + {b.banReason || "—"} + {b.banExpire === 0 ? ( + + {t("permanent")} + + ) : ( + + {formatDate(new Date(b.banExpire * 1000))} + + )} + +
+ + +
+
+
+ )} +
+ + ); } diff --git a/src/app/admin/help-tickets/[id]/admin-help-ticket-detail.tsx b/src/app/admin/help-tickets/[id]/admin-help-ticket-detail.tsx index 85e9f91a..ea4ed7bb 100644 --- a/src/app/admin/help-tickets/[id]/admin-help-ticket-detail.tsx +++ b/src/app/admin/help-tickets/[id]/admin-help-ticket-detail.tsx @@ -6,6 +6,7 @@ import { useTranslations } from "next-intl"; import { useEffect, useRef, useState } from "react"; import { closeHelpCenterTicket, + liftBanFromHelpTicket, reopenHelpCenterTicket, replyHelpCenterTicket, } from "@/actions/admin-help-tickets"; @@ -30,6 +31,7 @@ interface HelpTicketInfo { open: boolean; createdAt: string; updatedAt: string; + categoryName?: string | null; creator: { id: number; username: string; @@ -42,12 +44,17 @@ export function AdminHelpTicketDetail({ ticket, messages: initialMessages, canEdit, + canLiftBan = false, + hasActiveBan = false, listHref = "/admin/help-tickets", hideRequesterContact = false, }: { ticket: HelpTicketInfo; messages: ThreadMessage[]; canEdit: boolean; + /** Staff may lift requester bans (USERS_BAN). */ + canLiftBan?: boolean; + hasActiveBan?: boolean; listHref?: string; /** Hide email/IP-style contact for mod-lite surfaces. */ hideRequesterContact?: boolean; @@ -94,6 +101,13 @@ export function AdminHelpTicketDetail({ }); } + function handleLiftBan() { + if (!ticket.creator || isPending) return; + run(() => liftBanFromHelpTicket({ ticketId: ticket.id }), { + successMessage: t("success.banLifted"), + }); + } + return (
@@ -110,6 +124,12 @@ export function AdminHelpTicketDetail({ {ticket.open ? t("statusOpen") : t("statusClosed")} + {ticket.categoryName ? ( + {ticket.categoryName} + ) : null} + {hasActiveBan ? ( + {t("requesterBanned")} + ) : null}

{ticket.title}

@@ -208,13 +228,24 @@ export function AdminHelpTicketDetail({
- {canEdit && ( + {(canEdit || (canLiftBan && hasActiveBan)) && ( {t("actionsTitle")} - {ticket.open ? ( + {canLiftBan && hasActiveBan && ticket.creator ? ( + + ) : null} + {canEdit && ticket.open ? ( - ) : ( + ) : null} + {canEdit && !ticket.open ? ( - )} + ) : null} )} diff --git a/src/app/admin/help-tickets/[id]/page.tsx b/src/app/admin/help-tickets/[id]/page.tsx index a15bd33a..4a386b1f 100644 --- a/src/app/admin/help-tickets/[id]/page.tsx +++ b/src/app/admin/help-tickets/[id]/page.tsx @@ -3,6 +3,7 @@ import { getTranslations } from "next-intl/server"; import { TicketQueueBanner } from "@/components/admin/ticket-queue-banner"; import { getMinStaffRank } from "@/lib/admin/min-staff-rank"; import { positiveBigInt } from "@/lib/api"; +import { activeBanWhere } from "@/lib/bans"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; import { AdminHelpTicketDetail } from "./admin-help-ticket-detail"; @@ -29,6 +30,7 @@ export default async function AdminHelpTicketDetailPage({ select: { id: true, userId: true, + categoryId: true, title: true, content: true, open: true, @@ -57,9 +59,9 @@ export default async function AdminHelpTicketDetailPage({ ]), ]; - const users = + const [users, category, activeBan] = await Promise.all([ authorIds.length > 0 - ? await prisma.user.findMany({ + ? prisma.user.findMany({ where: { id: { in: authorIds } }, select: { id: true, @@ -68,7 +70,20 @@ export default async function AdminHelpTicketDetailPage({ mail: true, }, }) - : []; + : Promise.resolve([]), + ticket.categoryId != null + ? prisma.websiteHelpCenterCategories.findUnique({ + where: { id: ticket.categoryId }, + select: { name: true }, + }) + : Promise.resolve(null), + ticket.userId != null + ? prisma.ban.findFirst({ + where: { userId: ticket.userId, ...activeBanWhere() }, + select: { id: true }, + }) + : Promise.resolve(null), + ]); const userById = new Map(users.map((u) => [u.id, u])); const openerId = ticket.userId; @@ -104,6 +119,10 @@ export default async function AdminHelpTicketDetailPage({ const creator = openerId != null ? userById.get(openerId) : undefined; const canEdit = canAccess(permissions, PERMS.TICKETS_EDIT, session.user.rank); + const canLiftBan = canAccess(permissions, PERMS.USERS_BAN, session.user.rank); + const isBanAppeal = + /ban\s*appeal/i.test(ticket.title) || + /ban\s*appeal/i.test(category?.name ?? ""); return (
@@ -119,6 +138,7 @@ export default async function AdminHelpTicketDetailPage({ open: ticket.open, createdAt: (ticket.createdAt ?? new Date()).toISOString(), updatedAt: (ticket.updatedAt ?? new Date()).toISOString(), + categoryName: category?.name ?? (isBanAppeal ? "Ban appeal" : null), creator: creator ? { id: creator.id, @@ -130,6 +150,8 @@ export default async function AdminHelpTicketDetailPage({ }} messages={messages} canEdit={canEdit} + canLiftBan={canLiftBan} + hasActiveBan={!!activeBan} />
); diff --git a/src/app/admin/photos/page.tsx b/src/app/admin/photos/page.tsx index d8eee3e5..d41ae904 100644 --- a/src/app/admin/photos/page.tsx +++ b/src/app/admin/photos/page.tsx @@ -1,35 +1,62 @@ +import { Camera } from "lucide-react"; +import Link from "next/link"; import { redirect } from "next/navigation"; import { getTranslations } from "next-intl/server"; import { deletePhoto } from "@/actions/admin-photos"; +import { AdminPageShell } from "@/components/admin/admin-page-shell"; import { StatusCard } from "@/components/admin/dashboard"; import { Button } from "@/components/ui/button"; +import { calcPagination, parseListParams } from "@/lib/admin-helpers"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { prisma } from "@/lib/prisma"; export const dynamic = "force-dynamic"; -export default async function AdminPhotos() { +export default async function AdminPhotos({ + searchParams, +}: { + searchParams: Promise>; +}) { const { session, permissions } = await getAdminContext(); if (!canAccess(permissions, PERMS.PAGES_VIEW, session.user.rank)) { redirect("/admin"); } const t = await getTranslations("pages.admin.photos"); + const parsed = parseListParams(new URLSearchParams(await searchParams)); + + const total = await prisma.cameraWeb.count().catch(() => 0); + const pagination = calcPagination(total, parsed.page, parsed.perPage || 24); + let photos: Awaited> = []; try { photos = await prisma.cameraWeb.findMany({ orderBy: { timestamp: "desc" }, - take: 60, + skip: pagination.offset, + take: pagination.perPage, }); } catch { photos = []; } + const userIds = [...new Set(photos.map((p) => p.userId))]; + const users = + userIds.length > 0 + ? await prisma.user + .findMany({ + where: { id: { in: userIds } }, + select: { id: true, username: true }, + }) + .catch(() => []) + : []; + const usernameById = new Map(users.map((u) => [u.id, u.username])); + const uniqueRooms = new Set(photos.map((p) => p.roomId)).size; const uniqueUsers = new Set(photos.map((p) => p.userId)).size; + const canEdit = canAccess(permissions, PERMS.PAGES_EDIT, session.user.rank); return ( -
+
+
-
+

{t("recentPhotos")}

{photos.length === 0 ? (
{t("noPhotos")}
) : (
- {photos.map((p) => ( -
- {/* eslint-disable-next-line @next/next/no-img-element */} - {t("photoAlt", -
-
{ + const username = usernameById.get(p.userId) ?? `#${p.userId}`; + return ( +
+ {/* eslint-disable-next-line @next/next/no-img-element */} + {t("photoAlt", - #{String(p.id)} - - {t("colRoom")} {p.roomId} - + /> +
+
+ #{String(p.id)} + + {t("colRoom")} {p.roomId} + +
+

+ + {t("byUser", { username })} + {" "} + ·{" "} + {new Date(p.timestamp * 1000) + .toISOString() + .slice(0, 16) + .replace("T", " ")} +

+ {canEdit ? ( +
+ + +
+ ) : null}
-

- {t("colUser")} {p.userId} ·{" "} - {new Date(p.timestamp * 1000) - .toISOString() - .slice(0, 16) - .replace("T", " ")} -

-
- - -
-
- ))} + ); + })}
)} + {pagination.lastPage > 1 ? ( +

+ {t("pageOf", { + page: pagination.page, + last: pagination.lastPage, + total, + })} +

+ ) : null}
-
+ ); } diff --git a/src/app/admin/users/[id]/user-quick-actions.tsx b/src/app/admin/users/[id]/user-quick-actions.tsx index 38eaffac..2a8b6bd7 100644 --- a/src/app/admin/users/[id]/user-quick-actions.tsx +++ b/src/app/admin/users/[id]/user-quick-actions.tsx @@ -10,6 +10,7 @@ import { VolumeX, } from "lucide-react"; import { useState } from "react"; +import { setTradeLock } from "@/actions/bulk-users"; import { alertUser, disconnectUser, @@ -234,6 +235,40 @@ export function UserQuickActions({
+ + {/* Trade lock (DB only — no RCON helper) */} +
+ +
+ + +
+
diff --git a/src/app/admin/users/page.tsx b/src/app/admin/users/page.tsx index 40a18baa..5387734a 100644 --- a/src/app/admin/users/page.tsx +++ b/src/app/admin/users/page.tsx @@ -1,3 +1,6 @@ +import { Users } 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 { CreateUserDialog } from "./_components/create-user-dialog"; @@ -43,17 +46,22 @@ export default async function UsersPage({ searchParams, ); + const t = await getTranslations("pages.admin.users"); const canEdit = canAccess(result.permissions, PERMS.USERS_EDIT, result.rank); const canBan = canAccess(result.permissions, PERMS.USERS_BAN, result.rank); return ( -
-
-

- {result.total.toLocaleString()} users total -

- {canEdit && } -
+ : undefined + } + > +

+ {result.total.toLocaleString()} users total +

-
+ ); } diff --git a/src/app/admin/users/show/[id]/page.tsx b/src/app/admin/users/show/[id]/page.tsx index c90cf581..bf525622 100644 --- a/src/app/admin/users/show/[id]/page.tsx +++ b/src/app/admin/users/show/[id]/page.tsx @@ -1,4 +1,5 @@ import { redirect } from "next/navigation"; +import { getTranslations } from "next-intl/server"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { canAccess, getAdminContext, PERMS } from "@/lib/permissions"; import { getWatchedUserIds } from "@/lib/services/watch"; @@ -23,6 +24,7 @@ export default async function UserShowPage({ redirect("/admin"); } + const t = await getTranslations("pages.admin.users.detail"); const canEdit = canAccess(permissions, PERMS.USERS_EDIT, session.user.rank); const data = await loadUserById(id); const sanctions = await loadUserSanctions(data.user.id); @@ -53,15 +55,17 @@ export default async function UserShowPage({ > - Account Info + {t("tabInfo")} - Badges ({data.user.badges.length}) + {t("tabBadges", { count: data.user.badges.length })} + + + {t("tabRooms", { count: data.roomCount })} - Rooms ({data.roomCount}) - Sanctions ({sanctions.events.length}) + {t("tabSanctions", { count: sanctions.events.length })} - Activity + {t("tabActivity")} diff --git a/src/app/admin/users/users-table.tsx b/src/app/admin/users/users-table.tsx index a595d79e..b161d894 100644 --- a/src/app/admin/users/users-table.tsx +++ b/src/app/admin/users/users-table.tsx @@ -5,9 +5,9 @@ import Link from "next/link"; import { useState } from "react"; import { toast } from "sonner"; import { + bulkAdjustCurrency, bulkBan, bulkGiveBadge, - bulkGiveCurrency, bulkUnban, } from "@/actions/bulk-users"; import { DataTable } from "@/components/admin/data-table"; @@ -283,20 +283,20 @@ export function UsersTable({ data, canEdit, canBan }: UsersTableProps) { if (pending?.kind !== "give-currency") return; setBusy(true); try { - const result = await bulkGiveCurrency({ + const result = await bulkAdjustCurrency({ userIds: pending.ids, amount: Number(currencyAmount), type: currencyType, }); if (result.ok) { const data = result.data as { - given: number; + adjusted: number; total: number; failedIds: { userId: number; reason: string }[]; }; reportResult( - `Given ${currencyAmount} ${currencyType}`, - data.given, + `Adjusted ${currencyAmount} ${currencyType}`, + data.adjusted, data.total, data.failedIds.length, ); @@ -344,7 +344,7 @@ export function UsersTable({ data, canEdit, canBan }: UsersTableProps) { bulkActions.push({ label: "Unban", value: "unban" }); } if (canEdit) { - bulkActions.push({ label: "Give credits", value: "give-currency" }); + bulkActions.push({ label: "Adjust currency", value: "give-currency" }); bulkActions.push({ label: "Give badge", value: "give-badge" }); } @@ -424,7 +424,7 @@ export function UsersTable({ data, canEdit, canBan }: UsersTableProps) { - Give currency to{" "} + Adjust currency for{" "} {pending?.kind === "give-currency" ? pending.ids.length : 0} users @@ -459,7 +459,9 @@ export function UsersTable({ data, canEdit, canBan }: UsersTableProps) {
- + diff --git a/src/lib/staff-smoke-contract.test.ts b/src/lib/staff-smoke-contract.test.ts new file mode 100644 index 00000000..482cb6a9 --- /dev/null +++ b/src/lib/staff-smoke-contract.test.ts @@ -0,0 +1,85 @@ +import { existsSync, readFileSync } from "node:fs"; +import { describe, expect, it } from "vitest"; + +describe("staff smoke contract", () => { + it("gates photos delete/bulk with PAGES_EDIT", () => { + const src = readFileSync("src/actions/admin-photos.ts", "utf8"); + expect(src).toContain("PERMS.PAGES_EDIT"); + expect(src).toContain("deletePhoto"); + expect(src).toContain("bulkDeletePhotos"); + expect(src).toContain("logStaffActivity"); + expect(existsSync("src/app/admin/photos/page.tsx")).toBe(true); + expect(readFileSync("src/app/admin/photos/page.tsx", "utf8")).toContain( + "AdminPageShell", + ); + }); + + it("gates bans create/lift", () => { + expect(existsSync("src/app/admin/bans/page.tsx")).toBe(true); + const bans = readFileSync("src/actions/admin-bans.ts", "utf8"); + expect(bans).toContain("liftBan"); + expect(bans).toContain("createBan"); + expect(readFileSync("src/app/admin/bans/page.tsx", "utf8")).toContain( + "AdminPageShell", + ); + }); + + it("wires ban appeal lift on help tickets", () => { + const actions = readFileSync("src/actions/admin-help-tickets.ts", "utf8"); + expect(actions).toContain("liftBanFromHelpTicket"); + expect(actions).toContain("PERMS.USERS_BAN"); + const detail = readFileSync( + "src/app/admin/help-tickets/[id]/admin-help-ticket-detail.tsx", + "utf8", + ); + expect(detail).toContain("liftBanFromHelpTicket"); + expect(detail).toContain("canLiftBan"); + }); + + it("exposes sanction timeline on user show", () => { + expect( + existsSync("src/app/admin/users/_components/user-sanction-timeline.tsx"), + ).toBe(true); + expect(existsSync("src/app/admin/users/_lib/load-user-sanctions.ts")).toBe( + true, + ); + expect( + readFileSync("src/app/admin/users/show/[id]/page.tsx", "utf8"), + ).toContain("UserSanctionTimeline"); + }); + + it("ships shared ops + ticket queue helpers", () => { + expect(existsSync("src/lib/admin/ops-health.ts")).toBe(true); + expect(existsSync("src/lib/admin/ticket-queue-counts.ts")).toBe(true); + expect(existsSync("src/lib/admin/ops-online-users.ts")).toBe(true); + }); + + it("caches analytics via redisCache", () => { + for (const path of [ + "src/app/admin/analytics/page.tsx", + "src/app/admin/analytics/activity/page.tsx", + "src/app/admin/analytics/economy/page.tsx", + ]) { + expect(readFileSync(path, "utf8"), path).toContain("redisCache"); + } + }); + + it("exports bulk adjust currency and trade lock", () => { + const src = readFileSync("src/actions/bulk-users.ts", "utf8"); + expect(src).toContain("bulkAdjustCurrency"); + expect(src).toContain("setTradeLock"); + expect(src).toContain("tradeLockedUntil"); + }); + + it("guards dual ticket queues on admin and mod", () => { + for (const path of [ + "src/app/admin/tickets/page.tsx", + "src/app/admin/help-tickets/page.tsx", + "src/app/mod/tickets/page.tsx", + "src/app/mod/help-tickets/page.tsx", + ]) { + expect(existsSync(path), path).toBe(true); + expect(readFileSync(path, "utf8"), path).toContain("TicketQueueBanner"); + } + }); +}); diff --git a/src/messages/bg.json b/src/messages/bg.json index 5c83353e..754fc6c9 100644 --- a/src/messages/bg.json +++ b/src/messages/bg.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Кандидатствайте за персонал на {hotel}", @@ -1111,7 +1112,15 @@ "colTimestamp": "Времево клеймо", "colActions": "Действия", "photoAlt": "Снимка на потребител #{id}", - "byUser": "от {username}" + "byUser": "от {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Потребители", @@ -1158,7 +1167,12 @@ "banUser": "Бан потребител", "resetPassword": "Нулирайте паролата", "deleteUser": "Изтриване на потребител", - "confirmDelete": "Сигурни ли сте, че искате да изтриете {username}? Това не може да бъде отменено." + "confirmDelete": "Сигурни ли сте, че искате да изтриете {username}? Това не може да бъде отменено.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Редактиране на потребител: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/cs.json b/src/messages/cs.json index 0d0529cb..19a8eae3 100644 --- a/src/messages/cs.json +++ b/src/messages/cs.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Požádejte o zaměstnance {hotel}", @@ -1111,7 +1112,15 @@ "colTimestamp": "Časové razítko", "colActions": "Akce", "photoAlt": "Uživatelská fotka č. {id}", - "byUser": "od {username}" + "byUser": "od {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Uživatelé", @@ -1158,7 +1167,12 @@ "banUser": "Zakázat uživatele", "resetPassword": "Obnovit heslo", "deleteUser": "Smazat uživatele", - "confirmDelete": "Opravdu chcete smazat uživatele {username}? Toto nelze vrátit zpět." + "confirmDelete": "Opravdu chcete smazat uživatele {username}? Toto nelze vrátit zpět.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Upravit uživatele: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/da.json b/src/messages/da.json index b5ba343a..3c02b804 100644 --- a/src/messages/da.json +++ b/src/messages/da.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Ansøg om {hotel} personale", @@ -1111,7 +1112,15 @@ "colTimestamp": "Tidsstempel", "colActions": "Handlinger", "photoAlt": "User photo #{id}", - "byUser": "by {username}" + "byUser": "by {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Brugere", @@ -1158,7 +1167,12 @@ "banUser": "Forbyd bruger", "resetPassword": "Nulstil adgangskode", "deleteUser": "Slet bruger", - "confirmDelete": "Er du sikker på, at du vil slette {username}? Dette kan ikke fortrydes." + "confirmDelete": "Er du sikker på, at du vil slette {username}? Dette kan ikke fortrydes.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Rediger bruger: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/de.json b/src/messages/de.json index e9adb9ff..49b2ea0b 100644 --- a/src/messages/de.json +++ b/src/messages/de.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Bewirb dich als {hotel} Team", @@ -1081,7 +1082,15 @@ "colTimestamp": "Zeitstempel", "colActions": "Aktionen", "photoAlt": "Benutzerfoto #{id}", - "byUser": "von {username}" + "byUser": "von {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Benutzer", @@ -1128,7 +1137,12 @@ "banUser": "Benutzer bannen", "resetPassword": "Passwort zurücksetzen", "deleteUser": "Benutzer löschen", - "confirmDelete": "Bist du sicher, dass du {username} löschen möchtest? Dies kann nicht rückgängig gemacht werden." + "confirmDelete": "Bist du sicher, dass du {username} löschen möchtest? Dies kann nicht rückgängig gemacht werden.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Benutzer bearbeiten: {username}", @@ -2695,11 +2709,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/el.json b/src/messages/el.json index 0e9381ff..f35cb140 100644 --- a/src/messages/el.json +++ b/src/messages/el.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Κάντε αίτηση για προσωπικό {hotel}", @@ -1111,7 +1112,15 @@ "colTimestamp": "Χρονική σήμανση", "colActions": "Δράσεις", "photoAlt": "Φωτογραφία χρήστη #{id}", - "byUser": "από {username}" + "byUser": "από {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Χρήστες", @@ -1158,7 +1167,12 @@ "banUser": "Απαγόρευση χρήστη", "resetPassword": "Επαναφορά κωδικού πρόσβασης", "deleteUser": "Διαγραφή χρήστη", - "confirmDelete": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το {username}; Αυτό δεν μπορεί να αναιρεθεί." + "confirmDelete": "Είστε βέβαιοι ότι θέλετε να διαγράψετε το {username}; Αυτό δεν μπορεί να αναιρεθεί.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Επεξεργασία χρήστη: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/en.json b/src/messages/en.json index 89859a82..5401ee72 100644 --- a/src/messages/en.json +++ b/src/messages/en.json @@ -539,6 +539,7 @@ "subjectPlaceholder": "Subject", "contentPlaceholder": "Describe your issue…", "submit": "Submit ticket", + "banAppealLabel": "This is a ban appeal", "listTitle": "Your tickets", "listSubtitle": "Every request you have sent to staff", "listEmpty": "You have no tickets yet.", @@ -1359,7 +1360,15 @@ "colTimestamp": "Timestamp", "colActions": "Actions", "photoAlt": "User photo #{id}", - "byUser": "by {username}" + "byUser": "by {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Users", @@ -1386,6 +1395,11 @@ "detail": { "title": "User: {username}", "subtitle": "Account details and management", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity", "overview": "Overview", "actions": "Actions", "username": "Username", @@ -1537,6 +1551,8 @@ "replyHint": "Ctrl+Enter to send.", "closeSubmit": "Close ticket", "reopenSubmit": "Reopen ticket", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned", "closedHint": "This ticket is closed.", "actionsTitle": "Actions", "requesterTitle": "Requester", @@ -1546,7 +1562,8 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." } }, "cfh": { diff --git a/src/messages/es.json b/src/messages/es.json index f65547ee..06668011 100644 --- a/src/messages/es.json +++ b/src/messages/es.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Solicitar unirte al equipo de {hotel}", @@ -1081,7 +1082,15 @@ "colTimestamp": "Fecha", "colActions": "Acciones", "photoAlt": "Foto de usuario #{id}", - "byUser": "por {username}" + "byUser": "por {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Usuarios", @@ -1128,7 +1137,12 @@ "banUser": "Sancionar usuario", "resetPassword": "Restablecer contraseña", "deleteUser": "Eliminar usuario", - "confirmDelete": "¿Estás seguro de que quieres eliminar a {username}? Esto no se puede deshacer." + "confirmDelete": "¿Estás seguro de que quieres eliminar a {username}? Esto no se puede deshacer.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Editar usuario: {username}", @@ -2695,11 +2709,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/fr.json b/src/messages/fr.json index cbe801c1..dadc4a70 100644 --- a/src/messages/fr.json +++ b/src/messages/fr.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Postuler pour {hotel}", @@ -1081,7 +1082,15 @@ "colTimestamp": "Horodatage", "colActions": "Actions", "photoAlt": "Photo utilisateur #{id}", - "byUser": "par {username}" + "byUser": "par {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Utilisateurs", @@ -1128,7 +1137,12 @@ "banUser": "Bannir l'utilisateur", "resetPassword": "Réinitialiser le mot de passe", "deleteUser": "Supprimer l'utilisateur", - "confirmDelete": "Êtes-vous sûr de vouloir supprimer {username} ? Cette action est irréversible." + "confirmDelete": "Êtes-vous sûr de vouloir supprimer {username} ? Cette action est irréversible.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Modifier l'utilisateur : {username}", @@ -2695,11 +2709,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/hr.json b/src/messages/hr.json index 25b24f2a..df35eb95 100644 --- a/src/messages/hr.json +++ b/src/messages/hr.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Prijavite se za osoblje {hotel}", @@ -1111,7 +1112,15 @@ "colTimestamp": "Vremenska oznaka", "colActions": "Radnje", "photoAlt": "Korisnička fotografija #{id}", - "byUser": "od {username}" + "byUser": "od {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Korisnici", @@ -1158,7 +1167,12 @@ "banUser": "Ban korisnika", "resetPassword": "Resetiraj lozinku", "deleteUser": "Izbriši korisnika", - "confirmDelete": "Jeste li sigurni da želite izbrisati {username}? Ovo se ne može poništiti." + "confirmDelete": "Jeste li sigurni da želite izbrisati {username}? Ovo se ne može poništiti.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Uredi korisnika: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/hu.json b/src/messages/hu.json index 069d13f4..a6da3bb1 100644 --- a/src/messages/hu.json +++ b/src/messages/hu.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Jelentkezzen a {hotel} személyzetére", @@ -1111,7 +1112,15 @@ "colTimestamp": "Időbélyeg", "colActions": "Akciók", "photoAlt": "Felhasználói fotó #{id}", - "byUser": "szerző: {username}" + "byUser": "szerző: {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Felhasználók", @@ -1158,7 +1167,12 @@ "banUser": "Felhasználó kitiltása", "resetPassword": "Jelszó visszaállítása", "deleteUser": "Felhasználó törlése", - "confirmDelete": "Biztosan törli a következőt: {username}? Ezt nem lehet visszavonni." + "confirmDelete": "Biztosan törli a következőt: {username}? Ezt nem lehet visszavonni.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Felhasználó szerkesztése: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/it.json b/src/messages/it.json index abc5f439..079352de 100644 --- a/src/messages/it.json +++ b/src/messages/it.json @@ -556,7 +556,8 @@ "moderated": "Quel ticket è stato bloccato dalla moderazione.", "ratelimit": "Stai inviando troppo in fretta. Attendi un momento e riprova.", "error": "Qualcosa è andato storto. Riprova." - } + }, + "banAppealLabel": "This is a ban appeal" }, "helpTicketDetail": { "backToList": "← I tuoi ticket", @@ -1334,7 +1335,15 @@ "colTimestamp": "Data", "colActions": "Azioni", "photoAlt": "Foto utente #{id}", - "byUser": "di {username}" + "byUser": "di {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Utenti", @@ -1381,7 +1390,12 @@ "banUser": "Banna utente", "resetPassword": "Reimposta password", "deleteUser": "Elimina utente", - "confirmDelete": "Sei sicuro di voler eliminare {username}? Questa azione è irreversibile." + "confirmDelete": "Sei sicuro di voler eliminare {username}? Questa azione è irreversibile.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Modifica utente: {username}", @@ -1518,11 +1532,14 @@ "success": { "replied": "Risposta inviata.", "closed": "Ticket chiuso.", - "reopened": "Ticket riaperto." + "reopened": "Ticket riaperto.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "Questa coda e il help center pubblico. I ticket del supporto CMS stanno nella tab Supporto CMS.", "queueOtherCta": "Apri supporto CMS", - "queueCounts": "Aperti qui: {here} · Supporto CMS: {other}" + "queueCounts": "Aperti qui: {here} · Supporto CMS: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "cfh": { "title": "Call for Help", diff --git a/src/messages/nl.json b/src/messages/nl.json index 7b11d38d..8c1768be 100644 --- a/src/messages/nl.json +++ b/src/messages/nl.json @@ -556,7 +556,8 @@ "moderated": "Dat ticket is geblokkeerd door moderatie.", "ratelimit": "Je dient te snel in. Wacht even en probeer opnieuw.", "error": "Er ging iets mis. Probeer het opnieuw." - } + }, + "banAppealLabel": "This is a ban appeal" }, "helpTicketDetail": { "backToList": "← Jouw tickets", @@ -1364,7 +1365,15 @@ "colTimestamp": "Tijdstempel", "colActions": "Acties", "photoAlt": "Gebruikersfoto #{id}", - "byUser": "door {username}" + "byUser": "door {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Gebruikers", @@ -1411,7 +1420,12 @@ "banUser": "Gebruiker verbanne", "resetPassword": "Wachtwoord resetten", "deleteUser": "Gebruiker verwijderen", - "confirmDelete": "Weet je zeker dat je {username} wilt verwijderen? Dit kan niet ongedaan worden gemaakt." + "confirmDelete": "Weet je zeker dat je {username} wilt verwijderen? Dit kan niet ongedaan worden gemaakt.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Gebruiker bewerken: {username}", @@ -2902,11 +2916,14 @@ "success": { "replied": "Antwoord verzonden.", "closed": "Ticket gesloten.", - "reopened": "Ticket heropend." + "reopened": "Ticket heropend.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "Deze wachtrij is het publieke helpcenter. CMS support desk tickets staan onder Supportbalie.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open hier: {here} · Supportbalie: {other}" + "queueCounts": "Open hier: {here} · Supportbalie: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "cfh": { "title": "Call for Help", diff --git a/src/messages/no.json b/src/messages/no.json index 559ef45a..a489ca4d 100644 --- a/src/messages/no.json +++ b/src/messages/no.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Søk på {hotel}-ansatte", @@ -1111,7 +1112,15 @@ "colTimestamp": "Tidsstempel", "colActions": "Handlinger", "photoAlt": "Brukerbilde nr. {id}", - "byUser": "av {username}" + "byUser": "av {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Brukere", @@ -1158,7 +1167,12 @@ "banUser": "Ban bruker", "resetPassword": "Tilbakestill passord", "deleteUser": "Slett bruker", - "confirmDelete": "Er du sikker på at du vil slette {username}? Dette kan ikke angres." + "confirmDelete": "Er du sikker på at du vil slette {username}? Dette kan ikke angres.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Rediger bruker: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/pl.json b/src/messages/pl.json index 6e3539b2..4d7fdf0d 100644 --- a/src/messages/pl.json +++ b/src/messages/pl.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Aplikuj na pracownika {hotel}", @@ -1111,7 +1112,15 @@ "colTimestamp": "Znacznik czasu", "colActions": "Działania", "photoAlt": "Zdjęcie użytkownika nr{id}", - "byUser": "przez: {username}" + "byUser": "przez: {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Użytkownicy", @@ -1158,7 +1167,12 @@ "banUser": "Zablokuj użytkownika", "resetPassword": "Zresetuj hasło", "deleteUser": "Usuń użytkownika", - "confirmDelete": "Czy na pewno chcesz usunąć użytkownika {username}? Tego nie można cofnąć." + "confirmDelete": "Czy na pewno chcesz usunąć użytkownika {username}? Tego nie można cofnąć.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Edytuj użytkownika: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/pt.json b/src/messages/pt.json index 39e3da8b..c461b6bb 100644 --- a/src/messages/pt.json +++ b/src/messages/pt.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Inscreva-se para funcionários de {hotel}", @@ -1111,7 +1112,15 @@ "colTimestamp": "Carimbo de data e hora", "colActions": "Ações", "photoAlt": "Foto do usuário #{id}", - "byUser": "por {username}" + "byUser": "por {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Usuários", @@ -1158,7 +1167,12 @@ "banUser": "Banir usuário", "resetPassword": "Redefinir senha", "deleteUser": "Excluir usuário", - "confirmDelete": "Tem certeza de que deseja excluir {username}? Isto não pode ser desfeito." + "confirmDelete": "Tem certeza de que deseja excluir {username}? Isto não pode ser desfeito.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Editar usuário: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/ro.json b/src/messages/ro.json index e7b21cac..82ef40bd 100644 --- a/src/messages/ro.json +++ b/src/messages/ro.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Aplicați pentru personalul {hotel}", @@ -1111,7 +1112,15 @@ "colTimestamp": "Marca temporală", "colActions": "Acțiuni", "photoAlt": "Fotografia utilizatorului #{id}", - "byUser": "de {username}" + "byUser": "de {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Utilizatori", @@ -1158,7 +1167,12 @@ "banUser": "Ban utilizator", "resetPassword": "Resetează parola", "deleteUser": "Ștergeți utilizatorul", - "confirmDelete": "Sigur doriți să ștergeți {username}? Acest lucru nu poate fi anulat." + "confirmDelete": "Sigur doriți să ștergeți {username}? Acest lucru nu poate fi anulat.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Editați utilizatorul: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/ru.json b/src/messages/ru.json index dacfe432..d561dcf9 100644 --- a/src/messages/ru.json +++ b/src/messages/ru.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Подать заявку на персонал {hotel}", @@ -1109,7 +1110,15 @@ "colTimestamp": "Метка времени", "colActions": "Действия", "photoAlt": "Фото пользователя #{id}", - "byUser": "на {username}" + "byUser": "на {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Пользователи", @@ -1156,7 +1165,12 @@ "banUser": "Заблокировать пользователя", "resetPassword": "Сбросить пароль", "deleteUser": "Удалить пользователя", - "confirmDelete": "Вы уверены, что хотите удалить {username}? Это действие нельзя отменить." + "confirmDelete": "Вы уверены, что хотите удалить {username}? Это действие нельзя отменить.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Редактировать пользователя: {username}", @@ -2723,11 +2737,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/sk.json b/src/messages/sk.json index 3ea48123..b0dcbdcc 100644 --- a/src/messages/sk.json +++ b/src/messages/sk.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Požiadajte o personál {hotel}", @@ -1111,7 +1112,15 @@ "colTimestamp": "Časová pečiatka", "colActions": "Akcie", "photoAlt": "Používateľská fotografia č. {id}", - "byUser": "od používateľa {username}" + "byUser": "od používateľa {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Používatelia", @@ -1158,7 +1167,12 @@ "banUser": "Zakázať používateľovi", "resetPassword": "Obnoviť heslo", "deleteUser": "Odstrániť používateľa", - "confirmDelete": "Naozaj chcete odstrániť používateľa {username}? Toto sa nedá vrátiť späť." + "confirmDelete": "Naozaj chcete odstrániť používateľa {username}? Toto sa nedá vrátiť späť.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Upraviť používateľa: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/sr.json b/src/messages/sr.json index 606aa675..1befdf44 100644 --- a/src/messages/sr.json +++ b/src/messages/sr.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Пријавите се за особље {hotel}", @@ -1111,7 +1112,15 @@ "colTimestamp": "Временска ознака", "colActions": "Акције", "photoAlt": "Корисничка фотографија #{id}", - "byUser": "од {username}" + "byUser": "од {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Корисници", @@ -1158,7 +1167,12 @@ "banUser": "Бан корисника", "resetPassword": "Ресетујте лозинку", "deleteUser": "Избриши корисника", - "confirmDelete": "Are you sure you want to delete {username}? Ово се не може поништити." + "confirmDelete": "Are you sure you want to delete {username}? Ово се не може поништити.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Уреди корисника: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/sv.json b/src/messages/sv.json index c56cc101..0bfceda0 100644 --- a/src/messages/sv.json +++ b/src/messages/sv.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Ansök om {hotel} personal", @@ -1111,7 +1112,15 @@ "colTimestamp": "Tidsstämpel", "colActions": "Åtgärder", "photoAlt": "Användarfoto #{id}", - "byUser": "av {username}" + "byUser": "av {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Användare", @@ -1158,7 +1167,12 @@ "banUser": "Förbjud användare", "resetPassword": "Återställ lösenord", "deleteUser": "Ta bort användare", - "confirmDelete": "Är du säker på att du vill ta bort {username}? Detta kan inte ångras." + "confirmDelete": "Är du säker på att du vill ta bort {username}? Detta kan inte ångras.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Redigera användare: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/tr.json b/src/messages/tr.json index b9fe14f5..1222b806 100644 --- a/src/messages/tr.json +++ b/src/messages/tr.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "{hotel} personeli için başvur", @@ -1111,7 +1112,15 @@ "colTimestamp": "Zaman damgası", "colActions": "Eylemler", "photoAlt": "Kullanıcı fotoğrafı #{id}", - "byUser": "{username} tarafından" + "byUser": "{username} tarafından", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Kullanıcılar", @@ -1158,7 +1167,12 @@ "banUser": "Kullanıcıyı yasakla", "resetPassword": "Şifreyi sıfırla", "deleteUser": "Kullanıcıyı sil", - "confirmDelete": "{username}'i silmek istediğinizden emin misiniz? Bu geri alınamaz." + "confirmDelete": "{username}'i silmek istediğinizden emin misiniz? Bu geri alınamaz.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Kullanıcıyı düzenle: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace", diff --git a/src/messages/uk.json b/src/messages/uk.json index 3661f456..4b75d047 100644 --- a/src/messages/uk.json +++ b/src/messages/uk.json @@ -485,7 +485,8 @@ "moderated": "That ticket was blocked by moderation.", "ratelimit": "You're submitting too fast. Please wait a moment and try again.", "error": "Something went wrong. Please try again." - } + }, + "banAppealLabel": "This is a ban appeal" }, "applyStaff": { "title": "Подайте заявку на роботу в {hotel}", @@ -1111,7 +1112,15 @@ "colTimestamp": "Мітка часу", "colActions": "Дії", "photoAlt": "Фото користувача #{id}", - "byUser": "від {username}" + "byUser": "від {username}", + "photosShown": "Shown", + "rooms": "Rooms", + "users": "Users", + "distinctRooms": "Distinct rooms on this page", + "distinctPhotographers": "Distinct photographers on this page", + "total": "Total photos", + "recentPhotos": "Recent photos", + "pageOf": "Page {page} of {last} · {total} total" }, "users": { "title": "Користувачі", @@ -1158,7 +1167,12 @@ "banUser": "Забанити користувача", "resetPassword": "Скинути пароль", "deleteUser": "Видалити користувача", - "confirmDelete": "Ви впевнені, що хочете видалити {username}? Це неможливо скасувати." + "confirmDelete": "Ви впевнені, що хочете видалити {username}? Це неможливо скасувати.", + "tabInfo": "Account Info", + "tabBadges": "Badges ({count})", + "tabRooms": "Rooms ({count})", + "tabSanctions": "Sanctions ({count})", + "tabActivity": "Activity" }, "editForm": { "title": "Редагувати користувача: {username}", @@ -2725,11 +2739,14 @@ "success": { "replied": "Reply sent.", "closed": "Ticket closed.", - "reopened": "Ticket reopened." + "reopened": "Ticket reopened.", + "banLifted": "Ban lifted and ticket closed." }, "queueHint": "This queue is the public help center. CMS support desk tickets are in the Support desk tab.", "queueOtherCta": "Open CMS support desk", - "queueCounts": "Open here: {here} · Support desk: {other}" + "queueCounts": "Open here: {here} · Support desk: {other}", + "liftBanSubmit": "Lift requester ban & close", + "requesterBanned": "Requester banned" }, "marketplace": { "title": "Marketplace",