Commit Graph

271 Commits

Author SHA1 Message Date
Matt
3b12078e04 feat: mount score explainer dialog in admin and observer surfaces
Adds the 'How scores are calculated' affordance to:
- the admin ranking dashboard side panel (next to the Avg Score card)
- the observer full project detail page (in the score card)
- the observer reports preview dialog (next to Evaluation Summary)

so all three audiences can open the same explainer dialog.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:31:29 +02:00
Matt
b4f5189a8e feat: shared 'How scores are calculated' explainer dialog
Reusable component used by admin and observer surfaces. Covers the
algorithm, a five-step plain-language walkthrough, a worked example
with three jurors of different grading styles, edge cases, and why
both Raw and Balanced are always shown.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:25:20 +02:00
Matt
ee68f8af41 feat: side panel shows per-juror baseline and balanced contribution
Extends the ranking router's roundEvaluationScores response with
per-juror grading stats (mean, stddev, count) plus the round's overall
mean/stddev. The side-sheet juror rows render 'typical X.XX →
contributes Y.YY' next to each Score badge whenever balanced is on,
making the z-rescaling visible per individual rather than only as a
project-level number.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:24:34 +02:00
Matt
664a682585 feat: side panel shows raw + balanced averages, list drops delta
Removes the per-row '⇢ X.X' annotation from the ranking list — the
list view stays clean. The side panel's stats area gains a combined
Avg Score card that shows Raw and Balanced side-by-side, with the
active one (per the round's toggle) bolded and tagged 'used for
ranking'. Pass Rate and Evaluators move below into a 2-col grid.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:23:29 +02:00
Matt
e12f26092a feat: list sort respects useBalancedRanking toggle
The two existing sort sites (initial init + threshold cutoff) now read
from the local toggle. A second effect re-sorts the list when the
toggle flips, but only when no manual reorder is pinned to the
snapshot — persisted manual reorders always win, matching prior
behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:22:11 +02:00
Matt
387f84c338 feat: per-round balanced-scoring toggle in side sheet
A Switch at the top of the project side panel writes
useBalancedRanking onto Round.configJson via the existing round.update
mutation. The flip is shared across all viewers because the value
lives in the round's persisted config; hydration runs on every
roundData refetch so the UI converges quickly when another admin
flips it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:20:21 +02:00
Matt
6f3e8885e0 feat: resolve observer project page round default and add selector
The observer full project page used to call getProjectDetail without
a round, getting cross-round contaminated stats. It now resolves a
default — the currently OPEN round the project is in, falling back
to the most recently CLOSED one — and renders a selector chip in
the score card whenever the project participated in more than one
candidate round. Initial selection respects the ?round= query param.

A new observer procedure (getProjectRoundsForObserver) returns the
project's open or closed rounds for the picker.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:18:36 +02:00
Matt
cfd9dc6afe fix: scope observer reports preview dialog to selected round
Threads the active roundId through ProjectPreviewDialog and its two
callers (filtering tabs, expandable juror table). When a round is in
scope, the preview's stats card now matches the per-juror list and
the page-level round selector. The roundId prop is optional so the
component still works in any future caller that lacks round context.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:17:18 +02:00
Matt
9a2c10a6f8 fix: scope admin ranking dashboard side-sheet stats to current round
The admin dashboard fetches its side-sheet detail from project.getFullDetail
(not analytics.getProjectDetail as the audit assumed), and that procedure
had the same cross-round contamination bug. Add an optional roundId to
its input, filter the SUBMITTED-evaluations query when provided, and pass
roundId from the dashboard's useQuery so the Avg Score / Pass Rate /
Evaluators card now matches the per-juror list rendered below it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-27 13:15:47 +02:00
Matt
901d9ba982 feat: rank projects by balanced score in ranking dashboard
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m0s
Initial ranking order, advancement cutoff line, and per-row "advancing"
highlight now all use the juror-balanced (z-score corrected) average,
falling back to the raw avgGlobalScore when no balanced score exists.
The init effect waits for evalScores so the sort has the data it needs.

Admin drag-reorders still take precedence — saved reorders override the
default sort exactly as before.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 15:33:56 +02:00
Matt
982d5193c5 feat: surface juror-balanced scores and AI calibration advisory
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m27s
Adds a shared juror-balancing utility (z-score normalization per juror,
rescaled back onto the raw 1-10 scale) and wires it into:

- Admin reports page: Top-10 project table now shows "Raw Avg" and
  "Balanced" columns side by side, and the summary stats row shows a
  balanced-average tile. Sort defaults to balanced so harsh and lenient
  graders no longer skew the ranking.
- Ranking dashboard: each project row shows a green/amber balanced-score
  chip next to the raw average when the two differ by ≥0.05, making it
  obvious when juror calibration moved a project's effective ranking.

Also adds AI Juror Calibration Advisory — a mutation that takes
anonymized per-juror stats, calls OpenAI, and produces a plain-language
explanation of the cohort's grading patterns plus per-juror severity
(normal / notable / outlier) with a one-sentence narrative. The advisory
describes the statistical balance that already runs; it does not
introduce a new weighting layer. Rendered as a panel in the Juror
Consistency tab when a specific round is selected.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 16:19:00 +02:00
Matt
f36f68bbf9 fix: don't mark next round as current before advancement is declared
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m48s
The applicant timeline sidebar was treating any previous round with
status ROUND_CLOSED as "advanced past", which caused the Grand Finale
to show "You are here" as soon as the semi-final round closed — before
admins declared which teams actually advanced.

Require explicit PASSED/COMPLETED project state on every prior round
before marking the next one as current. A closed-but-unresolved round
now correctly keeps the applicant anchored to it.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-24 14:39:43 +02:00
Matt
9cb3b9de13 feat: admin can fill in evaluations on behalf of jurors
All checks were successful
Build and Push Docker Image / build (push) Successful in 11m54s
When a juror cannot connect during an evaluation round, an admin
can now submit evaluations for them.

Router — new admin procedures:
- adminStart / adminAutosave: create and save drafts for any juror.
- adminSubmitOnBehalf: submit bypassing ROUND_ACTIVE and voting-window
  checks. COI block and feedback/criterion validation still enforced.
  Audit log records both admin and juror IDs plus bypassedWindow flag.
- getJurorAssignmentsForRound: list a juror's assignments + eval state.

UI — two new admin pages under /admin/rounds/[roundId]/jurors/[userId]/:
- evaluate: list of pending + completed assignments, COI flagged.
- evaluate/[projectId]: evaluation form reusing the juror's scoring UI,
  with an "acting on behalf" banner and confirmation dialog before
  submit. Back button returns to the assignments list.

Entry point: FilePen icon on each juror row in JuryProgressTable.

Refactor: extracted the scoring form JSX into shared
EvaluationFormFields component so the juror page and the admin proxy
page render identical inputs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 16:41:14 +02:00
Matt
fd4f6dde16 fix: humanize category label in unassigned queue row
Queue payload exposes the field as `category` (aliased from
`competitionCategory`), not `competitionCategory` itself, so the
previous lookup was always undefined and every project rendered as
"No category". Now shows "Startup" / "Business Concept" correctly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-21 16:40:54 +02:00
Matt
c7488b3e07 fix: save roundId on admin file upload and group assignments by round
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m45s
The admin upload flow accepted roundId but never wrote it to the
ProjectFile record, causing all admin-uploaded files to appear under
"General". Fixed the create call, the listByProject filter, and the
listByProjectForStage grouping to also use the direct roundId field.

Jury assignments on the project detail page are now grouped by round
with per-round completion counts instead of a flat list.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 23:20:48 -04:00
Matt
498baa7e01 feat: add clickable projects and doc counts to finalization page
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m36s
Project names now link to their detail page on all finalization tabs.
Submission/intake rounds show a docs submitted/required column.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-09 11:38:05 -04:00
Matt
22a08ef957 feat: add reusable BulkInviteForm component for multi-row name+email invites
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 20:11:45 -04:00
Matt
158eba416d feat: add auto-retry (3 attempts) for file uploads on flaky connections
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-07 20:10:52 -04:00
Matt
9fcafec346 feat: add award-master route group layout and nav component
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:36:20 -04:00
Matt
f61cebe6ae feat: wire AWARD_MASTER into auth redirects and role-switch navigation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 16:35:34 -04:00
Matt
3ccf9b0542 feat: per-category evaluation criteria (startup vs business concept)
Add ability to define completely different evaluation criteria for each
competition category. Admins toggle "Separate Criteria per Category" in
round config, then configure criteria independently via tabbed editor.

- Schema: add nullable `category` to EvaluationForm with updated constraints
- Config: add `perCategoryCriteria` boolean to EvaluationConfigSchema
- Helper: new `findActiveForm()` with category-aware resolution + fallback
- Backend: getForm, upsertForm, getStageForm, startStage all category-aware
- AI services: use project category for form lookup in summaries + ranking
- Export/ranking: merge criteria from all active forms for cross-category reports
- Admin UI: toggle switch + tabbed criteria editor with per-category builders
- Jury UI: auto-selects correct form based on project category (invisible to juror)
- Fully backwards compatible: toggle defaults OFF, existing forms unchanged

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 13:03:22 -04:00
Matt
7ead21114e fix: pipeline progress, message variables, jury invite flow, accept-invite UX
- Pipeline: SUBMISSION rounds count IN_PROGRESS + COMPLETED for progress %
- Round engine: remove phantom SubmissionFileRequirement check blocking auto-transition
- Messages: implement {{userName}}, {{projectName}}, {{roundName}}, {{programName}}, {{deadline}} substitution
- Email preview: show greeting, CTA button, and footer matching actual sent email
- Message composer: add green dot indicator for active rounds in round selector
- User create: generate invite token atomically (prevents stuck INVITED state on email failure)
- Jury invites: use jury-specific email template mentioning round context
- Bulk invite: animated progress bar, batch size hint, success/failure counts
- Accept invite: distinguish server errors (retry button) from expired tokens (redirect)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 13:47:42 -04:00
1356809cb1 fix: tech debt batch 1 — TS errors, vulnerabilities, dead code
- Fixed 12 TypeScript errors across analytics.ts, observer-project-detail.tsx, bulk-upload/page.tsx, settings/profile/page.tsx
- npm audit: 8 vulnerabilities resolved (1 critical, 4 high, 3 moderate)
- Deleted 3 dead files: live-control.ts (618 lines), feature-flags.ts, file-type-categories.ts
- Removed typescript.ignoreBuildErrors: true — TS errors now block builds

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 23:51:44 +01:00
a68ec3fb45 fix: batch 4 — connection pooling, graceful shutdown, email verification UX
- Prisma: connection_limit=10, pool_timeout=30 on DATABASE_URL in both compose files
- Graceful shutdown: SIGTERM/SIGINT forwarded to Node process in docker-entrypoint.sh
- testEmailConnection: replaced real email send with transporter.verify(), simplified UI to single button
- NotificationLog.userId index: confirmed already present, no change needed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-07 18:16:29 +01:00
a8b8643936 feat: group observer project files by round
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m15s
Files on the observer project detail page are now grouped by round
(e.g., "Application Intake", "Semi-Finals Document Submission") instead
of shown in a flat list. Uses FileViewer's existing groupedFiles prop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 16:24:33 +01:00
0390d05727 fix: submission round completion %, document details, project teams UX
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m32s
- Fix 0% completion on SUBMISSION pipeline cards — now based on teams
  with uploads / total projects instead of evaluation completions
- Add page count, detected language, and non-English warning indicator
  to Recent Documents list items
- Add project avatar (logo) to document and project list rows
- Make document rows clickable into project detail page
- Remove team name from project list (none have names), show country only
- Rename "Teams Submitted" to "Teams with Uploads" for clarity
- Add "See all" link to Projects section → /observer/projects
- Rename section from "Project Teams" to "Projects"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:50:51 +01:00
ec30dc83d6 feat: country flag display in remaining app pages (mentor, jury, admin, applicant)
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m23s
Extends CountryDisplay component usage to all remaining pages that showed
raw country codes: mentor dashboard/projects, jury competitions/awards,
admin awards/project detail, applicant team, and project-list-compact.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:07:40 +01:00
37351044ed feat: multi-role jury fix, country flags, applicant deadline banner, timeline
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
- Fix project list returning empty for users with both SUPER_ADMIN and
  JURY_MEMBER roles (jury filter now skips admins) in project, assignment,
  and evaluation routers
- Add CountryDisplay component showing flag emoji + name everywhere
  country is displayed (admin, observer, jury, mentor views — 17 files)
- Add countdown deadline banner on applicant dashboard for INTAKE,
  SUBMISSION, and MENTORING rounds with live timer
- Remove quick action buttons from applicant dashboard
- Fix competition timeline sidebar: green dots/connectors only up to
  current round, yellow dot for current round, red connector into
  rejected round, grey after

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 15:00:29 +01:00
a1e758bc39 feat: router.back() navigation, read-only evaluation view, auth audit logging
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m53s
- Convert all Back buttons platform-wide (38 files) to use router.back()
  for natural browser-back behavior regardless of entry point
- Add read-only view for submitted evaluations in closed rounds with
  blue banner, disabled inputs, and contextual back navigation
- Add auth audit logs: MAGIC_LINK_SENT, PASSWORD_RESET_LINK_CLICKED,
  PASSWORD_RESET_LINK_EXPIRED, PASSWORD_RESET_LINK_INVALID
- Learning Hub links navigate in same window for all roles
- Update settings descriptions to reflect all-user scope

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 14:25:56 +01:00
a556732b46 feat: observer UX overhaul — reports, projects, charts, session & email
All checks were successful
Build and Push Docker Image / build (push) Successful in 11m2s
- Observer projects: default sort by status (rejected last), sortable status column
- Observer projects: search by country, institution, geographic zone
- Observer project detail: vertical timeline connectors between rounds
- Fix React key warning in ExpandableJurorTable and FilteringReportTabs
- Fix ScoreBadge text always white for better contrast on all backgrounds
- Remove misleading /30 denominator from heatmap juror reviewed count
- INTAKE stats: show Start-ups, Business Concepts, Countries (not States/Categories)
- DiversityMetrics: extractCountry() for country-only display in charts
- Fix nested button hydration error in filtering report mobile view
- Color project titles by outcome in filtering report (green/red/amber)
- Redesign CrossStageComparisonChart: funnel viz + metrics table with attrition %
- Center doughnut chart in StatusBreakdownChart
- Remove redundant RoundTypeStatsCards from evaluation report
- Move evaluation tab bar below overview header, rename to "Juror Assignments"
- Dev email override system (DEV_EMAIL_OVERRIDE env var)
- Session refresh on role change without re-login
- Role switcher in user dropdown menu
- formatCategory() utility for consistent category display
- Activity feed max height constraint

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 13:37:50 +01:00
2e8ab91e07 fix: version guard uses static file, members table shows project name with round badge
All checks were successful
Build and Push Docker Image / build (push) Successful in 11m12s
Version guard:
- Replace API route with prebuild-generated public/build-id.json
- Captures build ID on first load, only notifies on mismatch
- Fixes false positive refresh prompts from env mismatch

Members table (applicants):
- Show project name + round badge instead of round name + state
- Red badge for rejected, gray for withdrawn, green for passed,
  outline for active rounds
- Include projectName in applicantRoundInfo from backend

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 11:23:24 +01:00
8427999578 feat: version guard — prompt stale clients to refresh after deploys
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m42s
- Inject NEXT_PUBLIC_BUILD_ID at build time via next.config.ts
- /api/version static route returns current build ID
- VersionGuard client component checks on tab focus + every 5 min
- Shows persistent toast with Refresh button (no auto-reload)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:54:52 +01:00
a358e9940d feat: revamp admin member detail page, observer dashboard round timeline
All checks were successful
Build and Push Docker Image / build (push) Successful in 13m37s
- Member detail: tabs layout, impersonate button, icon-pill card headers,
  profile details grid, quick info sidebar, jury groups, mentor assignments
- Observer dashboard: round timeline with special award support,
  round node cards, completion indicators
- Analytics: include specialAwardId/Name in observer round overview

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:39:21 +01:00
ea46d7293f feat: show applicant's current round instead of assignments in members table
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
For APPLICANT users in the admin members list, the Assignments column now
shows the project's current round name and state badge (Active, Pending,
Rejected, etc.) instead of "0 assigned".

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 10:22:40 +01:00
22731e7978 fix: build speed, observer AI details, round tracker empty state
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
- Disable typedRoutes and skip TS in build (run tsc separately) — build
  drops from ~9min to ~36s
