From 34b31577275b7e283ab94f579cf57f16c6694042 Mon Sep 17 00:00:00 2001 From: slarson Date: Tue, 24 Mar 2026 20:24:08 -0400 Subject: [PATCH 01/25] fix: redirect logged-out users to login when clicking Follow button When a logged-out user clicks the Follow button on the Testimony Detail page, the action silently fails because uid is undefined and Firestore rejects the operation with "Missing or insufficient permissions." This adds an auth guard that redirects to /login?redirect=currentPath, matching the existing auth redirect pattern used elsewhere in the app. Closes #2059 Co-authored-with: Claude Code --- components/shared/FollowButton.tsx | 7 +++++++ components/testimony/TestimonyDetailPage/PolicyActions.tsx | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/components/shared/FollowButton.tsx b/components/shared/FollowButton.tsx index 87fa5ec81..0d227d1a9 100644 --- a/components/shared/FollowButton.tsx +++ b/components/shared/FollowButton.tsx @@ -1,5 +1,6 @@ import { StyledImage } from "components/ProfilePage/StyledProfileComponents" import { useTranslation } from "next-i18next" +import { useRouter } from "next/router" import { useEffect, useContext } from "react" import { Button } from "react-bootstrap" import { useAuth } from "../auth" @@ -22,6 +23,7 @@ export const BaseFollowButton = ({ const { user } = useAuth() const uid = user?.uid + const router = useRouter() const { followStatus, setFollowStatus } = useContext(FollowContext) @@ -52,6 +54,11 @@ export const BaseFollowButton = ({ ) : null const handleClick = (event: React.FormEvent) => { event.preventDefault() + if (!uid) { + const currentPath = router.asPath + router.push(`/login?redirect=${encodeURIComponent(currentPath)}`) + return + } isFollowing ? UnfollowClick() : FollowClick() } diff --git a/components/testimony/TestimonyDetailPage/PolicyActions.tsx b/components/testimony/TestimonyDetailPage/PolicyActions.tsx index 90d8c4d52..bd979c465 100644 --- a/components/testimony/TestimonyDetailPage/PolicyActions.tsx +++ b/components/testimony/TestimonyDetailPage/PolicyActions.tsx @@ -5,6 +5,7 @@ import { formUrl } from "components/publish" import { FC, ReactElement, useContext, useEffect } from "react" import { useCurrentTestimonyDetails } from "./testimonyDetailSlice" import { useTranslation } from "next-i18next" +import { useRouter } from "next/router" import { useAuth } from "components/auth" import { TopicQuery } from "components/shared/FollowingQueries" import { StyledImage } from "components/ProfilePage/StyledProfileComponents" @@ -39,6 +40,7 @@ export const PolicyActions: FC> = ({ const { user } = useAuth() const uid = user?.uid + const router = useRouter() const { followStatus, setFollowStatus } = useContext(FollowContext) @@ -69,6 +71,11 @@ export const PolicyActions: FC> = ({ ) : null const handleClick = (event: React.MouseEvent) => { event.preventDefault() + if (!uid) { + const currentPath = router.asPath + router.push(`/login?redirect=${encodeURIComponent(currentPath)}`) + return + } isFollowing ? UnfollowClick() : FollowClick() } From 2e2de7af9a26ce6a1b2a58bc986e6c65c0ee1f3a Mon Sep 17 00:00:00 2001 From: Nathan Date: Tue, 7 Jul 2026 11:31:19 -0400 Subject: [PATCH 02/25] feat: lobbying explorer Phase 1 foundation - Extend LobbyingFiling type with registrantId; add LobbyingStats, LobbyingClientSummary, LobbyingPositionCounts aggregate types using Dictionary for string-keyed maps (runtypes v5 compat) - Add lobbyingMeta/stats singleton collection constants; add CLIENTS_COLLECTION - Add Firestore composite indexes for registrantId+year and clientNameNorm+year (DESCENDING) on lobbyingFilings to support firm and client detail queries - Add data hooks (useLobbyingFilingsForBill, useLobbyingRegistrant, etc.) in components/db/lobbying.ts following existing useAsync pattern - Add lobbying.json locale namespace; add navigation.lobbying to common.json - Add NavbarLinkLobbying component; wire into Navbar behind lobbyingTable flag - Add LobbyingPositionChip (support/oppose/neutral/none pill), LobbyingFilingsTable (configurable columns), LobbyingBillCard (sidebar card with position stacked bar, truncated table, explorer link) - Replace dummy LobbyingTable in BillDetails with LobbyingBillCard - Enable lobbyingTable feature flag in development env - Install recharts (v3); update chartTheme.tsx for v3 TooltipContentProps API Co-Authored-By: Claude Sonnet 4.6 --- components/Navbar.tsx | 10 + components/NavbarComponents.tsx | 21 + components/bill/BillDetails.tsx | 8 +- components/db/lobbying.ts | 164 ++++++++ components/featureFlags.ts | 2 +- components/lobbying/LobbyingBillCard.tsx | 238 +++++++++++ components/lobbying/LobbyingFilingsTable.tsx | 126 ++++++ components/lobbying/LobbyingPositionChip.tsx | 74 ++++ components/lobbying/chartTheme.tsx | 317 ++++++++++++++ docs/lobbying-frontend-plan.md | 416 +++++++++++++++++++ firestore.indexes.json | 28 ++ functions/src/lobbying/types.ts | 54 +++ package.json | 1 + public/locales/en/common.json | 1 + public/locales/en/lobbying.json | 54 +++ yarn.lock | 239 +++++++++++ 16 files changed, 1750 insertions(+), 3 deletions(-) create mode 100644 components/db/lobbying.ts create mode 100644 components/lobbying/LobbyingBillCard.tsx create mode 100644 components/lobbying/LobbyingFilingsTable.tsx create mode 100644 components/lobbying/LobbyingPositionChip.tsx create mode 100644 components/lobbying/chartTheme.tsx create mode 100644 docs/lobbying-frontend-plan.md create mode 100644 public/locales/en/lobbying.json diff --git a/components/Navbar.tsx b/components/Navbar.tsx index 3c68cdd44..a8b708eae 100644 --- a/components/Navbar.tsx +++ b/components/Navbar.tsx @@ -15,6 +15,7 @@ import { NavbarLinkAiTools, NavbarLinkBills, NavbarLinkHearings, + NavbarLinkLobbying, NavbarLinkEditProfile, NavbarLinkEffective, NavbarLinkFAQ, @@ -72,6 +73,9 @@ const MobileNav: React.FC> = () => { return (