fix(secretary): allow insurance pages; add grantable subscription resource
Insurance pages redirected to the dashboard: the insurance-pricing/claims routes never listed `secretary`, so RoleRoute bounced a secretary who had insurances.view and saw the menu item. Added secretary + permission ['insurances','view'] to both routes; also gated my-financial with ['payments','view'] for consistency. «خرید اشتراک» was owner-only with no permission toggle, so it could not be granted. Added a `subscription` secretary resource (view/create) end-to-end: - entity DEFAULT_PERMISSIONS + SecretaryPermissions type + both secretary forms. - backend: SubscriptionController::my (view) and trial (create), PaymentController::initiateSubscription (create). resolveEntity in SubscriptionController was already secretary-aware. - frontend: subscription + subscription/success routes accept secretary + permission; settings navs gate «خرید اشتراک» by ['subscription','view']. Tests: subscription denied-by-default / allowed-when-granted. docs/api secretary.md updated (resource list, enforcement map, JSON example). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -242,10 +242,10 @@ export default function App() {
|
||||
<Route path="my-patients/:recordUuid/session/:sessionUuid/pay" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope><SessionPaymentPage /></RoleRoute>} />
|
||||
<Route path="patients/:recordUuid/session/:sessionUuid/edit" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope><EditSessionPage /></RoleRoute>} />
|
||||
<Route path="my-patients/:recordUuid/session/:sessionUuid/edit" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} blockClinicScope><EditSessionPage /></RoleRoute>} />
|
||||
<Route path="insurance-pricing" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><InsurancePricingPage /></RoleRoute>} />
|
||||
<Route path="claims" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><ClaimsPage /></RoleRoute>} />
|
||||
<Route path="insurance-pricing" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['insurances', 'view']}><InsurancePricingPage /></RoleRoute>} />
|
||||
<Route path="claims" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['insurances', 'view']}><ClaimsPage /></RoleRoute>} />
|
||||
<Route path="claims/:patientUuid" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><ClaimPatientDetailPage /></RoleRoute>} />
|
||||
<Route path="my-financial" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']}><MyFinancialPage /></RoleRoute>} />
|
||||
<Route path="my-financial" element={<RoleRoute roles={['doctor', 'secretary', 'clinic']} permission={['payments', 'view']}><MyFinancialPage /></RoleRoute>} />
|
||||
|
||||
{/* فاز ۲ — دکتر / کلینیک */}
|
||||
<Route path="staff" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['staff', 'view']}><StaffPage /></RoleRoute>} />
|
||||
@@ -253,9 +253,9 @@ export default function App() {
|
||||
<Route path="account-settings" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']}><AccountSettingsPage /></RoleRoute>} />
|
||||
<Route path="tags-settings" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['tags', 'view']}><TagsSettingsPage /></RoleRoute>} />
|
||||
<Route path="appointment-settings" element={<RoleRoute roles={['doctor', 'secretary']} blockClinicScope permission={['appointment_settings', 'view']}><AppointmentSettingsPage /></RoleRoute>} />
|
||||
<Route path="subscription" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><SubscriptionPage /></RoleRoute>} />
|
||||
<Route path="subscription" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['subscription', 'view']}><SubscriptionPage /></RoleRoute>} />
|
||||
<Route path="discounts" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['discounts', 'view']}><DiscountsPage /></RoleRoute>} />
|
||||
<Route path="subscription/success" element={<RoleRoute roles={['doctor', 'clinic']} blockClinicScope><PaymentSuccessPage /></RoleRoute>} />
|
||||
<Route path="subscription/success" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['subscription', 'view']}><PaymentSuccessPage /></RoleRoute>} />
|
||||
<Route path="clinic-services" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['services', 'view']}><ClinicServicesPage /></RoleRoute>} />
|
||||
<Route path="clinic-services/:uuid" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['services', 'view']}><ServiceDetailPage /></RoleRoute>} />
|
||||
<Route path="inventory" element={<RoleRoute roles={['doctor', 'clinic', 'secretary']} blockClinicScope permission={['inventory', 'view']}><InventoryPage /></RoleRoute>} />
|
||||
|
||||
@@ -31,7 +31,7 @@ type NavItem = {
|
||||
};
|
||||
|
||||
const NAV_ITEMS: NavItem[] = [
|
||||
{ key: 'subscription', label: 'خرید اشتراک', to: '/admin/subscription' },
|
||||
{ key: 'subscription', label: 'خرید اشتراک', to: '/admin/subscription', perm: ['subscription', 'view'] },
|
||||
{ key: 'payment', label: 'مدیریت پرداخت', to: '/admin/my-financial', perm: ['payments', 'view'] },
|
||||
{ key: 'appointment', label: 'مدیریت نوبت دهی', to: '/admin/appointment-settings', roles: ['doctor'], perm: ['appointment_settings', 'view'] },
|
||||
{ key: 'appointment', label: 'مدیریت نوبت دهی', to: '/admin/settings/appointment-settings', roles: ['clinic'], perm: ['appointment_settings', 'view'] },
|
||||
|
||||
@@ -24,7 +24,7 @@ export type SettingsMenuItem = {
|
||||
};
|
||||
|
||||
export const SETTINGS_MENU: SettingsMenuItem[] = [
|
||||
{ key: 'subscription', label: 'خرید اشتراک', icon: CreditCardIcon, to: '/admin/subscription' },
|
||||
{ key: 'subscription', label: 'خرید اشتراک', icon: CreditCardIcon, to: '/admin/subscription', perm: ['subscription', 'view'] },
|
||||
{ key: 'doctor', label: 'مدیریت پزشک', icon: UserIcon, to: '/admin/profile', roles: ['doctor'] },
|
||||
{ key: 'appointment', label: 'مدیریت نوبت دهی', icon: CalendarDaysIcon, to: '/admin/appointment-settings', roles: ['doctor'], perm: ['appointment_settings', 'view'] },
|
||||
{ key: 'appointment', label: 'مدیریت نوبت دهی', icon: CalendarDaysIcon, to: '/admin/settings/appointment-settings', roles: ['clinic'], perm: ['appointment_settings', 'view'] },
|
||||
|
||||
@@ -81,6 +81,7 @@ const EMPTY_PERMISSIONS: SecretaryPermissions = {
|
||||
sms: { view: false, create: false, update: false, delete: false },
|
||||
appointment_settings: { view: false, update: false },
|
||||
clinic_doctors: { view: false, create: false, update: false, delete: false },
|
||||
subscription: { view: false, create: false },
|
||||
};
|
||||
|
||||
type PermSection = keyof SecretaryPermissions;
|
||||
@@ -229,6 +230,14 @@ const PERMISSION_SECTIONS: {
|
||||
{ key: "delete", label: "حذف پزشک" },
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "subscription",
|
||||
title: "خرید اشتراک",
|
||||
items: [
|
||||
{ key: "view", label: "مشاهده اشتراک" },
|
||||
{ key: "create", label: "خرید/فعالسازی اشتراک" },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
function PermissionAccordions({
|
||||
|
||||
@@ -27,6 +27,7 @@ const DEFAULT_PERMISSIONS: SecretaryPermissions = {
|
||||
sms: { view: false, create: false, update: false, delete: false },
|
||||
appointment_settings: { view: false, update: false },
|
||||
clinic_doctors: { view: false, create: false, update: false, delete: false },
|
||||
subscription: { view: false, create: false },
|
||||
};
|
||||
|
||||
type PermSection = keyof SecretaryPermissions;
|
||||
@@ -154,6 +155,13 @@ const PERMISSION_LABELS: Record<PermSection, { label: string; actions: { key: st
|
||||
{ key: 'delete', label: 'حذف' },
|
||||
],
|
||||
},
|
||||
subscription: {
|
||||
label: 'خرید اشتراک',
|
||||
actions: [
|
||||
{ key: 'view', label: 'مشاهده' },
|
||||
{ key: 'create', label: 'خرید' },
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
function PermissionsMatrix({
|
||||
|
||||
@@ -536,6 +536,10 @@ export interface SecretaryPermissions {
|
||||
update: boolean;
|
||||
delete: boolean;
|
||||
};
|
||||
subscription: {
|
||||
view: boolean;
|
||||
create: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
export interface Specialty {
|
||||
|
||||
Reference in New Issue
Block a user