"use client"; import { useQuery } from "@tanstack/react-query"; import Link from "next/link"; import { alignmentAPI } from "@/lib/api"; import { useAuthStore } from "@/stores/authStore"; import type { AlignmentScore } from "@/lib/types"; function partyColor(party?: string) { if (!party) return "bg-muted text-muted-foreground"; const p = party.toLowerCase(); if (p.includes("republican") || p === "r") return "bg-red-100 text-red-700 dark:bg-red-900/30 dark:text-red-400"; if (p.includes("democrat") || p === "d") return "bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-400"; return "bg-muted text-muted-foreground"; } function AlignmentBar({ pct }: { pct: number }) { const color = pct >= 66 ? "bg-emerald-500" : pct >= 33 ? "bg-amber-500" : "bg-red-500"; return (
); } function MemberRow({ member }: { member: AlignmentScore }) { const pct = member.alignment_pct; return ( {member.photo_url ? ( // eslint-disable-next-line @next/next/no-img-element {member.name} ) : (
{member.name.charAt(0)}
)}
{member.name} {pct != null ? `${Math.round(pct)}%` : "—"}
{member.party && ( {member.party.charAt(0)} )} {member.state && ( {member.state} )} {pct != null && }

{member.aligned} aligned · {member.opposed} opposed · {member.total} overlapping vote{member.total !== 1 ? "s" : ""}

); } export default function AlignmentPage() { const currentUser = useAuthStore((s) => s.user); const { data, isLoading } = useQuery({ queryKey: ["alignment"], queryFn: () => alignmentAPI.get(), enabled: !!currentUser, staleTime: 5 * 60 * 1000, }); if (!currentUser) { return (

Sign in to see your representation alignment.

Sign in →
); } if (isLoading) { return
Loading alignment data…
; } const members = data?.members ?? []; const hasStance = (data?.total_bills_with_stance ?? 0) > 0; const hasFollowedMembers = members.length > 0 || (data?.total_bills_with_votes ?? 0) > 0; return (

Representation Alignment

How often do your followed members vote with your bill positions?

{/* How it works */}

How this works

For every bill you follow with Pocket Boost or Pocket Veto, we check how each of your followed members voted on that bill. A Yea vote on a boosted bill counts as aligned; a Nay vote on a vetoed bill counts as aligned. All other combinations count as opposed. Not Voting and Present are excluded.

{data && (

{data.total_bills_with_stance} bill{data.total_bills_with_stance !== 1 ? "s" : ""} with a stance ·{" "} {data.total_bills_with_votes} had roll-call votes

)}
{/* Empty states */} {!hasStance && (

No bill stances yet.

Follow some bills with{" "} Pocket Boost or Pocket Veto{" "} to start tracking alignment.

)} {hasStance && members.length === 0 && (

No overlapping votes found yet.

Make sure you're{" "} following some members , and that those members have voted on bills you've staked a position on.

)} {/* Member list */} {members.length > 0 && (
{members.map((m) => ( ))}
)}
); }