Skip to content

Commit 019260e

Browse files
authored
Redesign Edit Contest Modal with improved scoring system UX (#150)
feat: redesign Edit Contest Modal with improved scoring system UX - Add clear lock/unlock status banners for scoring mode - Implement visual indicators for editable vs locked fields - Enhance multi-parameter scoring configuration UI - Add contextual help messages and warnings - Improve dark theme compatibility throughout modal - Refactor scoring parameter validation with real-time feedback <img width="1913" height="786" alt="image" src="https://github.com/user-attachments/assets/fc37a112-8921-4b1c-bccc-83ddf5e7b997" />
2 parents f40eb00 + a1061c4 commit 019260e

File tree

3 files changed

+913
-460
lines changed

3 files changed

+913
-460
lines changed

backend/app/models/contest.py

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -522,6 +522,47 @@ def is_organizer(self, username):
522522
username = username.strip()
523523
return username in self.get_organizers()
524524

525+
def can_change_scoring_system(self):
526+
"""
527+
Determine if scoring system can be changed.
528+
529+
Rules:
530+
- Cannot change if contest has any reviewed submissions
531+
- Can change if contest is brand new (no submissions)
532+
- Can change if only pending submissions exist
533+
534+
Returns:
535+
tuple: (can_change: bool, reason: str or None)
536+
"""
537+
from app.models.submission import Submission
538+
539+
# Check if any submissions have been reviewed
540+
reviewed_count = (
541+
Submission.query.filter(Submission.contest_id == self.id)
542+
.filter(Submission.status.in_(["accepted", "rejected"]))
543+
.count()
544+
)
545+
546+
if reviewed_count > 0:
547+
return (
548+
False,
549+
f"Cannot change scoring system: {reviewed_count} submissions have already been reviewed",
550+
)
551+
552+
return True, None
553+
554+
def get_scoring_mode(self):
555+
"""
556+
Get the current scoring mode for this contest.
557+
558+
Returns:
559+
str: 'simple' or 'multi_parameter'
560+
"""
561+
params = self.get_scoring_parameters()
562+
if params and params.get("enabled") is True:
563+
return "multi_parameter"
564+
return "simple"
565+
525566
# ------------------------------------------------------------------------
526567
# SERIALIZATION
527568
# ------------------------------------------------------------------------

backend/app/routes/contest_routes.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -744,6 +744,38 @@ def update_contest(contest_id):
744744
):
745745
return jsonify({"error": "Permission denied"}), 403
746746

747+
# --- CRITICAL: Scoring System Change Validation ---
748+
if "scoring_parameters" in data:
749+
# Get current and proposed scoring modes
750+
current_mode = contest.get_scoring_mode()
751+
752+
proposed_params = data.get("scoring_parameters")
753+
if proposed_params is None:
754+
proposed_mode = "simple"
755+
elif (
756+
isinstance(proposed_params, dict)
757+
and proposed_params.get("enabled") is True
758+
):
759+
proposed_mode = "multi_parameter"
760+
else:
761+
proposed_mode = "simple"
762+
763+
# Check if mode is changing
764+
if current_mode != proposed_mode:
765+
can_change, reason = contest.can_change_scoring_system()
766+
if not can_change:
767+
return (
768+
jsonify(
769+
{
770+
"error": reason,
771+
"current_mode": current_mode,
772+
"attempted_mode": proposed_mode,
773+
"locked": True,
774+
}
775+
),
776+
400,
777+
)
778+
747779
# --- Basic Metadata Fields ---
748780
if "name" in data:
749781
contest.name = data.get("name") or contest.name

0 commit comments

Comments
 (0)