Client cards/table: parent (branch icon) and child (arrow icon) visual indicators with tooltips

This commit is contained in:
lorentz 2026-04-09 17:13:19 +00:00
parent ceb82c9ef7
commit 0c6fccadd6
6 changed files with 127 additions and 8 deletions

View file

@ -49,10 +49,12 @@ export default async function ClientsPage() {
premiumAmount: true,
},
},
parentClient: { select: { id: true, name: true } },
_count: {
select: {
policies: true,
tasks: true,
subsidiaries: true,
},
},
},

View file

@ -143,10 +143,12 @@ export async function GET(request: NextRequest) {
},
take: 1,
},
parentClient: { select: { id: true, name: true } },
_count: {
select: {
policies: true,
tasks: true,
subsidiaries: true,
},
},
},

View file

@ -3,7 +3,8 @@
import Link from 'next/link'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { Badge } from '@/components/ui/badge'
import { Building2, Calendar, FileText, CheckSquare } from 'lucide-react'
import { Building2, Calendar, FileText, CheckSquare, GitBranch, CornerLeftUp } from 'lucide-react'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { formatDate, daysUntil } from '@/lib/utils'
interface ClientCardProps {
@ -32,9 +33,11 @@ interface ClientCardProps {
csrName: string | null
status: string | null
}>
parentClient?: { id: string; name: string } | null
_count: {
policies: number
tasks: number
subsidiaries?: number
}
}
}
@ -56,7 +59,34 @@ export function ClientCard({ client }: ClientCardProps) {
</p>
)}
</div>
<Building2 className="h-5 w-5 text-muted-foreground" />
<div className="flex items-center gap-1.5">
{(client._count.subsidiaries ?? 0) > 0 && (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<div className="flex items-center gap-0.5 rounded-full bg-violet-500/10 border border-violet-500/30 px-1.5 py-0.5">
<GitBranch className="h-3.5 w-3.5 text-violet-600 dark:text-violet-400" />
<span className="text-[10px] font-semibold text-violet-700 dark:text-violet-400">{client._count.subsidiaries}</span>
</div>
</TooltipTrigger>
<TooltipContent side="top"><p>Parent · {client._count.subsidiaries} {client._count.subsidiaries === 1 ? 'subsidiary' : 'subsidiaries'}</p></TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{client.parentClient && (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<div className="rounded-full bg-blue-500/10 border border-blue-500/30 p-0.5">
<CornerLeftUp className="h-3.5 w-3.5 text-blue-600 dark:text-blue-400" />
</div>
</TooltipTrigger>
<TooltipContent side="top"><p>Child of {client.parentClient.name}</p></TooltipContent>
</Tooltip>
</TooltipProvider>
)}
<Building2 className="h-5 w-5 text-muted-foreground" />
</div>
</div>
</CardHeader>
<CardContent className="space-y-3">

View file

@ -28,9 +28,11 @@ interface ClientWithRelations extends Client {
designation: Designation | null
designation2: Designation | null
policies: any[]
parentClient?: { id: string; name: string } | null
_count: {
policies: number
tasks: number
subsidiaries?: number
}
}

View file

@ -10,6 +10,8 @@ import {
TableRow,
} from '@/components/ui/table'
import { Badge } from '@/components/ui/badge'
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip'
import { GitBranch, CornerLeftUp } from 'lucide-react'
import { formatDate, daysUntil } from '@/lib/utils'
interface ClientTableProps {
@ -38,9 +40,11 @@ interface ClientTableProps {
csrName: string | null
status: string | null
}>
parentClient?: { id: string; name: string } | null
_count: {
policies: number
tasks: number
subsidiaries?: number
}
}>
}
@ -72,12 +76,34 @@ export function ClientTable({ clients }: ClientTableProps) {
return (
<TableRow key={client.id} className="cursor-pointer hover:bg-muted/50">
<TableCell>
<Link
href={`/clients/${client.id}`}
className="font-medium hover:underline"
>
{client.name}
</Link>
<div className="flex items-center gap-1.5">
<Link
href={`/clients/${client.id}`}
className="font-medium hover:underline"
>
{client.name}
</Link>
{(client._count.subsidiaries ?? 0) > 0 && (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<GitBranch className="h-3.5 w-3.5 text-violet-600 dark:text-violet-400 shrink-0" />
</TooltipTrigger>
<TooltipContent side="top"><p>Parent · {client._count.subsidiaries} {client._count.subsidiaries === 1 ? 'subsidiary' : 'subsidiaries'}</p></TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{client.parentClient && (
<TooltipProvider delayDuration={200}>
<Tooltip>
<TooltipTrigger asChild>
<CornerLeftUp className="h-3.5 w-3.5 text-blue-600 dark:text-blue-400 shrink-0" />
</TooltipTrigger>
<TooltipContent side="top"><p>Child of {client.parentClient.name}</p></TooltipContent>
</Tooltip>
</TooltipProvider>
)}
</div>
</TableCell>
<TableCell className="text-muted-foreground">
{[client.city, client.state].filter(Boolean).join(', ') || '-'}

View file

@ -0,0 +1,57 @@
"use client"
import * as React from "react"
import { Tooltip as TooltipPrimitive } from "radix-ui"
import { cn } from "@/lib/utils"
function TooltipProvider({
delayDuration = 0,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Provider>) {
return (
<TooltipPrimitive.Provider
data-slot="tooltip-provider"
delayDuration={delayDuration}
{...props}
/>
)
}
function Tooltip({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Root>) {
return <TooltipPrimitive.Root data-slot="tooltip" {...props} />
}
function TooltipTrigger({
...props
}: React.ComponentProps<typeof TooltipPrimitive.Trigger>) {
return <TooltipPrimitive.Trigger data-slot="tooltip-trigger" {...props} />
}
function TooltipContent({
className,
sideOffset = 0,
children,
...props
}: React.ComponentProps<typeof TooltipPrimitive.Content>) {
return (
<TooltipPrimitive.Portal>
<TooltipPrimitive.Content
data-slot="tooltip-content"
sideOffset={sideOffset}
className={cn(
"z-50 w-fit origin-(--radix-tooltip-content-transform-origin) animate-in rounded-md bg-foreground px-3 py-1.5 text-xs text-balance text-background fade-in-0 zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
className
)}
{...props}
>
{children}
<TooltipPrimitive.Arrow className="z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px] bg-foreground fill-foreground" />
</TooltipPrimitive.Content>
</TooltipPrimitive.Portal>
)
}
export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }