Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
e38dcde
refactor(citizen-pwa): extract useGpsLocation hook from Step2WhoWhere
claude Apr 23, 2026
eef40e9
refactor(citizen-pwa): address review feedback on useGpsLocation hook
claude Apr 23, 2026
19132c0
refactor(citizen-pwa): extract MunicipalitySelector and BarangaySelec…
claude Apr 23, 2026
6f44406
refactor(citizen-pwa): address review feedback on MunicipalitySelecto…
claude Apr 23, 2026
eb90461
refactor(citizen-pwa): extract ContactFields sub-component from Step2…
claude Apr 23, 2026
85248da
refactor(citizen-pwa): address review feedback on ContactFields
claude Apr 23, 2026
8f96905
refactor(citizen-pwa): complete Step2WhoWhere extraction (Phases 1-4)
claude Apr 23, 2026
d36782e
refactor(shared-sms-parser,functions): split inbound.ts and extract d…
claude Apr 23, 2026
7824af7
refactor(monorepo): standardize catch patterns and clean up P2/P3 aud…
claude Apr 23, 2026
6442b69
feat(shared-ui): consolidate auth provider and protected route from apps
claude Apr 23, 2026
c2f4951
test(shared-ui): add characterization tests for AuthProvider and Prot…
claude Apr 23, 2026
29e4eee
chore(functions): rebuild compiled artifacts after source changes
claude Apr 23, 2026
b62a7d6
refactor(functions,sms-parser,shared-ui): address CodeRabbit PR revie…
claude Apr 23, 2026
5b57840
chore(shared-sms-parser): rebuild compiled lib artifacts after source…
claude Apr 23, 2026
1e2a23b
a11y(citizen-pwa): label association and aria-pressed on ContactFields
claude Apr 23, 2026
3e3e0c9
feat(citizen-pwa): municipality validation and validated barangay setter
claude Apr 23, 2026
49a8c2d
feat(citizen-pwa): handle POSITION_UNAVAILABLE (code 2) in useGpsLoca…
claude Apr 23, 2026
62930e1
docs(progress): remove future date from refactor audit heading
claude Apr 23, 2026
f2aca29
refactor(sms-parser): tighten regex quote matching in gazetteer modul…
claude Apr 24, 2026
ef7ee43
feat(citizen-pwa): validate municipality IDs in useMunicipalityBarangays
claude Apr 24, 2026
aeb7889
refactor(functions): defer rawStatus narrowing until after transition…
claude Apr 24, 2026
d93c02b
refactor(functions): import DocumentReference from firebase-admin/fir…
claude Apr 24, 2026
68208a3
feat(sms-parser): support three-word barangay names with municipality…
claude Apr 24, 2026
d9a438f
chore(shared-sms-parser): rebuild compiled lib artifacts after source…
claude Apr 24, 2026
3f3924a
fix(citizen-pwa): treat empty string as clear in handleSelectBarangay
claude Apr 24, 2026
3ce8f43
refactor(functions): assertResponderOnShift object param + explicit v…
claude Apr 24, 2026
9fc70f2
feat(functions): enforce explicit smsConsent before building SMS payload
claude Apr 24, 2026
c42c2e6
refactor(sms-parser): validate gazetteer entries and check 3-word bar…
claude Apr 24, 2026
33ddb00
chore(shared-sms-parser): rebuild compiled lib artifacts after source…
claude Apr 24, 2026
c78a720
fix(functions): remove pre-transaction shift read, use Object.hasOwn …
claude Apr 24, 2026
5a858a6
fix(parser,dispatch): regex details extraction, secret binding, provi…
claude Apr 24, 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: 3 additions & 2 deletions apps/admin-desktop/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { RouterProvider } from 'react-router-dom'
import { AuthProvider } from './app/auth-provider'
import { AuthProvider } from '@bantayog/shared-ui'
import { auth } from './app/firebase'
import { router } from './routes'

