feat: enhance member management features; add quick stats and search capabilities
This commit is contained in:
@@ -0,0 +1,178 @@
|
||||
'use client';
|
||||
|
||||
import type { ReactNode } from 'react';
|
||||
|
||||
import Link from 'next/link';
|
||||
import { usePathname, useRouter } from 'next/navigation';
|
||||
|
||||
import {
|
||||
FileDown,
|
||||
FileUp,
|
||||
IdCard,
|
||||
KeyRound,
|
||||
LayoutList,
|
||||
Settings,
|
||||
Users,
|
||||
} from 'lucide-react';
|
||||
|
||||
import {
|
||||
MemberStatsBar,
|
||||
MemberCommandPalette,
|
||||
} from '@kit/member-management/components';
|
||||
import { Badge } from '@kit/ui/badge';
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from '@kit/ui/dropdown-menu';
|
||||
import { PageBody } from '@kit/ui/page';
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
interface MembersCmsLayoutClientProps {
|
||||
header: ReactNode;
|
||||
children: ReactNode;
|
||||
account: string;
|
||||
accountId: string;
|
||||
stats: {
|
||||
total: number;
|
||||
active: number;
|
||||
pending: number;
|
||||
newThisYear: number;
|
||||
pendingApplications: number;
|
||||
};
|
||||
}
|
||||
|
||||
export function MembersCmsLayoutClient({
|
||||
header,
|
||||
children,
|
||||
account,
|
||||
accountId,
|
||||
stats,
|
||||
}: MembersCmsLayoutClientProps) {
|
||||
const pathname = usePathname();
|
||||
const basePath = `/home/${account}/members-cms`;
|
||||
|
||||
const isOnMembersTab =
|
||||
pathname.endsWith('/members-cms') ||
|
||||
pathname.includes('/members-cms/new') ||
|
||||
/\/members-cms\/[^/]+$/.test(pathname);
|
||||
const isOnApplicationsTab = pathname.includes('/applications');
|
||||
const isOnSubPage =
|
||||
pathname.includes('/import') ||
|
||||
pathname.includes('/edit') ||
|
||||
(/\/members-cms\/[^/]+$/.test(pathname) &&
|
||||
!pathname.endsWith('/members-cms'));
|
||||
|
||||
return (
|
||||
<>
|
||||
{header}
|
||||
|
||||
<PageBody>
|
||||
<div className="space-y-4">
|
||||
{/* Stats bar — only on main views */}
|
||||
{!isOnSubPage && <MemberStatsBar stats={stats} />}
|
||||
|
||||
{/* Tab navigation + settings */}
|
||||
{!isOnSubPage && (
|
||||
<div className="flex items-center justify-between border-b">
|
||||
<nav className="-mb-px flex gap-4">
|
||||
<TabLink
|
||||
href={basePath}
|
||||
active={isOnMembersTab && !isOnApplicationsTab}
|
||||
>
|
||||
<Users className="size-4" />
|
||||
Mitglieder
|
||||
</TabLink>
|
||||
<TabLink
|
||||
href={`${basePath}/applications`}
|
||||
active={isOnApplicationsTab}
|
||||
>
|
||||
<FileUp className="size-4" />
|
||||
Aufnahmeanträge
|
||||
{stats.pendingApplications > 0 && (
|
||||
<Badge
|
||||
variant="destructive"
|
||||
className="ml-1 h-5 min-w-5 px-1 text-xs"
|
||||
>
|
||||
{stats.pendingApplications}
|
||||
</Badge>
|
||||
)}
|
||||
</TabLink>
|
||||
</nav>
|
||||
|
||||
<SettingsMenu basePath={basePath} />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{children}
|
||||
</div>
|
||||
|
||||
<MemberCommandPalette account={account} accountId={accountId} />
|
||||
</PageBody>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function TabLink({
|
||||
href,
|
||||
active,
|
||||
children,
|
||||
}: {
|
||||
href: string;
|
||||
active: boolean;
|
||||
children: ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<Link
|
||||
href={href}
|
||||
className={cn(
|
||||
'inline-flex items-center gap-1.5 border-b-2 px-1 pb-2 text-sm font-medium transition-colors',
|
||||
active
|
||||
? 'border-primary text-foreground'
|
||||
: 'text-muted-foreground hover:text-foreground border-transparent',
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
function SettingsMenu({ basePath }: { basePath: string }) {
|
||||
const router = useRouter();
|
||||
|
||||
const navigate = (path: string) => () => router.push(path);
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger
|
||||
className="hover:bg-muted inline-flex size-7 items-center justify-center rounded-lg"
|
||||
data-test="members-settings-menu"
|
||||
>
|
||||
<Settings className="size-4" />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-52">
|
||||
<DropdownMenuItem onClick={navigate(`${basePath}/dues`)}>
|
||||
<LayoutList className="mr-2 size-4" />
|
||||
Beitragskategorien
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={navigate(`${basePath}/departments`)}>
|
||||
<Users className="mr-2 size-4" />
|
||||
Abteilungen
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={navigate(`${basePath}/cards`)}>
|
||||
<IdCard className="mr-2 size-4" />
|
||||
Mitgliedsausweise
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={navigate(`${basePath}/invitations`)}>
|
||||
<KeyRound className="mr-2 size-4" />
|
||||
Portal-Einladungen
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem onClick={navigate(`${basePath}/import`)}>
|
||||
<FileDown className="mr-2 size-4" />
|
||||
Import
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user