feat(frontend): refactor Providers page to useProviders + primitives

This commit is contained in:
Tyler
2026-06-19 19:44:54 -06:00
parent deca199951
commit 22b9eb59d2
+74 -47
View File
@@ -1,9 +1,13 @@
import { Building2, MapPin, Phone } from "lucide-react"; import { Building2, MapPin, Phone } from "lucide-react";
import { useAppStore } from "@/store"; import { Skeleton } from "@/components/ui/skeleton";
import { EmptyState } from "@/components/ui/empty-state";
import { ErrorState } from "@/components/ui/error-state";
import { useProviders } from "@/hooks/useProviders";
import { fmt } from "@/lib/format"; import { fmt } from "@/lib/format";
export function Providers() { export function Providers() {
const providers = useAppStore((s) => s.providers); const { data, isLoading, isError, error, refetch } = useProviders();
const items = data?.items ?? [];
return ( return (
<div className="space-y-8 animate-fade-in"> <div className="space-y-8 animate-fade-in">
@@ -19,60 +23,83 @@ export function Providers() {
</p> </p>
</header> </header>
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3"> {isError ? (
{providers.map((p) => ( <ErrorState
<article message="Couldn't load providers from the backend."
key={p.npi} detail={error instanceof Error ? error.message : String(error)}
className="surface rounded-xl p-6 flex flex-col gap-4 hover:bg-muted/20 transition-colors" onRetry={() => refetch()}
> />
<div className="flex items-start gap-3"> ) : null}
<div className="h-10 w-10 rounded-lg bg-muted/60 flex items-center justify-center text-foreground">
<Building2 className="h-5 w-5" strokeWidth={1.5} />
</div>
<div className="min-w-0 flex-1">
<h3 className="text-[15px] font-semibold tracking-tight">
{p.name}
</h3>
<p className="text-[11px] text-muted-foreground num mt-0.5">
NPI {p.npi} · TIN {p.taxId}
</p>
</div>
</div>
<div className="space-y-1.5 text-[13px] text-muted-foreground"> {isLoading ? (
<div className="flex items-center gap-2"> <div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
<MapPin className="h-3.5 w-3.5" strokeWidth={1.75} /> {Array.from({ length: 6 }).map((_, i) => (
<span> <Skeleton key={i} variant="default" height={180} />
{p.address}, {p.city}, {p.state} {p.zip} ))}
</span> </div>
) : items.length === 0 ? (
<div className="surface rounded-xl">
<EmptyState
eyebrow="Providers · directory empty"
message="Providers appear here as soon as an 837P file is parsed."
/>
</div>
) : (
<div className="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
{items.map((p) => (
<article
key={p.npi}
className="surface rounded-xl p-6 flex flex-col gap-4 hover:bg-muted/20 transition-colors"
>
<div className="flex items-start gap-3">
<div className="h-10 w-10 rounded-lg bg-muted/60 flex items-center justify-center text-foreground">
<Building2 className="h-5 w-5" strokeWidth={1.5} />
</div>
<div className="min-w-0 flex-1">
<h3 className="text-[15px] font-semibold tracking-tight">
{p.name}
</h3>
<p className="text-[11px] text-muted-foreground num mt-0.5">
NPI {p.npi} · TIN {p.taxId}
</p>
</div>
</div> </div>
<div className="flex items-center gap-2">
<Phone className="h-3.5 w-3.5" strokeWidth={1.75} />
<span className="num">{p.phone}</span>
</div>
</div>
<div className="grid grid-cols-2 gap-4 pt-4 border-t border-border/40"> <div className="space-y-1.5 text-[13px] text-muted-foreground">
<div> <div className="flex items-center gap-2">
<div className="text-[11px] uppercase tracking-wider text-muted-foreground"> <MapPin className="h-3.5 w-3.5" strokeWidth={1.75} />
Claims <span>
{p.address}, {p.city}, {p.state} {p.zip}
</span>
</div> </div>
<div className="display text-[20px] leading-none mt-1.5"> <div className="flex items-center gap-2">
{fmt.num(p.claimCount)} <Phone className="h-3.5 w-3.5" strokeWidth={1.75} />
<span className="num">{p.phone}</span>
</div> </div>
</div> </div>
<div>
<div className="text-[11px] uppercase tracking-wider text-muted-foreground"> <div className="grid grid-cols-2 gap-4 pt-4 border-t border-border/40">
Outstanding AR <div>
<div className="text-[11px] uppercase tracking-wider text-muted-foreground">
Claims
</div>
<div className="display text-[20px] leading-none mt-1.5">
{fmt.num(p.claimCount)}
</div>
</div> </div>
<div className="display text-[20px] leading-none mt-1.5"> <div>
{fmt.usd(p.outstandingAr)} <div className="text-[11px] uppercase tracking-wider text-muted-foreground">
Outstanding AR
</div>
<div className="display text-[20px] leading-none mt-1.5">
{fmt.usd(p.outstandingAr)}
</div>
</div> </div>
</div> </div>
</div> </article>
</article> ))}
))} </div>
</div> )}
</div> </div>
); );
} }