- Expand optimizePackageImports for sonner, date-fns, recharts, motion, zod
- Docker: mount .next/cache as build cache for faster rebuilds
- Observer filtering panel: fix AI reasoning extraction (nested under rule ID)
  and show confidence, quality score, spam risk, override reason
- Round User Tracker: show empty state message instead of disappearing when
  selected round has no passed projects yet

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:30:11 +01:00
0d94ee1fe8 feat: clickable status badges, observer status alignment, CSV export all
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m36s
- Admin projects: status summary badges are clickable to filter by round state
  with ring highlight, opacity fade, and clear button
- Add roundStates filter param to project.list backend query
  (filters by latest round state per project, consistent with counts)
- Observer status dropdown now uses ProjectRoundState values
  (Pending/In Progress/Completed/Passed/Rejected/Withdrawn)
- Observer status derived from latest ProjectRoundState instead of stale Project.status
- Observer CSV export fetches all matching projects, not just current page
- Add PENDING and PASSED styles to StatusBadge component

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:19:12 +01:00
ffe12a9e85 feat: applicant dashboard — team cards, editable description, feedback visibility
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m20s
- Replace flat team names list with proper cards showing roles and badges
- Hide TeamMembers from metadata display, remove Withdraw from header
- Add inline-editable project description (admin-toggleable setting)
- Move applicant feedback visibility from per-round config to admin settings
- Support EVALUATION, LIVE_FINAL, DELIBERATION round types in feedback
- Backwards-compatible: falls back to old per-round config if no settings exist
- Add observer team tab toggle and 10 new SystemSettings seed entries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 17:08:19 +01:00
94814bd505 feat: observer team tab, admin-controlled applicant feedback visibility
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m13s
- Add Team tab to observer project detail (configurable via admin settings)
- Move applicant jury feedback visibility from per-round config to admin settings
- Add per-round-type controls: evaluation, live final, deliberation
- Support anonymous LiveVote and DeliberationVote display for applicants
- Add fine-grained toggles: scores, criteria, written feedback, hide from rejected
- Backwards compatible: falls back to old per-round config if admin settings not set
- New admin settings section under Analytics tab with all visibility controls
- Seed new SystemSettings keys for observer/applicant visibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 16:50:20 +01:00
b7905a82e1 fix: impersonation, dashboard counter, logo lightbox, submission config, auth logs
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m12s
- Fix impersonation by bypassing useSession().update() loading gate with direct session POST
- Fix dashboard account counter defaulting to latest round with PASSED projects
- Add clickToEnlarge lightbox for project logos on admin detail page
- Remove submission eligibility config (all passed projects must upload)
- Suppress CredentialsSignin auth errors in production (minified name check)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 15:40:08 +01:00
fd2624f198 feat: fix project status counts, add top pagination and sortable columns
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m39s
- Status counts now show each project's latest round state only
  (no more inflated counts from projects passing multiple rounds)
