m MiniEHR / learn
Foundry ↗About MiniEHROpen workspace ↗
LESSON 6 OF 97 min read

6. Understand the frontend and hosting

The browser is plain HTML, CSS, and JavaScript. You do not need React to understand or modify it. The backend is C#; the browser code is JavaScript.

Three files

FileJob
index.htmlSidebar, patient directory, selected record, modal form
app.cssColors, layout, responsive rules, focus styles
app.jsAPI requests, state, rendering, forms, write versions

ASP.NET serves wwwroot. index.html loads app.css and the JavaScript module. $ is this project's helper for document.getElementById, not a jQuery dependency. el creates an element and assigns textContent. Patient names and notes are inserted as text, so strings containing HTML are not interpreted as markup.

Follow a browser interaction

  1. run(loadPatients) loads the first patient page when the app starts.
  2. loadPatients requests ten patients and renders buttons with identity details.
  3. selectPatient loads patient details and every encounter page, then renders demographics and encounter buttons.
  4. selectEncounter loads encounter details, vitals, and notes.
  5. A form function opens the shared dialog with the relevant fields.
  6. Its save callback submits JSON with the expected versions.
  7. After a successful write, the dialog closes and a returned refresh callback reloads the view.

state holds current page, patient, encounter, and a busy flag. run prevents overlapping ordinary actions and sets aria-busy while asynchronous work runs. Form submission separately disables its save/cancel/dismiss controls. Escape cancellation is blocked while saving.

API helper and pagination

api(path, method, body) fetches /api/ paths on the current origin, sends JSON when needed, and translates error bodies to messages. A 409 explains that the record must be refreshed and preserves unsaved form text.

collection(path) requests clinical pages of 100 until a shorter page is returned. This avoids silently dropping all records after the first 20. Because there is no total count, an exactly full final page needs one additional request. Concurrent inserts can still shift offset pages; this is not a snapshot cursor API.

Saving and refreshing are different outcomes

The original form awaited the write and refresh in one try block. If the write succeeded but the refresh failed, the form reported a failed save and allowed resubmission. Candidate 0d4d28b separates these operations: write callbacks return a function to refresh after confirmed save. A refresh failure says the change was saved and asks the user to refresh; it does not reopen the submitted form.

Two focused JavaScript checks covered confirmed write/failed refresh and rejected write/preserved form. They were supplementary checks, not Foundry full-suite or interactive browser evidence. An ambiguous network failure during the write itself can still leave uncertainty; check saved state before retrying.

Forms and closure

PatientForm handles registration/correction. EncounterForm handles start times. VitalForm builds explicit-unit measurement inputs. NoteForm uses a bounded text area. CloseForm requires an acknowledgement before permanent closure.

Native required/min/max constraints catch some invalid inputs. Server validation remains authoritative. Dates are displayed in the browser's local timezone; datetime-local form values are converted to ISO timestamps for the API.

Closed encounters render a closure notice and omit edit/create controls. This is presentation, not the persistence safeguard. The backend still rejects requests from older pages or direct API clients.

Practice the local browser workflow

After the owner-signed launch in lesson 1:

  1. Register Synthetic Example with a fresh SYN-prefixed MRN.
  2. Select the patient, correct its name, and start an encounter in the past.
  3. Add paired blood pressure and a synthetic note; correct both.
  4. Refresh and verify the changes remain visible.
  5. Close the encounter with the acknowledgement; verify editing controls disappear.
  6. Refresh/restart against the same synthetic database and verify closure persists.
  7. Check keyboard navigation, modal focus, errors, and narrow-screen layout.

These are manual acceptance steps to perform, not a claim that browser QA has already passed. API persistence/closure is covered by automated C# tests.

Local hosting versus Netlify

The owner has deferred paid backend hosting. The intended experience is the hosted read-only demo and tutorial, with the functional C# application available locally through Foundry. Online registration and editing are optional future work, not a remaining hosting requirement. Foundry evidence and review gates still apply to the engineering evaluation.

View detailed diagram notation
flowchart LR
    L[Local browser] --> A[ASP.NET: frontend and API]
    A --> D[(Local SQLite)]
    N[Hosted browser] --> S[Netlify: static files]
    S -. API proxy after configuration .-> H[Separately hosted ASP.NET backend]
    H --> P[(Persistent backend SQLite)]

Local requests share an origin automatically. Netlify cannot execute this ASP.NET/SQLite process as static files. A hosted functional frontend needs a separately available public HTTPS backend and its persistent storage.

build-netlify.mjs requires MINIEHR_API_ORIGIN and rejects non-HTTPS, credentials, paths, queries/fragments, and named local origins. It copies exactly index.html/app.css/app.js to netlify-dist and generates a forced /api/* proxy rewrite. The browser continues requesting its own origin; it does not directly fetch a different backend domain.

netlify.toml sets Node 22, the build command, publish directory, and response headers. For a Git-connected deployment, select the intended branch, base src/MiniEHR.Api, and the build-scoped backend origin. Follow Netlify's configuration docs and proxy rewrite docs. This describes prepared settings; Git-triggered deployment is not yet configured by this tutorial. A host providing the backend remains separate authorized work.

What is live today

https://miniehr-workspace.netlify.app now includes four read-only invented patients and the complete learning guide at /learn/. Select a patient to browse example encounters, vitals, and notes. One example is closed, one has no vitals, and one has no encounters, so you can learn the different states.

The preview package substitutes demo.js for app.js and disables data-entry buttons. Demo.js renders demo-data.js fixtures without API requests or database writes. Those IDs are frontend-only demo keys, not real API GUIDs. Refreshing restores the same fixtures. This does not demonstrate backend seeding or a working hosted C# API. The functional wwwroot index still loads app.js for local API use.

The generator reads the repository Markdown to produce static pages in wwwroot/learn. To update a lesson, edit its Markdown under the tutorial ownership scope and regenerate the pages under the frontend scope. The Netlify packager also regenerates them. MINIEHR_PREVIEW=1 explicitly selects demo packaging without requiring an API origin; normal packaging still requires a valid backend origin.

Netlify site ID: 563f8163-7663-4e87-94e6-2f0879f5a2da. The original layout-only deployment was 6aca669e769320e5c2a442a7; later frontend updates add the learning pages and demo records. See the site's deploy history and Foundry artifacts for their exact identities. Backend hosting is deferred; the current demo is not waiting for a required online API launch. The public URL returned HTTP 200. CLI upload happened outside a Foundry runtime; it is not a supervised build or browser-acceptance pass. Foundry registered the deployment as EHR-4/art-0004, a submitted diagnostic artifact.

Checkpoint

Which file must change to alter the API contract? Usually backend request/service code plus its consumers and meaningful tests—not just an HTML label. Which host currently runs the hosted C# API? None has been configured.

Next: Tests and exercises.

MiniEHR learning guide · Based on this project’s source · Read on GitHub