Files
pn-new-crm/src/components/shared/detail-page-shell.tsx

78 lines
2.5 KiB
TypeScript
Raw Normal View History

'use client';
import { useEffect, type ReactNode } from 'react';
import { cn } from '@/lib/utils';
import { useMobileChrome } from '@/components/layout/mobile/mobile-layout-provider';
/**
* Wrapper for entity detail pages (clients, yachts, companies, etc.). Renders:
* - desktop sticky compact header (entity name + status pill)
* - the children (existing tab dropdown selector + tab body)
* - optional sticky bottom action shelf, pinned above the bottom tab bar on
* mobile (`pb-[calc(56px+env(safe-area-inset-bottom))]` content padding).
*
* Mobile: the desktop sticky header is hidden because the mobile topbar already
* shows the entity name (pushed via `useMobileChrome`). The optional back
* button is also enabled on mobile so detail pages get an arrow back to the
* list.
*/
export function DetailPageShell({
entityName,
status,
children,
bottomActions,
className,
}: {
entityName: string;
status?: ReactNode;
children: ReactNode;
bottomActions?: ReactNode;
className?: string;
}) {
const { setChrome } = useMobileChrome();
useEffect(() => {
setChrome({ title: entityName, showBackButton: true });
return () => {
setChrome({ title: null, showBackButton: false });
};
}, [entityName, setChrome]);
return (
<div className={cn('flex flex-col min-h-full', className)}>
{/* Desktop-only sticky header — mobile topbar covers this on small viewports. */}
<div className="hidden sm:block sticky top-0 z-10 bg-background/95 backdrop-blur border-b border-border px-4 py-3 sm:px-6">
<div className="flex items-center gap-3 min-w-0">
<h2 className="truncate text-lg font-semibold text-foreground">{entityName}</h2>
{status ? <div className="ml-auto shrink-0">{status}</div> : null}
</div>
</div>
{/* Mobile inline status row — only shown when the page wants to display a status pill. */}
{status ? (
<div className="sm:hidden flex items-center justify-end px-1 pt-1">
<div className="shrink-0">{status}</div>
</div>
) : null}
<div className={cn('flex-1 sm:px-6 sm:py-6', bottomActions && 'pb-24 sm:pb-6')}>
{children}
</div>
{bottomActions ? (
<div
className={cn(
'sm:hidden',
'fixed inset-x-0 bottom-[calc(56px+env(safe-area-inset-bottom))] z-30',
'border-t border-border bg-background/95 backdrop-blur px-4 py-3',
'flex items-center gap-2',
)}
>
{bottomActions}
</div>
) : null}
</div>
);
}