export default function App() {
return (
<AuthProvider>
<AuthProvider auth={auth}>
<RouterProvider router={router} />
</AuthProvider>
)
Expand Down
70 changes: 0 additions & 70 deletions apps/admin-desktop/src/app/auth-provider.tsx

This file was deleted.

32 changes: 0 additions & 32 deletions apps/admin-desktop/src/app/protected-route.tsx

This file was deleted.

6 changes: 4 additions & 2 deletions apps/admin-desktop/src/pages/DispatchModal.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from 'react'
import { useAuth } from '../app/auth-provider'
import { useAuth } from '@bantayog/shared-ui'
import { useEligibleResponders } from '../hooks/useEligibleResponders'
import { callables } from '../services/callables'

Expand All @@ -13,7 +13,9 @@ export function DispatchModal({
onError: (msg: string) => void
}) {
const { claims } = useAuth()
const eligible = useEligibleResponders(claims?.municipalityId)
const municipalityId =
typeof claims?.municipalityId === 'string' ? claims.municipalityId : undefined
const eligible = useEligibleResponders(municipalityId)
const [picked, setPicked] = useState<string | null>(null)
const [submitting, setSubmitting] = useState(false)

Expand Down
8 changes: 5 additions & 3 deletions apps/admin-desktop/src/pages/TriageQueuePage.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useState } from 'react'
import { useAuth } from '../app/auth-provider'
import { useAuth } from '@bantayog/shared-ui'
import { useMuniReports } from '../hooks/useMuniReports'
import { ReportDetailPanel } from './ReportDetailPanel'
import { DispatchModal } from './DispatchModal'
Expand All @@ -8,7 +8,9 @@ import { callables } from '../services/callables'

export function TriageQueuePage() {
const { claims, signOut } = useAuth()
const { rows, loading, error } = useMuniReports(claims?.municipalityId)
const municipalityId =
typeof claims?.municipalityId === 'string' ? claims.municipalityId : undefined
const { rows, loading, error } = useMuniReports(municipalityId)
const [selected, setSelected] = useState<string | null>(null)
const [dispatchForReportId, setDispatchForReportId] = useState<string | null>(null)
const [closeForReportId, setCloseForReportId] = useState<string | null>(null)
Expand Down Expand Up @@ -50,7 +52,7 @@ export function TriageQueuePage() {
return (
<main>
<header>
<h1>Triage · {claims?.municipalityId ?? 'N/A'}</h1>
<h1>Triage · {municipalityId ?? 'N/A'}</h1>
<button onClick={() => void signOut()}>Sign out</button>
</header>
{banner && <div role="alert">{banner}</div>}
Expand Down
13 changes: 11 additions & 2 deletions apps/admin-desktop/src/routes.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { createBrowserRouter } from 'react-router-dom'
import { ProtectedRoute } from './app/protected-route'
import { ProtectedRoute } from '@bantayog/shared-ui'
import { LoginPage } from './pages/LoginPage'
import { TriageQueuePage } from './pages/TriageQueuePage'

Expand All @@ -8,7 +8,16 @@ export const router = createBrowserRouter([
{
path: '/',
element: (
<ProtectedRoute>
<ProtectedRoute
allowedRoles={['municipal_admin', 'provincial_superadmin']}
requireActive
requireMunicipalityIdForRoles={['municipal_admin']}
unauthorizedFallback={
<div role="alert">
You do not have access to this page. Please contact your superadmin.
</div>
}
>
<TriageQueuePage />
</ProtectedRoute>
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { describe, it, expect, vi } from 'vitest'
import { fireEvent, render, screen } from '@testing-library/react'
import { BarangaySelector } from './BarangaySelector.js'

describe('BarangaySelector', () => {
it('renders nothing when municipalityId is empty', () => {
const { container } = render(<BarangaySelector municipalityId="" value="" onChange={vi.fn()} />)

expect(container.firstChild).toBeNull()
})

it('renders barangay options for the given municipality', () => {
render(<BarangaySelector municipalityId="daet" value="" onChange={vi.fn()} />)

const select = screen.getByRole('combobox')
expect(select).toBeInTheDocument()

const options = screen.getAllByRole('option')
expect(options[0]).toHaveTextContent('Select barangay (optional)...')

// Daet has specific barangays — spot-check a couple
expect(screen.getByRole('option', { name: 'Alawihao' })).toBeInTheDocument()
expect(screen.getByRole('option', { name: 'Bagasbas' })).toBeInTheDocument()
})

it('calls onChange when selection changes', () => {
const onChange = vi.fn()
render(<BarangaySelector municipalityId="daet" value="" onChange={onChange} />)

const select = screen.getByRole('combobox')
fireEvent.change(select, { target: { value: 'Bagasbas' } })

expect(onChange).toHaveBeenCalledExactlyOnceWith('Bagasbas')
})

it('shows optional label', () => {
render(<BarangaySelector municipalityId="daet" value="" onChange={vi.fn()} />)

expect(screen.getByText(/— optional/)).toBeInTheDocument()
})

it('reflects the value prop on the select element', () => {
render(<BarangaySelector municipalityId="daet" value="Bagasbas" onChange={vi.fn()} />)

const select = screen.getByRole('combobox')
expect(select).toHaveValue('Bagasbas')
})
})
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { useMemo } from 'react'
import { FALLBACK_BARANGAYS, MUNICIPALITY_LABELS } from './location-constants.js'

interface BarangaySelectorProps {
municipalityId: string
value: string
onChange: (barangayId: string) => void
}

export function BarangaySelector({ municipalityId, value, onChange }: BarangaySelectorProps) {
const barangayOptions = useMemo(() => {
if (!municipalityId) return []
return FALLBACK_BARANGAYS.filter(
(b) => MUNICIPALITY_LABELS[municipalityId] === b.municipality,
).sort((a, b) => a.name.localeCompare(b.name))
}, [municipalityId])

if (!municipalityId) {
return null
}

return (
<div className="field-group">
<p className="field-label">
Barangay
<span className="field-label-optional"> — optional</span>
</p>
<select
className="text-select"
value={value}
onChange={(e) => {
onChange(e.target.value)
}}
>
<option value="">Select barangay (optional)...</option>
{barangayOptions.map((b) => (
<option key={b.name} value={b.name}>
{b.name}
</option>
))}
</select>
</div>
)
}
Loading
Loading