Add for leave family
This commit is contained in:
46
apps/frontend/components/id/mobile-nav.tsx
Normal file
46
apps/frontend/components/id/mobile-nav.tsx
Normal file
@@ -0,0 +1,46 @@
|
||||
'use client';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { FileText, Heart, Home, LockKeyhole, ShieldCheck, UserRound } from 'lucide-react';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { getAdminLandingPath } from '@/lib/admin-access';
|
||||
import { useAuth } from './auth-provider';
|
||||
|
||||
const baseItems = [
|
||||
{ href: '/', label: 'Главная', icon: Home },
|
||||
{ href: '/data', label: 'Данные', icon: UserRound },
|
||||
{ href: '/documents', label: 'Документы', icon: FileText },
|
||||
{ href: '/family', label: 'Семья', icon: Heart },
|
||||
{ href: '/security', label: 'Защита', icon: LockKeyhole }
|
||||
];
|
||||
|
||||
export function MobileNav({ active }: { active: string }) {
|
||||
const { user } = useAuth();
|
||||
const items = user?.canAccessAdmin
|
||||
? [...baseItems, { href: getAdminLandingPath(user), label: 'Админ', icon: ShieldCheck }]
|
||||
: baseItems;
|
||||
|
||||
return (
|
||||
<nav className="fixed inset-x-0 bottom-0 z-50 border-t border-[#eceef4] bg-white/95 px-2 pb-[max(0.5rem,env(safe-area-inset-bottom))] pt-2 backdrop-blur lg:hidden">
|
||||
<div className="mx-auto flex max-w-lg items-stretch justify-around gap-1">
|
||||
{items.map((item) => {
|
||||
const isActive =
|
||||
active === item.href || (item.href.startsWith('/admin') && active.startsWith('/admin')) || (item.href === '/family' && active.startsWith('/family'));
|
||||
return (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={cn(
|
||||
'flex min-w-0 flex-1 flex-col items-center gap-1 rounded-xl px-1 py-1.5 text-[10px] font-medium transition',
|
||||
isActive ? 'text-[#3390ec]' : 'text-[#667085]'
|
||||
)}
|
||||
>
|
||||
<item.icon className={cn('h-5 w-5 shrink-0', isActive && 'stroke-[2.25]')} />
|
||||
<span className="truncate">{item.label}</span>
|
||||
</Link>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</nav>
|
||||
);
|
||||
}
|
||||
@@ -26,7 +26,7 @@ export function SettingsFieldRow({ setting, onChange }: SettingsFieldRowProps) {
|
||||
<p className="mt-1 text-sm text-[#667085]">{meta?.hint ?? setting.description ?? setting.key}</p>
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 items-center gap-2">
|
||||
<div className="flex w-full shrink-0 items-center gap-2 md:w-auto">
|
||||
{type === 'boolean' ? (
|
||||
<button
|
||||
type="button"
|
||||
@@ -41,7 +41,7 @@ export function SettingsFieldRow({ setting, onChange }: SettingsFieldRowProps) {
|
||||
</button>
|
||||
) : type === 'select' ? (
|
||||
<select
|
||||
className="h-10 min-w-[220px] rounded-xl border border-[#e4e7ec] bg-white px-3 text-sm outline-none focus:border-[#98a2b3]"
|
||||
className="h-10 w-full min-w-0 rounded-xl border border-[#e4e7ec] bg-white px-3 text-sm outline-none focus:border-[#98a2b3] md:min-w-[220px] md:w-auto"
|
||||
value={setting.value}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
>
|
||||
@@ -55,7 +55,7 @@ export function SettingsFieldRow({ setting, onChange }: SettingsFieldRowProps) {
|
||||
<textarea
|
||||
value={setting.value}
|
||||
rows={3}
|
||||
className="min-h-[88px] w-full min-w-[280px] resize-y rounded-xl border border-[#e4e7ec] bg-white px-3 py-2 text-sm outline-none focus:border-[#98a2b3] md:w-[360px]"
|
||||
className="min-h-[88px] w-full min-w-0 resize-y rounded-xl border border-[#e4e7ec] bg-white px-3 py-2 text-sm outline-none focus:border-[#98a2b3] md:w-[360px]"
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
/>
|
||||
) : (
|
||||
@@ -63,7 +63,7 @@ export function SettingsFieldRow({ setting, onChange }: SettingsFieldRowProps) {
|
||||
<Input
|
||||
type={setting.isSecret ? 'password' : type === 'number' ? 'number' : 'text'}
|
||||
value={setting.value}
|
||||
className={setting.isSecret ? 'w-full min-w-[240px] bg-white md:w-[280px]' : 'w-full min-w-[180px] bg-white md:w-[220px]'}
|
||||
className={setting.isSecret ? 'w-full min-w-0 bg-white md:w-[280px]' : 'w-full min-w-0 bg-white md:w-[220px]'}
|
||||
onChange={(event) => onChange(event.target.value)}
|
||||
/>
|
||||
{meta?.unit ? <span className="text-sm text-[#667085]">{meta.unit}</span> : null}
|
||||
|
||||
@@ -1,25 +1,61 @@
|
||||
'use client';
|
||||
|
||||
import { Sidebar } from './sidebar';
|
||||
import { MobileNav } from './mobile-nav';
|
||||
import { UserMenu } from './user-menu';
|
||||
import { NotificationBell } from '@/components/notifications/notification-bell';
|
||||
import { EmojiSpriteProvider } from '@/components/chat/emoji-sprite-provider';
|
||||
import { FamilyOverlayProvider } from '@/components/family/family-overlay-provider';
|
||||
import { MiniFamilyChat } from '@/components/family/mini-family-chat';
|
||||
import { BrandLogo } from '@/components/id/brand-logo';
|
||||
import Link from 'next/link';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export function IdShell({ active, children, wide = false }: { active: string; children: React.ReactNode; wide?: boolean }) {
|
||||
export function IdShell({
|
||||
active,
|
||||
children,
|
||||
wide = false,
|
||||
fullBleed = false
|
||||
}: {
|
||||
active: string;
|
||||
children: React.ReactNode;
|
||||
wide?: boolean;
|
||||
fullBleed?: boolean;
|
||||
}) {
|
||||
return (
|
||||
<FamilyOverlayProvider>
|
||||
<EmojiSpriteProvider />
|
||||
<div className="min-h-screen bg-white">
|
||||
<div className="min-h-screen overflow-x-hidden bg-white">
|
||||
<Sidebar active={active} />
|
||||
<div className="fixed right-4 top-4 z-40 flex items-center gap-2 lg:right-8 lg:top-6">
|
||||
<div className="fixed left-0 right-0 top-0 z-40 flex items-center justify-between border-b border-[#eceef4] bg-white/95 px-4 py-3 backdrop-blur lg:hidden">
|
||||
<Link href="/" className="shrink-0">
|
||||
<BrandLogo size="sm" variant="dark" />
|
||||
</Link>
|
||||
<div className="flex items-center gap-2">
|
||||
<NotificationBell />
|
||||
<UserMenu />
|
||||
</div>
|
||||
</div>
|
||||
<div className="fixed right-4 top-4 z-40 hidden items-center gap-2 lg:right-8 lg:top-6 lg:flex">
|
||||
<NotificationBell />
|
||||
<UserMenu />
|
||||
</div>
|
||||
<main className="px-5 py-8 lg:pl-[220px] lg:pr-8">
|
||||
<div className={wide ? 'mx-auto max-w-[920px]' : 'mx-auto max-w-[560px]'}>{children}</div>
|
||||
<main
|
||||
className={cn(
|
||||
'pb-[calc(4.5rem+env(safe-area-inset-bottom))] pt-14 lg:pb-8 lg:pt-8 lg:pl-[220px] lg:pr-8',
|
||||
fullBleed && 'px-0 pb-[calc(4.5rem+env(safe-area-inset-bottom))] pt-14 lg:px-8'
|
||||
)}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'mx-auto w-full min-w-0',
|
||||
fullBleed ? 'max-w-none' : wide ? 'max-w-[920px] px-4 sm:px-5 lg:px-0' : 'max-w-[560px] px-4 sm:px-5 lg:px-0'
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
</main>
|
||||
<MobileNav active={active} />
|
||||
<MiniFamilyChat />
|
||||
</div>
|
||||
</FamilyOverlayProvider>
|
||||
|
||||
Reference in New Issue
Block a user