Skip to content

feat(facility): settings for location, opening details and price list… - #169

Merged
afeezorobsco-cyber merged 10 commits into
DocForum:mainfrom
Bolajiomo99:feat/facility-settings-location-opening-details-and
Oct 30, 2026
Merged

afeezorobsco-cyber merged 10 commits into
DocForum:mainfrom
Bolajiomo99:feat/facility-settings-location-opening-details-and

Conversation

@Bolajiomo99

Copy link
Copy Markdown
Contributor

Closes #56
Summary
This PR implements issue #56 — Phase W4 Facility settings: location, opening details and price list. It gives facility owners and staff a single, dedicated Facility settings screen (/facility/settings) where they can:

  1. Maintain the facility's location, contact details and opening hours — including a map pin they can place, and
  2. Edit the facility's price list — per catalog item and per payment asset, with a full effective-from version history.
    The screen is role-gated to the facility role, added to the header navigation, and cross-linked from every existing facility page tab bar so it is discoverable from the fulfillment queue, order verification, team management and payouts screens.
    This implements the facility management flow described in the PRD §4 facility core flows (Phase W4).
    Description
    Background
    Facilities were already able to manage staff ([Phase W4] Facility staff accounts: invite, list and remove #51), verify orders ([Phase W4] Scan-to-verify orders with the device camera #52) and view their payout ledger ([Phase W4] Facility payout ledger with per-order escrow status and terms #53), but they had no way to self-serve the data that patients and the platform depend on most:
  • The address and coordinates that drive distance-sorted facility search.
  • The opening hours patients use to decide whether a facility is available.
  • The contact details patients need to reach the facility.
  • The prices that will be used to create future payment intents.
    Without this screen, every one of those changes required a manual backend/database change, which is unscalable and error-prone. [Phase W4] Facility settings: location, opening details and price list #56 closes that gap.
    What was built
  1. Location & contact editor
    src/features/facility-settings/components/LocationContactEditor.tsx
  • Street address, city, contact phone, contact email — email is optional but format-validated when present.
  • Map pin placement — an OpenStreetMap tile preview (LocationMapPicker.tsx) where clicking anywhere moves the facility pin. Coordinates update live in the latitude/longitude fields.
  • Keyboard-accessible alternative to the map (edge case from the issue) — the latitude and longitude fields are always visible and fully editable by keyboard and screen reader. The map is explicitly a convenience for pointer users; the accessible region's aria-label tells assistive-tech users to use the coordinate fields instead. This was called out in the issue as a requirement and is covered by a test.
  • Opening hours — a per-day table (Monday–Sunday), each day with an opens/closes time and a "closed" toggle. Closed days disable their time inputs.
  • Validation:
  • Required: street address, city, contact phone.
  • Optional email must look like an email.
  • Latitude must be −90…90, longitude −180…180.
  • Working days must have both an opening and closing time, and closing must be after opening.
  • Save / Discard — saving sends the editable subset via PATCH; discarding restores the last-saved values.
  • Unsaved-changes badge and a success/error banner.
  1. Price list editor
    src/features/facility-settings/components/PriceListEditor.tsx
  • Catalog search — searches docforum-core's catalog by item name or code.
  • Per-asset pricing — each price targets one asset (XLM or USDC).
  • Effective-from timestamp — a datetime-local field so a price can be scheduled to take effect at a specific moment.
  • Version history — a newest-first table of every version, each labelled:
  • Current — the version in force right now,
  • Scheduled — a future version that will apply later,
  • Superseded — an earlier version that has been replaced.
  • Decimal-aware amount entry (reviewer focus):
  • Amounts are entered as human decimals and converted to raw stroops using BigInt — never floating-point math — so a large amount can never drift by a stroop.
  • Both assets settle to 7 decimal places (ASSET_DECIMALS); the input is rejected client-side (with a clear message) if it has more than 7 decimals, is negative, is zero, or is non-numeric. This happens before any network request.
  • The stored/sent value is always a raw integer stroop string (1 XLM/USDC = 10,000,000 stroops).
  • Existing payments are never repriced (edge case from the issue) — a prominent, always-visible notice on the price list states that saving a price creates a new version for future orders only, and that payment intents that already exist keep the amount snapshotted when they were created. There is no UI path in this screen that can mutate an existing payment intent.
  1. Unsaved-changes guard
    src/features/facility-settings/hooks/useUnsavedChangesGuard.ts
    The app uses HashRouter, which is not a data router, so useBlocker / unstable_usePrompt are unavailable. The guard therefore:
  • warns on a full page unload via beforeunload, and
  • intercepts in-app link navigation with a capture-phase click listener on document, confirming with the user before letting the router navigate away.
    The guard is active while either editor has unsaved changes, so the two editors are combined via the FacilitySettingsView wrapper.
  1. Wiring
  • New route /facility/settings in src/App.tsx, gated to the facility role.
  • Settings links added to the header nav (Layout.tsx) and to the tab bar rendered on every facility page (FacilityDashboardPage, OrderVerifyPage, FacilityTeamPage, FacilityPaymentsPage, FacilitySettingsPage).
  • New FacilitySettingsPage (page shell + tab bar) and FacilitySettingsView (loads settings, combines dirty state from both editors, mounts the guard).
    Assumed API contract (dependency not yet landed)
    This feature depends on two docforum-core issues that have not landed yet:
  • [Phase 5] Facility location and distance-sorted facility selection docforum-core#74 — facility location / distance-sorted selection.
  • [Phase 5] Facility price lists used to price PaymentIntents docforum-core#127 — facility price lists used to price PaymentIntents.
    Because of that, the endpoint shapes below are assumed and mirrored in src/types/models.ts. They are recorded in ARCHITECTURE_ESSENTIALS.md under "Known assumptions" so they can be reconciled once the backend lands. No deviation will be silent — this section is the contract to check.
    Method Path Body
    GET /facilities/:facilityId/settings —
    PATCH /facilities/:facilityId/settings FacilitySettingsInput
    GET /catalog/items?query= —
    GET /facilities/:facilityId/prices —
    POST /facilities/:facilityId/prices FacilityPriceInput
  • FacilitySettings = { facilityId, name, type, address, city, latitude, longitude, phone, email, openingHours, updatedAt }.
  • FacilitySettingsInput = { address, city, latitude, longitude, phone, email, openingHours }.
  • openingHours is always all seven Weekday entries (monday…sunday), each { day, closed, opensAt, closesAt } with HH:MM local strings or null when closed.
  • CatalogItem = { code, name, type, description? } where code is the stable id stored on a price.
  • FacilityPrice = { id, facilityId, catalogItemCode, catalogItemName, asset, amountStroops, effectiveFrom, createdAt }.
  • FacilityPriceInput = { catalogItemCode, asset, amountStroops, effectiveFrom }.
    All calls go through docforum-core's API via apiFetch — no direct Stellar/escrow calls are made from this repo (architecture hard rule 1).
    Important note for the maintainer
    If docforum-core#74 / [Platform] Demo mode for the public preview: explore every role without a backend #127 land with different shapes, the single places to change are:
  • src/types/models.ts (the type definitions), and
  • src/services/facility-settings-service.ts (the request/response mapping).
    The components consume those through React Query hooks only, so a shape change should be a contained update.
    Acceptance checklist
  • Dedicated Facility settings page (/facility/settings), role-gated to facility.
  • Location editor: address, city, contact details.
  • Map pin placement updating latitude / longitude.
  • Keyboard-accessible alternative to the map (manual latitude/longitude fields).
  • Opening hours per day, with a closed toggle.
  • Price list editor: catalog search by name or code.
  • Set a price per asset (XLM / USDC).
  • Effective-from history, labelled current / scheduled / superseded.
  • Decimal validation per asset (7 decimals, no floats, client-side, before request).
  • Required-field validation (address, city, phone, coordinates, opening times).
  • Unsaved-changes guard covering both editors.
  • Price changes never affect existing payment intents, and the UI says so.
  • Tests for both editors.
  • Settings discoverable from the header nav and every facility page.
  • Mock fixtures/handlers with a multi-version price history.
  • ROADMAP.md and ARCHITECTURE_ESSENTIALS.md updated (per repo rules).
  • Desktop + mobile screenshots captured.
    Tests
    New tests:
  • src/features/facility-settings/validation.test.ts — decimal→stroop conversion (including exact 7-decimal and rejection cases), settings validation, opening-hours ordering, effective-from parsing.
  • src/features/facility-settings/components/FacilitySettings.test.tsx — location/contact editor render + validation + save + discard + dirty state; price list history/statuses, catalog search, exact-stroop save, over-precision rejection, empty effective-from.
  • src/features/facility-settings/hooks/useUnsavedChangesGuard.test.tsx — prompts before leaving a dirty editor, stays silent when clean.
  • src/mocks/handlers.test.ts — added handler coverage for settings GET/PATCH (including 400 on bad coordinates / missing fields), catalog search, and price list GET/POST (including 400 on non-stroop amounts and unknown catalog codes).
    Results
    npm run typecheck # clean
    npm test # 43 files, 396/396 tests passing
    npm run build # green (tsc -b && vite build)
    npm run check:dist # green (no mock code in production bundle)
    Screenshots
    Captured with Playwright against the mock dev server, desktop (1440×1000) and mobile (390×844):
  • screenshots/facility-settings-desktop.png / -mobile.png — location & contact editor incl. map picker and opening hours.
  • screenshots/facility-settings-price-list-desktop.png / -mobile.png — price list with version history and statuses.
  • screenshots/facility-settings-price-validation-desktop.png / -mobile.png — the over-precision decimal validation message.
    Screens changed / PRD flows
  • New screen: Facility Settings (/facility/settings) — implements the PRD §4 facility management flow (Phase W4, docforum-core).
  • Touched screens: added a Settings tab link to the facility fulfillment queue, order verification, team management and payouts pages (navigation only, no behaviour change).
    Follow-ups / notes for maintainers
  • Reconcile the assumed endpoint shapes when docforum-core#74 and [Platform] Demo mode for the public preview: explore every role without a backend #127 land (see "Assumed API contract" above).
  • Server-side validation of coordinates, assets and stroop amounts is presumed but not implemented here (this repo never talks to the backend directly for writes beyond the documented API); the client validation is a UX guard, not a security boundary.
  • Money remains BigInt-safe throughout; no floating-point currency math is introduced.

@drips-wave

drips-wave Bot commented Oct 20, 2026

Copy link
Copy Markdown

@Bolajiomo99 Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits.

You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀

Learn more about application limits

@afeezorobsco-cyber

Copy link
Copy Markdown
Contributor

@Bolajiomo99 Thanks for the PR. It can't be merged yet. To unblock it:

Merge conflicts: this branch conflicts with main. Please rebase onto the latest main (git fetch upstream && git rebase upstream/main), resolve the conflicts (often ROADMAP.md, ARCHITECTURE*.md or prisma/schema.prisma), and force-push.

Push to this same branch and CI will re-run. Once it's green and conflict-free, it'll be reviewed for merge.

@github-actions github-actions Bot added needs-rebase Has merge conflicts with main; the author needs to rebase and removed needs-rebase Has merge conflicts with main; the author needs to rebase labels Oct 20, 2026
@github-actions

Copy link
Copy Markdown

@Bolajiomo99 This PR now has merge conflicts with main, most likely because #170 (Feat/facility performance insights turnaround times) was just merged, so it can't be merged until it's rebased:

git fetch upstream            # upstream = https://github.com/DocForum/docforum-web.git
git rebase upstream/main
# resolve the conflicts, re-run the tests, then:
git push --force-with-lease

The usual culprits are shared docs like ROADMAP.md and ARCHITECTURE*.md. If the conflict is in the ROADMAP.md changelog, move your entry into a new file under changelog.d/ (see changelog.d/README.md) rather than resolving it in place, so it can't conflict again. CI re-runs when you push, and the needs-rebase label is removed automatically once the conflicts are resolved.

Automated message. It won't repeat unless the PR conflicts again after your next push.

@afeezorobsco-cyber

Copy link
Copy Markdown
Contributor

@Bolajiomo99 CI is failing on this branch against the latest main, so it can't be merged yet. The errors from the latest run:

Frontend (typecheck + test + build)

src/features/facility-settings/components/FacilitySettings.test.tsx(6,15): error TS2305: Module '"../../../types/models"' has no exported member 'CatalogItem'.
src/features/facility-settings/components/FacilitySettings.test.tsx(6,28): error TS2724: '"../../../types/models"' has no exported member named 'FacilityPrice'. Did you mean 'FacilityProfile'?
src/features/facility-settings/components/FacilitySettings.test.tsx(6,43): error TS2305: Module '"../../../types/models"' has no exported member 'FacilitySettings'.
src/features/facility-settings/components/LocationContactEditor.tsx(6,15): error TS2305: Module '"../../../types/models"' has no exported member 'FacilitySettings'.
src/features/facility-settings/components/LocationContactEditor.tsx(6,33): error TS2305: Module '"../../../types/models"' has no exported member 'FacilityOpeningHours'.
src/features/facility-settings/components/LocationContactEditor.tsx(6,55): error TS2305: Module '"../../../types/models"' has no exported member 'Weekday'.
src/features/facility-settings/components/PriceListEditor.tsx(11,15): error TS2305: Module '"../../../types/models"' has no exported member 'CatalogItem'.
src/features/facility-settings/components/PriceListEditor.tsx(11,28): error TS2724: '"../../../types/models"' has no exported member named 'FacilityPrice'. Did you mean 'FacilityProfile'?

src/types/models.ts doesn't export the types this PR uses any more. The additions to that file were probably dropped while resolving a conflict, so re-add them.

This PR also conflicts with main again, so please rebase first, then fix the above.

@Bolajiomo99
Bolajiomo99 force-pushed the feat/facility-settings-location-opening-details-and branch from 5f7a052 to ccc5737 Compare October 25, 2026 15:32
@github-actions github-actions Bot removed the needs-rebase Has merge conflicts with main; the author needs to rebase label Oct 25, 2026
@afeezorobsco-cyber

Copy link
Copy Markdown
Contributor

@Bolajiomo99 The Browser (journeys + accessibility + visual) job fails the visual regression check: the rendered page no longer matches the committed baseline:


Expected: e2e/__screenshots__/visual-mobile/facility-dashboard.png

If the visual change is intentional, regenerate those baselines in the same Linux/Playwright setup CI uses (see the visual-testing notes in e2e/) and commit them. If it isn't intentional, fix the layout. Then push.

@github-actions

github-actions Bot commented Oct 27, 2026 •

Copy link
Copy Markdown

🔎 Preview build (downloadable artifact)

Download the preview artifact · build log

  • Built from 8e3398a; GitHub sign-in is required to download.
  • This build is not published to GitHub Pages or any other shared origin.
  • Runs against the mock API only — fake seed data, no real backend, no real credentials.
  • To view it locally: unzip the artifact, then serve the folder (for example
    npx serve <folder>) and open the URL it prints.
  • The artifact expires after 14 days and is replaced on every push to this PR.

@afeezorobsco-cyber

Copy link
Copy Markdown
Contributor

@Bolajiomo99 After updating this branch with main, two jobs fail:

Unit tests (npm run test:coverage, and the same failure in the preview build):

FAIL src/mocks/handlers.test.ts > facility settings and prices > returns version history and appends a new price version
AssertionError: expected [ …(12) ] to have a length of 6 but got 12

The price-version mock data now has twice the rows the test expects. It looks like both your seed and main's (from #168, merged just now) are loaded. Please de-duplicate the mock seed, or update the expectation if both sets are intended.

Browser (visual): e2e/__screenshots__/visual-desktop/facility-dashboard.png and visual-mobile/facility-dashboard.png no longer match. If the facility dashboard change is intended, regenerate those baselines in the same Linux/Playwright setup CI uses and commit them.

Then push.

@afeezorobsco-cyber

Copy link
Copy Markdown
Contributor

@Bolajiomo99 Still the same test failure as my earlier comment: src/mocks/handlers.test.ts > facility settings and prices gets 12 price versions instead of 6, because the mock seed is loaded twice after the merge. Please de-duplicate the price-version seed in src/mocks/ and run npm test before pushing.

@afeezorobsco-cyber afeezorobsco-cyber left a comment

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.

Malware scan clean, all checks green.

@afeezorobsco-cyber
afeezorobsco-cyber merged commit e5121fa into DocForum:main Oct 30, 2026
4 checks passed
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.

[Phase W4] Facility settings: location, opening details and price list

2 participants