Skip to content

Judging Timeline Enforcement #402

@Benjtalkshow

Description

@Benjtalkshow

Judging Timeline Enforcement

Objective

Enforce the active judging window by disabling all scoring interactions outside of the defined judgingStartjudgingEnd period, so judges can never accidentally submit or modify scores when the phase is closed. This closes a critical gap where the judging UI remains fully interactive regardless of whether the judging phase is actually open, creating the risk of out-of-window score submissions that could compromise the integrity of hackathon results.


Technical Implementation

Target Route

  • app/organization/[id]/hackathons/[hackathonId]/judging

Target Components

  • JudgingResultsTable.tsx
  • JudgingCriteriaList.tsx

Hook — useHackathonStatus()

  • Use useHackathonStatus() as the single source of truth for all timeline-based evaluations
  • Extend this hook if needed to derive and return an isJudgingActive boolean — computed from whether Date.now() falls within the judgingStart and judgingEnd window
  • The boolean should be reactive — components consuming it must automatically reflect state changes as time progresses, without requiring a manual refresh or page reload
  • Centralizing this logic in the hook prevents duplicated Date.now() comparisons scattered across JudgingResultsTable and JudgingCriteriaList

Enforcement Logic

  • In both JudgingResultsTable.tsx and JudgingCriteriaList.tsx, wrap all scoring inputs and the "Grade" button in a conditional check against isJudgingActive
  • If isJudgingActive is false, all scoring interactions must be disabled — not hidden, but explicitly non-interactive so judges understand the inputs exist but are currently locked
  • The disable state must be applied at the component level using the disabled prop on inputs and buttons so the browser enforces non-interactivity natively
  • Any submission or save action tied to scoring must also be blocked at the function level as a secondary guard, in case a disabled input is bypassed via devtools

Alert — Alert Component

  • Render the Alert component from @/components/ui/alert at the top of the judging dashboard whenever isJudgingActive is false
  • The alert must clearly communicate one of two states:
    • The judging phase has not yet started — optionally surfacing when it will begin
    • The judging phase has already ended — optionally surfacing when it closed
  • The alert must be non-dismissible while the window is closed — judges must not be able to hide it and forget that scoring is unavailable
  • When isJudgingActive is true, the alert must not be rendered at all — its absence implicitly confirms that scoring is open

Logic & Enforcement

  • The time comparison must account for timezone consistency — ensure both Date.now() and the judgingStart/judgingEnd values are evaluated in the same timezone context to avoid off-by-one errors around window boundaries
  • The enforcement must be reactive — if a judge has the dashboard open as the judging window opens or closes, the UI should reflect the new state without requiring a page refresh

⚠️ Caution

This is a production environment — not a sandbox.

  • Code must be performant, accessible, and clean
  • No dummy data — all enforcement logic must operate on real judgingStart and judgingEnd values from the hackathon data source
  • AI-generated code will be scrutinized; poorly structured or "hallucinated" code will result in immediate issue closure
  • Follow the existing design system: shadcn/ui, Tailwind, Framer Motion

Testing & Verification

Automated Tests

npm run lint    # Ensure code quality
npm run build   # Verify no breaking changes in routing or types

Manual Verification

  • Set judgingStart to a future timestamp — confirm all score inputs and the "Grade" button are disabled in both JudgingResultsTable and JudgingCriteriaList, and the Alert is visible with a "not yet started" message
  • Set judgingEnd to a past timestamp — confirm all score inputs and the "Grade" button are disabled and the Alert is visible with a "phase ended" message
  • Set both timestamps to bracket the current time — confirm score inputs and the "Grade" button are enabled and the Alert is not rendered
  • Leave the dashboard open across a judging window boundary — confirm the UI updates reactively via useHackathonStatus() without a page refresh
  • Attempt to submit a score while the window is closed via devtools or form manipulation — confirm the action is blocked at the function level, not just the UI
  • Verify the Alert is non-dismissible and cannot be hidden while the judging window is closed
  • Confirm isJudgingActive is correctly derived in useHackathonStatus() and both components consume it from the hook rather than computing it independently
  • Confirm timezone consistency — test with judgingStart/judgingEnd values near DST boundaries or across timezone offsets
  • Provide video evidence

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions