"use client"; import { useState } from "react"; import Link from "next/link"; import { MapPin, Search, Heart } from "lucide-react"; import { useQuery } from "@tanstack/react-query"; import { useMembers, useMember } from "@/lib/hooks/useMembers"; import { useFollows } from "@/lib/hooks/useFollows"; import { useAuthStore } from "@/stores/authStore"; import { FollowButton } from "@/components/shared/FollowButton"; import { membersAPI } from "@/lib/api"; import { cn, partyBadgeColor } from "@/lib/utils"; import type { Member } from "@/lib/types"; function MemberCard({ member }: { member: Member }) { return (
{member.photo_url ? ( {member.name} ) : (
{member.name[0]}
)}
{member.name}
{member.party && ( {member.party} )} {member.state && {member.state}} {member.chamber && {member.chamber}}
{(member.phone || member.official_url) && (
{member.phone && ( {member.phone} )} {member.official_url && ( Contact )}
)}
); } function FollowedMemberRow({ bioguideId }: { bioguideId: string }) { const { data: member } = useMember(bioguideId); if (!member) return null; return ; } export default function MembersPage() { const [q, setQ] = useState(""); const [chamber, setChamber] = useState(""); const [party, setParty] = useState(""); const [page, setPage] = useState(1); const [zipInput, setZipInput] = useState(""); const [submittedZip, setSubmittedZip] = useState(""); const { data, isLoading } = useMembers({ ...(q && { q }), ...(chamber && { chamber }), ...(party && { party }), page, per_page: 50, }); const token = useAuthStore((s) => s.token); const { data: follows } = useFollows(); const followedMemberIds = follows?.filter((f) => f.follow_type === "member").map((f) => f.follow_value) ?? []; const isValidZip = /^\d{5}$/.test(submittedZip); const { data: myReps, isFetching: repsFetching, error: repsError } = useQuery({ queryKey: ["members-by-zip", submittedZip], queryFn: () => membersAPI.byZip(submittedZip), enabled: isValidZip, staleTime: 24 * 60 * 60 * 1000, retry: false, }); function handleZipSubmit(e: React.FormEvent) { e.preventDefault(); setSubmittedZip(zipInput.trim()); } return (

Members

Browse current Congress members

{/* Zip lookup */}

Find your representatives

setZipInput(e.target.value)} placeholder="Enter ZIP code" maxLength={5} className="px-3 py-2 text-sm bg-background border border-border rounded-md focus:outline-none focus:ring-1 focus:ring-primary w-40" />
{repsFetching && (

Looking up representatives…

)} {repsError && (

Could not look up representatives. Check your ZIP and try again.

)} {isValidZip && !repsFetching && myReps && myReps.length === 0 && (

No representatives found for {submittedZip}.

)} {myReps && myReps.length > 0 && (

Representatives for ZIP {submittedZip}

{myReps.map((rep) => ( ))}
)}
{/* Filters */}
{ setQ(e.target.value); setPage(1); }} className="w-full pl-9 pr-3 py-2 text-sm bg-card border border-border rounded-md focus:outline-none" />
{token && followedMemberIds.length > 0 && (

Following ({followedMemberIds.length})

{followedMemberIds.map((id) => ( ))}
)} {isLoading ? (
Loading members...
) : ( <>
{data?.total ?? 0} members
{data?.items?.map((member) => ( ))}
{data && data.pages > 1 && (
)} )}
); }