- Add pagination controls at top of projects, members, and observer lists
- Add sortable column headers to admin projects table (title, category,
  program, assignments, status) and members table (name, role, status,
  last login)
- Backend: add sortBy/sortDir params to project.list and user.list

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 14:49:17 +01:00
8d6f3ca11f fix: dashboard flickering + clickable logo change for applicants
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m7s
- Fix session auto-refresh causing re-render cascade by using useRef
  instead of useState and delaying the refresh by 3s
- Make project logo clickable on dashboard and team page for team leads
  with hover pencil overlay and ProjectLogoUpload dialog

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 14:23:27 +01:00
12e4864d36 fix: impersonation logout, applicant learning hub redirect, nav click tracking
- Sign Out button during impersonation now returns to admin instead of
  destroying the session (fixes multi-click logout issue)
- Applicant nav now respects learning_hub_external setting like jury/mentor
- Track Learning Hub nav clicks via audit log (LEARNING_HUB_CLICK action)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 14:15:59 +01:00
8cdcc85555 feat: round user tracker + fix INVITED status not updating on login
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
- Replace Semi-Finalist Tracker with Round User Tracker on dashboard
- New getRoundUserStats query: round-aware account activation stats
- Round selector dropdown to view any round's passed projects
- sendAccountReminders now accepts optional roundId for scoped reminders
- Fix: signIn callback now sets status=ACTIVE for INVITED users on login
- DB fix: 5 users who logged in via magic link but stayed INVITED → ACTIVE

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 14:00:19 +01:00
b6ba5d7145 feat: member profile pages with clickable links from all member lists
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m51s
- Member detail page (/admin/members/[id]) now shows:
  - Profile details card (nationality, country, institution, bio)
  - Team memberships / projects with links to project pages
  - Jury groups with role (Chair/Member/Observer)
  - All roles including Applicant, Award Master, Audience in role selector
- Project detail page team members now show:
  - Nationality, institution, country inline
  - Names are clickable links to member profile pages
- Members list: names are clickable links to profile pages (all tabs)
- Applicants tab: added nationality and institution columns
- Backend: user.get includes teamMemberships and juryGroupMemberships
- Backend: project.getFullDetail includes nationality/country/institution

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 13:29:56 +01:00
78334676d0 fix: avatar/logo display diagnostics and upload error handling
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m31s
Add error logging to silent catch blocks in avatar/logo URL generation,
show user avatar on admin member detail page, and surface specific error
messages for upload failures (CORS/network issues) instead of generic errors.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-05 02:11:16 +01:00
335c736219 fix: pipeline selected round ring cutoff by overflow scroll
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m46s
Add vertical padding to the scrollable pipeline container so
the ring-2 highlight on selected rounds isn't clipped.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 20:47:28 +01:00
ca888b4eb7 fix: impersonation navigation uses full page reload
All checks were successful
Build and Push Docker Image / build (push) Successful in 9m59s
Replace router.push() with window.location.href for both
start and end impersonation to ensure the updated JWT cookie
is sent with the new request. Client-side routing can race
with cookie propagation, causing the server to see the old
session and redirect back to admin.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 20:30:46 +01:00
875c2e8f48 fix: security hardening — block self-registration, SSE auth, audit logging fixes
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Security fixes:
- Block self-registration via magic link (PrismaAdapter createUser throws)
- Magic links only sent to existing ACTIVE users (prevents enumeration)
- signIn callback rejects non-existent users (defense-in-depth)
- Change schema default role from JURY_MEMBER to APPLICANT
- Add authentication to live-voting SSE stream endpoint
- Fix false FILE_OPENED/FILE_DOWNLOADED audit events on page load
  (remove purpose from eagerly pre-fetched URL queries)

