# joalroselin/civicsync-ph architecture

> Architecture diagram of the GitHub repository joalroselin/civicsync-ph, generated by GitDiagram from its file tree, README and a sample of source files (last updated 2026-09-30). It is an AI-made overview: check details against the source.

- Interactive diagram: https://gitdiagram.com/joalroselin/civicsync-ph
- Repository: https://github.com/joalroselin/civicsync-ph

## Overview

CivicSync PH is a Philippine legislation discovery app: users search lawmakers or bills, review authorship and live bill details, then bookmark bills for status updates. The core flow combines Open Congress authorship/history with BatasWatch’s current-congress filings and status. Separate branches cover CMS-managed site content and intake, plus install/offline behavior. Some subsystem links are documented in the README; unsampled implementation details and unshown call relationships are not inferred.

Main entry points: [`lib/bills.ts`](https://github.com/joalroselin/civicsync-ph/blob/main/lib/bills.ts) (Bill merger), [`app/components/WatchlistProvider.tsx`](https://github.com/joalroselin/civicsync-ph/blob/main/app/components/WatchlistProvider.tsx) (Watchlist state), [`lib/batasWatch.ts`](https://github.com/joalroselin/civicsync-ph/blob/main/lib/batasWatch.ts) (BatasWatch client), [`app/page.tsx`](https://github.com/joalroselin/civicsync-ph/blob/main/app/page.tsx) (Home feed)

## Diagram (Mermaid)

```mermaid
flowchart TD

subgraph group_discovery["Bill discovery"]
  node_home["Home feed<br/>[page.tsx]"]
  node_receipts["Receipts search<br/>[page.tsx]"]
  node_person["Lawmaker bills<br/>[page.tsx]"]
  node_billdetail["Bill detail<br/>[page.tsx]"]
  node_billapi["Bill JSON API<br/>[route.ts]"]
  node_opencongress["Open Congress client<br/>[openCongress.ts]"]
  node_bataswatch["BatasWatch client<br/>[batasWatch.ts]"]
  node_billmerge["Bill merger<br/>[bills.ts]"]
  node_cards["Bill cards<br/>[BillCard.tsx]"]
  node_searchnav["Search navigation"]
end

subgraph group_watch["Watchlist"]
  node_watchbutton["Watch controls<br/>[WatchButton.tsx]"]
  node_watchprovider["Watchlist state"]
  node_watchpage["Watchlist page<br/>[page.tsx]"]
  node_watchsync["Cloud sync<br/>[watchlistSync.ts]"]
  node_firestore[("Firestore")]
end

subgraph group_content["Content and intake"]
  node_contentpages["CMS pages<br/>[content.ts]"]
  node_sanity["Sanity client<br/>[sanity.ts]"]
  node_defaults["Fallback content"]
  node_intakeapi["Intake endpoint<br/>[route.ts]"]
  node_intakelib["Intake rules<br/>[intake.ts]"]
  node_studio["Sanity Studio schemas"]
  node_revalidate["Revalidation endpoint<br/>[route.ts]"]
end

subgraph group_platform["App platform"]
  node_serviceworker["Offline service worker<br/>[sw.js]"]
  node_offline["Offline fallback<br/>[page.tsx]"]
end

node_visitor(("Site visitor"))
node_google(("Google sign-in"))

node_visitor -->|"opens"| node_home
node_visitor -->|"searches"| node_receipts
node_home -->|"lists filings"| node_bataswatch
node_home -->|"loads suggestions"| node_contentpages
node_home -->|"summarizes filings"| node_billmerge
node_receipts -->|"searches records"| node_opencongress
node_receipts -->|"checks new bills"| node_bataswatch
node_receipts -->|"renders results"| node_cards
node_person -->|"loads authorship"| node_opencongress
node_person -->|"loads live bills"| node_bataswatch
node_person -->|"uses bill summaries"| node_billmerge
node_billdetail -->|"loads detail"| node_billmerge
node_billmerge -->|"reads records"| node_opencongress
node_billmerge -->|"reads live status"| node_bataswatch
node_billapi -->|"serves detail"| node_billmerge
node_billdetail -->|"offers bookmarking"| node_watchbutton
node_cards -->|"renders control"| node_watchbutton
node_watchbutton -->|"updates watchlist"| node_watchprovider
node_watchpage -->|"reads items"| node_watchprovider
node_watchprovider -->|"refreshes status"| node_bataswatch
node_watchprovider -->|"syncs account data"| node_watchsync
node_watchsync -.->|"stores remotely"| node_firestore
node_google -.->|"enables sign-in"| node_watchprovider
node_contentpages -->|"fetches published content"| node_sanity
node_contentpages -->|"falls back to"| node_defaults
node_intakeapi -->|"validates submissions"| node_intakelib
node_intakeapi -->|"stores submissions"| node_sanity
node_studio -.->|"reviews inbox entries"| node_intakeapi
node_revalidate -->|"handles webhook"| node_sanity
node_serviceworker -->|"falls back offline"| node_offline

click node_home "https://github.com/joalroselin/civicsync-ph/blob/main/app/page.tsx"
click node_receipts "https://github.com/joalroselin/civicsync-ph/blob/main/app/receipts/page.tsx"
click node_person "https://github.com/joalroselin/civicsync-ph/blob/main/app/people/%5Bid%5D/page.tsx"
click node_billdetail "https://github.com/joalroselin/civicsync-ph/blob/main/app/bills/%5Bid%5D/page.tsx"
click node_billapi "https://github.com/joalroselin/civicsync-ph/blob/main/app/api/bills/%5Bid%5D/route.ts"
click node_opencongress "https://github.com/joalroselin/civicsync-ph/blob/main/lib/openCongress.ts"
click node_bataswatch "https://github.com/joalroselin/civicsync-ph/blob/main/lib/batasWatch.ts"
click node_billmerge "https://github.com/joalroselin/civicsync-ph/blob/main/lib/bills.ts"
click node_cards "https://github.com/joalroselin/civicsync-ph/blob/main/app/components/BillCard.tsx"
click node_searchnav "https://github.com/joalroselin/civicsync-ph/blob/main/app/components/SearchNavigation.tsx"
click node_watchbutton "https://github.com/joalroselin/civicsync-ph/blob/main/app/components/WatchButton.tsx"
click node_watchprovider "https://github.com/joalroselin/civicsync-ph/blob/main/app/components/WatchlistProvider.tsx"
click node_watchpage "https://github.com/joalroselin/civicsync-ph/blob/main/app/watchlist/page.tsx"
click node_watchsync "https://github.com/joalroselin/civicsync-ph/blob/main/lib/watchlistSync.ts"
click node_contentpages "https://github.com/joalroselin/civicsync-ph/blob/main/lib/content.ts"
click node_sanity "https://github.com/joalroselin/civicsync-ph/blob/main/lib/sanity.ts"
click node_defaults "https://github.com/joalroselin/civicsync-ph/blob/main/lib/content-defaults.ts"
click node_intakeapi "https://github.com/joalroselin/civicsync-ph/blob/main/app/api/intake/route.ts"
click node_intakelib "https://github.com/joalroselin/civicsync-ph/blob/main/lib/intake.ts"
click node_studio "https://github.com/joalroselin/civicsync-ph/tree/main/studio/schemaTypes"
click node_revalidate "https://github.com/joalroselin/civicsync-ph/blob/main/app/api/revalidate/route.ts"
click node_serviceworker "https://github.com/joalroselin/civicsync-ph/blob/main/public/sw.js"
click node_offline "https://github.com/joalroselin/civicsync-ph/blob/main/app/offline/page.tsx"

classDef toneNeutral fill:#f8fafc,stroke:#334155,stroke-width:1.5px,color:#0f172a
classDef toneBlue fill:#dbeafe,stroke:#2563eb,stroke-width:1.5px,color:#172554
classDef toneAmber fill:#fef3c7,stroke:#d97706,stroke-width:1.5px,color:#78350f
classDef toneMint fill:#dcfce7,stroke:#16a34a,stroke-width:1.5px,color:#14532d
classDef toneRose fill:#ffe4e6,stroke:#e11d48,stroke-width:1.5px,color:#881337
classDef toneIndigo fill:#e0e7ff,stroke:#4f46e5,stroke-width:1.5px,color:#312e81
classDef toneTeal fill:#ccfbf1,stroke:#0f766e,stroke-width:1.5px,color:#134e4a
class node_home,node_receipts,node_person,node_billdetail,node_billapi,node_opencongress,node_bataswatch,node_billmerge,node_cards,node_searchnav toneBlue
class node_watchbutton,node_watchprovider,node_watchpage,node_watchsync,node_firestore toneAmber
class node_contentpages,node_sanity,node_defaults,node_intakeapi,node_intakelib,node_studio,node_revalidate toneMint
class node_serviceworker,node_offline toneRose
class node_visitor,node_google toneIndigo
```

## Components

### Bill discovery

- [Home feed](https://github.com/joalroselin/civicsync-ph/blob/main/app/page.tsx): `app/page.tsx`
- [Receipts search](https://github.com/joalroselin/civicsync-ph/blob/main/app/receipts/page.tsx): `app/receipts/page.tsx`
- [Lawmaker bills](https://github.com/joalroselin/civicsync-ph/blob/main/app/people/%5Bid%5D/page.tsx): `app/people/[id]/page.tsx`
- [Bill detail](https://github.com/joalroselin/civicsync-ph/blob/main/app/bills/%5Bid%5D/page.tsx): `app/bills/[id]/page.tsx`
- [Bill JSON API](https://github.com/joalroselin/civicsync-ph/blob/main/app/api/bills/%5Bid%5D/route.ts): `app/api/bills/[id]/route.ts`
- [Open Congress client](https://github.com/joalroselin/civicsync-ph/blob/main/lib/openCongress.ts): `lib/openCongress.ts`
- [BatasWatch client](https://github.com/joalroselin/civicsync-ph/blob/main/lib/batasWatch.ts): `lib/batasWatch.ts`
- [Bill merger](https://github.com/joalroselin/civicsync-ph/blob/main/lib/bills.ts): `lib/bills.ts`
- [Bill cards](https://github.com/joalroselin/civicsync-ph/blob/main/app/components/BillCard.tsx): `app/components/BillCard.tsx`
- [Search navigation](https://github.com/joalroselin/civicsync-ph/blob/main/app/components/SearchNavigation.tsx): `app/components/SearchNavigation.tsx`

### Watchlist

- [Watch controls](https://github.com/joalroselin/civicsync-ph/blob/main/app/components/WatchButton.tsx): `app/components/WatchButton.tsx`
- [Watchlist state](https://github.com/joalroselin/civicsync-ph/blob/main/app/components/WatchlistProvider.tsx): `app/components/WatchlistProvider.tsx`
- [Watchlist page](https://github.com/joalroselin/civicsync-ph/blob/main/app/watchlist/page.tsx): `app/watchlist/page.tsx`
- [Cloud sync](https://github.com/joalroselin/civicsync-ph/blob/main/lib/watchlistSync.ts): `lib/watchlistSync.ts`
- **Firestore**

### Content and intake

- [CMS pages](https://github.com/joalroselin/civicsync-ph/blob/main/lib/content.ts): `lib/content.ts`
- [Sanity client](https://github.com/joalroselin/civicsync-ph/blob/main/lib/sanity.ts): `lib/sanity.ts`
- [Fallback content](https://github.com/joalroselin/civicsync-ph/blob/main/lib/content-defaults.ts): `lib/content-defaults.ts`
- [Intake endpoint](https://github.com/joalroselin/civicsync-ph/blob/main/app/api/intake/route.ts): `app/api/intake/route.ts`
- [Intake rules](https://github.com/joalroselin/civicsync-ph/blob/main/lib/intake.ts): `lib/intake.ts`
- [Sanity Studio schemas](https://github.com/joalroselin/civicsync-ph/tree/main/studio/schemaTypes): `studio/schemaTypes`
- [Revalidation endpoint](https://github.com/joalroselin/civicsync-ph/blob/main/app/api/revalidate/route.ts): `app/api/revalidate/route.ts`

### App platform

- [Offline service worker](https://github.com/joalroselin/civicsync-ph/blob/main/public/sw.js): `public/sw.js`
- [Offline fallback](https://github.com/joalroselin/civicsync-ph/blob/main/app/offline/page.tsx): `app/offline/page.tsx`

### Other components

- **Site visitor**
- **Google sign-in**

## Connections

- Site visitor → Home feed: opens (evidence: `README.md`)
- Site visitor → Receipts search: searches (evidence: `README.md`)
- Home feed → BatasWatch client: lists filings (evidence: `app/page.tsx`)
- Home feed → CMS pages: loads suggestions (evidence: `app/page.tsx`)
- Home feed → Bill merger: summarizes filings (evidence: `app/page.tsx`)
- Receipts search → Open Congress client: searches records (evidence: `app/receipts/page.tsx`)
- Receipts search → BatasWatch client: checks new bills (evidence: `app/receipts/page.tsx`)
- Receipts search → Bill cards: renders results (evidence: `app/receipts/page.tsx`)
- Lawmaker bills → Open Congress client: loads authorship (evidence: `app/people/[id]/page.tsx`)
- Lawmaker bills → BatasWatch client: loads live bills (evidence: `app/people/[id]/page.tsx`)
- Lawmaker bills → Bill merger: uses bill summaries (evidence: `app/people/[id]/page.tsx`)
- Bill detail → Bill merger: loads detail (evidence: `app/bills/[id]/page.tsx`)
- Bill merger → Open Congress client: reads records (evidence: `lib/bills.ts`)
- Bill merger → BatasWatch client: reads live status (evidence: `lib/bills.ts`)
- Bill JSON API → Bill merger: serves detail (evidence: `app/api/bills/[id]/route.ts`)
- Bill detail → Watch controls: offers bookmarking (evidence: `app/bills/[id]/page.tsx`)
- Bill cards → Watch controls: renders control (evidence: `app/components/BillCard.tsx`)
- Watch controls → Watchlist state: updates watchlist (evidence: `app/components/WatchButton.tsx`)
- Watchlist page → Watchlist state: reads items (evidence: `app/watchlist/page.tsx`)
- Watchlist state → BatasWatch client: refreshes status (evidence: `README.md`)
- Watchlist state → Cloud sync: syncs account data (evidence: `app/components/WatchlistProvider.tsx`)
- Cloud sync → Firestore: stores remotely (evidence: `README.md`)
- Google sign-in → Watchlist state: enables sign-in (evidence: `README.md`)
- CMS pages → Sanity client: fetches published content (evidence: `lib/content.ts`)
- CMS pages → Fallback content: falls back to (evidence: `lib/content.ts`)
- Intake endpoint → Intake rules: validates submissions (evidence: `app/api/intake/route.ts`)
- Intake endpoint → Sanity client: stores submissions (evidence: `README.md`)
- Sanity Studio schemas → Intake endpoint: reviews inbox entries (evidence: `README.md`)
- Revalidation endpoint → Sanity client: handles webhook (evidence: `app/api/revalidate/route.ts`)
- Offline service worker → Offline fallback: falls back offline (evidence: `README.md`)
