feat(rbac): full permission catalog in the custom-role matrix UI (fa/en/ar)
CI/CD / CI · API (dotnet build + test) (push) Successful in 42s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 30s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m7s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 45s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 3m24s

Mirrors the expanded backend catalog on the client: the Permission type and the
custom-role permission matrix now expose all ~80 capabilities grouped into 16
sections (admin, branches, menu, inventory, taxes, staff, tables, orders,
register, queue/kitchen, delivery, customers, coupons, marketing, reports,
expenses), each with fa/en/ar labels. Nav visibility now maps each page to its
View permission; taxes & branches become permission-driven (managers can view),
leaving billing as the sole hard owner-only nav gate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
soroush.asadi
2026-06-21 05:49:11 +03:30
parent 7a5ea75b50
commit 53d90fa357
6 changed files with 471 additions and 113 deletions
@@ -34,31 +34,84 @@ interface PermGroup {
const PERM_GROUPS: PermGroup[] = [
{
labelKey: "customRoles.groupAdmin",
perms: ["ManageCafeSettings", "ManageBilling", "ManageBranches"],
perms: [
"ViewCafeSettings", "ManageCafeSettings", "ManageDiscoverProfile",
"ViewBilling", "ManageBilling", "ManageRoles",
"ViewPrintSettings", "ManagePrintSettings",
],
},
{
labelKey: "customRoles.groupBranches",
perms: ["ViewBranches", "CreateBranch", "EditBranch", "DeleteBranch"],
},
{
labelKey: "customRoles.groupMenu",
perms: ["ManageMenu", "ManageInventory", "ManageTaxes", "ManagePrintSettings"],
perms: ["ViewMenu", "CreateMenuItem", "EditMenuItem", "DeleteMenuItem"],
},
{
labelKey: "customRoles.groupInventory",
perms: ["ViewInventory", "CreateInventory", "EditInventory", "DeleteInventory"],
},
{
labelKey: "customRoles.groupTaxes",
perms: ["ViewTaxes", "CreateTax", "EditTax", "DeleteTax"],
},
{
labelKey: "customRoles.groupStaff",
perms: ["ManageStaff", "ManageSalaries", "ReviewLeave"],
perms: [
"ViewStaff", "CreateStaff", "EditStaff", "DeleteStaff",
"ManageStaff", "ManageStaffCredentials",
"ViewAttendance", "ManageAttendance",
"ViewSchedules", "ManageSchedules",
"ViewLeave", "ReviewLeave",
"ViewSalaries", "ManageSalaries",
],
},
{
labelKey: "customRoles.groupCustomer",
perms: ["ManageReservations", "ManageTables", "ManageCoupons"],
labelKey: "customRoles.groupTables",
perms: [
"ViewTables", "ManageTables",
"ViewReservations", "CreateReservation", "EditReservation", "DeleteReservation",
],
},
{
labelKey: "customRoles.groupOrders",
perms: [
"ViewOrders", "ProcessOrders", "EditOrder", "VoidOrder", "RefundOrder",
"ApplyDiscount", "CompOrder", "HandlePayments", "UpdateOrderStatus",
],
},
{
labelKey: "customRoles.groupRegister",
perms: ["OperateRegister", "OpenCashDrawer"],
},
{
labelKey: "customRoles.groupQueueKitchen",
perms: ["ViewQueue", "ManageQueue", "ViewKitchen", "ManageKitchenStations"],
},
{
labelKey: "customRoles.groupDelivery",
perms: ["ViewDelivery", "HandleDelivery", "AssignDelivery"],
},
{
labelKey: "customRoles.groupCustomers",
perms: ["ViewCustomers", "CreateCustomer", "EditCustomer", "DeleteCustomer"],
},
{
labelKey: "customRoles.groupCoupons",
perms: ["ViewCoupons", "CreateCoupon", "EditCoupon", "DeleteCoupon"],
},
{
labelKey: "customRoles.groupMarketing",
perms: ["ViewSms", "SendSms", "ManageSmsSettings", "ViewReviews", "ManageReviews"],
},
{
labelKey: "customRoles.groupReports",
perms: ["ViewReports", "ManageExpenses"],
perms: ["ViewReports", "ExportReports", "ViewAuditLog", "ViewFinancials", "ManageFinancials"],
},
{
labelKey: "customRoles.groupOps",
perms: ["ProcessOrders", "HandlePayments", "OperateRegister", "ManageQueue"],
},
{
labelKey: "customRoles.groupKitchen",
perms: ["ViewKitchen", "HandleDelivery"],
labelKey: "customRoles.groupExpenses",
perms: ["ViewExpenses", "CreateExpense", "EditExpense", "DeleteExpense"],
},
];
+4 -1
View File
@@ -11,7 +11,10 @@ export function isBranchAccount(branchId: string | null | undefined): boolean {
return !!branchId;
}
export const OWNER_ONLY_NAV_KEYS = ["subscription", "taxes", "branches"] as const;
// Billing stays a hard owner gate (also covers legacy sessions with no
// permission list). Taxes & branches are now permission-driven via
// NAV_REQUIRED_PERMISSION (ViewTaxes / ViewBranches), which managers hold.
export const OWNER_ONLY_NAV_KEYS = ["subscription"] as const;
export function canSeeNavItem(
key: string,
+112 -26
View File
@@ -5,50 +5,136 @@ import type { NavItemKey } from "@/lib/sidebar-nav";
* Client mirror of the backend `Meezi.Core.Authorization.Permission` enum. The
* server (EnsurePermission) remains the single source of truth — these values
* only drive what the UI *shows* (pages, action buttons). Never rely on them
* for actual security.
* for actual security. Keep this list in sync with Permission.cs.
*/
export type Permission =
// Café administration
| "ViewCafeSettings"
| "ManageCafeSettings"
| "ManageDiscoverProfile"
| "ViewBilling"
| "ManageBilling"
| "ManageBranches"
| "ManageStaff"
| "ManageMenu"
| "ManageInventory"
| "ManageExpenses"
| "ManageTaxes"
| "ManageCoupons"
| "ManageReservations"
| "ManageTables"
| "ViewReports"
| "ReviewLeave"
| "ManageSalaries"
| "ViewBranches"
| "CreateBranch"
| "EditBranch"
| "DeleteBranch"
| "ManageRoles"
| "ViewPrintSettings"
| "ManagePrintSettings"
// Taxes
| "ViewTaxes"
| "CreateTax"
| "EditTax"
| "DeleteTax"
// Staff & HR
| "ViewStaff"
| "CreateStaff"
| "EditStaff"
| "DeleteStaff"
| "ManageStaff"
| "ManageStaffCredentials"
| "ViewAttendance"
| "ManageAttendance"
| "ViewSchedules"
| "ManageSchedules"
| "ViewLeave"
| "ReviewLeave"
| "ViewSalaries"
| "ManageSalaries"
// Menu
| "ViewMenu"
| "CreateMenuItem"
| "EditMenuItem"
| "DeleteMenuItem"
// Inventory
| "ViewInventory"
| "CreateInventory"
| "EditInventory"
| "DeleteInventory"
// Tables
| "ViewTables"
| "ManageTables"
// Reservations
| "ViewReservations"
| "CreateReservation"
| "EditReservation"
| "DeleteReservation"
// Orders & POS
| "ViewOrders"
| "ProcessOrders"
| "EditOrder"
| "VoidOrder"
| "RefundOrder"
| "ApplyDiscount"
| "CompOrder"
| "HandlePayments"
| "UpdateOrderStatus"
// Register / cash
| "OperateRegister"
| "OpenCashDrawer"
// Queue
| "ViewQueue"
| "ManageQueue"
// Kitchen
| "ViewKitchen"
| "HandleDelivery";
| "ManageKitchenStations"
// Delivery
| "ViewDelivery"
| "HandleDelivery"
| "AssignDelivery"
// Customers / CRM
| "ViewCustomers"
| "CreateCustomer"
| "EditCustomer"
| "DeleteCustomer"
// Coupons
| "ViewCoupons"
| "CreateCoupon"
| "EditCoupon"
| "DeleteCoupon"
// SMS / marketing
| "ViewSms"
| "SendSms"
| "ManageSmsSettings"
// Reviews
| "ViewReviews"
| "ManageReviews"
// Reports & finance
| "ViewReports"
| "ExportReports"
| "ViewAuditLog"
| "ViewFinancials"
| "ManageFinancials"
// Expenses
| "ViewExpenses"
| "CreateExpense"
| "EditExpense"
| "DeleteExpense";
/**
* Permission a nav page requires to be visible. Pages not listed here fall back
* to the existing owner-only / branch-account visibility logic in
* {@link file://./auth-permissions.ts}.
* Permission a nav page requires to be visible. Each maps to the page's "View"
* capability. Pages not listed fall back to the owner-only / branch-account
* visibility logic in {@link file://./auth-permissions.ts}.
*/
export const NAV_REQUIRED_PERMISSION: Partial<Record<NavItemKey, Permission>> = {
pos: "ProcessOrders",
tables: "ManageTables",
queue: "ManageQueue",
tables: "ViewTables",
queue: "ViewQueue",
kds: "ViewKitchen",
reservations: "ManageReservations",
menu: "ManageMenu",
inventory: "ManageInventory",
coupons: "ManageCoupons",
reservations: "ViewReservations",
menu: "ViewMenu",
reports: "ViewReports",
expenses: "ManageExpenses",
crm: "ViewCustomers",
coupons: "ViewCoupons",
sms: "ViewSms",
reviews: "ViewReviews",
inventory: "ViewInventory",
expenses: "ViewExpenses",
shifts: "OperateRegister",
taxes: "ManageTaxes",
hr: "ManageStaff",
taxes: "ViewTaxes",
hr: "ViewStaff",
branches: "ViewBranches",
subscription: "ViewBilling",
};
/** Read the effective permission set off an auth response (null = legacy session). */