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>
getConfig now throws FORBIDDEN when round is not ROUND_ACTIVE,
preventing the form from loading entirely. Also blocks draft
saving when round is inactive. Defense-in-depth: submit already
rejected inactive rounds, this adds the frontend gate.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add withErrorAudit middleware tracking FORBIDDEN/UNAUTHORIZED/NOT_FOUND per user
- Fix impersonation attribution: log real admin ID, prefix IMPERSONATED_ on actions
- Add ACCOUNT_LOCKED audit events on login lockout (distinct from LOGIN_FAILED)
- Audit export of assignments and audit logs (meta-audit gap)
- Update audit page UI with new security event types and colors
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
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>
getDownloadUrl was logging FILE_DOWNLOADED on every call including
inline previews and thumbnail loads. Now only logs when forDownload
is true (explicit download button click), massively reducing noise.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Applicant saveFileMetadata and deleteFile mutations now log
APPLICANT_UPLOAD_FILE and APPLICANT_DELETE_FILE to the audit trail,
matching the admin file router's existing audit coverage.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add getSemiFinalistStats query with per-category/per-award breakdown
- Add sendAccountReminders mutation with invite token generation and dedup
- Add SemiFinalistTracker dashboard widget with progress bars and remind buttons
- Add ACCOUNT_REMINDER email template
- Extend project search to match team member name/email (7 locations)
- Fix Passed count deduplication: count distinct projects, not round-state rows
- Fix role switcher: visible pills above user section, auto-refresh session on mount
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add attachProjectLogoUrls utility mirroring avatar URL pattern. Pipe
project.list and analytics.getAllProjects through logo URL resolver so
ProjectLogo components receive presigned URLs. Add logos to observer
projects table and mobile cards.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix round-specific document uploads (submittedAt no longer blocks uploads),
add view/download buttons for existing files, enforce active-round-only for
uploads/deletes. Harden auth layout and set-password page. Filter applicant
portal rounds by award track membership.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Custom body support for advancement/rejection notification emails, evaluation
config toggle fix, user actions improvements, round finalization with reorder
support, project detail page enhancements, award pool duplicate prevention.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add notifiedAt field to AwardEligibility. notifyEligibleProjects now
skips already-notified entries and stamps notifiedAt after sending,
so re-clicking "Notify Pool" only emails newly added projects.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- getNavFlags/getMyEvaluations: return empty when project has no round
states instead of dropping the filter (prevented phantom eval rounds)
- getUpcomingDeadlines: detect isInAwardTrack from ProjectRoundState
join instead of pre-filtered deadline rounds
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Projects in SEPARATE_POOL awards now only see their award rounds (not main
pool rounds) across all applicant queries: openRounds, deadlines, document
completeness, nav flags, and evaluations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The email was implying projects had won the award. Updated banner, subject,
and body copy to clarify they are being considered, not awarded.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add drag-and-drop round reordering on award detail Rounds tab (dnd-kit)
- Replace "Open Voting" with "Assign to First Round" for SEPARATE_POOL awards
- Add reorderAwardRounds mutation (two-phase transaction for unique constraint)
- Add assignToFirstRound mutation (re-runnable, moves/creates ProjectRoundState)
- Extend applicant timeline to show award-specific rounds for SEPARATE_POOL projects
- Hide irrelevant main competition rounds when project is in award track
- Prefix award round labels with award name in timeline
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add admin settings: learning_hub_external, learning_hub_external_url, support_email
- Jury/Mentor nav respects external Learning Hub URL (opens in new tab)
- RoleNav supports external nav items with ExternalLink icon
- Applicant header shows Help button with configurable support email
- Settings update mutation now upserts (creates on first use)
- Shared inferSettingCategory for consistent category assignment
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Award "Notify Pool" dialog now uses EmailPreviewDialog with live preview
- Button shows eligible project count: "Notify Pool (38)"
- Finalization tab email section has "Preview" buttons for both
advancement and rejection messages
- EmailPreviewDialog supports previewOnly mode (close button, no send)
- Backend: previewAwardSelectionEmail, previewFinalizationAdvancementEmail,
previewFinalizationRejectionEmail queries
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- processRoundClose EVALUATION uses ranking scores + advanceMode config
(threshold vs count) to auto-set proposedOutcome instead of defaulting all to PASSED
- Advancement emails generate invite tokens for passwordless users with
"Create Your Account" CTA; rejection emails have no link
- Finalization UI shows account stats (invite vs dashboard link counts)
- Fixed getFinalizationSummary ranking query (was using non-existent rankingsJson)
- New award pool notification system: getAwardSelectionNotificationTemplate email,
notifyEligibleProjects mutation with invite token generation,
"Notify Pool" button on award detail page with custom message dialog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add scoreWeight and passRateWeight (0-10) to evaluation config for
configurable composite score formula. When ranking criteria text is
empty, triggerAutoRank uses pure formula ranking (no LLM calls).
When criteria text is present, AI-assisted ranking runs as before.
- Add FORMULA to RankingMode enum with migration
- Extract fetchCategoryProjects helper, add formulaRank service
- Update computeCompositeScore to accept configurable weights
- Add score/pass-rate weight sliders to ranking dashboard UI
- Mode-aware button labels (Calculator/formula vs Sparkles/AI)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Create snapshot with status RUNNING before AI call starts
- Update to COMPLETED/FAILED when done
- Dashboard derives rankingInProgress from server snapshot status
- All admins see the spinner, not just the one who triggered it
- Poll snapshots every 3s so progress updates appear quickly
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Backfilled 166 evaluations' binaryDecision from criterionScoresJson on production DB
- Fixed roundEvaluationScores and ai-ranking to look in EvaluationForm.criteriaJson
instead of round.configJson for the boolean "Move to the Next Stage?" criterion
- Added advanceMode (count/threshold) toggle to round config Advancement Targets
- Added "Assign to Jurors" button on Unassigned Projects section and Projects tab bulk bar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add criteriaWeights to EvaluationConfig for per-criterion weight assignment (0-10)
- Rewrite ai-ranking service: fetch eval form criteria, compute per-criterion averages,
z-score normalize juror scores to correct grading bias, send weighted criteria to AI
- Update AI prompts with criteria_definitions and per-project criteria_scores
- compositeScore uses weighted criteria when configured, falls back to globalScore
- Add collapsible ranking config section to dashboard (criteria text + weight sliders)
- Move rankingCriteria textarea from eval config tab to ranking dashboard
- Store criteriaWeights in ranking snapshot parsedRulesJson for audit
- Enhance projectScores CSV export with per-criterion averages, category, country
- Add Export CSV button to ranking dashboard header
- Add threshold-based advancement mode (decimal score threshold, e.g. 6.5)
alongside existing top-N mode in advance dialog
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add adminEditEvaluation mutation and getJurorEvaluations query
- Create shared EvaluationEditSheet component with inline feedback editing
- Add Evaluations tab to member detail page (grouped by round)
- Make jury group member names clickable (link to member detail)
- Replace inline EvaluationDetailSheet on project page with shared component
- Fix project status transition validation (skip when status unchanged)
- Fix frontend to not send status when unchanged on project edit
- Ranking dashboard improvements and boolean decision converter fixes
- Backfill script updates for binary decisions
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add nationality/institution fields to User model with migration
- Applicant onboarding wizard (name, photo, nationality, country, institution, bio, project logo, preferences)
- Project logo upload from applicant context with team membership verification
- APPLICANT redirects in set-password, onboarding, and auth layout
- Mask evaluation round names as "Evaluation Round 1/2/..." for applicants
- Extend inviteTeamMember with nationality/country/institution/sendInvite fields
- Admin getApplicants query with search/filter/pagination
- Admin bulkInviteApplicants mutation with token generation and emails
- Applicants tab on Members page with bulk select and floating invite bar
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a new tRPC procedure `round.getSubmissionRoundForProgram` that
fetches the most recent SUBMISSION round for a given program, then
displays any `requiredDocuments` from its configJson as labeled info
cards above the general file upload section on the project edit page.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- ProjectStatesTable local search now covers country, institution, competitionCategory, geographicZone
- project-pool.ts DB search extended to institution, country, geographicZone, team member names
- AwardShortlist eligibility table gains a search input filtering by title, team, country, institution, category
- IndividualAssignmentsTable project filter extended to include country and institution
- Add "Generate AI Tags" dropdown item per row in ProjectStatesTable using tag.tagProject mutation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add optional roundId field to project.create mutation input schema
- After project creation, update project.roundId FK and create a
ProjectRoundState record (state: PENDING) when roundId is supplied
- Pass selectedRoundId from the new-project form to createProject.mutate()
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds competitionCategory, oceanIssue, institution, geographicZone,
wantsMentorship, and foundedAt to the tRPC update mutation input schema
and the admin project edit form UI (with CountrySelect + Switch).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- parseRankingCriteria: parse natural-language criteria, returns ParsedRankingRule[]
- executeRanking: fetch+rank both categories, persist CONFIRMED RankingSnapshot
- quickRank: parse+execute in one step, persist QUICK RankingSnapshot
- listSnapshots: list snapshots for a round ordered by createdAt desc
- getSnapshot: fetch full snapshot by ID with NOT_FOUND guard
- All procedures use adminProcedure (SUPER_ADMIN, PROGRAM_ADMIN only)
- Casts to Prisma.InputJsonValue via unknown to satisfy strict TS
- Update listByStage query to include evaluation form criteriaJson and criterionScoresJson
- Add Advance column to individual assignments table showing YES/NO badge per submitted evaluation
- Create AdvancementSummaryCard component showing yes/no/pending vote counts with stacked bar
- Wire AdvancementSummaryCard into the EVALUATION round overview tab
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add getMyProgress juryProcedure query to evaluationRouter: fetches all
assignments for the current juror in a round, tallies completed/total,
advance yes/no counts, per-project numeric scores and averages
- Create JurorProgressDashboard client component with progress bar, advance
badge summary, and collapsible per-submission score table
- Wire dashboard into jury round page, gated by configJson.showJurorProgressDashboard
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds a new 'advance' criterion type representing "should this project advance to the next round?". Only one advance criterion is allowed per form (button disabled once added). No weight, no condition fields, always required. Also updates the upsertForm Zod schema to accept the new type.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add roles UserRole[] to User model with migration + backfill from existing role column
- Update auth JWT/session to propagate roles array with [role] fallback for stale tokens
- Update tRPC hasRole() middleware and add userHasRole() helper for inline role checks
- Update ~15 router inline checks and ~13 DB queries to use roles array
- Add updateRoles admin mutation with SUPER_ADMIN guard and priority-based primary role
- Add role switcher UI in admin sidebar and role-nav for multi-role users
- Remove redundant stats cards from round detail, add window dates to header banner
- Merge Members section into JuryProgressTable with inline cap editor and remove buttons
- Reorder round detail assignments tab: Progress > Score Dist > Assignments > Coverage > Jury Group
- Make score distribution fill full vertical height, reassignment history always open
- Add per-juror progress bars to admin dashboard ActiveRoundPanel for EVALUATION rounds
- Fix evaluation submit bug: use isSubmitting state instead of startMutation.isPending
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add mail/transfer/reshuffle/redistribute icons to each juror row in Members card
- New redistributeJurorAssignments procedure: reassign all pending projects without dropping juror from group
- New DROPOUT_REASSIGNED email template with project names, deadline, and dropped juror context
- Update reassignDroppedJuror to send per-juror DROPOUT_REASSIGNED emails instead of generic BATCH_ASSIGNED
- Transfer dialog now shows all candidates with "Already assigned" / "At cap" labels instead of hiding them
- SQL script for prod DB insertion of new notification setting without seeding
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add COI_REASSIGNED and MANUAL_REASSIGNED notification types with distinct
email templates, icons, and priorities
- COI declaration dialog now shows a confirmation step warning that the
project will be reassigned before submitting
- reassignAfterCOI now checks historical assignments (all rounds, audit logs)
to never assign the same project to a juror twice, and prefers jurors with
incomplete evaluations over those who have finished all their work
- Admin transfer (transferAssignments) sends per-juror MANUAL_REASSIGNED
notifications with actual project names instead of generic batch emails
- docker-entrypoint syncs notification settings on every deploy via upsert
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrite ctaButton to use td-background pattern (works in all clients
including Outlook, Gmail, Yahoo, Apple Mail) instead of VML/conditional
comments that broke link clicking in Outlook desktop
- Add plaintext fallback URL below every CTA button so users always have
a working link even if the button fails
- Add getBaseUrl() and ensureAbsoluteUrl() helpers in email.ts to
guarantee all email links are absolute https:// URLs
- Apply ensureAbsoluteUrl safety net in sendStyledNotificationEmail and
sendNotificationEmail so relative paths can never reach email templates
- Standardize all NEXTAUTH_URL fallbacks to https://portal.monaco-opc.com
(was inconsistently http://localhost:3000 or https://monaco-opc.com)
- Fix legacy notification.ts: wrong argument order in
sendJuryInvitationEmail (URL was passed as name parameter)
- Fix legacy notification.ts: missing NEXTAUTH_URL fallback for
evaluation reminder URL construction
- Change tooltip styling from red bg to white bg with black text
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The getUploadUrl procedure generated a presigned PUT URL without first
checking that the mopc-learning bucket exists, causing all uploads to fail.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add getTransferCandidates/transferAssignments procedures for targeted
assignment moves between jurors with TOCTOU guards and audit logging
- Add getOverCapPreview/redistributeOverCap for auto-redistributing
assignments when a juror's cap is lowered below their current load
- Add TransferAssignmentsDialog (2-step: select projects, pick destinations)
- Extend InlineMemberCap with over-cap detection and redistribute banner
- Extend getReassignmentHistory to show ASSIGNMENT_TRANSFER and CAP_REDISTRIBUTE events
- Learning hub: replace ResourceType/CohortLevel enums with accessJson JSONB,
add coverImageKey, resource detail pages for jury/mentor, shared renderer
- Migration: 20260221200000_learning_hub_overhaul
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Anthropic API:
- Add @anthropic-ai/sdk with adapter wrapping OpenAI-shaped interface
- Support Claude models (opus, sonnet, haiku) with extended thinking
- Auto-reset model on provider switch, JSON retry logic
- Add Claude model pricing to ai-usage tracker
- Update AI settings form with Anthropic provider option
- Add provider field to AIUsageLog for cross-provider cost tracking
Locale Settings Removal:
- Strip Localization tab from admin settings (mobile + desktop)
- Remove i18n settings from router and feature flags
- Remove LOCALIZATION from SettingCategory enum
- Keep franc document language detection intact
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Rewrite diversity metrics: horizontal bar charts for ocean issues and
geographic distribution (replaces unreadable vertical/donut charts)
- Rewrite juror score heatmap: expandable table with score distribution
- Rewrite juror consistency: horizontal bar visual with juror names
- Merge filtering tabs into single screening view with per-project
AI reasoning and expandable rows
- Add project preview dialog for juror performance table
- Fix status breakdown for evaluation rounds (Fully/Partially/Not Reviewed)
- Show active round name instead of count on observer dashboard
- Move Global tab to last position, default to first round-specific tab
- Add 4-card stats layout for evaluation with reviews/project ratio
- Fix oceanIssue field (singular) and remove non-existent aiSummary
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>