Bug fixes:
- Fix impersonation skeleton screen on applicant dashboard
- Fix onboarding redirect loop in auth layout

Observer dashboard redesign (Steps 1-6):
- Clickable round pipeline with selected round highlighting
- Round-type-specific dashboard panels (intake, filtering, evaluation,
  submission, mentoring, live final, deliberation)
- Enhanced activity feed with server-side humanization
- Previous round comparison section
- New backend queries for round-specific analytics

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 20:18:50 +01:00
c8c26beed2 feat: granular file access audit logging (viewed/opened/downloaded)
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled
Replace single FILE_DOWNLOADED action with three granular actions:
- FILE_VIEWED: inline preview loaded in the UI
- FILE_OPENED: file opened in a new browser tab
- FILE_DOWNLOADED: explicit download button clicked

Add 'purpose' field to getDownloadUrl input (preview/open/download).
All client callers updated to pass the appropriate purpose.
Audit page updated with new filter options and color mappings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 18:18:28 +01:00
6c52e519e5 feat: impersonation system, semi-finalist detail page, tRPC resilience
- Add super-admin impersonation: "Login As" from user list, red banner
  with "Return to Admin", audit logged start/end, nested impersonation
  blocked, onboarding gate skipped during impersonation
- Fix semi-finalist stats: check latest terminal state (not any PASSED),
  use passwordHash OR status=ACTIVE for activation check
- Add /admin/semi-finalists detail page with search, category/status filters
- Add account_reminder_days setting to notifications tab
- Add tRPC resilience: retry on 503/HTML responses, custom fetch detects
  nginx error pages, exponential backoff (2s/4s/8s)
- Reduce dashboard polling intervals (60s stats, 30s activity, 120s semi)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 17:55:44 +01:00