Skip to content

fix(citizen-pwa): redesign spec gap fixes — shell layout, FAB, labels, confirmation ceremony#86

Merged
Exc1D merged 53 commits intomainfrom
fix/citizen-pwa-redesign-spec-gaps
May 1, 2026
Merged

fix(citizen-pwa): redesign spec gap fixes — shell layout, FAB, labels, confirmation ceremony#86
Exc1D merged 53 commits intomainfrom
fix/citizen-pwa-redesign-spec-gaps

Conversation

@Exc1D
Copy link
Copy Markdown
Owner

@Exc1D Exc1D commented May 1, 2026

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

  • CitizenShell layout: replace min-h + fixed nav with h-[100dvh] flex-col so main gets a definite height — fixes Leaflet tile z-index bleed-through on splash overlay
  • Report FAB: AlertTriangle gradient replaced with CirclePlus solid bg-brand-600 64px circle; absolute -top-10 floats above nav bar
  • Report label: absolute bottom-[14px] aligns it flush with all other tab labels
  • Offline banner: sticky replaced with shrink-0 so it participates in flex flow

Map Tab

  • Incident labels aligned with form: accident → Accidents/Rescue, structural → Damages, other → Others
  • LOW severity color: hardcoded navy #001e40 migrated to charcoal #414849 for better readability
  • FilterBar active chip: bg-[#001e40] replaced with bg-surface-900 Tailwind token
  • MapTab outer div: isolation: isolate contains Leaflet z-indices below SplashScreen

Report Form (Step1Evidence + Step3Review)

  • Incident grid: Earthquake → Accidents/Rescue (Car icon), Landslide → Damages (Wrench icon), Storm Surge → Others (HelpCircle icon)
  • utils/incident-meta.tsx LABEL_MAP updated to match

Confirmation Screen (RevealSheet)

  • Radar pulse ceremony covers all submission states: green for success, amber for queued/failed
  • Failed state: reassuring copy, ShieldCheck icon, amber banner instead of alarming red
  • Queued state: Save icon with "Saved. We'll send it for you."
  • Guardian invitation card for unregistered users on success state

ProfileTab & SettingsPage

  • Achievement badge system (First Report, Verified Reporter, Community Helper, Active Citizen)
  • Report Milestones tracker with progress bars
  • Settings moved to standalone card with chevron nav; Sign Out moved to ProfileTab
  • Tests updated to match new structure

Test plan

  • npx vitest run — 216/216 pass, 42 files
  • pnpm lint — clean (lint-staged runs per commit)
  • pnpm typecheck — clean

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Splash screen with animated startup
    • Three-step onboarding with consent gating
    • Data export request action
    • Secret-code reveal + slot-machine-style reference
    • Map recenter FAB
  • UI/UX Improvements

    • App-wide animated page transitions and framer-motion toasts
    • Animated offline banner and enhanced receipt ceremony (haptics, visuals)
    • Profile: badges, milestones, sign-out/share actions
    • Global restyle to Tailwind CSS; forms, tabs, cards, and sheets redesigned
    • Lookup button relabeled to “Check Status”

Exc1D added 30 commits April 30, 2026 12:39
… 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>
Exc1D and others added 14 commits April 30, 2026 22:10
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>
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @Exc1D, your pull request is larger than the review limit of 150000 diff characters

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 1, 2026

Warning

Rate limit exceeded

@Exc1D has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 45 minutes and 24 seconds before requesting another review.

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 @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 4468b6e0-56cb-4be6-896a-0c7295bc8098

📥 Commits

Reviewing files that changed from the base of the PR and between 769040a and 69bec65.

📒 Files selected for processing (6)
  • apps/citizen-pwa/src/components/CitizenShell.tsx
  • apps/citizen-pwa/src/components/LookupScreen.tsx
  • apps/citizen-pwa/src/components/MapTab/PeekSheet.test.tsx
  • apps/citizen-pwa/src/components/MapTab/PeekSheet.tsx
  • apps/citizen-pwa/src/components/RevealSheet.tsx
  • apps/citizen-pwa/src/components/SubmitReportForm/ContactFields.tsx
📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Dependencies & Tooling
apps/citizen-pwa/package.json, apps/citizen-pwa/postcss.config.js, apps/citizen-pwa/tailwind.config.cjs, apps/citizen-pwa/vitest.config.ts, eslint.config.js
Added framer-motion runtime dep and Tailwind/PostCSS tooling; added Tailwind config and PostCSS file; updated Vitest setup to load framer-motion test mock; adjusted ESLint config for project config files.
Design Tokens & Styles
apps/citizen-pwa/src/styles/design-tokens.css, apps/citizen-pwa/src/styles/globals.css, apps/citizen-pwa/src/lib/design-tokens.ts
Replaced TS design-tokens module with Tailwind-first CSS tokens in @layer base; updated global CSS variables and primary color; removed former design-tokens.ts.
Routing & RootLayout
apps/citizen-pwa/src/routes.tsx, apps/citizen-pwa/src/App.routes.test.tsx
Introduced nested routes under a new RootLayout that shows SplashScreen then conditionally routes to onboarding or app; adjusted route tests to mock splash/onboarding gating and updated assertions.
Splash & Onboarding
apps/citizen-pwa/src/pages/SplashScreen.tsx, apps/citizen-pwa/src/pages/SplashScreen.test.tsx, apps/citizen-pwa/src/pages/Onboarding.tsx, apps/citizen-pwa/src/pages/Onboarding.test.tsx
Added animated SplashScreen with timed finish and onboarding redirect; added Onboarding component (3-step swipe flow, consent gating) and tests for flow and persistence.
UI Store
apps/citizen-pwa/src/lib/store.ts, apps/citizen-pwa/src/__tests__/uiStore.test.ts
Extended zustand UI store with navDirection and hasCompletedOnboarding (persisted to localStorage) plus setters; added tests for initialization, setters, and storage behavior.
Shell & Navigation
apps/citizen-pwa/src/components/CitizenShell.tsx, apps/citizen-pwa/src/components/CitizenShell.test.tsx
Reworked shell to use AnimatePresence/motion for page transitions keyed by pathname and driven by navDirection; replaced static offline banner with animated banner and reworked bottom nav with center Report button; updated tests/mocks.
Component Restyles (Tailwind + Motion)
apps/citizen-pwa/src/components/... (many files, see cohorts below)
Bulk migration from inline style objects to Tailwind classes across components (AlertsTab, FeedTab, ProfileTab, MapTab/*, DetailSheet, PeekSheet, many SubmitReportForm steps, ReceiptScreen, RevealSheet, Toast, Toggle, Toggle tests, etc.); added framer-motion animations and adjusted UI copy/labels and small behavioral tweaks (e.g., LookupScreen verifiedBy, RevealSheet prop removal).
Map & Map Layers
apps/citizen-pwa/src/components/MapTab/index.tsx, .../FilterBar.tsx, .../PeekSheet.tsx, .../DetailSheet.tsx, MapTab/IncidentLayer.tsx, MapTab/MyReportLayer.tsx
Moved map chrome to Tailwind; added recenter FAB; added ResizeObserver to keep Leaflet sizing correct; adjusted low-severity color from #001e40#414849; replaced several inline styles with classes.
Submit Report Wizard
apps/citizen-pwa/src/components/SubmitReportForm/... (ContactFields.tsx, Step1Evidence.tsx, Step2WhoWhere.tsx, Step3Review.tsx, OfflineBanner.tsx, SmsFallbackButton.tsx)
Converted multi-step report UI to Tailwind-driven layout with sticky headers, step indicators, reworked photo evidence handling, contact fields with ARIA improvements, patient counter UI, and CTA label/placement changes.
Feed, Alerts, Profile
apps/citizen-pwa/src/components/FeedTab.tsx, FeedTab.test.tsx, AlertsTab.tsx, ProfileTab.tsx, ProfileTab.test.tsx
Restyled feed/alerts/profile UIs to Tailwind, replaced emoji with Lucide icons, changed severity/badge classes and empty/error renders; ProfileTab adds badges/milestones, Firebase gating via hasFirebaseConfig, and sign-out/share behaviors; tests updated accordingly.
Reveal & Receipt Ceremonies
apps/citizen-pwa/src/components/RevealSheet.tsx, RevealSheet.test.tsx, ReceiptScreen.tsx
Rewrote reveal/receipt ceremonies to use useSlotMachine, Framer Motion visuals (radar rings, animated check), deferred secret reveal, vibration patterns, navigate via React Router, and updated tests to assert secret-code behavior. Removed reportCount prop from RevealSheet props.
Hooks & Utilities
apps/citizen-pwa/src/hooks/useOfflineQueueCount.ts, .../useOfflineQueueCount.test.ts, apps/citizen-pwa/src/hooks/useSlotMachine.ts, .../useSlotMachine.test.ts, apps/citizen-pwa/src/utils/incident-meta.tsx
useOfflineQueueCount now returns { isOnline, queueCount }, broadens non-synced draft criteria and polls every 2000ms; added useSlotMachine hook (exported) with tests; updated incident icon/label mappings.
Testing: framer-motion mock
apps/citizen-pwa/src/__tests__/setup-framer-motion.ts
Added Vitest module mock providing stub motion.* components, AnimatePresence, and placeholder hooks for framer-motion to stabilize tests.
Functions: Data Export
functions/src/callables/request-data-export.ts, functions/src/index.ts
Added requestDataExport callable Cloud Function (auth role 'citizen', App Check) and exported it from functions entry.
Docs & Plans
docs/superpowers/*, docs/reference-gap-analysis.md
Added detailed specs, execution plans, and gap analyses describing the redesign, token migration, test strategy, and tasks to finish verification.

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
Loading
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
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

Poem

🐰 In a burrow of classes and motion I play,

Tailwind threads and rippling rings sway,
Splash to onboarding, the route hops along,
Animations hum a bright little song,
Hooray — code sprouted wings today!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: spec gap fixes focused on shell layout, FAB styling, labels, and confirmation ceremony. It is specific, concise, and directly reflects the core redesign polish work.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/citizen-pwa-redesign-spec-gaps

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.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 45 minutes and 24 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Low-severity color is inconsistent with the map filter palette.

PeekSheet still uses #001e40 for 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 win

Incident labels still use pre-redesign copy for three types.

accident, structural, and other labels 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 win

The upgrade prompt is still unreachable from the live success flow.

This branch only renders when reportCount is passed, but the current success caller still creates RevealSheet without 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 win

Associate each validation message with its input.

The new error text is only visual right now. Without aria-invalid and aria-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

📥 Commits

Reviewing files that changed from the base of the PR and between 86c9b10 and cc3e714.

⛔ Files ignored due to path filters (2)
  • apps/citizen-pwa/public/watchtower.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (51)
  • apps/citizen-pwa/package.json
  • apps/citizen-pwa/postcss.config.js
  • apps/citizen-pwa/src/App.routes.test.tsx
  • apps/citizen-pwa/src/__tests__/setup-framer-motion.ts
  • apps/citizen-pwa/src/__tests__/uiStore.test.ts
  • apps/citizen-pwa/src/components/AlertsTab.tsx
  • apps/citizen-pwa/src/components/CitizenShell.test.tsx
  • apps/citizen-pwa/src/components/CitizenShell.tsx
  • apps/citizen-pwa/src/components/FeedTab.test.tsx
  • apps/citizen-pwa/src/components/FeedTab.tsx
  • apps/citizen-pwa/src/components/LookupScreen.test.tsx
  • apps/citizen-pwa/src/components/LookupScreen.tsx
  • apps/citizen-pwa/src/components/MapTab/DetailSheet.tsx
  • apps/citizen-pwa/src/components/MapTab/FilterBar.tsx
  • apps/citizen-pwa/src/components/MapTab/PeekSheet.tsx
  • apps/citizen-pwa/src/components/MapTab/index.tsx
  • apps/citizen-pwa/src/components/ProfileTab.test.tsx
  • apps/citizen-pwa/src/components/ProfileTab.tsx
  • apps/citizen-pwa/src/components/ReceiptScreen.tsx
  • apps/citizen-pwa/src/components/RevealSheet.test.tsx
  • apps/citizen-pwa/src/components/RevealSheet.tsx
  • apps/citizen-pwa/src/components/SubmitReportForm/ContactFields.tsx
  • apps/citizen-pwa/src/components/SubmitReportForm/Step1Evidence.tsx
  • apps/citizen-pwa/src/components/SubmitReportForm/Step2WhoWhere.tsx
  • apps/citizen-pwa/src/components/SubmitReportForm/Step3Review.tsx
  • apps/citizen-pwa/src/components/Toast.tsx
  • apps/citizen-pwa/src/components/Toggle.tsx
  • apps/citizen-pwa/src/hooks/useOfflineQueueCount.test.ts
  • apps/citizen-pwa/src/hooks/useOfflineQueueCount.ts
  • apps/citizen-pwa/src/hooks/useSlotMachine.test.ts
  • apps/citizen-pwa/src/hooks/useSlotMachine.ts
  • apps/citizen-pwa/src/lib/design-tokens.ts
  • apps/citizen-pwa/src/lib/store.ts
  • apps/citizen-pwa/src/pages/Onboarding.test.tsx
  • apps/citizen-pwa/src/pages/Onboarding.tsx
  • apps/citizen-pwa/src/pages/RegisterPage.tsx
  • apps/citizen-pwa/src/pages/SettingsPage.test.tsx
  • apps/citizen-pwa/src/pages/SettingsPage.tsx
  • apps/citizen-pwa/src/pages/SplashScreen.test.tsx
  • apps/citizen-pwa/src/pages/SplashScreen.tsx
  • apps/citizen-pwa/src/routes.tsx
  • apps/citizen-pwa/src/styles/design-tokens.css
  • apps/citizen-pwa/src/utils/incident-meta.tsx
  • apps/citizen-pwa/tailwind.config.cjs
  • apps/citizen-pwa/vitest.config.ts
  • docs/superpowers/plans/2026-04-30-citizen-pwa-redesign-spec-gaps.md
  • docs/superpowers/plans/2026-04-30-citizen-pwa-ui-restyle.md
  • docs/superpowers/specs/2026-04-30-citizen-pwa-ui-restyle-design.md
  • eslint.config.js
  • functions/src/callables/request-data-export.ts
  • functions/src/index.ts
💤 Files with no reviewable changes (1)
  • apps/citizen-pwa/src/lib/design-tokens.ts

Comment thread apps/citizen-pwa/src/__tests__/uiStore.test.ts Outdated
Comment thread apps/citizen-pwa/src/components/AlertsTab.tsx
Comment thread apps/citizen-pwa/src/components/FeedTab.tsx
Comment thread apps/citizen-pwa/src/components/LookupScreen.tsx
Comment thread apps/citizen-pwa/src/components/LookupScreen.tsx
Comment on lines +33 to +36
return (
<>
<AnimatePresence>{showSplash && <SplashScreen onDone={onSplashDone} />}</AnimatePresence>
<Outlet />
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Comment on lines +1 to +3
@tailwind base;
@tailwind components;
@tailwind utilities;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 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.json

Repository: 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.

Comment thread docs/superpowers/plans/2026-04-30-citizen-pwa-ui-restyle.md
Comment on lines +675 to +678
```bash
cp /Users/superman/Downloads/Citizen_PWA/app/dist/watchtower.svg \
apps/citizen-pwa/public/watchtower.svg
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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}`)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

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.

Suggested change
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.

Exc1D added 7 commits May 1, 2026 12:35
… 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>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Low severity color inconsistent within this component.

severityMeta() returns color: '#001e40' (old navy blue) for low, but severityBorderClass() and severityIconColor() use #64748b (slate). The badge text (line 116-119) uses severityMeta, 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

📥 Commits

Reviewing files that changed from the base of the PR and between cc3e714 and 769040a.

📒 Files selected for processing (27)
  • apps/citizen-pwa/src/__tests__/uiStore.test.ts
  • apps/citizen-pwa/src/components/AlertsTab.tsx
  • apps/citizen-pwa/src/components/FeedTab.tsx
  • apps/citizen-pwa/src/components/LookupScreen.tsx
  • apps/citizen-pwa/src/components/MapTab/FilterBar.tsx
  • apps/citizen-pwa/src/components/MapTab/IncidentLayer.tsx
  • apps/citizen-pwa/src/components/MapTab/MyReportLayer.tsx
  • apps/citizen-pwa/src/components/MapTab/PeekSheet.tsx
  • apps/citizen-pwa/src/components/MapTab/index.tsx
  • apps/citizen-pwa/src/components/ProfileTab.test.tsx
  • apps/citizen-pwa/src/components/ProfileTab.tsx
  • apps/citizen-pwa/src/components/RevealSheet.test.tsx
  • apps/citizen-pwa/src/components/RevealSheet.tsx
  • apps/citizen-pwa/src/components/SubmitReportForm/ContactFields.tsx
  • apps/citizen-pwa/src/components/SubmitReportForm/OfflineBanner.tsx
  • apps/citizen-pwa/src/components/SubmitReportForm/SmsFallbackButton.tsx
  • apps/citizen-pwa/src/components/SubmitReportForm/Step1Evidence.tsx
  • apps/citizen-pwa/src/components/SubmitReportForm/Step3Review.tsx
  • apps/citizen-pwa/src/hooks/useSlotMachine.ts
  • apps/citizen-pwa/src/lib/store.ts
  • apps/citizen-pwa/src/pages/SettingsPage.test.tsx
  • apps/citizen-pwa/src/pages/SettingsPage.tsx
  • apps/citizen-pwa/src/styles/design-tokens.css
  • apps/citizen-pwa/src/styles/globals.css
  • apps/citizen-pwa/src/utils/incident-meta.tsx
  • apps/citizen-pwa/tailwind.config.cjs
  • docs/reference-gap-analysis.md

Comment thread apps/citizen-pwa/src/components/LookupScreen.tsx
Comment on lines +80 to +87
<button
type="button"
aria-label="Pull up for full detail"
onClick={onExpand}
className="text-brand-500 text-sm font-medium"
>
Track
</button>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Suggested change
<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.

Comment thread apps/citizen-pwa/src/components/RevealSheet.tsx Outdated
Comment thread apps/citizen-pwa/src/components/SubmitReportForm/ContactFields.tsx Outdated
Comment on lines +165 to 179
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="+"
>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Comment on lines +39 to +41
* {
-webkit-tap-highlight-color: transparent;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 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.

Suggested change
* {
-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} />,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Suggested change
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`
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Comment on lines +54 to +62
```
brand-600: #0D7377
brand-500: #0F9488
brand-400: #4DB6A8
brand-300: #8FD4CA
brand-200: #C4E8E2
brand-100: #E8F6F3
brand-50: #F3FAF9
```
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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.

Comment on lines +497 to +499
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`

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

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 start

Also 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).

Exc1D added 2 commits May 1, 2026 13:27
…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>
@Exc1D Exc1D merged commit 7cd391a into main May 1, 2026
14 checks passed
@Exc1D Exc1D deleted the fix/citizen-pwa-redesign-spec-gaps branch May 1, 2026 05:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant