feat(rbac): gate pages and action buttons in the UI by permission
CI/CD / CI · API (dotnet build + test) (push) Successful in 39s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 28s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m8s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m45s
CI/CD / CI · API (dotnet build + test) (push) Successful in 39s
CI/CD / CI · Admin API (dotnet build) (push) Successful in 28s
CI/CD / CI · Dashboard (tsc) (push) Successful in 1m8s
CI/CD / CI · Admin Web (tsc) (push) Successful in 37s
CI/CD / CI · Website (tsc) (push) Successful in 46s
CI/CD / CI · Koja (tsc) (push) Successful in 49s
CI/CD / Deploy · all services (push) Successful in 2m45s
Nav already hides pages a role can't view (NAV_REQUIRED_PERMISSION). This wraps the sensitive/CRUD action controls in <Can permission> so users only see what they can do (server still enforces): - POS/orders: void → VoidOrder, cancel → VoidOrder, transfer → EditOrder, pay/split → HandlePayments - menu/inventory/coupons/customers/reservations/expenses/taxes/branches: add/edit/delete buttons → the matching Create/Edit/Delete permission - reports CSV export → ExportReports; SMS send → SendSms, settings → ManageSmsSettings - home dashboard: revenue/orders KPI queries gated on ViewReports so non-report roles don't 403 on the landing page (Refund/discount/comp/cash-drawer have no UI control yet — no buttons to gate.) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { notify } from "@/lib/notify";
|
||||
import { Can } from "@/components/auth/can";
|
||||
|
||||
type Item = { id: string; name: string; price: number; cat: string };
|
||||
type Line = { item: Item; qty: number };
|
||||
@@ -402,18 +403,22 @@ function Ticket({
|
||||
className="flex min-h-[56px] items-center justify-center gap-2 rounded-xl bg-primary text-base font-bold text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-40 active:scale-[0.98]">
|
||||
<Send className="size-5" /> ارسال
|
||||
</button>
|
||||
<button type="button" disabled={!count} onClick={onPay}
|
||||
className="flex min-h-[56px] items-center justify-center gap-2 rounded-xl border-2 border-primary text-base font-bold text-primary transition-colors hover:bg-primary/10 disabled:opacity-40 active:scale-[0.98]">
|
||||
<CreditCard className="size-5" /> پرداخت
|
||||
</button>
|
||||
<Can permission="HandlePayments">
|
||||
<button type="button" disabled={!count} onClick={onPay}
|
||||
className="flex min-h-[56px] items-center justify-center gap-2 rounded-xl border-2 border-primary text-base font-bold text-primary transition-colors hover:bg-primary/10 disabled:opacity-40 active:scale-[0.98]">
|
||||
<CreditCard className="size-5" /> پرداخت
|
||||
</button>
|
||||
</Can>
|
||||
<button type="button" disabled={!count} onClick={onHold}
|
||||
className="flex min-h-[48px] items-center justify-center gap-1.5 rounded-xl bg-muted text-sm font-medium text-muted-foreground hover:bg-accent disabled:opacity-40">
|
||||
<Pause className="size-4" /> نگهداشتن
|
||||
</button>
|
||||
<button type="button" disabled={!count} onClick={onSplit}
|
||||
className="flex min-h-[48px] items-center justify-center gap-1.5 rounded-xl bg-muted text-sm font-medium text-muted-foreground hover:bg-accent disabled:opacity-40">
|
||||
<SplitSquareHorizontal className="size-4" /> تقسیم
|
||||
</button>
|
||||
<Can permission="HandlePayments">
|
||||
<button type="button" disabled={!count} onClick={onSplit}
|
||||
className="flex min-h-[48px] items-center justify-center gap-1.5 rounded-xl bg-muted text-sm font-medium text-muted-foreground hover:bg-accent disabled:opacity-40">
|
||||
<SplitSquareHorizontal className="size-4" /> تقسیم
|
||||
</button>
|
||||
</Can>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@@ -25,6 +25,7 @@ import { apiGet, apiPost, ApiClientError } from "@/lib/api/client";
|
||||
import { submitOrderToApi, orderAmountDue, isLocalOrder } from "@/lib/pos/submit-order";
|
||||
import { requestPosPayment, posDeviceErrorMessage } from "@/lib/api/pos-device";
|
||||
import { PosCustomerPicker } from "@/components/pos/pos-customer-picker";
|
||||
import { Can } from "@/components/auth/can";
|
||||
import type { Customer, MenuItem, Order, TableBoardItem } from "@/lib/api/types";
|
||||
import { usePos2Categories, usePos2Menu, usePos2Tables, useMenuById } from "@/lib/pos2/use-pos2";
|
||||
|
||||
@@ -729,17 +730,21 @@ function Ticket({
|
||||
className="flex min-h-[56px] items-center justify-center gap-2 rounded-xl bg-primary text-base font-bold text-primary-foreground transition-colors hover:bg-primary/90 disabled:opacity-40 active:scale-[0.98]">
|
||||
<Send className="size-5" /> ارسال{pendingCount > 0 ? ` (${fmt(pendingCount)})` : ""}
|
||||
</button>
|
||||
<button type="button" disabled={count === 0} onClick={onPay}
|
||||
className="flex min-h-[56px] items-center justify-center gap-2 rounded-xl border-2 border-primary text-base font-bold text-primary transition-colors hover:bg-primary/10 disabled:opacity-40 active:scale-[0.98]">
|
||||
<CreditCard className="size-5" /> پرداخت
|
||||
</button>
|
||||
<Can permission="HandlePayments">
|
||||
<button type="button" disabled={count === 0} onClick={onPay}
|
||||
className="flex min-h-[56px] items-center justify-center gap-2 rounded-xl border-2 border-primary text-base font-bold text-primary transition-colors hover:bg-primary/10 disabled:opacity-40 active:scale-[0.98]">
|
||||
<CreditCard className="size-5" /> پرداخت
|
||||
</button>
|
||||
</Can>
|
||||
<button type="button" disabled className="flex min-h-[48px] items-center justify-center gap-1.5 rounded-xl bg-muted text-sm font-medium text-muted-foreground opacity-50">
|
||||
<Pause className="size-4" /> نگهداشتن
|
||||
</button>
|
||||
<button type="button" disabled={count === 0} onClick={onSplit}
|
||||
className="flex min-h-[48px] items-center justify-center gap-1.5 rounded-xl bg-muted text-sm font-medium text-muted-foreground hover:bg-accent disabled:opacity-40">
|
||||
<SplitSquareHorizontal className="size-4" /> تقسیم
|
||||
</button>
|
||||
<Can permission="HandlePayments">
|
||||
<button type="button" disabled={count === 0} onClick={onSplit}
|
||||
className="flex min-h-[48px] items-center justify-center gap-1.5 rounded-xl bg-muted text-sm font-medium text-muted-foreground hover:bg-accent disabled:opacity-40">
|
||||
<SplitSquareHorizontal className="size-4" /> تقسیم
|
||||
</button>
|
||||
</Can>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user