update
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<div class="app-shell" :class="'lang-' + language">
|
||||
<div class="app-shell" :class="['lang-' + language, 'mode-' + mode]">
|
||||
<input ref="imageInput" type="file" class="hidden-file-input" accept="image/*" capture="environment" @change="onImageSelected" />
|
||||
<input ref="scoreProofInput" type="file" class="hidden-file-input" accept="image/*" capture="environment" @change="onScoreProofSelected" />
|
||||
|
||||
@@ -71,6 +71,7 @@
|
||||
v-else-if="mode === 'live'"
|
||||
:t="t"
|
||||
:live-settings="liveSettings"
|
||||
:position-boards="positionBoards"
|
||||
:live-group-code="liveMonitorGroupCode"
|
||||
:live-group-members="liveMonitorMembers"
|
||||
:prelim-tie-rows="prelimTieRows"
|
||||
@@ -78,6 +79,7 @@
|
||||
:finalists="finalists"
|
||||
:live-final-group="liveMonitorFinalGroup"
|
||||
:live-final-rows="liveMonitorFinalRows"
|
||||
:final-overall-rows="finalRows"
|
||||
:final-tie-rows="finalTieRows"
|
||||
:podium-ordered="podiumOrdered"
|
||||
:player-image="playerImage"
|
||||
@@ -112,6 +114,12 @@
|
||||
:admin-tab="adminTab"
|
||||
:view-proof-in-view="canViewProofs"
|
||||
:live-settings="liveSettings"
|
||||
:stage-control="stageControl"
|
||||
:stage-options="stageOptions"
|
||||
:current-stage="currentStage"
|
||||
:last-stage="lastStage"
|
||||
:active-scoring-stage="currentScoringStageKey"
|
||||
:active-scoring-group="currentScoringGroupKey"
|
||||
:group-setup-input="groupSetupInput"
|
||||
:admin-group-cards="adminGroupCards"
|
||||
:new-player="newPlayer"
|
||||
@@ -131,6 +139,7 @@
|
||||
:final-total-score="finalTotalScore"
|
||||
:prelim-tie="prelimTie"
|
||||
:final-tie="finalTie"
|
||||
:position-boards="positionBoards"
|
||||
:score-filters="scoreFilters"
|
||||
:display-name="displayName"
|
||||
:secondary-name="secondaryName"
|
||||
@@ -149,6 +158,10 @@
|
||||
@logout="adminLogout"
|
||||
@toggle-view-proof="updateViewProofSetting"
|
||||
@update-live-setting="updateLiveModeSetting"
|
||||
@update-stage-control="updateStageControl"
|
||||
@start-stage="startCurrentStage"
|
||||
@end-stage="endCurrentStage"
|
||||
@navigate-current-stage="navigateToCurrentStage"
|
||||
@change-admin-tab="adminTab = $event"
|
||||
@update:group-setup-input="groupSetupInput = $event"
|
||||
@save-group-setup="saveGroupSetup"
|
||||
@@ -165,6 +178,7 @@
|
||||
@remove-player-image="removePlayerImage"
|
||||
@delete-player="deletePlayer"
|
||||
@request-reset-stage="openResetStageModal"
|
||||
@save-position-board="savePositionBoard"
|
||||
@update-score-filter="updateScoreFilter"
|
||||
/>
|
||||
</template>
|
||||
@@ -219,6 +233,7 @@ const FALLBACK_SYNC_MS = 15000
|
||||
const STREAM_RETRY_MS = 1500
|
||||
const PRELIM_ROUND_STAGES = ['prelim_r1', 'prelim_r2', 'prelim_r3']
|
||||
const FINAL_ROUND_STAGES = ['final_r1', 'final_r2']
|
||||
const POSITION_BOARDS = ['preliminary', 'final', 'prelim_tiebreak', 'final_tiebreak']
|
||||
|
||||
const loading = ref(true)
|
||||
const mode = ref('live')
|
||||
@@ -247,6 +262,11 @@ const scoreFilters = reactive({
|
||||
prelim_tiebreak: '',
|
||||
final_tiebreak: '',
|
||||
})
|
||||
const stageControl = reactive({
|
||||
phase: 'preliminary',
|
||||
group: '',
|
||||
round: 1,
|
||||
})
|
||||
|
||||
const liveGroupIndex = ref(0)
|
||||
const liveMode = ref('rotate')
|
||||
@@ -298,6 +318,14 @@ const finalRows = computed(() => state.value.derived?.finalRanking?.rows || [])
|
||||
const podiumRows = computed(() => state.value.derived?.podium || [])
|
||||
const prelimTie = computed(() => state.value.derived?.preliminaryRanking?.tieBreak || { required: false, resolved: true, slots: 0, playerIds: [] })
|
||||
const finalTie = computed(() => state.value.derived?.finalRanking?.tieBreak || { required: false, resolved: true, slots: 0, playerIds: [] })
|
||||
const positionBoards = computed(() => {
|
||||
const raw = state.value.positionBoards || {}
|
||||
const out = {}
|
||||
for (const board of POSITION_BOARDS) {
|
||||
out[board] = raw[board] || {}
|
||||
}
|
||||
return out
|
||||
})
|
||||
const canViewProofs = computed(() => Boolean(state.value.settings?.viewProofInView))
|
||||
const liveSettings = computed(() => {
|
||||
const raw = state.value.settings?.liveMode || {}
|
||||
@@ -320,6 +348,10 @@ const liveSettings = computed(() => {
|
||||
rotationPlayerCount: Number(raw.rotationPlayerCount) > 0 ? Number(raw.rotationPlayerCount) : 12,
|
||||
}
|
||||
})
|
||||
const currentStage = computed(() => state.value.current_stage || null)
|
||||
const lastStage = computed(() => state.value.last_stage || null)
|
||||
const currentScoringStageKey = computed(() => mapStageSessionToScoreStage(currentStage.value))
|
||||
const currentScoringGroupKey = computed(() => mapStageSessionToScoreGroup(currentStage.value))
|
||||
|
||||
const viewTabs = computed(() => [
|
||||
{ id: 'groups', label: t('tabs.groups') },
|
||||
@@ -409,7 +441,8 @@ const liveGroupCode = computed(() => {
|
||||
const liveMembers = computed(() => {
|
||||
const code = normalizedGroupCode(liveGroupCode.value)
|
||||
if (!code) return []
|
||||
return playersSorted.value.filter((player) => normalizedGroupCode(player.groupCode) === code)
|
||||
const rows = playersSorted.value.filter((player) => normalizedGroupCode(player.groupCode) === code)
|
||||
return sortRowsByPreliminaryPosition(rows)
|
||||
})
|
||||
|
||||
const liveMonitorGroupRotationCodes = computed(() => activeGroupCodes.value)
|
||||
@@ -424,7 +457,8 @@ const liveMonitorGroupCode = computed(() => {
|
||||
const liveMonitorMembers = computed(() => {
|
||||
const code = normalizedGroupCode(liveMonitorGroupCode.value)
|
||||
if (!code) return []
|
||||
return playersSorted.value.filter((player) => normalizedGroupCode(player.groupCode) === code)
|
||||
const rows = playersSorted.value.filter((player) => normalizedGroupCode(player.groupCode) === code)
|
||||
return sortRowsByPreliminaryPosition(rows)
|
||||
})
|
||||
const liveMonitorFinalGroup = computed(() => {
|
||||
if (liveSettings.value.finalDisplayMode === 'fixed') {
|
||||
@@ -432,17 +466,32 @@ const liveMonitorFinalGroup = computed(() => {
|
||||
}
|
||||
return liveMonitorFinalIndex.value % 2 === 0 ? 1 : 2
|
||||
})
|
||||
const liveMonitorFinalRows = computed(() => (liveMonitorFinalGroup.value === 2 ? finalGroup2.value : finalGroup1.value))
|
||||
const finalBoardGroupedRows = computed(() => {
|
||||
const sorted = sortRowsByBoardPosition('final', adminFinalRows.value)
|
||||
const grouped = {}
|
||||
for (const row of sorted) {
|
||||
const key = positionSlotFor('final', row.playerId)?.groupKey || String(row.finalGroup || 1)
|
||||
if (!grouped[key]) grouped[key] = []
|
||||
grouped[key].push(row)
|
||||
}
|
||||
return grouped
|
||||
})
|
||||
const liveMonitorFinalRows = computed(() => {
|
||||
const key = String(liveMonitorFinalGroup.value === 2 ? 2 : 1)
|
||||
return finalBoardGroupedRows.value[key] || []
|
||||
})
|
||||
const podiumOrdered = computed(() => [podiumRows.value[1] || null, podiumRows.value[0] || null, podiumRows.value[2] || null])
|
||||
|
||||
const prelimTieRows = computed(() => {
|
||||
const ids = new Set(prelimTie.value.playerIds || [])
|
||||
return preliminaryRows.value.filter((row) => ids.has(row.playerId))
|
||||
const rows = preliminaryRows.value.filter((row) => ids.has(row.playerId))
|
||||
return sortRowsByBoardPosition('prelim_tiebreak', rows)
|
||||
})
|
||||
|
||||
const finalTieRows = computed(() => {
|
||||
const ids = new Set(finalTie.value.playerIds || [])
|
||||
return finalRows.value.filter((row) => ids.has(row.playerId))
|
||||
const rows = finalRows.value.filter((row) => ids.has(row.playerId))
|
||||
return sortRowsByBoardPosition('final_tiebreak', rows)
|
||||
})
|
||||
|
||||
const playerByID = computed(() => {
|
||||
@@ -483,8 +532,7 @@ const adminPreliminaryRows = computed(() => {
|
||||
rank: derived?.rank ?? 0,
|
||||
}
|
||||
})
|
||||
rows.sort(compareRowsByGroupThenId)
|
||||
return rows
|
||||
return sortRowsByBoardPosition('preliminary', rows)
|
||||
})
|
||||
|
||||
const adminPrelimTieRows = computed(() => {
|
||||
@@ -504,8 +552,7 @@ const adminPrelimTieRows = computed(() => {
|
||||
}
|
||||
})
|
||||
.filter(Boolean)
|
||||
rows.sort(compareRowsByScoreGroupThenId)
|
||||
return rows
|
||||
return sortRowsByBoardPosition('prelim_tiebreak', rows)
|
||||
})
|
||||
|
||||
const adminFinalRows = computed(() => {
|
||||
@@ -525,17 +572,7 @@ const adminFinalRows = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
rows.sort((a, b) => {
|
||||
const groupA = Number(a.finalGroup || 0)
|
||||
const groupB = Number(b.finalGroup || 0)
|
||||
if (groupA !== groupB) return groupA - groupB
|
||||
const seedA = Number(a.seed || 0)
|
||||
const seedB = Number(b.seed || 0)
|
||||
if (seedA !== seedB) return seedA - seedB
|
||||
return a.playerId - b.playerId
|
||||
})
|
||||
|
||||
return rows
|
||||
return sortRowsByBoardPosition('final', rows)
|
||||
})
|
||||
|
||||
const adminFinalTieRows = computed(() => {
|
||||
@@ -558,8 +595,29 @@ const adminFinalTieRows = computed(() => {
|
||||
})
|
||||
.filter(Boolean)
|
||||
|
||||
rows.sort(compareRowsByScoreGroupThenId)
|
||||
return rows
|
||||
return sortRowsByBoardPosition('final_tiebreak', rows)
|
||||
})
|
||||
|
||||
const stageOptions = computed(() => {
|
||||
const preliminaryGroups = collectUniqueStrings([
|
||||
...assignableGroups.value,
|
||||
...activeGroupCodes.value,
|
||||
])
|
||||
const finalGroups = collectUniqueStrings([
|
||||
...Object.keys(finalBoardGroupedRows.value || {}),
|
||||
'1',
|
||||
'2',
|
||||
]).filter((value) => value === '1' || value === '2')
|
||||
|
||||
const prelimTieGroups = collectUniqueStrings((adminPrelimTieRows.value || []).map((row) => String(row._boardGroupKey || row.scoreGroup || '1')))
|
||||
const finalTieGroups = collectUniqueStrings((adminFinalTieRows.value || []).map((row) => String(row._boardGroupKey || row.scoreGroup || '1')))
|
||||
|
||||
return {
|
||||
preliminary: preliminaryGroups,
|
||||
prelim_tiebreak: prelimTieGroups.length > 0 ? prelimTieGroups : ['1'],
|
||||
final: finalGroups.length > 0 ? finalGroups : ['1', '2'],
|
||||
final_tiebreak: finalTieGroups.length > 0 ? finalTieGroups : ['1'],
|
||||
}
|
||||
})
|
||||
|
||||
watch(language, (value) => {
|
||||
@@ -643,6 +701,14 @@ watch(
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
watch(
|
||||
() => `${stageControl.phase}|${JSON.stringify(stageOptions.value)}`,
|
||||
() => {
|
||||
applyStageControlDefaults()
|
||||
},
|
||||
{ immediate: true },
|
||||
)
|
||||
|
||||
onMounted(async () => {
|
||||
onFullscreenChange()
|
||||
document.addEventListener('fullscreenchange', onFullscreenChange)
|
||||
@@ -728,7 +794,7 @@ async function toggleFullscreen() {
|
||||
|
||||
function normalizeLiveActiveView(value) {
|
||||
const normalized = String(value || '').trim().toLowerCase()
|
||||
const allowed = new Set(['group_live', 'prelim_tie', 'prelim_overall', 'final_groups', 'final_tie', 'podium'])
|
||||
const allowed = new Set(['group_live', 'prelim_tie', 'prelim_overall', 'final_groups', 'final_tie', 'final_overall', 'podium'])
|
||||
return allowed.has(normalized) ? normalized : 'group_live'
|
||||
}
|
||||
|
||||
@@ -788,6 +854,116 @@ function compareRowsByScoreGroupThenId(a, b) {
|
||||
return compareRowsByGroupThenId(a, b)
|
||||
}
|
||||
|
||||
function positionSlotFor(board, playerId) {
|
||||
const boardMap = positionBoards.value?.[board] || {}
|
||||
return boardMap[String(playerId)] || boardMap[playerId] || null
|
||||
}
|
||||
|
||||
function normalizePositionGroup(board, raw, fallback = '') {
|
||||
if (board === 'preliminary') {
|
||||
const normalized = normalizedGroupCode(raw || '')
|
||||
if (normalized) return normalized
|
||||
if (String(raw || '').trim().toUpperCase() === 'UNASSIGNED') return 'UNASSIGNED'
|
||||
return fallback || 'UNASSIGNED'
|
||||
}
|
||||
const parsed = Number.parseInt(String(raw || ''), 10)
|
||||
if (Number.isFinite(parsed) && parsed > 0) return String(parsed)
|
||||
return fallback || '1'
|
||||
}
|
||||
|
||||
function normalizePositionValue(raw, fallback = 1) {
|
||||
const parsed = Number(raw)
|
||||
if (Number.isFinite(parsed) && parsed > 0) return Math.floor(parsed)
|
||||
return fallback
|
||||
}
|
||||
|
||||
function positionGroupSortValue(board, groupKey) {
|
||||
if (board === 'preliminary') {
|
||||
if (groupKey === 'UNASSIGNED') return Number.MAX_SAFE_INTEGER - 1
|
||||
const idx = assignableGroups.value.indexOf(groupKey)
|
||||
if (idx >= 0) return idx
|
||||
return Number.MAX_SAFE_INTEGER
|
||||
}
|
||||
return Number.parseInt(String(groupKey || '0'), 10) || Number.MAX_SAFE_INTEGER
|
||||
}
|
||||
|
||||
function sortRowsByPreliminaryPosition(rows) {
|
||||
return sortRowsByBoardPosition('preliminary', rows)
|
||||
}
|
||||
|
||||
function sortRowsByBoardPosition(board, rows) {
|
||||
const list = Array.isArray(rows) ? rows.slice() : []
|
||||
if (list.length <= 1) return list
|
||||
|
||||
const fallbackRows = list.slice()
|
||||
if (board === 'preliminary') {
|
||||
fallbackRows.sort(compareRowsByGroupThenId)
|
||||
} else if (board === 'final') {
|
||||
fallbackRows.sort((a, b) => {
|
||||
const groupA = Number(a.finalGroup || 0)
|
||||
const groupB = Number(b.finalGroup || 0)
|
||||
if (groupA !== groupB) return groupA - groupB
|
||||
const seedA = Number(a.seed || 0)
|
||||
const seedB = Number(b.seed || 0)
|
||||
if (seedA !== seedB) return seedA - seedB
|
||||
return Number(a.playerId || a.id || 0) - Number(b.playerId || b.id || 0)
|
||||
})
|
||||
} else {
|
||||
fallbackRows.sort(compareRowsByScoreGroupThenId)
|
||||
}
|
||||
|
||||
const fallbackMap = new Map()
|
||||
const fallbackCountByGroup = new Map()
|
||||
for (let index = 0; index < fallbackRows.length; index += 1) {
|
||||
const row = fallbackRows[index]
|
||||
const playerId = Number(row.playerId || row.id || 0)
|
||||
let fallbackGroup = '1'
|
||||
if (board === 'preliminary') {
|
||||
fallbackGroup = normalizePositionGroup(board, row.groupCode, 'UNASSIGNED')
|
||||
} else if (board === 'final') {
|
||||
fallbackGroup = normalizePositionGroup(board, row.finalGroup, '1')
|
||||
} else if (board === 'prelim_tiebreak' || board === 'final_tiebreak') {
|
||||
const scoreGroup = Number.parseInt(String(row.scoreGroup || ''), 10)
|
||||
const finalGroup = Number.parseInt(String(row.finalGroup || ''), 10)
|
||||
if (Number.isFinite(scoreGroup) && scoreGroup > 0) {
|
||||
fallbackGroup = String(scoreGroup)
|
||||
} else if (Number.isFinite(finalGroup) && finalGroup > 0) {
|
||||
fallbackGroup = String(finalGroup)
|
||||
} else {
|
||||
fallbackGroup = '1'
|
||||
}
|
||||
} else {
|
||||
fallbackGroup = '1'
|
||||
}
|
||||
const nextPos = (fallbackCountByGroup.get(fallbackGroup) || 0) + 1
|
||||
fallbackCountByGroup.set(fallbackGroup, nextPos)
|
||||
fallbackMap.set(playerId, { groupKey: fallbackGroup, position: nextPos, index })
|
||||
}
|
||||
|
||||
return list
|
||||
.map((row) => {
|
||||
const playerId = Number(row.playerId || row.id || 0)
|
||||
const fallback = fallbackMap.get(playerId) || { groupKey: '1', position: Number.MAX_SAFE_INTEGER, index: Number.MAX_SAFE_INTEGER }
|
||||
const slot = positionSlotFor(board, playerId)
|
||||
const groupKey = normalizePositionGroup(board, slot?.groupKey, fallback.groupKey)
|
||||
const position = normalizePositionValue(slot?.position, fallback.position)
|
||||
return { row, groupKey, position, fallbackIndex: fallback.index }
|
||||
})
|
||||
.sort((a, b) => {
|
||||
const groupOrderA = positionGroupSortValue(board, a.groupKey)
|
||||
const groupOrderB = positionGroupSortValue(board, b.groupKey)
|
||||
if (groupOrderA !== groupOrderB) return groupOrderA - groupOrderB
|
||||
if (a.groupKey !== b.groupKey) return String(a.groupKey).localeCompare(String(b.groupKey))
|
||||
if (a.position !== b.position) return a.position - b.position
|
||||
return a.fallbackIndex - b.fallbackIndex
|
||||
})
|
||||
.map((entry) => ({
|
||||
...entry.row,
|
||||
_boardGroupKey: entry.groupKey,
|
||||
_boardPosition: entry.position,
|
||||
}))
|
||||
}
|
||||
|
||||
function saveGroupSetup() {
|
||||
const parsed = parseGroupList(groupSetupInput.value)
|
||||
primaryGroups.value = parsed.length > 0 ? parsed : DEFAULT_PRIMARY_GROUPS
|
||||
@@ -829,6 +1005,22 @@ function updateScoreFilter({ stage, value }) {
|
||||
scoreFilters[stage] = value
|
||||
}
|
||||
|
||||
async function savePositionBoard(payload) {
|
||||
const board = String(payload?.board || '').trim().toLowerCase()
|
||||
if (!POSITION_BOARDS.includes(board)) return
|
||||
const slots = Array.isArray(payload?.slots) ? payload.slots : []
|
||||
|
||||
try {
|
||||
state.value = await api(`/admin/positions/${board}`, {
|
||||
method: 'PUT',
|
||||
admin: true,
|
||||
body: { slots },
|
||||
})
|
||||
} catch (error) {
|
||||
showToast(`${t('messages.errorPrefix')}: ${error.message}`, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
async function autoGroupEvenly() {
|
||||
if (primaryGroups.value.length === 0) {
|
||||
showToast(t('messages.noGroupsConfigured'), 'error')
|
||||
@@ -990,6 +1182,139 @@ async function updateLiveModeSetting(patch) {
|
||||
await updateAdminSettings({ liveMode: patch })
|
||||
}
|
||||
|
||||
function collectUniqueStrings(values) {
|
||||
const set = new Set()
|
||||
for (const raw of values || []) {
|
||||
const value = String(raw || '').trim()
|
||||
if (!value) continue
|
||||
set.add(value)
|
||||
}
|
||||
return [...set].sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
|
||||
}
|
||||
|
||||
function stageRoundMax(phase) {
|
||||
if (phase === 'preliminary') return 3
|
||||
if (phase === 'final') return 2
|
||||
return 1
|
||||
}
|
||||
|
||||
function mapStageSessionToScoreStage(stage) {
|
||||
if (!stage || typeof stage !== 'object') return ''
|
||||
const phase = String(stage.phase || '').trim().toLowerCase()
|
||||
const round = Number(stage.round || 1)
|
||||
if (phase === 'preliminary') return `prelim_r${Math.min(3, Math.max(1, Math.floor(round || 1)))}`
|
||||
if (phase === 'final') return `final_r${Math.min(2, Math.max(1, Math.floor(round || 1)))}`
|
||||
if (phase === 'prelim_tiebreak') return 'prelim_tiebreak'
|
||||
if (phase === 'final_tiebreak') return 'final_tiebreak'
|
||||
return ''
|
||||
}
|
||||
|
||||
function mapStageSessionToScoreGroup(stage) {
|
||||
if (!stage || typeof stage !== 'object') return ''
|
||||
const phase = String(stage.phase || '').trim().toLowerCase()
|
||||
const rawGroup = String(stage.group || '').trim()
|
||||
if (!rawGroup) return ''
|
||||
if (phase === 'preliminary') {
|
||||
if (rawGroup.toUpperCase() === 'UNASSIGNED') return 'UNASSIGNED'
|
||||
return normalizedGroupCode(rawGroup)
|
||||
}
|
||||
const parsed = Number.parseInt(rawGroup, 10)
|
||||
if (Number.isFinite(parsed) && parsed > 0) return String(parsed)
|
||||
return rawGroup
|
||||
}
|
||||
|
||||
function mapStagePhaseToAdminTab(phase) {
|
||||
const normalized = String(phase || '').trim().toLowerCase()
|
||||
if (normalized === 'preliminary') return 'preliminary'
|
||||
if (normalized === 'prelim_tiebreak') return 'prelimTie'
|
||||
if (normalized === 'final') return 'final'
|
||||
if (normalized === 'final_tiebreak') return 'finalTie'
|
||||
return ''
|
||||
}
|
||||
|
||||
function applyStageControlDefaults() {
|
||||
const phase = String(stageControl.phase || 'preliminary').trim().toLowerCase()
|
||||
if (!['preliminary', 'prelim_tiebreak', 'final', 'final_tiebreak'].includes(phase)) {
|
||||
stageControl.phase = 'preliminary'
|
||||
} else {
|
||||
stageControl.phase = phase
|
||||
}
|
||||
|
||||
const maxRound = stageRoundMax(stageControl.phase)
|
||||
const round = Number(stageControl.round || 1)
|
||||
stageControl.round = Math.min(maxRound, Math.max(1, Number.isFinite(round) ? Math.floor(round) : 1))
|
||||
|
||||
const options = stageOptions.value?.[stageControl.phase] || []
|
||||
if (options.length === 0) {
|
||||
stageControl.group = ''
|
||||
return
|
||||
}
|
||||
const current = String(stageControl.group || '').trim()
|
||||
if (!options.includes(current)) {
|
||||
stageControl.group = options[0]
|
||||
}
|
||||
}
|
||||
|
||||
function updateStageControl(patch) {
|
||||
if (!patch || typeof patch !== 'object') return
|
||||
if (Object.prototype.hasOwnProperty.call(patch, 'phase')) {
|
||||
stageControl.phase = String(patch.phase || 'preliminary').trim().toLowerCase()
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(patch, 'group')) {
|
||||
stageControl.group = String(patch.group || '').trim()
|
||||
}
|
||||
if (Object.prototype.hasOwnProperty.call(patch, 'round')) {
|
||||
stageControl.round = Number(patch.round || 1)
|
||||
}
|
||||
applyStageControlDefaults()
|
||||
}
|
||||
|
||||
async function startCurrentStage() {
|
||||
applyStageControlDefaults()
|
||||
if (!stageControl.group) {
|
||||
showToast(t('messages.noGroupForPhase'), 'error')
|
||||
return
|
||||
}
|
||||
try {
|
||||
state.value = await api('/admin/stage/start', {
|
||||
method: 'POST',
|
||||
admin: true,
|
||||
body: {
|
||||
phase: stageControl.phase,
|
||||
group: stageControl.group,
|
||||
round: stageControl.round,
|
||||
},
|
||||
})
|
||||
showToast(t('messages.saved'))
|
||||
} catch (error) {
|
||||
showToast(`${t('messages.errorPrefix')}: ${error.message}`, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
async function endCurrentStage() {
|
||||
try {
|
||||
state.value = await api('/admin/stage/end', {
|
||||
method: 'POST',
|
||||
admin: true,
|
||||
})
|
||||
showToast(t('messages.saved'))
|
||||
} catch (error) {
|
||||
showToast(`${t('messages.errorPrefix')}: ${error.message}`, 'error')
|
||||
}
|
||||
}
|
||||
|
||||
function navigateToCurrentStage() {
|
||||
const active = currentStage.value
|
||||
if (!active) {
|
||||
showToast(t('labels.noCurrentStage'), 'error')
|
||||
return
|
||||
}
|
||||
const nextTab = mapStagePhaseToAdminTab(active.phase)
|
||||
if (nextTab) {
|
||||
adminTab.value = nextTab
|
||||
}
|
||||
}
|
||||
|
||||
async function createPlayer() {
|
||||
if (!newPlayer.nameAr || !newPlayer.nameEn) {
|
||||
showToast(t('messages.mustProvideNames'), 'error')
|
||||
|
||||
Reference in New Issue
Block a user