refactor(housekeeping): sortable columns, inline edit Enter/Escape, select all matching, i18n date
Local Build and Deploy / deploy (push) Successful in 1m0s
Local Build and Deploy / deploy (push) Successful in 1m0s
- Sortable columns: click headers to sort by permission/description/group/rank - Inline edit: click rank value to edit, Enter to save, Escape to cancel - Select all matching button when partial selection is active - Select-all checkbox shows all-pages selection state - AuditSection uses Intl.DateTimeFormat with CMS locale instead of toLocaleString - Add selectAllMatching translation key to all 22 locales
This commit is contained in:
1 parent
f1e4e32a1e
commit
5c58a2b469
23 files changed
+245
-87
No files matched your search
@@ -1,7 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useRouter, useSearchParams } from "next/navigation";
|
import { useRouter, useSearchParams } from "next/navigation";
|
||||||
import { useTranslations } from "next-intl";
|
import { useLocale, useTranslations } from "next-intl";
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import {
|
import {
|
||||||
@@ -96,10 +96,16 @@ export function ManageSection(props: SharedProps) {
|
|||||||
const [page, setPage] = useState(1);
|
const [page, setPage] = useState(1);
|
||||||
const [pendingDel, setPendingDel] = useState<string | null>(null);
|
const [pendingDel, setPendingDel] = useState<string | null>(null);
|
||||||
const [selected, setSelected] = useState<Set<string>>(new Set());
|
const [selected, setSelected] = useState<Set<string>>(new Set());
|
||||||
|
const [selectAllMatching, setSelectAllMatching] = useState(false);
|
||||||
const [busyBulk, setBusyBulk] = useState(false);
|
const [busyBulk, setBusyBulk] = useState(false);
|
||||||
const [confirmDel, setConfirmDel] = useState<string | null>(null);
|
const [confirmDel, setConfirmDel] = useState<string | null>(null);
|
||||||
|
const [editingId, setEditingId] = useState<string | null>(null);
|
||||||
|
const [editValue, setEditValue] = useState("");
|
||||||
|
const [sortKey, setSortKey] = useState<string>("permission");
|
||||||
|
const [sortDir, setSortDir] = useState<"asc" | "desc">("asc");
|
||||||
const searchTimer = useRef<ReturnType<typeof setTimeout>>(null);
|
const searchTimer = useRef<ReturnType<typeof setTimeout>>(null);
|
||||||
const formRef = useRef<HTMLFormElement>(null);
|
const formRef = useRef<HTMLFormElement>(null);
|
||||||
|
const editRef = useRef<HTMLInputElement>(null);
|
||||||
|
|
||||||
const filtered = useMemo(() => {
|
const filtered = useMemo(() => {
|
||||||
return props.permissions.filter((p) => {
|
return props.permissions.filter((p) => {
|
||||||
@@ -110,9 +116,35 @@ export function ManageSection(props: SharedProps) {
|
|||||||
});
|
});
|
||||||
}, [props.permissions, search, groupFilter]);
|
}, [props.permissions, search, groupFilter]);
|
||||||
|
|
||||||
const totalPages = Math.max(1, Math.ceil(filtered.length / PAGE_SIZE));
|
const sorted = useMemo(() => {
|
||||||
|
const arr = [...filtered];
|
||||||
|
arr.sort((a, b) => {
|
||||||
|
let cmp = 0;
|
||||||
|
switch (sortKey) {
|
||||||
|
case "permission":
|
||||||
|
cmp = a.permission.localeCompare(b.permission);
|
||||||
|
break;
|
||||||
|
case "minRank":
|
||||||
|
cmp = a.minRank - b.minRank;
|
||||||
|
break;
|
||||||
|
case "groupName":
|
||||||
|
cmp = (a.groupName ?? "").localeCompare(b.groupName ?? "");
|
||||||
|
break;
|
||||||
|
case "description":
|
||||||
|
cmp = (a.description ?? "").localeCompare(b.description ?? "");
|
||||||
|
break;
|
||||||
|
case "dependsOn":
|
||||||
|
cmp = (a.dependsOn ?? "").localeCompare(b.dependsOn ?? "");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return sortDir === "asc" ? cmp : -cmp;
|
||||||
|
});
|
||||||
|
return arr;
|
||||||
|
}, [filtered, sortKey, sortDir]);
|
||||||
|
|
||||||
|
const totalPages = Math.max(1, Math.ceil(sorted.length / PAGE_SIZE));
|
||||||
const safePage = Math.min(page, totalPages);
|
const safePage = Math.min(page, totalPages);
|
||||||
const paginated = filtered.slice(
|
const paginated = sorted.slice(
|
||||||
(safePage - 1) * PAGE_SIZE,
|
(safePage - 1) * PAGE_SIZE,
|
||||||
safePage * PAGE_SIZE,
|
safePage * PAGE_SIZE,
|
||||||
);
|
);
|
||||||
@@ -200,15 +232,64 @@ export function ManageSection(props: SharedProps) {
|
|||||||
else next.add(id);
|
else next.add(id);
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
|
setSelectAllMatching(false);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const toggleAll = useCallback(() => {
|
const toggleAll = useCallback(() => {
|
||||||
if (selected.size === paginated.length) {
|
if (selected.size === paginated.length && !selectAllMatching) {
|
||||||
setSelected(new Set());
|
setSelected(new Set());
|
||||||
} else {
|
} else {
|
||||||
setSelected(new Set(paginated.map((p) => String(p.id))));
|
setSelected(new Set(paginated.map((p) => String(p.id))));
|
||||||
|
setSelectAllMatching(false);
|
||||||
}
|
}
|
||||||
}, [paginated, selected]);
|
}, [paginated, selected, selectAllMatching]);
|
||||||
|
|
||||||
|
const handleSelectAllMatching = useCallback(() => {
|
||||||
|
setSelected(new Set(sorted.map((p) => String(p.id))));
|
||||||
|
setSelectAllMatching(true);
|
||||||
|
}, [sorted]);
|
||||||
|
|
||||||
|
const handleSort = useCallback(
|
||||||
|
(key: string) => {
|
||||||
|
setSortDir((prev) => (sortKey === key && prev === "asc" ? "desc" : "asc"));
|
||||||
|
setSortKey(key);
|
||||||
|
setPage(1);
|
||||||
|
},
|
||||||
|
[sortKey],
|
||||||
|
);
|
||||||
|
|
||||||
|
const startEdit = useCallback((id: string, value: number) => {
|
||||||
|
setEditingId(id);
|
||||||
|
setEditValue(String(value));
|
||||||
|
setTimeout(() => editRef.current?.focus(), 0);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const cancelEdit = useCallback(() => {
|
||||||
|
setEditingId(null);
|
||||||
|
setEditValue("");
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const commitEdit = useCallback(
|
||||||
|
async (p: HKPerm) => {
|
||||||
|
const fd = new FormData();
|
||||||
|
fd.set("permission", p.permission);
|
||||||
|
fd.set("minRank", editValue);
|
||||||
|
fd.set("description", p.description ?? "");
|
||||||
|
fd.set("groupName", p.groupName ?? "");
|
||||||
|
fd.set("dependsOn", p.dependsOn ?? "");
|
||||||
|
await handleUpsert(fd);
|
||||||
|
setEditingId(null);
|
||||||
|
},
|
||||||
|
[editValue, handleUpsert],
|
||||||
|
);
|
||||||
|
|
||||||
|
const thClass =
|
||||||
|
"cursor-pointer select-none hover:opacity-80 transition-opacity";
|
||||||
|
|
||||||
|
const sortIcon = (key: string) => {
|
||||||
|
if (sortKey !== key) return " \u2195";
|
||||||
|
return sortDir === "asc" ? " \u2191" : " \u2193";
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@@ -321,16 +402,27 @@ export function ManageSection(props: SharedProps) {
|
|||||||
{filtered.length}
|
{filtered.length}
|
||||||
</span>
|
</span>
|
||||||
</h2>
|
</h2>
|
||||||
{selected.size > 0 && (
|
<div className="flex items-center gap-2">
|
||||||
<button
|
{selected.size > 0 && selected.size < sorted.length && (
|
||||||
type="button"
|
<button
|
||||||
onClick={() => setConfirmDel("__bulk__")}
|
type="button"
|
||||||
disabled={busyBulk}
|
onClick={handleSelectAllMatching}
|
||||||
className="btn btn-danger btn-sm"
|
className="btn btn-outline btn-sm"
|
||||||
>
|
>
|
||||||
{t("deleteSelected", { count: selected.size })}
|
{t("selectAllMatching", { count: sorted.length })}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
|
{selected.size > 0 && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setConfirmDel("__bulk__")}
|
||||||
|
disabled={busyBulk}
|
||||||
|
className="btn btn-danger btn-sm"
|
||||||
|
>
|
||||||
|
{t("deleteSelected", { count: selected.size })}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{filtered.length === 0 ? (
|
{filtered.length === 0 ? (
|
||||||
<div className="admin-empty">{t("noPermissions")}</div>
|
<div className="admin-empty">{t("noPermissions")}</div>
|
||||||
@@ -340,20 +432,55 @@ export function ManageSection(props: SharedProps) {
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th className="w-[36px]">
|
<th className="w-[36px]">
|
||||||
<input
|
{selectAllMatching ? (
|
||||||
type="checkbox"
|
<input
|
||||||
checked={
|
type="checkbox"
|
||||||
paginated.length > 0 &&
|
checked={true}
|
||||||
selected.size === paginated.length
|
onChange={() => {
|
||||||
}
|
setSelected(new Set());
|
||||||
onChange={toggleAll}
|
setSelectAllMatching(false);
|
||||||
/>
|
}}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<input
|
||||||
|
type="checkbox"
|
||||||
|
checked={
|
||||||
|
paginated.length > 0 &&
|
||||||
|
selected.size === paginated.length
|
||||||
|
}
|
||||||
|
onChange={toggleAll}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</th>
|
</th>
|
||||||
<th className="w-[40px]">#</th>
|
<th className="w-[40px]">#</th>
|
||||||
<th>{t("colPermission")}</th>
|
<th
|
||||||
<th>{t("colDescription")}</th>
|
className={thClass}
|
||||||
<th className="w-[120px]">{t("colGroup")}</th>
|
onClick={() => handleSort("permission")}
|
||||||
<th className="w-[100px]">{t("colMinRank")}</th>
|
>
|
||||||
|
{t("colPermission")}
|
||||||
|
{sortIcon("permission")}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className={thClass}
|
||||||
|
onClick={() => handleSort("description")}
|
||||||
|
>
|
||||||
|
{t("colDescription")}
|
||||||
|
{sortIcon("description")}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className={"w-[120px] " + thClass}
|
||||||
|
onClick={() => handleSort("groupName")}
|
||||||
|
>
|
||||||
|
{t("colGroup")}
|
||||||
|
{sortIcon("groupName")}
|
||||||
|
</th>
|
||||||
|
<th
|
||||||
|
className={"w-[100px] " + thClass}
|
||||||
|
onClick={() => handleSort("minRank")}
|
||||||
|
>
|
||||||
|
{t("colMinRank")}
|
||||||
|
{sortIcon("minRank")}
|
||||||
|
</th>
|
||||||
<th className="w-[180px]">{t("colDepends")}</th>
|
<th className="w-[180px]">{t("colDepends")}</th>
|
||||||
<th className="w-[80px]" />
|
<th className="w-[80px]" />
|
||||||
</tr>
|
</tr>
|
||||||
@@ -366,7 +493,7 @@ export function ManageSection(props: SharedProps) {
|
|||||||
<td>
|
<td>
|
||||||
<input
|
<input
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
checked={selected.has(sid)}
|
checked={selectAllMatching || selected.has(sid)}
|
||||||
onChange={() => toggleSelect(sid)}
|
onChange={() => toggleSelect(sid)}
|
||||||
/>
|
/>
|
||||||
</td>
|
</td>
|
||||||
@@ -387,47 +514,48 @@ export function ManageSection(props: SharedProps) {
|
|||||||
)}
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<form
|
{editingId === sid ? (
|
||||||
onSubmit={(e) => {
|
<div className="flex gap-1 items-center">
|
||||||
e.preventDefault();
|
<input
|
||||||
handleUpsert(new FormData(e.currentTarget));
|
ref={editRef}
|
||||||
}}
|
type="number"
|
||||||
style={{ display: "flex", gap: "0.5rem" }}
|
min={0}
|
||||||
>
|
value={editValue}
|
||||||
<input
|
onChange={(e) => setEditValue(e.target.value)}
|
||||||
type="hidden"
|
onKeyDown={(e) => {
|
||||||
name="permission"
|
if (e.key === "Enter") {
|
||||||
value={p.permission}
|
e.preventDefault();
|
||||||
/>
|
commitEdit(p);
|
||||||
<input
|
} else if (e.key === "Escape") {
|
||||||
type="hidden"
|
cancelEdit();
|
||||||
name="description"
|
}
|
||||||
value={p.description ?? ""}
|
}}
|
||||||
/>
|
style={{ width: 70 }}
|
||||||
<input
|
/>
|
||||||
type="hidden"
|
<button
|
||||||
name="groupName"
|
type="button"
|
||||||
value={p.groupName ?? ""}
|
className="btn btn-outline btn-sm"
|
||||||
/>
|
onClick={() => commitEdit(p)}
|
||||||
<input
|
>
|
||||||
type="hidden"
|
\u2713
|
||||||
name="dependsOn"
|
</button>
|
||||||
value={p.dependsOn ?? ""}
|
<button
|
||||||
/>
|
type="button"
|
||||||
<input
|
className="btn btn-outline btn-sm"
|
||||||
name="minRank"
|
onClick={cancelEdit}
|
||||||
type="number"
|
>
|
||||||
min={0}
|
\u2715
|
||||||
defaultValue={p.minRank}
|
</button>
|
||||||
style={{ width: 80 }}
|
</div>
|
||||||
/>
|
) : (
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="button"
|
||||||
className="btn btn-outline btn-sm"
|
className="btn btn-outline btn-sm min-w-[70px] text-center"
|
||||||
|
onClick={() => startEdit(sid, p.minRank)}
|
||||||
>
|
>
|
||||||
{t("save")}
|
{p.minRank}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
)}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-xs">
|
<td className="text-xs">
|
||||||
{p.dependsOn ? (
|
{p.dependsOn ? (
|
||||||
@@ -813,7 +941,15 @@ interface AuditSectionProps {
|
|||||||
|
|
||||||
export function AuditSection({ initialLogs }: AuditSectionProps) {
|
export function AuditSection({ initialLogs }: AuditSectionProps) {
|
||||||
const t = useTranslations("pages.admin.housekeeping");
|
const t = useTranslations("pages.admin.housekeeping");
|
||||||
|
const locale = useLocale();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
|
const dateFmt = useMemo(
|
||||||
|
() => new Intl.DateTimeFormat(locale, {
|
||||||
|
dateStyle: "short",
|
||||||
|
timeStyle: "short",
|
||||||
|
}),
|
||||||
|
[locale],
|
||||||
|
);
|
||||||
|
|
||||||
if (!initialLogs || initialLogs.rows.length === 0) {
|
if (!initialLogs || initialLogs.rows.length === 0) {
|
||||||
return (
|
return (
|
||||||
@@ -856,7 +992,7 @@ export function AuditSection({ initialLogs }: AuditSectionProps) {
|
|||||||
{initialLogs.rows.map((log) => (
|
{initialLogs.rows.map((log) => (
|
||||||
<tr key={log.id}>
|
<tr key={log.id}>
|
||||||
<td className="text-xs whitespace-nowrap">
|
<td className="text-xs whitespace-nowrap">
|
||||||
{new Date(log.createdAt).toLocaleString()}
|
{dateFmt.format(new Date(log.createdAt))}
|
||||||
</td>
|
</td>
|
||||||
<td className="text-xs">{log.username ?? "System"}</td>
|
<td className="text-xs">{log.username ?? "System"}</td>
|
||||||
<td className="text-xs font-mono">{log.action}</td>
|
<td className="text-xs font-mono">{log.action}</td>
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Стаи",
|
"title": "Стаи",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Pokoje",
|
"title": "Pokoje",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Værelser",
|
"title": "Værelser",
|
||||||
|
|||||||
@@ -1932,7 +1932,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Räume",
|
"title": "Räume",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Δωμάτια",
|
"title": "Δωμάτια",
|
||||||
|
|||||||
@@ -1899,6 +1899,7 @@
|
|||||||
"confirmDeletePermission": "Delete this permission permanently?",
|
"confirmDeletePermission": "Delete this permission permanently?",
|
||||||
"confirmBulkDelete": "Delete {count} selected permissions?",
|
"confirmBulkDelete": "Delete {count} selected permissions?",
|
||||||
"deleteSelected": "Delete {count} selected",
|
"deleteSelected": "Delete {count} selected",
|
||||||
|
"selectAllMatching": "Select all {count} matching",
|
||||||
"cancel": "Cancel",
|
"cancel": "Cancel",
|
||||||
"confirm": "Confirm",
|
"confirm": "Confirm",
|
||||||
"colGroup": "Group",
|
"colGroup": "Group",
|
||||||
|
|||||||
@@ -1932,7 +1932,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Salas",
|
"title": "Salas",
|
||||||
|
|||||||
@@ -1932,7 +1932,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Salons",
|
"title": "Salons",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Sobe",
|
"title": "Sobe",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Szobák",
|
"title": "Szobák",
|
||||||
|
|||||||
@@ -1932,7 +1932,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Stanze",
|
"title": "Stanze",
|
||||||
|
|||||||
@@ -1899,6 +1899,7 @@
|
|||||||
"confirmDeletePermission": "Deze permissie permanent verwijderen?",
|
"confirmDeletePermission": "Deze permissie permanent verwijderen?",
|
||||||
"confirmBulkDelete": "{count} geselecteerde permissies verwijderen?",
|
"confirmBulkDelete": "{count} geselecteerde permissies verwijderen?",
|
||||||
"deleteSelected": "Verwijder {count} geselecteerd",
|
"deleteSelected": "Verwijder {count} geselecteerd",
|
||||||
|
"selectAllMatching": "Selecteer alle {count} overeenkomende",
|
||||||
"cancel": "Annuleren",
|
"cancel": "Annuleren",
|
||||||
"confirm": "Bevestigen",
|
"confirm": "Bevestigen",
|
||||||
"colGroup": "Groep",
|
"colGroup": "Groep",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Rom",
|
"title": "Rom",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Pokoje",
|
"title": "Pokoje",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Quartos",
|
"title": "Quartos",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Camerele",
|
"title": "Camerele",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Rooms",
|
"title": "Rooms",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Izby",
|
"title": "Izby",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Собе",
|
"title": "Собе",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Rum",
|
"title": "Rum",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Odalar",
|
"title": "Odalar",
|
||||||
|
|||||||
@@ -1968,7 +1968,8 @@
|
|||||||
"auditTime": "Time",
|
"auditTime": "Time",
|
||||||
"auditUser": "User",
|
"auditUser": "User",
|
||||||
"auditAction": "Action",
|
"auditAction": "Action",
|
||||||
"auditTarget": "Target"
|
"auditTarget": "Target",
|
||||||
|
"selectAllMatching": "Select all {count} matching"
|
||||||
},
|
},
|
||||||
"rooms": {
|
"rooms": {
|
||||||
"title": "Кімнати",
|
"title": "Кімнати",
|
||||||
|
|||||||
Reference in new issue
Block a user