frontend phase 14

This commit is contained in:
hamid
2026-07-10 18:46:16 +03:30
parent 85488bc25b
commit bc51cf59b4
73 changed files with 3582 additions and 13 deletions
+26 -4
View File
@@ -1,8 +1,11 @@
'use client';
import { FunctionComponent, PropsWithChildren, useMemo } from 'react';
import { Box, Stack } from '@mui/material';
import { useTranslations } from 'next-intl';
import { useLocale, useTranslations } from 'next-intl';
import { useRouter } from 'next/navigation';
import { ErrorBoundary } from '@/components';
import AppIconButton from '@/components/common/AppIconButton';
import { NotificationBell } from '@/components/notifications';
import { CONTENT_MAX_WIDTH } from '@/components/config';
import { ROUTES } from '@/constants';
import { LinkToPage } from '@/utils';
@@ -13,14 +16,17 @@ import { TOP_BAR_DESKTOP_HEIGHT, TOP_BAR_MOBILE_HEIGHT } from './config';
/**
* Customer (family) app shell — the primary, mobile-first experience.
* A slim TopBar, a scrollable content column constrained to reading width, and the
* 5-tab BottomBar (Home/Bookings/Patients/Wallet/Profile) from the wireframe.
* A slim TopBar (a "Support / My Tickets" action at the start, the notification bell + dark-mode toggle at
* the end), a scrollable content column constrained to reading width, and the 5-tab BottomBar
* (Home/Bookings/Patients/Wallet/Profile) from the wireframe.
* @layout CustomerLayout
*/
const CustomerLayout: FunctionComponent<PropsWithChildren> = ({ children }) => {
const t = useTranslations('nav');
const tShell = useTranslations('shell');
const onMobile = useIsMobile();
const router = useRouter();
const locale = useLocale();
const bottomNavItems: Array<LinkToPage> = useMemo(
() => [
@@ -36,7 +42,23 @@ const CustomerLayout: FunctionComponent<PropsWithChildren> = ({ children }) => {
return (
<Stack sx={{ height: '100dvh' }}>
<Stack component="header">
<TopBar title={tShell('customer_app')} endNode={<DarkModeToggleButton />} />
<TopBar
title={tShell('customer_app')}
startNode={
<AppIconButton
icon="support"
color="inherit"
title={t('support')}
onClick={() => router.push(`/${locale}${ROUTES.SUPPORT_TICKETS}`)}
/>
}
endNode={
<Stack direction="row" sx={{ alignItems: 'center' }}>
<NotificationBell role="customer" />
<DarkModeToggleButton />
</Stack>
}
/>
</Stack>
<Box
+3
View File
@@ -1,6 +1,7 @@
'use client';
import { FunctionComponent, PropsWithChildren, useMemo } from 'react';
import { useTranslations } from 'next-intl';
import { NotificationBell } from '@/components/notifications';
import { ROUTES } from '@/constants';
import { LinkToPage } from '@/utils';
import TopBarAndSideBarLayout from './TopBarAndSideBarLayout';
@@ -26,6 +27,7 @@ const NurseLayout: FunctionComponent<PropsWithChildren> = ({ children }) => {
{ title: t('verification'), path: ROUTES.NURSE_VERIFICATION, icon: 'verification' },
{ title: t('visits'), path: ROUTES.NURSE_VISITS, icon: 'visits' },
{ title: t('earnings'), path: ROUTES.NURSE_EARNINGS, icon: 'earnings' },
{ title: t('support'), path: ROUTES.NURSE_SUPPORT_TICKETS, icon: 'support' },
],
[t]
);
@@ -35,6 +37,7 @@ const NurseLayout: FunctionComponent<PropsWithChildren> = ({ children }) => {
sidebarItems={sidebarItems}
title={tShell('nurse_app')}
variant="sidebarPersistentOnDesktop"
headerActions={<NotificationBell role="nurse" />}
>
{children}
</TopBarAndSideBarLayout>
+14 -5
View File
@@ -1,5 +1,5 @@
'use client';
import { FunctionComponent, useMemo, useState } from 'react';
import { FunctionComponent, ReactNode, useMemo, useState } from 'react';
import { Stack, StackProps } from '@mui/material';
import { AppIconButton, ErrorBoundary } from '@/components';
import { LinkToPage } from '@/utils';
@@ -19,9 +19,11 @@ interface Props extends StackProps {
sidebarItems: Array<LinkToPage>;
title: string;
variant: 'sidebarAlwaysTemporary' | 'sidebarPersistentOnDesktop' | 'sidebarAlwaysPersistent';
/** Extra chrome actions (e.g. the notification bell) rendered next to the dark-mode toggle. */
headerActions?: ReactNode;
}
const TopBarAndSideBarLayout: FunctionComponent<Props> = ({ children, sidebarItems, title, variant }) => {
const TopBarAndSideBarLayout: FunctionComponent<Props> = ({ children, sidebarItems, title, variant, headerActions }) => {
const [sidebarVisible, setSidebarVisible] = useState(false);
const onMobile = useIsMobile();
@@ -75,11 +77,18 @@ const TopBarAndSideBarLayout: FunctionComponent<Props> = ({ children, sidebarIte
/*
* DarkModeToggleButton is a self-contained component that subscribes to
* useColorScheme() on its own. This layout component never reads the color
* scheme and therefore never re-renders when the theme switches.
* scheme and therefore never re-renders when the theme switches. The optional
* headerActions (e.g. the notification bell) travel alongside it.
*/
const headerControls = (
<Stack direction="row" sx={{ alignItems: 'center' }}>
{headerActions}
<DarkModeToggleButton />
</Stack>
);
const { startNode, endNode } = sidebarProps?.anchor?.includes('left')
? { startNode: LogoButton, endNode: <DarkModeToggleButton /> }
: { startNode: <DarkModeToggleButton />, endNode: LogoButton };
? { startNode: LogoButton, endNode: headerControls }
: { startNode: headerControls, endNode: LogoButton };
return (
<Stack sx={stackStyles}>