Conversation
…pgrade prompt per spec §4.1
…eOfflineQueueCount test
… for applying the reference design system to the citizen PWA:\nTailwind CSS + framer-motion restyle, SplashScreen, Onboarding, uiStore,\nCitizenShell navbar spring, ReceiptScreen radar pulse ceremony, MapTab\nchrome, FeedTab card layout. All existing 203 tests preserved.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… nav, page transitions
…ref reveal, no confetti
…g, fix banner assertion
Replace CSS transition with AnimatePresence + motion.div. Preserve 3s auto-dismiss in useToast hook. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…s, location FAB Tailwind-only FilterBar with glassmorphism. Add Crosshair recenter FAB. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…rd style - PeekSheet: compact floating card with drag handle, MapPin icon, brand-500 Track link, surface-500 Report Similar link - DetailSheet: full slide-up bottom sheet with rounded-t-3xl, X close button (lucide-react), Tailwind utility classes throughout, severity badge via inline style for dynamic colors, progress tracker dots via Tailwind + CSS vars Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…heet CSS vars to hex\n\n- Remove unhandled "Report Similar" button in PeekSheet\n- Replace CSS var(--color-primary) and var(--color-surface-container-low)\n with static hex values in DetailSheet progress tracker\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…tyles\n\n- Convert all inline style objects to Tailwind utility classes\n- Gradient CTA: bg-gradient-to-br from-[#0f9488] to-[#0d7377]\n- Focus ring: focus:border-[#0f9488] motion-safe:transition-colors\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…test assertion\n\n- Filter chips: border-none cursor-pointer classes\n- Scroll container: no-scrollbar class instead of inline scrollbarWidth\n- Update test to assert classList.contains("border-none") instead of style.border\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ine styles to Tailwind\n\n- ReceiptScreen: max-h-[85vh] class instead of inline maxHeight\n- SplashScreen: bg-white/10 instead of rgba(255,255,255,0.1)\n- Onboarding: conditional h-16/h-14 classes instead of inline height style\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ig + update test mock\n\n- Initialize authLoading via useState(() => hasFirebaseConfig()) to avoid\n set-state-in-effect lint error and prevent crash when env vars missing\n- Guard onAuthStateChanged subscription behind hasFirebaseConfig() check\n- Add hasFirebaseConfig: () => true to ProfileTab.test.tsx mock\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…eparated settings\n\n- Add achievement badge system (First Report, Verified Reporter,\n Community Helper, Active Citizen) with earned/unearned states\n- Add Report Milestones tracker with visual progress bars\n- Restyle Settings as a standalone card with chevron navigation\n- Convert all hardcoded hex colors to surface/brand Tailwind tokens\n- Keep existing auth state, reports list, delete account flow\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ep1Evidence: card-based photo capture, 2-col incident type grid,\n sticky header with backdrop blur, horizontal bar step indicators\n- Step2WhoWhere: restyled location picker, manual/GPS cards,\n contact fields with surface/brand tokens\n- ContactFields: keep "Is anyone hurt?" toggle with vanishing patient\n counter, restyled with danger/brand tokens\n- Step3Review: "We heard you..." banner in brand-50 card,\n summary review cards, consent checkbox, sticky submit button\n- All 216 tests pass, lint clean, typecheck clean\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…kens\n\n- DetailSheet LABELS: accident → Accidents/Rescue, structural → Damages,\n other → Others to match report form display names\n- Migrate LOW severity color from hardcoded navy to charcoal (#414849)\n for better contrast on light backgrounds\n- FilterBar active chip: hardcoded bg #001e40 → surface-900 Tailwind token\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n\n- Shell: replace min-h + fixed-nav layout with h-[100dvh] flex-col;\n main becomes flex-1 relative, nav becomes shrink-0 — fixes map z-index\n bleed-through on mobile and clips horizontal page-transition slide\n- Offline banner: sticky → shrink-0 so it pushes main down in flex flow\n- Page motion div: add absolute inset-0 so it fills the flex-1 main\n- Report FAB: AlertTriangle gradient → CirclePlus solid bg-brand-600 64px\n circle; positioned absolute -top-10 so it floats above the nav bar\n- Report label: absolute bottom-[14px] aligns it flush with all other\n tab labels regardless of the FAB negative-top offset\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Sorry @Exc1D, your pull request is larger than the review limit of 150000 diff characters
|
Warning Rate limit exceeded
To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
📝 WalkthroughWalkthroughAdds Tailwind CSS and Framer Motion to the Citizen PWA; introduces animated SplashScreen and Onboarding flows, a RootLayout route nesting, new UI store fields for nav direction and onboarding completion, many components restyled to Tailwind with motion-driven transitions, test updates, and a data-export callable. Changes
Sequence Diagram(s)sequenceDiagram
participant User
participant SplashScreen
participant UIStore
participant Router
participant Onboarding
User->>SplashScreen: App launches / initial load
SplashScreen->>UIStore: read hasCompletedOnboarding
SplashScreen->>SplashScreen: play animations (~1600ms)
alt hasCompletedOnboarding == false
SplashScreen-->>Router: navigate to /onboarding (replace)
Router->>Onboarding: render onboarding flow
User->>Onboarding: advance steps, give consent
Onboarding->>UIStore: setHasCompletedOnboarding(true)
UIStore->>Onboarding: persisted flag
Onboarding-->>Router: navigate to /
else
SplashScreen-->>Router: navigate to / (replace)
Router->>User: render app shell
end
sequenceDiagram
participant User
participant CitizenShell
participant UIStore
participant AnimatePresence
participant OfflineService
User->>CitizenShell: tap bottom nav tab
CitizenShell->>UIStore: setNavDirection('forward' or 'backward')
CitizenShell->>AnimatePresence: update key (pathname)
AnimatePresence->>AnimatePresence: exit old page (animated)
AnimatePresence->>AnimatePresence: enter new page (animated)
AnimatePresence-->>User: render new route with transition
OfflineService-->>CitizenShell: status change / queueCount update
CitizenShell->>AnimatePresence: render offline banner (AnimatePresence)
AnimatePresence-->>User: show animated offline banner with queue count
Estimated code review effort🎯 4 (Complex) | ⏱️ ~65 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Review rate limit: 0/1 reviews remaining, refill in 45 minutes and 24 seconds.Comment |
There was a problem hiding this comment.
Actionable comments posted: 26
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
apps/citizen-pwa/src/components/MapTab/PeekSheet.tsx (1)
4-8:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLow-severity color is inconsistent with the map filter palette.
PeekSheet still uses
#001e40for low severity, while filter chips use#414849, so severity cues are inconsistent within the same screen.Suggested fix
const SEVERITY_COLORS: Record<string, string> = { high: '#dc2626', medium: '#a73400', - low: '#001e40', + low: '#414849', }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/citizen-pwa/src/components/MapTab/PeekSheet.tsx` around lines 4 - 8, SEVERITY_COLORS in PeekSheet.tsx uses '#001e40' for the low severity key which conflicts with the map filter chip palette; update SEVERITY_COLORS (the low entry) to '#414849' so the low severity color matches the filter chips and stays consistent across the screen, keeping the Record<string,string> shape intact.apps/citizen-pwa/src/components/MapTab/index.tsx (1)
17-29:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winIncident labels still use pre-redesign copy for three types.
accident,structural, andotherlabels are still old values, so map labels can diverge from report/review terminology.Suggested fix
- accident: 'Accident', - structural: 'Structural', + accident: 'Accidents/Rescue', + structural: 'Damages', security: 'Security', - other: 'Other', + other: 'Others',🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/citizen-pwa/src/components/MapTab/index.tsx` around lines 17 - 29, Update the INCIDENT_LABELS constant so the three keys 'accident', 'structural', and 'other' use the redesigned wording used elsewhere (i.e., make their label strings identical to the report/review UI copy); specifically open the report/review label source and replace the values for INCIDENT_LABELS['accident'], INCIDENT_LABELS['structural'], and INCIDENT_LABELS['other'] with those exact new strings so map labels match the canonical terminology.apps/citizen-pwa/src/components/RevealSheet.tsx (1)
15-16:⚠️ Potential issue | 🟠 Major | ⚡ Quick winThe upgrade prompt is still unreachable from the live success flow.
This branch only renders when
reportCountis passed, but the current success caller still createsRevealSheetwithout that prop. As written, the new account-invitation card never appears outside tests.Also applies to: 368-417
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/citizen-pwa/src/components/RevealSheet.tsx` around lines 15 - 16, The upgrade/account-invitation card in RevealSheet is only shown when reportCount is passed, so it never appears for callers that construct RevealSheet without that prop; update the RevealSheet component (the render logic that checks reportCount around the account-invitation card) to treat an undefined reportCount as a valid case (e.g., render the card when reportCount === undefined || reportCount > 0, or default reportCount to 0 and change the condition to reportCount >= 0 as appropriate) so the upgrade prompt appears in the live success flow; adjust the prop handling near the reportCount definition and the conditional rendering block (also referenced in the 368-417 section) accordingly.apps/citizen-pwa/src/components/SubmitReportForm/ContactFields.tsx (1)
52-65:⚠️ Potential issue | 🟠 Major | ⚡ Quick winAssociate each validation message with its input.
The new error text is only visual right now. Without
aria-invalidandaria-describedby, screen readers won't announce that the field is invalid or read the matching message, which is a regression on a required report step.Suggested fix
<input id="reporter-name" type="text" value={reporterName} + aria-invalid={Boolean(nameError)} + aria-describedby={nameError ? 'reporter-name-error' : undefined} onChange={(e) => { onReporterNameChange(e.target.value) onNameErrorClear() }} @@ {nameError && ( - <p className="field-error text-xs text-danger-500 mt-1.5" data-testid="name-error"> + <p + id="reporter-name-error" + className="field-error text-xs text-danger-500 mt-1.5" + data-testid="name-error" + > {nameError} </p> )} @@ <input id="reporter-msisdn" type="tel" value={reporterMsisdn} + aria-invalid={Boolean(phoneError)} + aria-describedby={phoneError ? 'reporter-msisdn-error' : undefined} onChange={(e) => { onReporterMsisdnChange(e.target.value) onPhoneErrorClear() }} @@ {phoneError && ( - <p className="field-error text-xs text-danger-500 mt-1.5" data-testid="phone-error"> + <p + id="reporter-msisdn-error" + className="field-error text-xs text-danger-500 mt-1.5" + data-testid="phone-error" + > {phoneError} </p> )}Also applies to: 79-93
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/citizen-pwa/src/components/SubmitReportForm/ContactFields.tsx` around lines 52 - 65, The validation messages are currently only visual; update the input elements in ContactFields.tsx (e.g., the reporter-name input and the other inputs at the 79-93 block) to be accessible by adding aria-invalid and aria-describedby attributes when an error exists: give each error <p> a stable id (e.g., reporter-name-error, reporter-phone-error, reporter-email-error), set aria-invalid="true" on the corresponding input when its error flag (nameError, phoneError, emailError) is truthy, and set aria-describedby to that error id so screen readers will announce the message; ensure the data-testid on the error elements remains intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/citizen-pwa/src/__tests__/uiStore.test.ts`:
- Around line 33-37: Spy on or mock localStorage.setItem before calling
useUIStore.getState().setHasCompletedOnboarding(true), then call the setter and
assert both the in-memory state (useUIStore.getState().hasCompletedOnboarding)
and that localStorage.setItem was invoked; specifically assert setItem was
called with a string key and a serialized value that contains
'"hasCompletedOnboarding":true' (e.g.
expect(localStorage.setItem).toHaveBeenCalledWith(expect.any(String),
expect.stringContaining('"hasCompletedOnboarding":true')). Ensure you restore
the spy after the test.
In `@apps/citizen-pwa/src/components/AlertsTab.tsx`:
- Around line 29-55: severityBorderClass and severityIconColor are missing
explicit cases for 'medium' and 'low', causing them to fall through to the
default (green) while severityMeta treats them as amber and slate; update both
severityBorderClass(severity: string) and severityIconColor(severity: string) to
include cases for 'medium' (map to the amber color/class used for
'high'/'warning' e.g. `#d97706` / border-l-[`#d97706`]) and 'low' (map to the slate
color/class used in severityMeta e.g. `#64748b` / border-l-[`#64748b`]) so all
components render consistent severity color cues.
In `@apps/citizen-pwa/src/components/FeedTab.tsx`:
- Around line 17-23: The fallback branch in severityBadgeClass still returns the
old blue styling for low severity; update the function so that when severity ===
'low' (or in the fallback case) it returns the new neutral surface tone classes
(use surface-700 / `#414849`) instead of the blue classes. Locate the
severityBadgeClass function and replace the final return value (or add an
explicit severity === 'low' branch) with the equivalent tailwind/utility class
string that applies the neutral surface-700 text color and matching background
(neutral/Surface) to match the redesign.
In `@apps/citizen-pwa/src/components/LookupScreen.tsx`:
- Around line 65-112: The form currently relies on HTML required which allows
whitespace-only values; update the submit handler (handleSubmit) to trim and
validate publicRef and secret before calling the backend callable: compute const
trimmedPublic = publicRef.trim() and const trimmedSecret = secret.trim(), if
either is an empty string then prevent submission (return early), set an inline
error state or focus the offending input, and only call the callable / proceed
with loading when both trimmed values are non-empty; also ensure any state sent
to the callable uses trimmedPublic and trimmedSecret rather than the raw
publicRef/secret.
- Around line 136-140: The fallback verifier string is hardcoded to "Daet
MDRRMO" in the LookupScreen component; update the rendering that uses
result.verifiedBy to use a municipality-derived fallback (for example derive
from result.municipality, result.municipalityName, or report.municipality) or a
neutral label like "Local MDRRMO" when result.verifiedBy is null/undefined;
locate the JSX that renders {result.verifiedBy ?? 'Daet MDRRMO'} and replace the
fallback with a computed value that prefers result.verifiedBy, then
result.municipalityName (or result.municipality?.name), and finally a neutral
default string.
In `@apps/citizen-pwa/src/components/MapTab/FilterBar.tsx`:
- Around line 53-54: Rename the helper function chipClass to a verb-style name
(e.g., getChipClass or buildChipClass) and update all usages in this component
to the new name; specifically change the function declaration named chipClass
and every call site (examples shown where it's used in JSX
className={chipClass(...)} and the other occurrences around the severity and
filter chips) to the new name, and update any local exports/imports or tests
that reference chipClass so the symbol is consistent across the module.
In `@apps/citizen-pwa/src/components/MapTab/index.tsx`:
- Around line 187-188: The map's root container in MapTab (the div that uses ref
mapElRef) needs the CSS "isolate" class to create a local stacking context so
Leaflet panes cannot escape and overlay shell UI; update the JSX for the root
element in the MapTab component (the element with ref={mapElRef} / its parent
wrapper) to include the "isolate" class alongside existing classes (e.g., add
isolate to the className string).
In `@apps/citizen-pwa/src/components/RevealSheet.tsx`:
- Around line 146-152: The empty catch in handleDismissUpgrade swallows
localStorage write failures; update the catch to surface the error (e.g., log to
console or send to your telemetry/error-reporting) and still
setUpgradeDismissed(true) so the UI dismisses even if persistence fails;
reference handleDismissUpgrade, the localStorage.setItem call and
setUpgradeDismissed when making this change.
- Around line 155-157: The handler handleTrackReport currently sets
window.location.href which causes a full page reload; replace it with
react-router-dom navigation by importing and calling useNavigate and using
navigate(`/reports/${referenceCode}`) inside handleTrackReport (keep the
function name handleTrackReport and the referenceCode variable reference). Also
update RevealSheet unit tests to render the component wrapped in a
Router/MemoryRouter so useNavigate works in tests.
In `@apps/citizen-pwa/src/components/SubmitReportForm/Step1Evidence.tsx`:
- Around line 171-178: The "Skip photo for now" button currently triggers
document.getElementById('photo-input')?.click() (opening the file picker)
instead of advancing the flow; update the button in Step1Evidence so it either
invokes the same continue/submit handler used by the main Continue button
(passing photoFile: null) or remove the button entirely. Locate the button with
text "Skip photo for now" and replace the onClick that calls
document.getElementById('photo-input')?.click() with a call to the component's
continue handler (e.g., onContinue or handleContinue) supplying photoFile: null
so the form advances without evidence. Ensure the change preserves any
validation/analytics the regular Continue action performs.
- Around line 127-134: The native file input isn't cleared when you call
setPhotoFile(null), so re-selecting the same file won't fire onChange; update
the remove-photo handler (where you call setPhotoFile(null)) to also clear the
underlying input element by targeting the input with id "photo-input" (and the
other file input referenced around lines 159-163) and set its value = '' (or use
a ref and reset current.value = '') so the browser treats the next selection as
a new change.
In `@apps/citizen-pwa/src/hooks/useSlotMachine.ts`:
- Around line 13-46: The effect in useSlotMachine leaves done true across
updates, so reset the completion state at the start of a new reveal: inside the
useEffect (before scheduling requestAnimationFrame/tick), call setDone(false)
and optionally setDisplay('') to initial state so a new
target/durationMs/startDelayMs change clears previous results; this ensures the
tick logic (uses startTime, endTime, frame, and setDisplay) runs from a fresh
not-done state for each new target.
In `@apps/citizen-pwa/src/lib/store.ts`:
- Around line 15-16: The action parameter names in the store API are
ambiguous—rename the short parameters in setNavDirection and
setHasCompletedOnboarding (and the analogous signatures around lines 44–47) to
intent-revealing names: change setNavDirection(d: 'forward' | 'backward') =>
setNavDirection(direction: 'forward' | 'backward') and change
setHasCompletedOnboarding(v: boolean) => setHasCompletedOnboarding(hasCompleted:
boolean) (and update the matching implementations/usages of these functions to
use the new parameter names) so callers/readers see clear intent.
- Around line 50-52: The catch block in apps/citizen-pwa/src/lib/store.ts that
swallows errors during onboarding persistence must be changed to surface the
failure; update the try/catch around the onboarding storage write (the
persistence call in the store module) to capture the error object and log/report
it (e.g., console.error or the app telemetry/Sentry API) and optionally set a
local flag or propagate the error so the UI can notify the user about storage
failures instead of silently ignoring them. Ensure the catch references the
error (e) and include contextual information like "onboarding persistence
failed" and the failing key/value to aid debugging.
In `@apps/citizen-pwa/src/pages/Onboarding.tsx`:
- Around line 86-99: StepPrivacy currently keeps its own checked state so the
checkbox resets on remount; change it to be controlled by the parent by
accepting the parent's consent boolean (add a prop like consent: boolean) and
remove the internal useState; use that consent prop for the checkbox checked
value and in the toggle call invoke onConsentChange(!consent) (also apply the
same change to the duplicate component/occurrence around the other lines
referenced). Ensure you update the StepPrivacy prop types/signature to include
consent and remove local state usage.
- Around line 250-272: Onboarding currently doesn't redirect when a user has
already completed onboarding; add a check in the Onboarding component to read
hasCompletedOnboarding from the UI store and immediately navigate away if true.
Specifically, import/use the selector (e.g. const hasCompletedOnboarding =
useUIStore(s => s.hasCompletedOnboarding)) and add a useEffect that watches
hasCompletedOnboarding and calls navigate('/', { replace: true }) when it is
true; ensure the effect runs on mount (include navigate in deps) so direct
visits to /onboarding bounce home.
In `@apps/citizen-pwa/src/pages/RegisterPage.tsx`:
- Around line 100-104: The success toast in handleConsent is never seen because
navigate('/', { replace: true }) unmounts RegisterPage immediately; update
handleConsent to either wait for the toast to finish (use the toast API’s
returned id/promise or a setTimeout that matches toast duration) before calling
navigate, or move the notification to a global/shell-level toast host (emit an
app-level event or call a shared toast helper from the parent shell) so the
toast persists after RegisterPage unmounts; refer to handleConsent, toast,
navigate, and RegisterPage when making the change.
In `@apps/citizen-pwa/src/pages/SettingsPage.tsx`:
- Around line 39-45: Replace the boolean flag persistence for export cooldown
with a timestamp-based expiry: when initializing exportDisabled check
sessionStorage.getItem('bantayog_export_requested') for an expiry ISO/epoch
value and set exportDisabled true only if now < expiry; when starting the
cooldown (in the same place that currently calls setExportDisabled(true) and
writes the key) write expiry = Date.now() + 60000 to sessionStorage and schedule
a timeout to clear both exportDisabled and the stored key at (expiry - now); in
requestDataExport() ensure that on any failure you immediately clear the
sessionStorage key and call setExportDisabled(false) so the cooldown is removed
on error; apply the same timestamp pattern to the other block referenced (around
the same code that currently uses sessionStorage boolean between lines 95-112)
so reloads respect the expiry rather than a sticky boolean.
- Around line 197-223: The "Download my data" button calls handleDataExport
which invokes the request-data-export callable that requires an authenticated
citizen, but the button is shown to anonymous visitors; update SettingsPage.tsx
to gate this action by the user's auth state (e.g., hide or set exportDisabled =
true when there's no authenticated user) so anonymous users cannot trigger
handleDataExport; specifically, use the current auth/user object to
conditionally render or disable the button (and ensure the button label reflects
disabled state) and keep exportDisabled in sync with auth to prevent calling
request-data-export from unauthenticated users.
- Around line 77-93: The two handlers handleAlertSoundsToggle and
handleAutoLocationToggle optimistically set state then swallow localStorage
errors; change them to attempt localStorage.setItem first (or if keeping
optimistic update, catch errors and revert state by calling
setAlertSounds/setAutoLocation with previous value), and in the catch block
surface the failure (use the app's logger or a user-facing toast/snackbar)
including the caught error. Ensure you capture the previous value before
updating so you can revert on failure, and include a clear log/toast message
referencing which setting failed to persist.
In `@apps/citizen-pwa/src/pages/SplashScreen.tsx`:
- Around line 25-33: finish() currently sets visible to false then calls
onDone() immediately, preventing AnimatePresence exit animation from running;
change finish to only call onDone after the exit animation completes (e.g., move
the onDone() call into the existing delayed block or wait for an
animation/transition end) so that setVisible(false) can trigger
AnimatePresence's exit state before unmounting; reference the finish function,
setVisible, onDone, navigate and hasCompletedOnboarding when making the change.
In `@apps/citizen-pwa/src/routes.tsx`:
- Around line 33-36: The Outlet is mounted while SplashScreen is visible
(showSplash), allowing underlying routes to run effects and be accessible;
change rendering so the Outlet is only mounted after the splash finishes or make
the background inert/hidden while splash is up. Specifically, update the
component that uses AnimatePresence, SplashScreen, showSplash and onSplashDone
so that either (a) render <Outlet /> only when showSplash is false (i.e., gate
Outlet on !showSplash) or (b) wrap the Outlet (and any background content) in a
container that sets aria-hidden={showSplash} and applies the inert
attribute/prop when showSplash is true; ensure references to SplashScreen,
onSplashDone, showSplash and Outlet are used to locate and update the code.
In `@apps/citizen-pwa/src/styles/design-tokens.css`:
- Around line 1-3: The Stylelint config flags Tailwind's `@tailwind` directives;
update .stylelintrc.json to relax the scss/at-rule-no-unknown rule by either
adding an ignore for Tailwind (set "scss/at-rule-no-unknown" to an object with
"ignoreAtRules": ["tailwind"]) or disable the rule entirely (set
"scss/at-rule-no-unknown" to null) so `@tailwind` base/components/utilities are
not reported as unknown at-rules.
In `@docs/superpowers/plans/2026-04-30-citizen-pwa-ui-restyle.md`:
- Around line 675-678: The plan uses a hard-coded local path
(/Users/superman/Downloads/Citizen_PWA/app/dist/watchtower.svg) which won't work
for others; update the copy step to reference a repository-controlled source or
checked-in asset (for example the project's assets or a build artifact under the
repo) and use a repo-relative path (e.g., assets/, scripts/dist/, or the CI
artifact location) instead of a machine-local path; change the cp invocation in
the plan to copy from that repo-relative source into
apps/citizen-pwa/public/watchtower.svg so others and automated runners can
execute it.
- Around line 30-31: The plan currently instructs creating a new uiStore module
which will fork the onboarding/nav Zustand state and break existing imports;
instead, update the plan to point at the existing useUIStore module (the
exported hook named useUIStore) and remove the CREATE entry that proposes a new
uiStore file so hasCompletedOnboarding and navDirection remain in the single
shared store; also adjust any wording that references creating
src/lib/uiStore.ts or importing ../lib/uiStore.js to state “use the existing
useUIStore export” so onboarding/nav state and imports stay consistent.
In `@functions/src/callables/request-data-export.ts`:
- Line 9: The log statement logger.info(`Data export requested by ${uid}`)
exposes raw user UID (PII); replace it so no raw UID is written—either log an
anonymized identifier (e.g., compute a one-way hash/HMAC of uid using a
server-side secret), log a redacted/truncated form, or omit the UID entirely;
update the call in request-data-export.ts (the logger.info usage and any places
referencing uid for logging) to use the anonymized_id or a redaction string
instead of uid.
---
Outside diff comments:
In `@apps/citizen-pwa/src/components/MapTab/index.tsx`:
- Around line 17-29: Update the INCIDENT_LABELS constant so the three keys
'accident', 'structural', and 'other' use the redesigned wording used elsewhere
(i.e., make their label strings identical to the report/review UI copy);
specifically open the report/review label source and replace the values for
INCIDENT_LABELS['accident'], INCIDENT_LABELS['structural'], and
INCIDENT_LABELS['other'] with those exact new strings so map labels match the
canonical terminology.
In `@apps/citizen-pwa/src/components/MapTab/PeekSheet.tsx`:
- Around line 4-8: SEVERITY_COLORS in PeekSheet.tsx uses '#001e40' for the low
severity key which conflicts with the map filter chip palette; update
SEVERITY_COLORS (the low entry) to '#414849' so the low severity color matches
the filter chips and stays consistent across the screen, keeping the
Record<string,string> shape intact.
In `@apps/citizen-pwa/src/components/RevealSheet.tsx`:
- Around line 15-16: The upgrade/account-invitation card in RevealSheet is only
shown when reportCount is passed, so it never appears for callers that construct
RevealSheet without that prop; update the RevealSheet component (the render
logic that checks reportCount around the account-invitation card) to treat an
undefined reportCount as a valid case (e.g., render the card when reportCount
=== undefined || reportCount > 0, or default reportCount to 0 and change the
condition to reportCount >= 0 as appropriate) so the upgrade prompt appears in
the live success flow; adjust the prop handling near the reportCount definition
and the conditional rendering block (also referenced in the 368-417 section)
accordingly.
In `@apps/citizen-pwa/src/components/SubmitReportForm/ContactFields.tsx`:
- Around line 52-65: The validation messages are currently only visual; update
the input elements in ContactFields.tsx (e.g., the reporter-name input and the
other inputs at the 79-93 block) to be accessible by adding aria-invalid and
aria-describedby attributes when an error exists: give each error <p> a stable
id (e.g., reporter-name-error, reporter-phone-error, reporter-email-error), set
aria-invalid="true" on the corresponding input when its error flag (nameError,
phoneError, emailError) is truthy, and set aria-describedby to that error id so
screen readers will announce the message; ensure the data-testid on the error
elements remains intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 25af46ef-61df-4073-ace5-d3690fddc39d
⛔ Files ignored due to path filters (2)
apps/citizen-pwa/public/watchtower.svgis excluded by!**/*.svgpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (51)
apps/citizen-pwa/package.jsonapps/citizen-pwa/postcss.config.jsapps/citizen-pwa/src/App.routes.test.tsxapps/citizen-pwa/src/__tests__/setup-framer-motion.tsapps/citizen-pwa/src/__tests__/uiStore.test.tsapps/citizen-pwa/src/components/AlertsTab.tsxapps/citizen-pwa/src/components/CitizenShell.test.tsxapps/citizen-pwa/src/components/CitizenShell.tsxapps/citizen-pwa/src/components/FeedTab.test.tsxapps/citizen-pwa/src/components/FeedTab.tsxapps/citizen-pwa/src/components/LookupScreen.test.tsxapps/citizen-pwa/src/components/LookupScreen.tsxapps/citizen-pwa/src/components/MapTab/DetailSheet.tsxapps/citizen-pwa/src/components/MapTab/FilterBar.tsxapps/citizen-pwa/src/components/MapTab/PeekSheet.tsxapps/citizen-pwa/src/components/MapTab/index.tsxapps/citizen-pwa/src/components/ProfileTab.test.tsxapps/citizen-pwa/src/components/ProfileTab.tsxapps/citizen-pwa/src/components/ReceiptScreen.tsxapps/citizen-pwa/src/components/RevealSheet.test.tsxapps/citizen-pwa/src/components/RevealSheet.tsxapps/citizen-pwa/src/components/SubmitReportForm/ContactFields.tsxapps/citizen-pwa/src/components/SubmitReportForm/Step1Evidence.tsxapps/citizen-pwa/src/components/SubmitReportForm/Step2WhoWhere.tsxapps/citizen-pwa/src/components/SubmitReportForm/Step3Review.tsxapps/citizen-pwa/src/components/Toast.tsxapps/citizen-pwa/src/components/Toggle.tsxapps/citizen-pwa/src/hooks/useOfflineQueueCount.test.tsapps/citizen-pwa/src/hooks/useOfflineQueueCount.tsapps/citizen-pwa/src/hooks/useSlotMachine.test.tsapps/citizen-pwa/src/hooks/useSlotMachine.tsapps/citizen-pwa/src/lib/design-tokens.tsapps/citizen-pwa/src/lib/store.tsapps/citizen-pwa/src/pages/Onboarding.test.tsxapps/citizen-pwa/src/pages/Onboarding.tsxapps/citizen-pwa/src/pages/RegisterPage.tsxapps/citizen-pwa/src/pages/SettingsPage.test.tsxapps/citizen-pwa/src/pages/SettingsPage.tsxapps/citizen-pwa/src/pages/SplashScreen.test.tsxapps/citizen-pwa/src/pages/SplashScreen.tsxapps/citizen-pwa/src/routes.tsxapps/citizen-pwa/src/styles/design-tokens.cssapps/citizen-pwa/src/utils/incident-meta.tsxapps/citizen-pwa/tailwind.config.cjsapps/citizen-pwa/vitest.config.tsdocs/superpowers/plans/2026-04-30-citizen-pwa-redesign-spec-gaps.mddocs/superpowers/plans/2026-04-30-citizen-pwa-ui-restyle.mddocs/superpowers/specs/2026-04-30-citizen-pwa-ui-restyle-design.mdeslint.config.jsfunctions/src/callables/request-data-export.tsfunctions/src/index.ts
💤 Files with no reviewable changes (1)
- apps/citizen-pwa/src/lib/design-tokens.ts
| return ( | ||
| <> | ||
| <AnimatePresence>{showSplash && <SplashScreen onDone={onSplashDone} />}</AnimatePresence> | ||
| <Outlet /> |
There was a problem hiding this comment.
Don't mount the child route behind the splash gate.
<Outlet /> is rendered even while the splash is visible, so the underlying page can run effects/fetches and is still reachable to assistive tech before the onboarding decision resolves. Render the outlet only after the splash finishes, or make the background inert/aria-hidden while the overlay is up.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/citizen-pwa/src/routes.tsx` around lines 33 - 36, The Outlet is mounted
while SplashScreen is visible (showSplash), allowing underlying routes to run
effects and be accessible; change rendering so the Outlet is only mounted after
the splash finishes or make the background inert/hidden while splash is up.
Specifically, update the component that uses AnimatePresence, SplashScreen,
showSplash and onSplashDone so that either (a) render <Outlet /> only when
showSplash is false (i.e., gate Outlet on !showSplash) or (b) wrap the Outlet
(and any background content) in a container that sets aria-hidden={showSplash}
and applies the inert attribute/prop when showSplash is true; ensure references
to SplashScreen, onSplashDone, showSplash and Outlet are used to locate and
update the code.
| @tailwind base; | ||
| @tailwind components; | ||
| @tailwind utilities; |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -e
# Inspect Stylelint config and current at-rule handling.
fd -HI '(^\.stylelintrc|stylelint\.config|package\.json$)'
rg -n "scss/at-rule-no-unknown|at-rule-no-unknown|ignoreAtRules|tailwindcss" .Repository: Exc1D/bantayog-alert
Length of output: 40168
🏁 Script executed:
cat .stylelintrc.jsonRepository: Exc1D/bantayog-alert
Length of output: 1424
Add Stylelint exception for @tailwind directives in .stylelintrc.json.
The scss/at-rule-no-unknown rule from stylelint-config-standard-scss will flag all three @tailwind directives as errors during linting. Update the Stylelint config to allow Tailwind at-rules:
Suggested fix
Add to .stylelintrc.json rules:
"scss/at-rule-no-unknown": {
"ignoreAtRules": ["tailwind"]
}Or disable the rule entirely if other at-rules need similar exceptions:
"scss/at-rule-no-unknown": null🧰 Tools
🪛 Stylelint (17.9.0)
[error] 1-1: Unexpected unknown at-rule "@tailwind" (scss/at-rule-no-unknown)
(scss/at-rule-no-unknown)
[error] 2-2: Unexpected unknown at-rule "@tailwind" (scss/at-rule-no-unknown)
(scss/at-rule-no-unknown)
[error] 3-3: Unexpected unknown at-rule "@tailwind" (scss/at-rule-no-unknown)
(scss/at-rule-no-unknown)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/citizen-pwa/src/styles/design-tokens.css` around lines 1 - 3, The
Stylelint config flags Tailwind's `@tailwind` directives; update .stylelintrc.json
to relax the scss/at-rule-no-unknown rule by either adding an ignore for
Tailwind (set "scss/at-rule-no-unknown" to an object with "ignoreAtRules":
["tailwind"]) or disable the rule entirely (set "scss/at-rule-no-unknown" to
null) so `@tailwind` base/components/utilities are not reported as unknown
at-rules.
| ```bash | ||
| cp /Users/superman/Downloads/Citizen_PWA/app/dist/watchtower.svg \ | ||
| apps/citizen-pwa/public/watchtower.svg | ||
| ``` |
There was a problem hiding this comment.
Avoid hard-coded machine-local asset paths in the plan.
This copy step only works on the original author's laptop, so other engineers and agent runners cannot execute it as written. Point to a repo artifact or a checked-in source path instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/superpowers/plans/2026-04-30-citizen-pwa-ui-restyle.md` around lines 675
- 678, The plan uses a hard-coded local path
(/Users/superman/Downloads/Citizen_PWA/app/dist/watchtower.svg) which won't work
for others; update the copy step to reference a repository-controlled source or
checked-in asset (for example the project's assets or a build artifact under the
repo) and use a repo-relative path (e.g., assets/, scripts/dist/, or the CI
artifact location) instead of a machine-local path; change the cp invocation in
the plan to copy from that repo-relative source into
apps/citizen-pwa/public/watchtower.svg so others and automated runners can
execute it.
| { region: 'asia-southeast1', enforceAppCheck: true }, | ||
| (request) => { | ||
| const { uid } = requireAuth(request, ['citizen']) | ||
| logger.info(`Data export requested by ${uid}`) |
There was a problem hiding this comment.
Avoid logging raw user UID in callable logs.
uid is a user identifier and creates unnecessary PII traceability in log storage.
Suggested fix
- logger.info(`Data export requested by ${uid}`)
+ logger.info('Data export requested')📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| logger.info(`Data export requested by ${uid}`) | |
| logger.info('Data export requested') |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@functions/src/callables/request-data-export.ts` at line 9, The log statement
logger.info(`Data export requested by ${uid}`) exposes raw user UID (PII);
replace it so no raw UID is written—either log an anonymized identifier (e.g.,
compute a one-way hash/HMAC of uid using a server-side secret), log a
redacted/truncated form, or omit the UID entirely; update the call in
request-data-export.ts (the logger.info usage and any places referencing uid for
logging) to use the anonymized_id or a redaction string instead of uid.
… peek sheet\n\nFollow-up to 06df242 — LOW severity color #001e40 → #414849 was missed\nin IncidentLayer, MyReportLayer, and PeekSheet. These three files use\nthe same inline COLORS record and needed the same migration.\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…leTab: Guardian pitch card with gradient CTA, badge system\n (First Report, Verified Reporter, Community Helper, Active Citizen),\n Report Milestones tracker, 2x2 impact stats grid, sign-out button,\n share-your-impact prompt, locked badge previews for unregistered users\n- ProfileTab.test: update assertions for new Guardian pitch card and\n sign-out button\n- MapTab: CSS isolation: isolate on outer div to contain Leaflet z-indices,\n remove FilterBar (filters hardcoded to all/24h), ResizeObserver for\n invalidateSize on layout changes, incident labels aligned with form\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…n\n\n- RevealSheet.test.tsx: add firebase auth mocks for onAuthStateChanged,\n update test for Guardian invitation card (replaces reportCount prompt)\n- OfflineBanner: replace CSS var(--color-primary) with static hex #25292A\n- SmsFallbackButton: migrate inline style object to Tailwind classes\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… test cleanup\n\n- Step1Evidence: card-based incident type grid with per-type selection\n colors (Flood=info, Fire=danger, Accidents/Rescue=warning,\n Typhoon=warning, Damages=danger, Others=surface)\n- Step1Evidence: Earthquake→Accidents/Rescue (Car), Landslide→Damages\n (Wrench), Storm Surge→Others (HelpCircle)\n- Step3Review: mirror incident type changes + danger-red submit button\n- SettingsPage.test: remove Sign Out assertion (moved to ProfileTab)\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…t guard\n\n- SettingsPage: remove Sign Out button and handleSignOut (moved to ProfileTab)\n- globals.css: migrate --color-primary and --color-border-strong from\n hardcoded navy #001e40 to charcoal #25292A\n- design-tokens.css: add Leaflet tile border reset guard to prevent global\n border-color rules from breaking tile mosaic layout\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…on token\n\n- incident-meta.tsx LABEL_MAP: accident → Accidents/Rescue,\n structural → Damages, other → Others (matches report form + map tab)\n- tailwind.config.cjs: add radar-ring keyframe animation for\n SplashScreen and RevealSheet radar pulse ceremony\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…add aria-invalid/aria-describedby to reporter-name and reporter-msisdn inputs\n- RevealSheet: use useNavigate instead of window.location.href\n- Step1Evidence: wire Skip photo to advance flow; clear file input on removal\n- FilterBar: rename chipClass to buildChipClass\n- useSlotMachine: reset done state at effect start\n- store.ts: rename ambiguous params; log localStorage errors\n- SettingsPage: timestamp-based export cooldown; auth-gate data export; rollback optimistic state\n- AlertsTab: fix medium/low severity color consistency\n- FeedTab: use surface-700 for low severity badge\n- LookupScreen: trim/validate codes; neutral MDRRMO fallback\n- uiStore.test.ts: assert localStorage persistence\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
apps/citizen-pwa/src/components/AlertsTab.tsx (1)
22-23:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winLow severity color inconsistent within this component.
severityMeta()returnscolor: '#001e40'(old navy blue) forlow, butseverityBorderClass()andseverityIconColor()use#64748b(slate). The badge text (line 116-119) usesseverityMeta, so low-severity badges will show navy blue text while the border and icon show slate.Proposed fix to align low severity colors
case 'low': - return { label: 'LOW', bg: '#e0e7f0', color: '#001e40' } + return { label: 'LOW', bg: '#f1f5f9', color: '#64748b' }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/citizen-pwa/src/components/AlertsTab.tsx` around lines 22 - 23, severityMeta currently returns color: '#001e40' for the 'low' case which conflicts with severityBorderClass and severityIconColor that use '#64748b'; update the 'low' branch in severityMeta to use color: '#64748b' (keeping bg '#e0e7f0' as-is) so the badge text, border, and icon all use the same slate color; verify the functions severityMeta, severityBorderClass, and severityIconColor now produce consistent colors for 'low' severity.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/citizen-pwa/src/components/LookupScreen.tsx`:
- Around line 148-151: The UI shows result.verifiedBy in LookupScreen.tsx but
the backend callable request-lookup.ts currently returns only { status,
lastStatusAt, municipalityLabel }; either update the backend callable
(request-lookup.ts) to fetch the report document’s verifiedBy field and include
it in the returned payload (e.g., add verifiedBy to the returned object) so
LookupScreen can display real data, or remove the "Verified by" row in
LookupScreen.tsx until the backend supports verifiedBy; locate the callable
function in request-lookup.ts and the result usage in LookupScreen.tsx to
implement the chosen fix.
In `@apps/citizen-pwa/src/components/MapTab/PeekSheet.tsx`:
- Around line 80-87: The visible button text "Track" in PeekSheet.tsx does not
match its aria-label ("Pull up for full detail"); update the button element (the
<button> with onClick={onExpand}) so the accessible name matches the visible
label — either change aria-label to "Track" or replace the visible text with an
accurate label that matches the aria-label (e.g., "Expand to full detail"),
ensuring the aria-label and the button text are identical and descriptive for
screen reader users.
In `@apps/citizen-pwa/src/components/RevealSheet.tsx`:
- Around line 461-467: In RevealSheet replace the plain <a href="/register">
anchor with SPA navigation: either swap it for react-router-dom's <Link
to="/register"> preserving the existing className, children (LogIn and text) and
styles, or convert it to a button that calls the useNavigate hook
(navigate('/register')) onClick; also ensure the click handler mirrors the
behavior used by handleTrackReport (invoke any tracking call before navigation)
so registration navigation uses client-side routing and consistent tracking.
In `@apps/citizen-pwa/src/components/SubmitReportForm/ContactFields.tsx`:
- Around line 165-179: The current counter buttons use non-descriptive
aria-labels ("−" and "+"); update the two button elements that render the
counter (the decrement button and the increment button that calls
onPatientCountChange(patientCount + 1)) to use descriptive aria-labels such as
"Decrease patient count" and "Increase patient count" (or similar), so assistive
technologies convey the action and context of the patientCount control.
- Around line 90-112: The phone helper text isn't referenced by the input's
aria-describedby; add an id (e.g., "reporter-msisdn-help") to the helper <p> and
update the input's aria-describedby to include both the helper id and the error
id when present (compose a space-separated string using phoneError to
conditionally include "reporter-msisdn-error"). Update the element that
currently uses onReporterMsisdnChange/onPhoneErrorClear to set aria-describedby
to something like `${phoneError ? 'reporter-msisdn-error ' :
''}reporter-msisdn-help` so screen readers associate both the helper copy and
any error with the reporter-msisdn field.
In `@apps/citizen-pwa/src/components/SubmitReportForm/OfflineBanner.tsx`:
- Line 9: The hardcoded color value assigned to the fg property in OfflineBanner
should be replaced with the app theme token instead of the literal '#25292A';
update the fg assignment in the OfflineBanner component to read from the theme
(e.g. theme.palette.text.primary or your design-system token such as
theme.tokens.colors.submitText), ensure you import/use the theme via useTheme
(or receive theme from props/context) and remove the hex literal so the banner's
submit text color follows the central theme token.
In `@apps/citizen-pwa/src/pages/SettingsPage.tsx`:
- Around line 25-35: The catch block in setExportCooldown currently swallows
sessionStorage errors; update it to log the caught error instead of ignoring it
— e.g., catch (err) { console.error('setExportCooldown: sessionStorage failure',
err); } — so any failures when calling sessionStorage.setItem/removeItem are
surfaced for debugging while still preserving existing behavior; locate the
setExportCooldown function in SettingsPage.tsx and replace the empty catch with
an error log that includes the error object and a short contextual message.
In `@apps/citizen-pwa/src/styles/design-tokens.css`:
- Around line 39-41: The global rule using the universal selector (*) to set
-webkit-tap-highlight-color: transparent removes native tap feedback for all
elements; replace this global rule by scoping it to interactive controls only
(e.g., a, button, input, textarea, select, [role="button"], and any
project-specific interactive classes like .interactive) or use a negation to
exclude focusable elements so only non-controls lose the highlight, update the
CSS rule that contains "*" and the -webkit-tap-highlight-color declaration
accordingly, and then manually test main mobile flows (links, buttons, form
controls) to ensure tap feedback remains usable or intentionally styled where
you’ve provided explicit active styles.
In `@apps/citizen-pwa/src/utils/incident-meta.tsx`:
- Line 39: ICON_MAP currently maps the "structural" incident to Building2 while
Step1Evidence (component Step1Evidence) uses Wrench, causing inconsistent icons;
update ICON_MAP's "structural" entry to use Wrench instead of Building2 so both
the map/detail view and the Step1Evidence form show the same icon (locate
ICON_MAP and replace the Building2 reference for the "structural" key with
Wrench).
In `@docs/reference-gap-analysis.md`:
- Around line 497-499: The ordered lists in the referenced markdown subsections
are not normalized and trigger markdownlint MD029; edit the subsections around
the places that mention adding tokens and CSS properties (the ordered lists
under the "Add missing tokens to `tailwind.config.cjs`" and "Add missing CSS
custom properties to `globals.css`" items) and restart each subsection's ordered
list numbering by using "1." for every list item (or explicitly restart
numbering) so every subsection's list begins at 1 and conforms to markdownlint
MD029; update the lists at the same block where the lines mentioning
`surface-950`, `brand-600`, `brand-100`, `brand-50` and the CSS custom
properties are declared (also apply the same change to the subsequent subsection
block covering lines 502-516).
- Around line 54-62: Add explicit language identifiers to the fenced code blocks
that currently start with the color lists (e.g., the blocks beginning with
"brand-600: `#0D7377`", "surface-950: `#171A1A`", and "danger-600: `#C21F1F`") so
they read ```text instead of just ```, resolving markdownlint MD040; update
every similar block (also those at the ranges noted: 66-78 and 82-92) to use
```text at the opening fence and leave the closing fence as ``` so the blocks
are properly annotated.
- Line 4: Replace the machine-specific absolute path string
`/Users/superman/Downloads/CitizenPWA` in docs/reference-gap-analysis.md with a
portable identifier: either a repository URL (e.g.,
https://github.com/OWNER/REPO), a commit SHA, or a neutral label like "local
snapshot" (and optionally a note on how to reproduce) so the document no longer
contains environment-specific paths.
---
Outside diff comments:
In `@apps/citizen-pwa/src/components/AlertsTab.tsx`:
- Around line 22-23: severityMeta currently returns color: '#001e40' for the
'low' case which conflicts with severityBorderClass and severityIconColor that
use '#64748b'; update the 'low' branch in severityMeta to use color: '#64748b'
(keeping bg '#e0e7f0' as-is) so the badge text, border, and icon all use the
same slate color; verify the functions severityMeta, severityBorderClass, and
severityIconColor now produce consistent colors for 'low' severity.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 55bf7e7d-c1cf-4314-890d-fb894f21d47f
📒 Files selected for processing (27)
apps/citizen-pwa/src/__tests__/uiStore.test.tsapps/citizen-pwa/src/components/AlertsTab.tsxapps/citizen-pwa/src/components/FeedTab.tsxapps/citizen-pwa/src/components/LookupScreen.tsxapps/citizen-pwa/src/components/MapTab/FilterBar.tsxapps/citizen-pwa/src/components/MapTab/IncidentLayer.tsxapps/citizen-pwa/src/components/MapTab/MyReportLayer.tsxapps/citizen-pwa/src/components/MapTab/PeekSheet.tsxapps/citizen-pwa/src/components/MapTab/index.tsxapps/citizen-pwa/src/components/ProfileTab.test.tsxapps/citizen-pwa/src/components/ProfileTab.tsxapps/citizen-pwa/src/components/RevealSheet.test.tsxapps/citizen-pwa/src/components/RevealSheet.tsxapps/citizen-pwa/src/components/SubmitReportForm/ContactFields.tsxapps/citizen-pwa/src/components/SubmitReportForm/OfflineBanner.tsxapps/citizen-pwa/src/components/SubmitReportForm/SmsFallbackButton.tsxapps/citizen-pwa/src/components/SubmitReportForm/Step1Evidence.tsxapps/citizen-pwa/src/components/SubmitReportForm/Step3Review.tsxapps/citizen-pwa/src/hooks/useSlotMachine.tsapps/citizen-pwa/src/lib/store.tsapps/citizen-pwa/src/pages/SettingsPage.test.tsxapps/citizen-pwa/src/pages/SettingsPage.tsxapps/citizen-pwa/src/styles/design-tokens.cssapps/citizen-pwa/src/styles/globals.cssapps/citizen-pwa/src/utils/incident-meta.tsxapps/citizen-pwa/tailwind.config.cjsdocs/reference-gap-analysis.md
| <button | ||
| type="button" | ||
| aria-label="Pull up for full detail" | ||
| onClick={onExpand} | ||
| className="text-brand-500 text-sm font-medium" | ||
| > | ||
| Track | ||
| </button> |
There was a problem hiding this comment.
Aria-label doesn't match the visible button text.
The button displays "Track" but aria-label="Pull up for full detail" describes a different action. This can confuse screen reader users who expect the announced label to match the visible text.
Proposed fix
<button
type="button"
- aria-label="Pull up for full detail"
+ aria-label="Track incident"
onClick={onExpand}
className="text-brand-500 text-sm font-medium"
>
Track
</button>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <button | |
| type="button" | |
| aria-label="Pull up for full detail" | |
| onClick={onExpand} | |
| className="text-brand-500 text-sm font-medium" | |
| > | |
| Track | |
| </button> | |
| <button | |
| type="button" | |
| aria-label="Track incident" | |
| onClick={onExpand} | |
| className="text-brand-500 text-sm font-medium" | |
| > | |
| Track | |
| </button> |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/citizen-pwa/src/components/MapTab/PeekSheet.tsx` around lines 80 - 87,
The visible button text "Track" in PeekSheet.tsx does not match its aria-label
("Pull up for full detail"); update the button element (the <button> with
onClick={onExpand}) so the accessible name matches the visible label — either
change aria-label to "Track" or replace the visible text with an accurate label
that matches the aria-label (e.g., "Expand to full detail"), ensuring the
aria-label and the button text are identical and descriptive for screen reader
users.
| aria-label="−" | ||
| > | ||
| − | ||
| </button> | ||
| <div className="counter-display">{patientCount}</div> | ||
| <div className="flex-1 h-10 rounded-xl bg-surface-100 flex items-center justify-center font-bold text-surface-900"> | ||
| {patientCount} | ||
| </div> | ||
| <button | ||
| type="button" | ||
| onClick={() => { | ||
| onPatientCountChange(patientCount + 1) | ||
| }} | ||
| className="counter-increment-btn" | ||
| className="w-10 h-10 rounded-xl bg-brand-500 text-white flex items-center justify-center font-bold text-lg active:bg-brand-600" | ||
| aria-label="+" | ||
| > |
There was a problem hiding this comment.
Use descriptive aria-labels for the patient counter controls.
aria-label="−" and aria-label="+" are too vague; use action labels so assistive tech announces intent clearly.
Suggested fix
- aria-label="−"
+ aria-label="Decrease patient count"
...
- aria-label="+"
+ aria-label="Increase patient count"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/citizen-pwa/src/components/SubmitReportForm/ContactFields.tsx` around
lines 165 - 179, The current counter buttons use non-descriptive aria-labels
("−" and "+"); update the two button elements that render the counter (the
decrement button and the increment button that calls
onPatientCountChange(patientCount + 1)) to use descriptive aria-labels such as
"Decrease patient count" and "Increase patient count" (or similar), so assistive
technologies convey the action and context of the patientCount control.
| * { | ||
| -webkit-tap-highlight-color: transparent; | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Avoid removing tap feedback globally.
* { -webkit-tap-highlight-color: transparent; } strips the built-in pressed feedback from every link and button on touch browsers. Scope this to controls with explicit active styling, or keep a subtle highlight so taps do not feel inert. Please verify the main mobile flows after narrowing this rule.
Suggested change
- * {
- -webkit-tap-highlight-color: transparent;
- }
+ a,
+ button,
+ [role='button'] {
+ -webkit-tap-highlight-color: rgba(13, 115, 119, 0.18);
+ }📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| * { | |
| -webkit-tap-highlight-color: transparent; | |
| } | |
| a, | |
| button, | |
| [role='button'] { | |
| -webkit-tap-highlight-color: rgba(13, 115, 119, 0.18); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/citizen-pwa/src/styles/design-tokens.css` around lines 39 - 41, The
global rule using the universal selector (*) to set -webkit-tap-highlight-color:
transparent removes native tap feedback for all elements; replace this global
rule by scoping it to interactive controls only (e.g., a, button, input,
textarea, select, [role="button"], and any project-specific interactive classes
like .interactive) or use a negation to exclude focusable elements so only
non-controls lose the highlight, update the CSS rule that contains "*" and the
-webkit-tap-highlight-color declaration accordingly, and then manually test main
mobile flows (links, buttons, form controls) to ensure tap feedback remains
usable or intentionally styled where you’ve provided explicit active styles.
| accident: (s) => <AlertTriangle size={s} />, | ||
| structural: (s) => <Building size={s} />, | ||
| security: (s) => <Shield size={s} />, | ||
| structural: (s) => <Building2 size={s} />, |
There was a problem hiding this comment.
Icon mismatch for structural between incident-meta and Step1Evidence.
ICON_MAP uses Building2 for structural, but Step1Evidence.tsx (lines 41-48 in the context snippet) uses Wrench for the same incident type. This will cause visual inconsistency between the map/detail views and the report form.
Ensure both use the same icon. Based on the PR objective mentioning "Landslide→Damages (Wrench)", Wrench appears to be the intended icon.
Proposed fix
-import {
- Waves,
- MountainSnow,
- Flame,
- Wind,
- Building2,
- Car,
- HeartPulse,
- ShieldAlert,
- AlertTriangle,
- Zap,
- HelpCircle,
-} from 'lucide-react'
+import {
+ Waves,
+ MountainSnow,
+ Flame,
+ Wind,
+ Wrench,
+ Car,
+ HeartPulse,
+ ShieldAlert,
+ AlertTriangle,
+ Zap,
+ HelpCircle,
+} from 'lucide-react'
...
- structural: (s) => <Building2 size={s} />,
+ structural: (s) => <Wrench size={s} />,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| structural: (s) => <Building2 size={s} />, | |
| import { | |
| Waves, | |
| MountainSnow, | |
| Flame, | |
| Wind, | |
| Wrench, | |
| Car, | |
| HeartPulse, | |
| ShieldAlert, | |
| AlertTriangle, | |
| Zap, | |
| HelpCircle, | |
| } from 'lucide-react' | |
| // ... other code ... | |
| structural: (s) => <Wrench size={s} />, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/citizen-pwa/src/utils/incident-meta.tsx` at line 39, ICON_MAP currently
maps the "structural" incident to Building2 while Step1Evidence (component
Step1Evidence) uses Wrench, causing inconsistent icons; update ICON_MAP's
"structural" entry to use Wrench instead of Building2 so both the map/detail
view and the Step1Evidence form show the same icon (locate ICON_MAP and replace
the Building2 reference for the "structural" key with Wrench).
| # Reference Gap Analysis — Bantayog Citizen PWA | ||
|
|
||
| **Scan date:** 2026-05-01 | ||
| **Reference:** `/Users/superman/Downloads/CitizenPWA` |
There was a problem hiding this comment.
Remove machine-specific absolute path from committed docs.
Using a local workstation path makes this metadata non-portable and leaks environment-specific details. Prefer a repo URL, commit SHA, or a neutral “local snapshot” label.
Proposed fix
-**Reference:** `/Users/superman/Downloads/CitizenPWA`
+**Reference:** `CitizenPWA local snapshot (path omitted for portability)`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/reference-gap-analysis.md` at line 4, Replace the machine-specific
absolute path string `/Users/superman/Downloads/CitizenPWA` in
docs/reference-gap-analysis.md with a portable identifier: either a repository
URL (e.g., https://github.com/OWNER/REPO), a commit SHA, or a neutral label like
"local snapshot" (and optionally a note on how to reproduce) so the document no
longer contains environment-specific paths.
| ``` | ||
| brand-600: #0D7377 | ||
| brand-500: #0F9488 | ||
| brand-400: #4DB6A8 | ||
| brand-300: #8FD4CA | ||
| brand-200: #C4E8E2 | ||
| brand-100: #E8F6F3 | ||
| brand-50: #F3FAF9 | ||
| ``` |
There was a problem hiding this comment.
Add language identifiers to fenced code blocks.
These blocks trigger markdownlint MD040; annotate them with an explicit language (for example, text).
Proposed fix
-```
+```text
brand-600: `#0D7377`
...
-```
+```
-```
+```text
surface-950: `#171A1A`
...
-```
+```
-```
+```text
danger-600: `#C21F1F`
...
-```
+```Also applies to: 66-78, 82-92
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 54-54: Fenced code blocks should have a language specified
(MD040, fenced-code-language)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/reference-gap-analysis.md` around lines 54 - 62, Add explicit language
identifiers to the fenced code blocks that currently start with the color lists
(e.g., the blocks beginning with "brand-600: `#0D7377`", "surface-950: `#171A1A`",
and "danger-600: `#C21F1F`") so they read ```text instead of just ```, resolving
markdownlint MD040; update every similar block (also those at the ranges noted:
66-78 and 82-92) to use ```text at the opening fence and leave the closing fence
as ``` so the blocks are properly annotated.
| 2. **Add missing tokens** to `tailwind.config.cjs`: `surface-950`, `brand-600`, `brand-100`, `brand-50`, full surface/brand/severity scales | ||
| 3. **Add missing CSS custom properties** to `globals.css` | ||
|
|
There was a problem hiding this comment.
Normalize ordered-list numbering per subsection.
This currently trips markdownlint MD029. Restart each subsection list at 1. (or use 1. for all items).
Proposed fix
-2. **Add missing tokens** to `tailwind.config.cjs`: `surface-950`, `brand-600`, `brand-100`, `brand-50`, full surface/brand/severity scales
-3. **Add missing CSS custom properties** to `globals.css`
+1. **Add missing tokens** to `tailwind.config.cjs`: `surface-950`, `brand-600`, `brand-100`, `brand-50`, full surface/brand/severity scales
+2. **Add missing CSS custom properties** to `globals.css`
-4. **Implement `useSlotMachine` hook** — 600ms, 400ms delay, `SLOT_CHARS`
-5. **Implement `AnimatedCheck` SVG** — pathLength animations
-6. **Add confetti** — `canvas-confetti`, 120 particles, teal palette
-7. **Change submit button color to `bg-danger-500`** on Step 3 (red is the reference convention for critical submit)
+1. **Implement `useSlotMachine` hook** — 600ms, 400ms delay, `SLOT_CHARS`
+2. **Implement `AnimatedCheck` SVG** — pathLength animations
+3. **Add confetti** — `canvas-confetti`, 120 particles, teal palette
+4. **Change submit button color to `bg-danger-500`** on Step 3 (red is the reference convention for critical submit)
-8. **Verify incident type icons** match `Waves, Flame, Activity, Wind, Mountain, CloudLightning`
-9. **Add `animate-radar-ring`** to globals.css if not present
-10. **Align step indicator** — reference uses 3 horizontal bars with `animate-pulse`; our recent commit uses dots (check if this is an intentional departure)
+1. **Verify incident type icons** match `Waves, Flame, Activity, Wind, Mountain, CloudLightning`
+2. **Add `animate-radar-ring`** to globals.css if not present
+3. **Align step indicator** — reference uses 3 horizontal bars with `animate-pulse`; our recent commit uses dots (check if this is an intentional departure)
-11. **Design "Your Impact" section** — since neither reference nor plan.md has an implementation, this is ours to create
-12. **Design badge/milestone system** — same situation; our existing ProfileTab badges are a good start
+1. **Design "Your Impact" section** — since neither reference nor plan.md has an implementation, this is ours to create
+2. **Design badge/milestone system** — same situation; our existing ProfileTab badges are a good startAlso applies to: 502-516
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 497-497: Ordered list item prefix
Expected: 1; Actual: 2; Style: 1/2/3
(MD029, ol-prefix)
[warning] 498-498: Ordered list item prefix
Expected: 2; Actual: 3; Style: 1/2/3
(MD029, ol-prefix)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/reference-gap-analysis.md` around lines 497 - 499, The ordered lists in
the referenced markdown subsections are not normalized and trigger markdownlint
MD029; edit the subsections around the places that mention adding tokens and CSS
properties (the ordered lists under the "Add missing tokens to
`tailwind.config.cjs`" and "Add missing CSS custom properties to `globals.css`"
items) and restart each subsection's ordered list numbering by using "1." for
every list item (or explicitly restart numbering) so every subsection's list
begins at 1 and conforms to markdownlint MD029; update the lists at the same
block where the lines mentioning `surface-950`, `brand-600`, `brand-100`,
`brand-50` and the CSS custom properties are declared (also apply the same
change to the subsequent subsection block covering lines 502-516).
… Claude Sonnet 4.6 <noreply@anthropic.com>
…align aria-label with visible button text (Track incident)\n- RevealSheet: use navigate('/register') button instead of <a href>\n- ContactFields: link phone helper text to input via aria-describedby\n- LookupScreen: remove verifiedBy from interface (backend doesn't return it)\n- PeekSheet.test: update button selector to match new aria-label\n\nCo-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
Closes out the citizen PWA redesign spec gap fixes. Covers UI polish across the shell, map tab, report form, confirmation flow, and navbar.
Changes
Shell & Navigation
Map Tab
Report Form (Step1Evidence + Step3Review)
Confirmation Screen (RevealSheet)
ProfileTab & SettingsPage
Test plan
🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
UI/UX Improvements