Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
1887cb4
hearing held checks
mertbagt Apr 5, 2026
c7aaa3c
string rename
mertbagt Apr 5, 2026
3b62a93
translation added
mertbagt Apr 8, 2026
fae4baa
handle - hearing rescheduled
mertbagt Apr 8, 2026
c7eb865
cleanup
mertbagt Apr 8, 2026
4893610
Merge pull request #2103 from ACoullard/AC/fix-loading-state-in-newsfeed
ACoullard Apr 22, 2026
ad63191
feat: add the firearms veto referendum and associated schema changes …
fastfadingviolets Apr 28, 2026
5f83cdc
clarifying comments
mertbagt Apr 28, 2026
5a1b168
translation comments
mertbagt Apr 28, 2026
5f3b092
Merge pull request #2095 from mertbagt/hearing_held
mertbagt Apr 29, 2026
9dd240b
Phone verification frontend changes (#2106)
J-C-L Apr 29, 2026
652d0b3
Set maxInstances to 1 to prevent concurrent scraping (#2110)
kiminkim724 Apr 29, 2026
6495fdb
Remove noisy progress logs from audio processor - now that it's worke…
Mephistic Apr 30, 2026
08db8c5
Visual refresh and expanded ballot question support (#2114)
LexaMichaelides May 11, 2026
cd92e61
Bug fixes to direct users to email verification before submitting tes…
J-C-L May 11, 2026
1531aa1
Fix Bill History Scraper (#2123)
ACoullard May 11, 2026
af7134e
Legistator page (skeleton) (#2113)
mertbagt May 11, 2026
53525de
Hide news choice for few articles (#2116)
delexagon May 11, 2026
97715bb
Mock Firebase in Storybook (#2125)
Mephistic May 11, 2026
3c1a5b0
add plan
nesanders May 15, 2026
4cc6f7c
reflect ballot qs
nesanders May 15, 2026
10e53b8
address triggering
nesanders May 15, 2026
2a6deec
feat: implement MCP server with RAG operations and vector sync triggers
nesanders May 15, 2026
c1429b4
fix: resolve CI failures by upgrading firebase-admin and fixing TS er…
nesanders May 15, 2026
72b21e0
feat: upgrade to gemini-embedding-2 and update vector index dimension…
nesanders May 15, 2026
17e0d02
fix: handle potential 'embedding' field name and add missing composit…
nesanders May 15, 2026
fca540e
fix: resolve tsconfig deprecations and upgrade @types/node to fix CI …
nesanders May 15, 2026
c881191
fix: resolve all linting and type errors locally, update lockfiles to…
nesanders May 15, 2026
494c31d
fix: apply formatting fixes across all modified files
nesanders May 15, 2026
636ffcb
fix: remove unused Change import in createVectorIndexer.ts
nesanders May 15, 2026
f285709
Remove showLLMFeatures feature flag
c-tonneslan May 17, 2026
0ba7c98
Storybook stories for phone verification modal and verify account sec…
J-C-L May 20, 2026
b60dcaa
revert multi-column layout for browse pages (#2148)
J-C-L May 27, 2026
0677e44
Run Prettier on Summary.tsx
c-tonneslan May 27, 2026
e98850e
fix: adjust vector search dimensions to 768 to fit Firestore 2048 lim…
nesanders May 27, 2026
995489b
feat: support --limit flag in backfill script for testing on subset o…
nesanders May 27, 2026
d1eabd1
fix: safely resolve firebase project ID at runtime
nesanders May 27, 2026
a2cda9a
fix: resolve project ID from GCLOUD_PROJECT env var
nesanders May 27, 2026
99c79cc
fix: use correct gemini-embedding-2-preview model ID for Vertex AI
nesanders May 27, 2026
c580378
Merge pull request #2142 from c-tonneslan/chore/remove-showLLMFeature…
mertbagt May 27, 2026
b65c9e7
fix: switch embedding model to text-embedding-005 (GA, predict-API co…
nesanders May 27, 2026
bb5d21f
fix: store embeddings as Firestore VectorValue for findNearest vector…
nesanders May 27, 2026
9ba1bb3
feat(mcp): slim search responses, add relevanceScore and filters
nesanders May 27, 2026
de2f29e
feat(mcp): apply shapeTestimony to search_testimony tool
nesanders May 28, 2026
a1e318a
feat(mcp): add HTTP/SSE transport with hybrid auth
nesanders May 28, 2026
6e686fb
feat(dev): add /dev/token page for Firebase ID Token retrieval
nesanders May 28, 2026
17eac09
feat(dev): update /dev/token with MCP client config, expiry info, age…
nesanders May 28, 2026
c9442ce
fix: resolve CI TypeScript errors
nesanders May 28, 2026
134f02e
style: apply Prettier formatting to mcp-server and dev files
nesanders May 28, 2026
0940214
fix: address PR review comments from Mephistic
nesanders May 28, 2026
414f4bd
feat: add topic/committee/sponsor filters, list tools, and testimony …
nesanders May 28, 2026
45e80d7
feat: add court filter, remove dedup, use collection path scoping for…
nesanders May 28, 2026
3034096
feat: add missing vector composite indexes for testimony search filters
nesanders May 29, 2026
2c68ac5
fix: add COLLECTION-scoped vector index for bills search
nesanders May 29, 2026
44e8644
Merge pull request #2141 from nesanders/mcp-server-setup
nesanders May 29, 2026
8248ac9
Restore Testimony Callout Section (#2152)
Mephistic Jun 1, 2026
0afcaef
Navbar Icon Color Correction (#2150)
mertbagt Jun 1, 2026
cfdb926
Add PDF text fallback for bill documents (#2121)
Smoss Jun 1, 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
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,8 @@ cert.txt

# lets each user define their own vscode settings
.vscode/settings.json

.serena/
# local MCP server config (contains auth tokens)
.mcp.json
mcp-server/create-agent-key.ts
1 change: 1 addition & 0 deletions .nvmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20
4 changes: 3 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,6 @@ dist
coverage
storybook-static
llm
playwright-report
playwright-report
CLAUDE.md
.cursor/
118 changes: 118 additions & 0 deletions .storybook/firebase-guards/auth.guard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
const authModule = require("@firebase/auth")
const {
getStorybookAuthState,
setStorybookAuthState,
shouldAllowFirebaseCall,
throwBlockedFirebaseCall
} = require("./common")

function emitAuthState(callback) {
const { user } = getStorybookAuthState()
callback(user)
}

function block(apiName) {
return () => {
throwBlockedFirebaseCall(
"auth",
apiName,
"Mock auth state in your story providers or pass explicit props that avoid auth hooks."
)
}
}

function onAuthStateChanged(auth, nextOrObserver, error, completed) {
if (shouldAllowFirebaseCall()) {
return authModule.onAuthStateChanged(auth, nextOrObserver, error, completed)
}

const callback =
typeof nextOrObserver === "function"
? nextOrObserver
: nextOrObserver?.next ?? (() => undefined)

emitAuthState(callback)

return () => undefined
}

function patchAuthInstance(auth) {
if (!auth || shouldAllowFirebaseCall()) return auth

return new Proxy(auth, {
get(target, prop, receiver) {
if (prop === "onAuthStateChanged") {
return onAuthStateChanged
}
return Reflect.get(target, prop, receiver)
}
})
}

function getAuth(...args) {
const auth = authModule.getAuth(...args)
return patchAuthInstance(auth)
}

function initializeAuth(...args) {
const auth = authModule.initializeAuth(...args)
return patchAuthInstance(auth)
}

module.exports = {
...authModule,
getAuth,
initializeAuth,
onAuthStateChanged,
signInWithEmailAndPassword: block("signInWithEmailAndPassword"),
signInWithPopup: block("signInWithPopup"),
signInWithRedirect: block("signInWithRedirect"),
signInAnonymously: block("signInAnonymously"),
mockLoggedOutAuthState() {
return setStorybookAuthState({ user: null })
},
mockLoggedInUserAuthState(overrides = {}) {
const user = {
uid: "storybook-user",
email: "storybook-user@example.com",
emailVerified: true,
displayName: "Storybook User",
isAnonymous: false,
providerId: "firebase",
photoURL: null,
phoneNumber: null,
tenantId: null,
metadata: {
creationTime: "",
lastSignInTime: ""
},
providerData: [],
refreshToken: "storybook-refresh-token",
stsTokenManager: {
accessToken: "storybook-access-token",
refreshToken: "storybook-refresh-token",
expirationTime: Date.now() + 60 * 60 * 1000
},
getIdToken: async () => "storybook-id-token",
getIdTokenResult: async () => ({
token: "storybook-id-token",
authTime: "",
issuedAtTime: "",
expirationTime: "",
signInProvider: null,
claims: {
role: "user",
email_verified: true
}
}),
reload: async () => undefined,
delete: async () => undefined,
toJSON: () => ({})
}

return setStorybookAuthState({
user: { ...user, ...overrides },
claims: { role: "user", email_verified: true }
})
}
}
68 changes: 68 additions & 0 deletions .storybook/firebase-guards/common.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
const ALLOW_FLAG = "__MAPLE_STORYBOOK_ALLOW_FIREBASE__"
const WARNED_FLAG = "__MAPLE_STORYBOOK_FIREBASE_WARNED__"
const AUTH_STATE_KEY = "__MAPLE_STORYBOOK_FIREBASE_AUTH_STATE__"

function canAllowInRuntime() {
if (typeof globalThis !== "undefined") {
return Boolean(globalThis[ALLOW_FLAG])
}
return false
}

function canAllowFromEnv() {
return process.env.STORYBOOK_ALLOW_FIREBASE_CALLS === "1"
}

function shouldAllowFirebaseCall() {
return canAllowFromEnv() || canAllowInRuntime()
}

function warnOnce(key, message) {
if (typeof globalThis === "undefined") return

if (!globalThis[WARNED_FLAG]) {
globalThis[WARNED_FLAG] = new Set()
}

const warned = globalThis[WARNED_FLAG]
if (!warned.has(key)) {
warned.add(key)
console.warn(message)
}
}

function throwBlockedFirebaseCall(service, apiName, helpText) {
if (shouldAllowFirebaseCall()) return

const message = [
`[Storybook Firebase Guard] Blocked ${service} call: ${apiName}`,
"Real Firebase calls are disabled by default in Storybook.",
"Mock the component data/hooks used by this story instead.",
helpText,
"To opt out temporarily for a specific story, set: parameters.firebaseGuard.allow = true",
"To opt out globally, start Storybook with STORYBOOK_ALLOW_FIREBASE_CALLS=1"
].join("\n")

warnOnce(`${service}:${apiName}`, message)
throw new Error(message)
}

function getStorybookAuthState() {
if (typeof globalThis === "undefined") return { user: null }

return globalThis[AUTH_STATE_KEY] ?? { user: null }
}

function setStorybookAuthState(state) {
if (typeof globalThis !== "undefined") {
globalThis[AUTH_STATE_KEY] = state
}
return state
}

module.exports = {
getStorybookAuthState,
setStorybookAuthState,
shouldAllowFirebaseCall,
throwBlockedFirebaseCall
}
25 changes: 25 additions & 0 deletions .storybook/firebase-guards/firestore.guard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
const firestore = require("@firebase/firestore")
const { throwBlockedFirebaseCall } = require("./common")

function block(apiName) {
return () => {
throwBlockedFirebaseCall(
"firestore",
apiName,
"Provide mocked query results or inject mock props into the component under test."
)
}
}

module.exports = {
...firestore,
getDoc: block("getDoc"),
getDocs: block("getDocs"),
onSnapshot: block("onSnapshot"),
addDoc: block("addDoc"),
setDoc: block("setDoc"),
updateDoc: block("updateDoc"),
deleteDoc: block("deleteDoc"),
runTransaction: block("runTransaction"),
getCountFromServer: block("getCountFromServer")
}
24 changes: 24 additions & 0 deletions .storybook/main.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
/**
* @type {import('@storybook/react/types').StorybookConfig}
*/
const path = require("path")

module.exports = {
stories: [
"../stories/**/*.stories.mdx",
Expand All @@ -24,6 +26,28 @@ module.exports = {
use: ["file-loader"]
})
config.resolve.fallback = { fs: false, path: false }

const path = require("path")
const webpack = require("webpack")
config.plugins.push(
new webpack.NormalModuleReplacementPlugin(
/components\/db\/profile\/profile(\.tsx?)?$/,
path.resolve(__dirname, "../stories/__mocks__/db/profile.ts")
)
)

config.resolve.alias = {
...(config.resolve.alias || {}),
"firebase/firestore$": path.resolve(
__dirname,
"./firebase-guards/firestore.guard.js"
),
"firebase/auth$": path.resolve(
__dirname,
"./firebase-guards/auth.guard.js"
)
}

return config
},

Expand Down
9 changes: 9 additions & 0 deletions .storybook/preview.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import React, { Suspense } from "react"
import { I18nextProvider } from "react-i18next"
// import i18n from "i18next"
import i18n from "./i18n"
const { mockLoggedOutAuthState } = require("./firebase-guards/auth.guard.js")

mockLoggedOutAuthState()

export const parameters = {
actions: { argTypesRegex: "^on[A-Z].*" },
Expand Down Expand Up @@ -60,6 +63,12 @@ export const parameters = {

export const decorators = [
(Story, context) => {
if (typeof window !== "undefined") {
window.__MAPLE_STORYBOOK_ALLOW_FIREBASE__ = Boolean(
context?.parameters?.firebaseGuard?.allow
)
}

return (
<Suspense fallback="Loading...">
<I18nextProvider i18n={i18n}>
Expand Down
17 changes: 17 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,23 @@ git pull upstream main
- `yarn dev:down`: Stop the application.
- `yarn dev:update`: Update the application images. Run this whenever dependencies in `package.json` change.

### Storybook Firebase Guard

Storybook blocks real Firebase Auth and Firestore calls by default to prevent accidental network access from stories.

If a story triggers a blocked call, Storybook throws an error with guidance about what to mock.

Use these patterns when building stories:

- Prefer passing mock props/data to components instead of rendering hook-driven containers.
- If a component supports injection (for example, mock `profile`/`index` props), use that in the story args.
- For auth-driven stories, use the helpers in [stories/utils/storybookFirebaseAuth.ts](stories/utils/storybookFirebaseAuth.ts) to switch between logged-out and logged-in user mocks.

Opt-out options:

- Per story: set `parameters.firebaseGuard.allow = true`.
- Globally: run Storybook with `STORYBOOK_ALLOW_FIREBASE_CALLS=1`.

Install the [Redux DevTools](https://chrome.google.com/webstore/detail/redux-devtools/lmhkpmbekcpmknklioeibfkpmmfibljd) and [React DevTools](https://chrome.google.com/webstore/detail/react-developer-tools/fmkadmapgofadopljbjfkapdkoienihi) browser extensions if you're developing frontend

## Contributing Backend Features to Dev/Prod:
Expand Down
Loading
Loading