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>
This commit is contained in:
@@ -128,7 +128,7 @@ export default function MessagesPage() {
|
||||
// Fetch supporting data
|
||||
const { data: programs } = trpc.program.list.useQuery({ includeStages: true })
|
||||
const rounds = programs?.flatMap((p) =>
|
||||
((p.stages ?? []) as Array<{ id: string; name: string }>).map((s: { id: string; name: string }) => ({ ...s, program: { name: p.name } }))
|
||||
((p.stages ?? []) as Array<{ id: string; name: string; status: string }>).map((s: { id: string; name: string; status: string }) => ({ ...s, program: { name: p.name } }))
|
||||
) || []
|
||||
const { data: templates } = trpc.message.listTemplates.useQuery()
|
||||
const { data: users } = trpc.user.list.useQuery(
|
||||
@@ -465,8 +465,9 @@ export default function MessagesPage() {
|
||||
{rounds?.map((round) => {
|
||||
const label = round.program ? `${round.program.name} - ${round.name}` : round.name
|
||||
const isChecked = roundIds.includes(round.id)
|
||||
const isActive = round.status === 'ROUND_ACTIVE'
|
||||
return (
|
||||
<div key={round.id} className="flex items-center gap-2">
|
||||
<div key={round.id} className={`flex items-center gap-2 ${!isActive ? 'opacity-60' : ''}`}>
|
||||
<Checkbox
|
||||
id={`round-${round.id}`}
|
||||
checked={isChecked}
|
||||
@@ -478,7 +479,8 @@ export default function MessagesPage() {
|
||||
)
|
||||
}}
|
||||
/>
|
||||
<label htmlFor={`round-${round.id}`} className="text-sm cursor-pointer">
|
||||
<label htmlFor={`round-${round.id}`} className="text-sm cursor-pointer flex items-center gap-1.5">
|
||||
{isActive && <span className="inline-block h-2 w-2 rounded-full bg-green-500 shrink-0" />}
|
||||
{label}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user