18 Aug 2026
Student workflow, import, archive, and matching polish
This update records the implementation completed over the last two days. It is intentionally text-only: the work is grouped by workflow area so staff can see what changed, what is protected, and which parts remain planned for a later phase.
Import & operations
Review portrait imports, replacements, normalization, and long-running work before it changes the archive.
Portrait folder import now follows a review-first flow
Admins can drop a class, year, or parent folder, inspect the parsed students, and confirm the import before anything is saved.
What changed
The portrait import dialog now supports folder drag-and-drop, folder selection, filename parsing, thumbnail detection, invalid-file warnings, duplicate-ID warnings, name-conflict warnings, preview rows, and a clear import progress state. Original files remain unchanged.
Why
The old flow made it difficult to understand what a folder would create and gave staff little feedback during a large import.
Year and class are inferred from the folder context
Entering class C with Year 13 is interpreted as 13C rather than an unrelated class named C.
What changed
Year groups and class names are normalized when they are read from folder paths or entered as defaults. The import preview shows the final Year / class value before confirmation, and the same normalization is used by the student directory to avoid equivalent values appearing as separate filters.
Why
Folder naming is not always consistent, but the resulting student record still needs one stable academic grouping.
Archived student records can be replaced with explicit confirmation
A reused Student ID can restore/replace an archived test record only after the preview identifies the person and an admin confirms the action.
What changed
The import preview separates new students, archived records ready to replace, and active IDs that remain protected. Replacement names and IDs are shown before import, and the final action stays disabled until the replacement confirmation is checked.
Why
A deleted or archived test record can leave a real Student ID behind; silently blocking the real student or silently replacing the wrong person is unsafe.
Import no longer waits for a full archive match
Portrait saving and archive matching are separate steps, so staff can finish the import and run matching from the Students page.
What changed
Portrait indexing still reports progress during import, but the expensive archive-wide matching pass is moved to Check ready references. The import status explains this distinction so a long-running match is not mistaken for a stuck upload.
Why
Matching every imported reference during one modal made large imports feel frozen and made failures difficult to recover.
Student directory & lifecycle
Manage the active roster, progress cohorts, and preserve former-student records.
Students controls are easier to scan and operate
Search, Filters, Year, and Class now sit in a clearer command row with larger hit areas and more stable responsive layout.
What changed
The student directory keeps the current/former split, search, quick Year/Class controls, advanced filters, sorting, progressive browsing, role-scoped views, and management actions while improving spacing, focus states, and control sizing on desktop and smaller screens.
Why
The previous control row was visually crowded and the native dropdowns were difficult to hit accurately.
Restricted student filtering now matches the stored privacy state
Students restricted through photo-use controls now appear when staff filter for Restricted.
What changed
The Restricted filter now checks both the consent restriction field and the separate photosHidden/photo-use restriction flag. The control is labelled Consent / photo use to make the relationship clear without merging the underlying data meanings.
Why
The Restrict photo use action and the consent dropdown previously wrote/read different fields, so the person could be restricted but disappear from the Restricted result.
Equivalent year values no longer create duplicate dropdown options
Values such as 13 and Year 13 are normalized to one Year 13 option.
What changed
Quick Year options are built from canonical year groups before de-duplication. This keeps the dropdown, year rail, class inference, and filter state aligned even when legacy records contain different spellings.
Why
The same year was previously shown twice because the directory used raw stored values in one place and canonical values in another.
Photo archive
Browse academic years and large folder collections without losing the important counts and statuses.
Academic-year folders use a scalable directory layout
An academic year can show hundreds of folders as a compact, scannable table instead of a wall of oversized cards.
What changed
The year workspace now presents Photos, Folders, Need attention, and Restricted shown at the top, followed by folder search, sorting, table columns, All photos, per-folder photo counts, Ready/Preparing status, updated time, and pagination. The default page size is 15 folders and the directory is designed for 300+ folders.
Why
A school archive will outgrow a card grid quickly; staff need to find a folder by name or sort order without loading every photo.
Journey & matching
Build a long-term photo record with age-stage references and controlled archive matching.
Ready-reference matching is durable and cancellable
Staff can run a class or roster-wide matching pass without keeping a browser loop open, and can cancel it safely.
What changed
The global Check ready references action queues a durable job, processes bounded batches with leases and retry policy, checks the AWS Rekognition budget before provider calls, reports progress, preserves completed results, and exposes cancellation. Individual Find more matches remains available from a student Journey.
Why
Large matching work must survive browser navigation and deployment timeouts while avoiding uncontrolled AWS calls.
Journey exclude is reversible and scoped to one student
Staff can remove an incidental/edge-of-frame person from one student’s Journey without deleting the original photo or face data.
What changed
The Journey card now uses a compact X action with a hover/focus Exclude label. The decision is stored for the student-photo pair, excluded photos move to an expandable restore section, and Restore returns the photo to that student’s Journey. Other students and the archive remain unchanged.
Why
A student appearing at the edge of a group photo should lower their Journey inclusion without becoming a deletion or publication restriction.
Journey photos can be selected individually for export
Staff can select the useful cards one by one instead of relying on Select all photos.
What changed
Journey photo cards now expose an individual selection control, selected state, selected count, preview-aware selection, Select all, Clear selection, Create collection, and Prepare export. This keeps the common case practical when only a few photos in a long Journey are needed.
Why
Selecting an entire Journey is often too broad for a real export request.
Privacy & access
Make photo-use policy visible while keeping every viewer inside their authorised scope.
Journey rejection is no longer confused with publication restriction
Excluding a photo from a student Journey does not automatically mark the photo as Do not publish or change its privacy eligibility.
What changed
Journey curation, student photo-use restriction, consent restriction, and export eligibility remain separate concepts. A Journey reject only affects the selected student’s Journey; export and publication decisions continue to use their own server-side privacy checks.
Why
These decisions have different owners, scope, and consequences and should not be represented by one shared status.
Release & reliability
Record the checks that separate implemented work from work that has reached production.
Recent changes were typechecked, built, and deployed
The two-day implementation set is live on the production alias with the expected unauthenticated API behavior.
What changed
The latest source passed TypeScript checking and the Next.js production build, including public-data checks. Vercel production builds completed successfully, the homepage returned 200, and the reviews endpoint returned 401 when called without a session.
Why
The changelog should distinguish implemented work from work that has actually passed the deployment path.