frontend phase 15

This commit is contained in:
hamid
2026-07-10 20:28:06 +03:30
parent bc51cf59b4
commit 70cf00ce4a
151 changed files with 10711 additions and 44 deletions
+23 -10
View File
@@ -2,26 +2,39 @@
import { FunctionComponent, PropsWithChildren, useMemo } from 'react';
import { useTranslations } from 'next-intl';
import { ROUTES } from '@/constants';
import { useAdminCapabilities } from '@/hooks';
import { LinkToPage } from '@/utils';
import TopBarAndSideBarLayout from './TopBarAndSideBarLayout';
/**
* Admin / backoffice shell — desktop-oriented ops console (f15). Uses the shared
* TopBar + SideBar engine with a persistent sidebar on desktop.
* Admin / backoffice shell — the desktop ops console (f15). The sidebar is **role-gated**: each console
* appears only when the current admin role can act on it (`useAdminCapabilities`). This is a display
* convenience — the server enforces every command's scope — so a `support` admin never sees the payout or
* refund controls, a `moderation` admin only sees moderation, etc. (phase §3 "Routing & RBAC", §5).
* @layout AdminLayout
*/
const AdminLayout: FunctionComponent<PropsWithChildren> = ({ children }) => {
const t = useTranslations('nav');
const tShell = useTranslations('shell');
const caps = useAdminCapabilities();
const sidebarItems: Array<LinkToPage> = useMemo(
() => [
{ title: t('overview'), path: ROUTES.ADMIN, icon: 'admin' },
{ title: t('users'), path: ROUTES.ADMIN_USERS, icon: 'users' },
{ title: t('notifications'), path: ROUTES.ADMIN_NOTIFICATIONS, icon: 'notifications' },
],
[t]
);
const sidebarItems: Array<LinkToPage> = useMemo(() => {
const items: Array<LinkToPage & { show: boolean }> = [
{ title: t('overview'), path: ROUTES.ADMIN, icon: 'dashboard', show: true },
{ title: t('verification'), path: ROUTES.ADMIN_VERIFICATION, icon: 'verification', show: caps.canVerify },
{ title: t('tickets'), path: ROUTES.ADMIN_TICKETS, icon: 'support', show: caps.canManageTickets },
{ title: t('payouts'), path: ROUTES.ADMIN_PAYOUTS, icon: 'earnings', show: caps.canPayout },
{ title: t('reviews'), path: ROUTES.ADMIN_REVIEWS, icon: 'moderation', show: caps.canModerate },
{ title: t('config'), path: ROUTES.ADMIN_CONFIG, icon: 'config', show: caps.canConfig },
{ title: t('holidays'), path: ROUTES.ADMIN_HOLIDAYS, icon: 'calendar', show: caps.canConfig },
{ title: t('alerts'), path: ROUTES.ADMIN_ALERTS, icon: 'alerts', show: caps.canManageAlerts },
{ title: t('audit'), path: ROUTES.ADMIN_AUDIT, icon: 'audit', show: caps.canViewAudit },
{ title: t('partners'), path: ROUTES.ADMIN_PARTNERS, icon: 'partners', show: caps.canManagePartners },
{ title: t('roles'), path: ROUTES.ADMIN_ROLES, icon: 'roles', show: caps.canManageRoles },
{ title: t('notifications'), path: ROUTES.ADMIN_NOTIFICATIONS, icon: 'notifications', show: true },
];
return items.filter((i) => i.show).map(({ show: _show, ...rest }) => rest);
}, [t, caps]);
return (
<TopBarAndSideBarLayout
+40
View File
@@ -0,0 +1,40 @@
'use client';
import { FunctionComponent, PropsWithChildren, useMemo } from 'react';
import { useTranslations } from 'next-intl';
import { ROUTES } from '@/constants';
import { LinkToPage } from '@/utils';
import TopBarAndSideBarLayout from './TopBarAndSideBarLayout';
/**
* Partner-center portal shell (f15) — a **separate authz scope** from the Balinyaar admin console. A
* center admin is not a Balinyaar admin; they see only their own center's data (server-enforced tenancy).
* Same desktop sidebar engine as the admin shell, its own nav. The settlement tab always appears; the
* settlement page itself renders the "via Balinyaar" state for a non-merchant-of-record center.
* @layout PartnerLayout
*/
const PartnerLayout: FunctionComponent<PropsWithChildren> = ({ children }) => {
const t = useTranslations('nav');
const tShell = useTranslations('shell');
const sidebarItems: Array<LinkToPage> = useMemo(
() => [
{ title: t('partner_home'), path: ROUTES.PARTNER, icon: 'partners' },
{ title: t('partner_nurses'), path: ROUTES.PARTNER_NURSES, icon: 'patients' },
{ title: t('partner_bookings'), path: ROUTES.PARTNER_BOOKINGS, icon: 'bookings' },
{ title: t('partner_settlement'), path: ROUTES.PARTNER_SETTLEMENT, icon: 'earnings' },
],
[t],
);
return (
<TopBarAndSideBarLayout
sidebarItems={sidebarItems}
title={tShell('partner_console')}
variant="sidebarPersistentOnDesktop"
>
{children}
</TopBarAndSideBarLayout>
);
};
export default PartnerLayout;
+2 -1
View File
@@ -3,5 +3,6 @@ import PublicLayout from './PublicLayout';
import CustomerLayout from './CustomerLayout';
import NurseLayout from './NurseLayout';
import AdminLayout from './AdminLayout';
import PartnerLayout from './PartnerLayout';
export { PublicLayout, PrivateLayout, CustomerLayout, NurseLayout, AdminLayout };
export { PublicLayout, PrivateLayout, CustomerLayout, NurseLayout, AdminLayout, PartnerLayout };