Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
ce60f87
Upgrade functions and toolchain to node 22
fastfadingviolets Aug 31, 2026
c9d2c37
Merge pull request #2231 from hyphacoop/node-22
Mephistic Aug 31, 2026
352ca5b
searchHealthCheck: 256MB, the nodejs22 bundle no longer loads in 128MB
fastfadingviolets Aug 31, 2026
f0fb6c5
Merge pull request #2232 from hyphacoop/searchhealthcheck-256mb
Mephistic Aug 31, 2026
4e4cf43
search-eval: relevance harness and golden query set for bills (#79)
fastfadingviolets Aug 26, 2026
246ab4f
Upgrade Typesense 0.24 -> 30.2: server pins, JS client 3.x, bundled a…
fastfadingviolets Aug 26, 2026
c3cb1be
search: index versioning, chunked resumable backfill, status tooling
fastfadingviolets Aug 26, 2026
7ba68ce
search: query weights for bills, testimony and hearings
fastfadingviolets Aug 26, 2026
34aa469
search-eval: generalize the harness to hearings and testimony
fastfadingviolets Aug 26, 2026
867ae78
search: legislative synonyms and tokenization, upserted server-side
fastfadingviolets Aug 26, 2026
61cf5b9
search: index bill pinslip and LLM summary between title and body (#8…
fastfadingviolets Aug 26, 2026
bdd67f2
search: sort procedural orders below legislation, and facet the type …
fastfadingviolets Aug 26, 2026
c53c988
search: deployment runbook for the 30.2 cutover (#94)
fastfadingviolets Aug 26, 2026
a50a0fa
run formatter
fastfadingviolets Aug 26, 2026
0befc86
search: fix review findings from the 30.2 cutover branch (#120)
fastfadingviolets Aug 27, 2026
3dd2d23
search: highlight testimony snippets; preview the eval corpus locally
fastfadingviolets Aug 27, 2026
68d197c
[Legislator Page] Upcoming Hearings (#2219)
mertbagt Sep 2, 2026
1f67265
Merge pull request #2227 from hyphacoop/search-weights
Mephistic Sep 4, 2026
b7c08bf
search: bound backfill chunk memory and force collection per batch
fastfadingviolets Sep 8, 2026
99634de
Merge pull request #2235 from hyphacoop/backfillchunk-memory
Mephistic Sep 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/actions/setup-repo/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ inputs:
node-version:
description: "Version used by actions/setup-node"
required: true
default: "20"
default: "22"
yarn-version:
description: "Version of Yarn used to install dependencies"
required: true
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-backend-dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ jobs:
- name: Setup Build Environment
uses: ./.github/actions/setup-repo
- name: Build and Deploy to Firebase
uses: w9jds/firebase-action@v13.18.0
uses: w9jds/firebase-action@v15.28.1
with:
args: deploy --force --only firestore,functions:maple,storage
env:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/deploy-prod.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
- name: Setup Build Environment
uses: ./.github/actions/setup-repo
- name: Build and Deploy to Firebase
uses: w9jds/firebase-action@v13.18.0
uses: w9jds/firebase-action@v15.28.1
with:
args: deploy --force --only firestore,functions:maple,storage
env:
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/repo-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ jobs:

services:
typesense:
image: typesense/typesense:0.24.0
# Keep in sync with infra/Dockerfile.search
image: typesense/typesense:30.2
ports:
- 8108:8108
env:
Expand Down
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,9 @@ CLAUDE.md
#gcloud
.gcloudignore

# search-eval: every corpus is committed, because none of them can be rebuilt
# byte-for-byte from what the repo holds — bills now joins `summary` in from a
# live project on top of the emulator fixture, and hearings/publishedTestimony
# come from prod outright. See tests/search-eval/README.md. Labeling sheets are
# authoring scratch.
tests/search-eval/labeling-sheet-*.md
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20
22
1 change: 1 addition & 0 deletions components/HearingsScheduled/HearingsScheduled.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export type EventData = {
index: number
type: "hearing" | "session"
name: string
code?: string | null
id: number
location?: string
fullDate: Date // TODO: Could be a timestamp
Expand Down
1 change: 1 addition & 0 deletions components/LegislatorProfile/LegislatorProfilePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ export function LegislatorProfilePage({
</Col>
<Col md="3">
<LegislatorSidebar
committeeList={member.Committees}
court={court}
legislatorData={legislatorData}
legislatorId={legislatorId}
Expand Down
24 changes: 23 additions & 1 deletion components/LegislatorProfile/LegislatorSidebar.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
import styled from "styled-components"

import { Biography } from "./SidebarComponents/Biography"
import { OtherTestimony } from "./SidebarComponents/OtherTestimony"
import { UpcomingHearings } from "./SidebarComponents/UpcomingHearings"

export function LegislatorSidebar({
committeeList,
court,
legislatorData,
legislatorId,
memberCode
}: {
committeeList: any[]
court: number
legislatorData: any[]
legislatorId: string
Expand All @@ -16,7 +20,7 @@ export function LegislatorSidebar({
return (
<>
<OtherTestimony />
<UpcomingHearings />
<UpcomingHearings committeeList={committeeList} />
<Biography
court={court}
legislatorData={legislatorData}
Expand All @@ -26,3 +30,21 @@ export function LegislatorSidebar({
</>
)
}

export const SidebarBlock = styled.div`
background-color: white;
border-color: #b8c0c9;
border-radius: 5px;
border-style: solid;
border-width: 1px;
font-size: 11px;
padding: 16px;
`

export const SidebarTitle = styled.div`
font-weight: 700;
color: #0b0a3e;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 10px;
`
33 changes: 8 additions & 25 deletions components/LegislatorProfile/SidebarComponents/Biography.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,36 +6,19 @@ import styled from "styled-components"
import { Form } from "../../bootstrap"
import { Profile, ProfileHook, useProfile } from "../../db"
import Input from "../../forms/Input"
import { SidebarBlock, SidebarTitle } from "../LegislatorSidebar"

import { useAuth } from "components/auth"
import {
updateProfile,
UpdateProfileData
} from "components/EditProfilePage/PersonalInfoTab"

const BioBlock = styled.div`
background-color: white;
border-color: #b8c0c9;
border-radius: 5px;
border-style: solid;
border-width: 1px;
font-size: 11px;
padding: 16px;
`

const BioButton = styled.button`
font-size: 9px;
padding: 2px;
`

const BioTitle = styled.div`
font-weight: 700;
color: #0b0a3e;
text-transform: uppercase;
letter-spacing: 0.05em;
margin-bottom: 10px;
`

export function Biography({
court,
legislatorData,
Expand Down Expand Up @@ -108,12 +91,12 @@ function EditableBiography({
}, [isDirty, setFormUpdated])

return (
<BioBlock>
<SidebarBlock>
<Form onSubmit={onSubmit}>
<div className={`d-flex justify-content-between`}>
<BioTitle className={`align-self-center d-inline my-1`}>
<SidebarTitle className={`align-self-center d-inline my-1`}>
{t("biography")}
</BioTitle>
</SidebarTitle>
<BioButton
type="submit"
className={`btn btn-primary d-inline m-1 w-auto`}
Expand All @@ -131,19 +114,19 @@ function EditableBiography({
defaultValue={about ? about : t("addBio")}
/>
</Form>
</BioBlock>
</SidebarBlock>
)
}

function ReadonlyBiography({ legislatorData }: { legislatorData: any[] }) {
const { t } = useTranslation("legislators")

return (
<BioBlock>
<BioTitle className={`my-1`}>{t("biography")}</BioTitle>
<SidebarBlock>
<SidebarTitle className={`my-1`}>{t("biography")}</SidebarTitle>
<div style={{ whiteSpace: "pre-wrap" }}>
{legislatorData[0]?.about ? legislatorData[0].about : t("notClaimed")}
</div>
</BioBlock>
</SidebarBlock>
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
import { TabBlock } from "../LegislatorComponents"
import { useTranslation } from "next-i18next"

import { SidebarBlock, SidebarTitle } from "../LegislatorSidebar"

export function OtherTestimony() {
return <TabBlock>- Other Testimony</TabBlock>
const { t } = useTranslation("legislators")

return (
<SidebarBlock className="mb-2">
<SidebarTitle className={`my-1`}>{t("otherTestimony")}</SidebarTitle>
<div>Content To Be Added</div>
</SidebarBlock>
)
}
122 changes: 119 additions & 3 deletions components/LegislatorProfile/SidebarComponents/UpcomingHearings.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,121 @@
import { TabBlock } from "../LegislatorComponents"
import { DateTime } from "luxon"
import { useTranslation } from "next-i18next"
import styled from "styled-components"

export function UpcomingHearings() {
return <TabBlock>- Upcoming Hearings</TabBlock>
import * as links from "../../links"
import { Col } from "../../bootstrap"
import { SidebarBlock, SidebarTitle } from "../LegislatorSidebar"

import { useUpcomingEvents } from "components/db/events"
import { EventData } from "components/HearingsScheduled"
import { formatDate } from "components/HearingsScheduled/dateUtils"

const EventBlock = styled.div`
border-bottom: 1px solid #b8c0c9;
font-size: 12px;
font-weight: 700;
margin: 0 2px;
padding: 8px 0;
`

const EventDate = styled.div`
color: #1a3185;
`

const EventTitle = styled.div`
color: #212529;
font-weight: 600;
text-decoration: none;
`

const EventLocation = styled.div`
color: #6c757d;
margin-top: 1px;
`

export const HearingsLink = styled(links.Internal)`
font-size: 12px;
font-weight: 700;
color: #1a3185;
padding: 10px;
display: block;
text-align: center;
text-decoration: none;
`

export function UpcomingHearings({ committeeList }: { committeeList: any[] }) {
const { t } = useTranslation("legislators")

const events = useUpcomingEvents()
const thisMonth = DateTime.now().startOf("month")
const eventList: EventData[] = []
let latestDate = DateTime.now()

if (events) {
for (let e of events) {
const eventDate = DateTime.fromISO(e.content.EventDate)

if (eventDate > latestDate) latestDate = eventDate

const index = Math.floor(eventDate.diff(thisMonth, "months").months)
const date = formatDate(e.content.EventDate)
if (e.type === "hearing") {
eventList.push({
index,
type: e.type,
name: e.content.Name ?? "Hearing",
code: e.content.HearingHost.CommitteeCode,
id: e.content.EventId,
location: e.content.Location?.LocationName ?? undefined,
fullDate: eventDate.toJSDate(),
year: date.year,
month: date.month,
date: date.date,
day: date.day,
time: date.time
})
}
}
}

const LegislatorCommitteCodes = committeeList.map(code => code.CommitteeCode)

let legislatorEvents: any[] = []

if (events) {
eventList.forEach(event => {
if (LegislatorCommitteCodes.includes(event.code)) {
legislatorEvents.push(event)
}
})
}

console.log("legislatorEvents", legislatorEvents)

return (
<SidebarBlock className="mb-2">
<SidebarTitle className={`my-1`}>{t("upcomingHearings")}</SidebarTitle>
{legislatorEvents ? (
<>
{legislatorEvents.slice(0, 3).map(e => (
<EventBlock key={e.id}>
<EventDate>
{e.month} {e.date} {" · "} {e.time}
</EventDate>
<EventTitle>{e.name}</EventTitle>
<EventLocation>{e.location}</EventLocation>
</EventBlock>
))}
</>
) : (
<div>{t("noEvents")}</div>
)}
<Col>
<HearingsLink href="/hearings">
{t("viewAllHearings")}
{" ↗"}
</HearingsLink>
</Col>
</SidebarBlock>
)
}
6 changes: 5 additions & 1 deletion components/search/SortBy.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,11 @@ export type SortByWithConfigurationItem = SortByItem & {

export const SortBy = ({ items }: { items: SortByWithConfigurationItem[] }) => {
const sortBy = useSortBy({ items }),
selected = items.find(i => i.value === sortBy.currentRefinement)!
// A routed URL can restore a sort value that no longer exists — an option
// renamed since the link was shared. connectSortBy passes it through with
// only a dev-mode warning, so fall back to the default option instead of
// crashing the page on `.configure` of undefined.
selected = items.find(i => i.value === sortBy.currentRefinement) ?? items[0]
useConfigure(selected.configure ?? {})
return (
<StyledSelect
Expand Down
Loading
Loading