frontend phase 14
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}>
|
||||
|
||||
Reference in New Issue
Block a user