Capstone: Harbor Together event finder
The brief you would receive from a small community group
People want to find an event by title and category, keep a shortlist in this browser and try a practice interest check. Build the demonstration using only local invented fixtures. A successful practice check must never claim a booking or real submission. The static design connects to the companion HTML/CSS course's Harbor Together project; a copy is in examples/00-static.
Work in the starter, using your editor on capstone/starter. The source files contain intentional TODOs. Keep the separate reference closed until you have attempted a checkpoint. Serve the course root over HTTP so fixtures and modules work. Copy supplied HTML/CSS as needed; the JavaScript behavior is your work.
Checkpoint 1: pure rules
Implement seatLabel, filterEvents and toggleSaved in starter/core.js. seatLabel rejects a negative or non-integer count with “Check event data”, uses “Full” for zero and singular/plural wording for positive counts. Filtering combines trimmed case-insensitive title matching, category, and optional saved-only state. Toggling returns a new array without changing the input.
Run learner checks. They import your starter/core.js, so editing the reference cannot make them pass. Initial failures are expected. Record why each boundary case matters before fixing it.
Checkpoint 2: cards and a count
Use the local records in events.json as the model. Build each card with createElement, textContent and append. Include a heading, date, category, location, availability and native Save toggle button. Show the count and an explicit empty message. Do not insert record text through innerHTML. Keep the Save label stable while updating aria-pressed and separate status text.
Evidence: six initial cards, a markup-like title shown literally, and no-match feedback. You may temporarily use a small inline array until the loading checkpoint.
Checkpoint 3: connected controls
Attach search, category and saved-only listeners once. Reuse the pure filter rule. Do not rebuild the search control itself during rendering. A save changes the saved-ID list, not the source event. Preserve focus on a saved button; if unsaving removes its card in saved-only view, move focus to the saved-only control.
Evidence: all six events; two outdoors; query “ GARDEN ” gives one; a nonexistent query gives zero; saved-only gives only your saved event. Repeat a save and verify it does not create duplicates.
Checkpoint 4: the local form
Use only invented name/email values. Validate required fields, email shape, trimmed name and a selected available event. Handle submit at the form, prevent normal navigation, refresh custom validity and call reportValidity. Retain labels and correction text. Do not add requests or personal-data persistence.
Evidence: empty name, whitespace name, malformed email and unavailable event cannot produce success. A valid synthetic check stays local and explicitly says nothing was sent, stored or booked. This capstone does not ask for party size; exercise 07 separately practices numeric constraints.
Checkpoint 5: asynchronous loading
Replace the inline records with a fetch of events.json. Check HTTP status, parse JSON and verify record shape before rendering. Show loading, populated, empty and failure states. Restore controls after failure and provide retry. Do not start overlapping requests from repeated clicks.
Evidence: normal fixture, empty fixture, invalid structure, malformed JSON and missing file. After each failure, switch back to the normal fixture and recover. Keep an expected-failure Console entry distinct from an unexpected runtime exception.
Checkpoint 6: bounded persistence
Save only event IDs under a versioned key. Parse and validate stored text. Catch denied access, corrupted JSON and failed writes. Continue in memory with an honest warning. Ignore unknown IDs and duplicates. Clear only your app's key. Keep a user's in-memory changes through later retries when writes are unavailable.
Evidence: save, reload and recover a known ID; corrupt storage and reload without a crash; use the supplied harness to see the reference storage fallback, then test your own helper and UI as described below; confirm personal fields never appear in the saved value. The reference does not synchronize multiple tabs or devices; do not claim that it does.
Test your own storage implementation
The supplied browser harness imports reference/storage.js, so its passing result cannot certify your code. Give your own read/write helper a storage-provider parameter as explained in the walkthrough. Pass a provider that throws new Error("Denied for practice"); verify your helper returns a warning rather than throwing. Then temporarily use that provider in your own app and activate Save: the card must remain saved for this tab and the visible status must explain that it may be lost on reload. Restore the normal provider afterward. Also test an actual reload with corrupted JSON under your own app key.
Rubric: 20 points
- Data rules, boundary cases and non-mutating toggles: 4
- Correct safe rendering, filtering and empty state: 4
- Native labeled controls, stable toggle semantics and focus behavior: 3
- Truthful local form, constraints and correction path: 3
- Loading/error/retry and record checks: 3
- Storage resilience, bounded data and honest messages: 3
A suggested pass is 16/20, with all safety requirements mandatory: no real submissions, no personal data stored, no untrusted HTML insertion, no false registration or persistence claims. Instructors should calibrate this threshold with learner pilots. A working happy path cannot compensate for a safety violation.
Hand in
Your starter folder with completed files, a short acceptance-case record and a two-minute explanation of one action. Answer: where does the data come from; which function makes the decision; what changes on screen; what can fail; what stays usable after failure? Explain one bug you found and the evidence that led to its fix.
If you get stuck
Use the module-to-code map in the walkthrough one checkpoint at a time. Then compare the reference. A copied solution is useful for study but does not demonstrate independent completion. The package's QA report describes its own checks; it is not a substitute for checking your changed code.