@@ -38,9 +38,21 @@ const SCORE_STRATEGIES = [
3838
3939const TIMEOUT_MODES = [
4040 { value : "per_task" , label : "Per task timeout" } ,
41- { value : "per_round" , label : "Per round timeout" } ,
41+ { value : "per_round_fixed" , label : "Per round (fixed)" } ,
42+ { value : "per_round_with_rematch" , label : "Per round (with rematch)" } ,
43+ { value : "per_tournament" , label : "Per tournament timeout" } ,
4244] ;
4345
46+ const TIMEOUT_DESCRIPTIONS = {
47+ per_task :
48+ "Each game uses the task's own time limit. Different tasks may have different timeouts." ,
49+ per_round_fixed : "All games in a round share a fixed timeout. One task per round." ,
50+ per_round_with_rematch :
51+ "Each round has a fixed timeout. Players play multiple tasks (rematches) within the round until time runs out." ,
52+ per_tournament :
53+ "One global timeout for the entire tournament. Games use the remaining tournament time. Tournament ends automatically when time expires." ,
54+ } ;
55+
4456const PLAYERS_LIMITS = [ 2 , 4 , 8 , 16 , 32 , 64 , 128 , 256 , 512 , 1024 , 2048 , 4096 , 8192 , 16384 ] ;
4557
4658function TournamentForm ( {
@@ -71,6 +83,7 @@ function TournamentForm({
7183 rounds_limit : initialValues . rounds_limit || 7 ,
7284 timeout_mode : initialValues . timeout_mode || "per_task" ,
7385 round_timeout_seconds : initialValues . round_timeout_seconds ?? 177 ,
86+ tournament_timeout_seconds : initialValues . tournament_timeout_seconds ?? 3600 ,
7487 break_duration_seconds : initialValues . break_duration_seconds || 42 ,
7588 use_chat : initialValues . use_chat !== undefined ? initialValues . use_chat : true ,
7689 use_clan : initialValues . use_clan !== undefined ? initialValues . use_clan : false ,
@@ -106,8 +119,13 @@ function TournamentForm({
106119 . split ( / [ \s , ] + / )
107120 . map ( ( id ) => id . trim ( ) )
108121 . filter ( Boolean ) ;
109- payload . round_timeout_seconds =
110- formData . timeout_mode === "per_round" ? formData . round_timeout_seconds : null ;
122+ payload . round_timeout_seconds = [ "per_round_fixed" , "per_round_with_rematch" ] . includes (
123+ formData . timeout_mode ,
124+ )
125+ ? formData . round_timeout_seconds
126+ : null ;
127+ payload . tournament_timeout_seconds =
128+ formData . timeout_mode === "per_tournament" ? formData . tournament_timeout_seconds : null ;
111129
112130 onSubmit ( payload ) ;
113131 } ,
@@ -535,16 +553,44 @@ function TournamentForm({
535553 { renderError ( "rounds_limit" ) }
536554 </ div >
537555
556+ < div className = "col-md-4 mb-3" >
557+ < label htmlFor = "break_duration_seconds" className = "form-label text-white" >
558+ Break Duration (seconds)
559+ </ label >
560+ < input
561+ type = "number"
562+ id = "break_duration_seconds"
563+ name = "break_duration_seconds"
564+ className = { cn ( "form-control cb-bg-panel cb-border-color text-white cb-rounded" , {
565+ "is-invalid" : errors . break_duration_seconds ,
566+ } ) }
567+ value = { formData . break_duration_seconds }
568+ onChange = { handleChange }
569+ min = { 0 }
570+ max = { 100000 }
571+ />
572+ { renderError ( "break_duration_seconds" ) }
573+ </ div >
574+ </ div >
575+ </ div >
576+ </ div >
577+
578+ { /* Timeout Configuration Section */ }
579+ < div className = "card cb-card mb-4" >
580+ < div className = "card-header" >
581+ < h5 className = "mb-0" > Timeout Configuration</ h5 >
582+ </ div >
583+ < div className = "card-body" >
584+ < p className = "text-muted small mb-3" > { TIMEOUT_DESCRIPTIONS [ formData . timeout_mode ] } </ p >
585+ < div className = "row" >
538586 < div className = "col-md-4 mb-3" >
539587 < label htmlFor = "timeout_mode" className = "form-label text-white" >
540588 Timeout Mode
541589 </ label >
542590 < select
543591 id = "timeout_mode"
544592 name = "timeout_mode"
545- className = { cn (
546- "form-select custom-select cb-bg-panel cb-border-color text-white cb-rounded" ,
547- ) }
593+ className = "form-select custom-select cb-bg-panel cb-border-color text-white cb-rounded"
548594 value = { formData . timeout_mode }
549595 onChange = { handleChange }
550596 >
@@ -556,46 +602,70 @@ function TournamentForm({
556602 </ select >
557603 </ div >
558604
559- { formData . timeout_mode === "per_round" && (
560- < div className = "col-md-4 mb-3" >
561- < label htmlFor = "round_timeout_seconds" className = "form-label text-white" >
562- Round Timeout (seconds)
563- </ label >
564- < input
565- type = "number"
566- id = "round_timeout_seconds"
567- name = "round_timeout_seconds"
568- className = { cn ( "form-control cb-bg-panel cb-border-color text-white cb-rounded" , {
569- "is-invalid" : errors . round_timeout_seconds ,
570- } ) }
571- value = { formData . round_timeout_seconds }
572- onChange = { handleChange }
573- min = { 10 }
574- max = { 10000 }
575- />
576- { renderError ( "round_timeout_seconds" ) }
577- </ div >
578- ) }
579- </ div >
605+ < div className = "col-md-4 mb-3" >
606+ < label
607+ htmlFor = "round_timeout_seconds"
608+ className = { cn ( "form-label" , {
609+ "text-white" : [ "per_round_fixed" , "per_round_with_rematch" ] . includes (
610+ formData . timeout_mode ,
611+ ) ,
612+ "text-muted" : ! [ "per_round_fixed" , "per_round_with_rematch" ] . includes (
613+ formData . timeout_mode ,
614+ ) ,
615+ } ) }
616+ >
617+ Round Timeout (seconds)
618+ </ label >
619+ < input
620+ type = "number"
621+ id = "round_timeout_seconds"
622+ name = "round_timeout_seconds"
623+ className = { cn ( "form-control cb-bg-panel cb-border-color text-white cb-rounded" , {
624+ "is-invalid" : errors . round_timeout_seconds ,
625+ } ) }
626+ value = {
627+ [ "per_round_fixed" , "per_round_with_rematch" ] . includes ( formData . timeout_mode )
628+ ? formData . round_timeout_seconds
629+ : ""
630+ }
631+ onChange = { handleChange }
632+ min = { 10 }
633+ max = { 10000 }
634+ disabled = {
635+ ! [ "per_round_fixed" , "per_round_with_rematch" ] . includes ( formData . timeout_mode )
636+ }
637+ />
638+ { renderError ( "round_timeout_seconds" ) }
639+ </ div >
580640
581- < div className = "row" >
582641 < div className = "col-md-4 mb-3" >
583- < label htmlFor = "break_duration_seconds" className = "form-label text-white" >
584- Break Duration (seconds)
642+ < label
643+ htmlFor = "tournament_timeout_seconds"
644+ className = { cn ( "form-label" , {
645+ "text-white" : formData . timeout_mode === "per_tournament" ,
646+ "text-muted" : formData . timeout_mode !== "per_tournament" ,
647+ } ) }
648+ >
649+ Tournament Timeout (seconds)
585650 </ label >
586651 < input
587652 type = "number"
588- id = "break_duration_seconds "
589- name = "break_duration_seconds "
653+ id = "tournament_timeout_seconds "
654+ name = "tournament_timeout_seconds "
590655 className = { cn ( "form-control cb-bg-panel cb-border-color text-white cb-rounded" , {
591- "is-invalid" : errors . break_duration_seconds ,
656+ "is-invalid" : errors . tournament_timeout_seconds ,
592657 } ) }
593- value = { formData . break_duration_seconds }
658+ value = {
659+ formData . timeout_mode === "per_tournament"
660+ ? formData . tournament_timeout_seconds
661+ : ""
662+ }
594663 onChange = { handleChange }
595- min = { 0 }
596- max = { 100000 }
664+ min = { 60 }
665+ max = { 36000 }
666+ disabled = { formData . timeout_mode !== "per_tournament" }
597667 />
598- { renderError ( "break_duration_seconds " ) }
668+ { renderError ( "tournament_timeout_seconds " ) }
599669 </ div >
600670 </ div >
601671 </ div >
@@ -664,8 +734,14 @@ TournamentForm.propTypes = {
664734 tags : PropTypes . string ,
665735 players_limit : PropTypes . number ,
666736 rounds_limit : PropTypes . number ,
667- timeout_mode : PropTypes . oneOf ( [ "per_task" , "per_round" ] ) ,
737+ timeout_mode : PropTypes . oneOf ( [
738+ "per_task" ,
739+ "per_round_fixed" ,
740+ "per_round_with_rematch" ,
741+ "per_tournament" ,
742+ ] ) ,
668743 round_timeout_seconds : PropTypes . number ,
744+ tournament_timeout_seconds : PropTypes . number ,
669745 break_duration_seconds : PropTypes . number ,
670746 use_chat : PropTypes . bool ,
671747 use_clan : PropTypes . bool ,
0 commit comments