feat: home page per role — selector in RolesTab, saved to DB and applied on login
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -309,7 +309,7 @@ export const api = {
|
|||||||
'GET', `/api/hotels/${slug}/role-permissions`),
|
'GET', `/api/hotels/${slug}/role-permissions`),
|
||||||
|
|
||||||
save: (slug: string, roleKey: string, data: {
|
save: (slug: string, roleKey: string, data: {
|
||||||
name: string; color: string; isSystem: boolean; permissions: Record<string, boolean>
|
name: string; color: string; isSystem: boolean; permissions: Record<string, boolean>; homePage?: string | null
|
||||||
}) =>
|
}) =>
|
||||||
req<import('../contexts/RolePermissionsContext').SavedRolePermission>(
|
req<import('../contexts/RolePermissionsContext').SavedRolePermission>(
|
||||||
'PUT', `/api/hotels/${slug}/role-permissions/${roleKey}`, data),
|
'PUT', `/api/hotels/${slug}/role-permissions/${roleKey}`, data),
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ interface RolePermissions {
|
|||||||
color: string
|
color: string
|
||||||
isSystem: boolean
|
isSystem: boolean
|
||||||
permissions: Record<string, boolean>
|
permissions: Record<string, boolean>
|
||||||
|
homePage?: string | null
|
||||||
}
|
}
|
||||||
|
|
||||||
// ── Constants ──────────────────────────────────────────────────────────────────
|
// ── Constants ──────────────────────────────────────────────────────────────────
|
||||||
@@ -134,6 +135,36 @@ const ALL_MODULE_KEYS = MODULE_GROUPS.flatMap(g => g.modules.map(m => m.key))
|
|||||||
const allPerms = (v: boolean) =>
|
const allPerms = (v: boolean) =>
|
||||||
Object.fromEntries(ALL_MODULE_KEYS.map(k => [k, v]))
|
Object.fromEntries(ALL_MODULE_KEYS.map(k => [k, v]))
|
||||||
|
|
||||||
|
// Permission key → route path (for home page selector)
|
||||||
|
const PERM_TO_ROUTE: Record<string, string> = {
|
||||||
|
calendar: '/calendar',
|
||||||
|
bookings: '/bookings',
|
||||||
|
guests: '/guests',
|
||||||
|
rooms: '/rooms',
|
||||||
|
availability: '/availability',
|
||||||
|
housekeeping: '/housekeeping',
|
||||||
|
maintenance: '/maintenance',
|
||||||
|
room_service: '/room-service',
|
||||||
|
rental: '/rental',
|
||||||
|
pos: '/pos',
|
||||||
|
tariffs: '/tariffs',
|
||||||
|
pricing: '/dynamic-pricing',
|
||||||
|
discounts: '/discounts',
|
||||||
|
loyalty: '/loyalty',
|
||||||
|
reports: '/reports',
|
||||||
|
channels: '/channels',
|
||||||
|
reviews: '/reviews',
|
||||||
|
website: '/website',
|
||||||
|
floor_map: '/floor-map',
|
||||||
|
equipment: '/equipment',
|
||||||
|
wifi: '/wifi',
|
||||||
|
ttlock: '/ttlock',
|
||||||
|
users: '/users',
|
||||||
|
schedule: '/schedule',
|
||||||
|
documents: '/documents',
|
||||||
|
settings: '/settings',
|
||||||
|
}
|
||||||
|
|
||||||
const INITIAL_ROLE_PERMISSIONS: RolePermissions[] = [
|
const INITIAL_ROLE_PERMISSIONS: RolePermissions[] = [
|
||||||
{
|
{
|
||||||
id: 'rp_hotel_admin',
|
id: 'rp_hotel_admin',
|
||||||
@@ -492,7 +523,7 @@ function RolesTab() {
|
|||||||
if (!s) return def
|
if (!s) return def
|
||||||
// Ensure all module keys are present (new modules default to false)
|
// Ensure all module keys are present (new modules default to false)
|
||||||
const fullPerms = { ...allPerms(false), ...s.permissions }
|
const fullPerms = { ...allPerms(false), ...s.permissions }
|
||||||
return { ...def, permissions: fullPerms }
|
return { ...def, permissions: fullPerms, homePage: s.homePage ?? def.homePage }
|
||||||
})
|
})
|
||||||
|
|
||||||
// Custom roles (not in system list)
|
// Custom roles (not in system list)
|
||||||
@@ -505,6 +536,7 @@ function RolesTab() {
|
|||||||
color: r.color,
|
color: r.color,
|
||||||
isSystem: false,
|
isSystem: false,
|
||||||
permissions: { ...allPerms(false), ...r.permissions },
|
permissions: { ...allPerms(false), ...r.permissions },
|
||||||
|
homePage: r.homePage ?? null,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
setRoles([...merged, ...custom])
|
setRoles([...merged, ...custom])
|
||||||
@@ -534,6 +566,12 @@ function RolesTab() {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const setHomePage = (page: string | null) => {
|
||||||
|
setRoles(prev => prev.map(r =>
|
||||||
|
r.id === selectedRoleId ? { ...r, homePage: page } : r
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
// ── Save to API ────────────────────────────────────────────────────────────
|
// ── Save to API ────────────────────────────────────────────────────────────
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
if (!slug || saving) return
|
if (!slug || saving) return
|
||||||
@@ -546,6 +584,7 @@ function RolesTab() {
|
|||||||
color: role.color,
|
color: role.color,
|
||||||
isSystem: role.isSystem,
|
isSystem: role.isSystem,
|
||||||
permissions: role.permissions,
|
permissions: role.permissions,
|
||||||
|
homePage: role.homePage ?? null,
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
setSaveOk(true)
|
setSaveOk(true)
|
||||||
@@ -786,6 +825,38 @@ function RolesTab() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Home page selector */}
|
||||||
|
<div className="px-4 pb-4 border-t border-slate-100 dark:border-slate-700 pt-4">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<div className="flex-1">
|
||||||
|
<p className="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wide mb-1">
|
||||||
|
Стартовая страница
|
||||||
|
</p>
|
||||||
|
<p className="text-xs text-slate-400 dark:text-slate-500 mb-2">
|
||||||
|
Страница, которая открывается при входе в систему
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
value={selectedRole.homePage ?? ''}
|
||||||
|
onChange={e => setHomePage(e.target.value || null)}
|
||||||
|
className="input text-sm w-full sm:w-72"
|
||||||
|
>
|
||||||
|
<option value="">— По умолчанию для роли —</option>
|
||||||
|
{ALL_MODULE_KEYS
|
||||||
|
.filter(k => selectedRole.permissions[k] && PERM_TO_ROUTE[k])
|
||||||
|
.map(k => {
|
||||||
|
const mod = MODULE_GROUPS.flatMap(g => g.modules).find(m => m.key === k)
|
||||||
|
return (
|
||||||
|
<option key={k} value={PERM_TO_ROUTE[k]}>
|
||||||
|
{mod?.label ?? k}
|
||||||
|
</option>
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
{selectedRole.isSystem && (
|
{selectedRole.isSystem && (
|
||||||
<div className="px-4 pb-4">
|
<div className="px-4 pb-4">
|
||||||
<div className="flex items-center gap-2 p-3 rounded-xl bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800">
|
<div className="flex items-center gap-2 p-3 rounded-xl bg-amber-50 dark:bg-amber-900/20 border border-amber-200 dark:border-amber-800">
|
||||||
|
|||||||
Reference in New Issue
Block a user