feat(auth): implement token rotation and session management for persistent Android login
This commit is contained in:
@@ -403,6 +403,7 @@ const isEditing = ref(false)
|
||||
const editingIndex = ref(-1)
|
||||
const formData = ref({ mannschaft: '', liga: '', staffelleiter: '', telefon: '', heimspieltag: '', spielsystem: '', mannschaftsfuehrer: '', spielerListe: [], weitere_informationen_link: '', letzte_aktualisierung: '' })
|
||||
const moveTargetBySpielerId = ref({})
|
||||
const initialMoveTargetBySpielerId = ref({})
|
||||
const pendingSpielerNamesByTeamIndex = ref({})
|
||||
|
||||
function nowIsoDate() { return new Date().toISOString().split('T')[0] }
|
||||
@@ -449,7 +450,7 @@ async function loadSeasons() {
|
||||
if (!selectedSeason.value || !seasons.value.includes(selectedSeason.value)) {
|
||||
selectedSeason.value = result.defaultSeason || seasons.value[0]
|
||||
}
|
||||
} catch (_error) {
|
||||
} catch {
|
||||
if (!seasons.value.length) seasons.value = ['']
|
||||
if (!selectedSeason.value) selectedSeason.value = seasons.value[0] || ''
|
||||
}
|
||||
@@ -461,7 +462,7 @@ const mannschaftenSelectOptions = computed(() => {
|
||||
return [...new Set([current, ...names])].filter(Boolean)
|
||||
})
|
||||
|
||||
function resetSpielerDraftState() { moveTargetBySpielerId.value = {}; pendingSpielerNamesByTeamIndex.value = {} }
|
||||
function resetSpielerDraftState() { moveTargetBySpielerId.value = {}; initialMoveTargetBySpielerId.value = {}; pendingSpielerNamesByTeamIndex.value = {} }
|
||||
function getPendingSpielerNamesForTeamIndex(teamIndex) {
|
||||
if (pendingSpielerNamesByTeamIndex.value[teamIndex]) return pendingSpielerNamesByTeamIndex.value[teamIndex]
|
||||
const existing = mannschaften.value[teamIndex]; const list = existing ? getSpielerListe(existing) : []
|
||||
@@ -497,29 +498,60 @@ const openEditModal = (mannschaft, index) => {
|
||||
formData.value = { mannschaft: mannschaft.mannschaft || '', liga: mannschaft.liga || '', staffelleiter: mannschaft.staffelleiter || '', telefon: mannschaft.telefon || '', heimspieltag: mannschaft.heimspieltag || '', spielsystem: mannschaft.spielsystem || '', mannschaftsfuehrer: mannschaft.mannschaftsfuehrer || '', spielerListe: parseSpielerString(mannschaft.spieler || ''), weitere_informationen_link: mannschaft.weitere_informationen_link || '', letzte_aktualisierung: mannschaft.letzte_aktualisierung || nowIsoDate() }
|
||||
isEditing.value = true; editingIndex.value = index; showModal.value = true; errorMessage.value = ''; resetSpielerDraftState()
|
||||
const currentTeam = (formData.value.mannschaft || '').trim()
|
||||
for (const s of formData.value.spielerListe) { moveTargetBySpielerId.value[s.id] = currentTeam }
|
||||
for (const s of formData.value.spielerListe) {
|
||||
moveTargetBySpielerId.value[s.id] = currentTeam
|
||||
initialMoveTargetBySpielerId.value[s.id] = currentTeam
|
||||
}
|
||||
}
|
||||
const addSpieler = () => {
|
||||
const item = newSpielerItem('')
|
||||
const currentTeam = (formData.value.mannschaft || '').trim()
|
||||
formData.value.spielerListe.push(item)
|
||||
moveTargetBySpielerId.value[item.id] = currentTeam
|
||||
initialMoveTargetBySpielerId.value[item.id] = currentTeam
|
||||
}
|
||||
const removeSpieler = (id) => {
|
||||
const idx = formData.value.spielerListe.findIndex(s => s.id === id)
|
||||
if (idx === -1) return
|
||||
formData.value.spielerListe.splice(idx, 1)
|
||||
delete moveTargetBySpielerId.value[id]
|
||||
delete initialMoveTargetBySpielerId.value[id]
|
||||
}
|
||||
const addSpieler = () => { const item = newSpielerItem(''); formData.value.spielerListe.push(item); moveTargetBySpielerId.value[item.id] = (formData.value.mannschaft || '').trim() }
|
||||
const removeSpieler = (id) => { const idx = formData.value.spielerListe.findIndex(s => s.id === id); if (idx === -1) return; formData.value.spielerListe.splice(idx, 1); if (moveTargetBySpielerId.value[id]) delete moveTargetBySpielerId.value[id] }
|
||||
const moveSpielerUp = (index) => { if (index <= 0) return; const arr = formData.value.spielerListe; const item = arr[index]; arr.splice(index, 1); arr.splice(index - 1, 0, item) }
|
||||
const moveSpielerDown = (index) => { const arr = formData.value.spielerListe; if (index < 0 || index >= arr.length - 1) return; const item = arr[index]; arr.splice(index, 1); arr.splice(index + 1, 0, item) }
|
||||
const canMoveSpieler = (id) => { const t = (moveTargetBySpielerId.value[id] || '').trim(); const c = (formData.value.mannschaft || '').trim(); return Boolean(t) && Boolean(c) && t !== c }
|
||||
const canMoveSpieler = (id) => {
|
||||
const target = (moveTargetBySpielerId.value[id] || '').trim()
|
||||
const initialTarget = (initialMoveTargetBySpielerId.value[id] || '').trim()
|
||||
return Boolean(target) && Boolean(initialTarget) && target !== initialTarget
|
||||
}
|
||||
|
||||
const moveSpielerToMannschaft = (spielerId) => {
|
||||
if (!isEditing.value || editingIndex.value < 0) return
|
||||
const targetName = (moveTargetBySpielerId.value[spielerId] || '').trim(); if (!targetName) return
|
||||
if (!isEditing.value || editingIndex.value < 0) return false
|
||||
const targetName = (moveTargetBySpielerId.value[spielerId] || '').trim(); if (!targetName) return false
|
||||
const targetIndex = mannschaften.value.findIndex((m, idx) => { if (idx === editingIndex.value) return false; return (m?.mannschaft || '').trim() === targetName })
|
||||
if (targetIndex === -1) { errorMessage.value = 'Ziel-Mannschaft nicht gefunden.'; return }
|
||||
const idx = formData.value.spielerListe.findIndex(s => s.id === spielerId); if (idx === -1) return
|
||||
const spielerName = (formData.value.spielerListe[idx]?.name || '').trim(); if (!spielerName) { errorMessage.value = 'Bitte zuerst einen Spielernamen eintragen.'; return }
|
||||
if (targetIndex === -1) { errorMessage.value = 'Ziel-Mannschaft nicht gefunden.'; return false }
|
||||
const idx = formData.value.spielerListe.findIndex(s => s.id === spielerId); if (idx === -1) return false
|
||||
const spielerName = (formData.value.spielerListe[idx]?.name || '').trim(); if (!spielerName) { errorMessage.value = 'Bitte zuerst einen Spielernamen eintragen.'; return false }
|
||||
formData.value.spielerListe.splice(idx, 1)
|
||||
const pendingList = getPendingSpielerNamesForTeamIndex(targetIndex); pendingList.push(spielerName)
|
||||
delete moveTargetBySpielerId.value[spielerId]
|
||||
delete initialMoveTargetBySpielerId.value[spielerId]
|
||||
return true
|
||||
}
|
||||
|
||||
const applySelectedSpielerTransfers = () => {
|
||||
if (!isEditing.value || editingIndex.value < 0) return true
|
||||
const pendingIds = formData.value.spielerListe
|
||||
.filter(spieler => canMoveSpieler(spieler.id))
|
||||
.map(spieler => spieler.id)
|
||||
|
||||
return pendingIds.every(spielerId => moveSpielerToMannschaft(spielerId))
|
||||
}
|
||||
|
||||
const saveMannschaft = async () => {
|
||||
isSaving.value = true; errorMessage.value = ''
|
||||
try {
|
||||
if (!applySelectedSpielerTransfers()) return
|
||||
const spielerString = serializeSpielerList(formData.value.spielerListe)
|
||||
const updated = { mannschaft: formData.value.mannschaft || '', liga: formData.value.liga || '', staffelleiter: formData.value.staffelleiter || '', telefon: formData.value.telefon || '', heimspieltag: formData.value.heimspieltag || '', spielsystem: formData.value.spielsystem || '', mannschaftsfuehrer: formData.value.mannschaftsfuehrer || '', spieler: spielerString, weitere_informationen_link: formData.value.weitere_informationen_link || '', letzte_aktualisierung: formData.value.letzte_aktualisierung || nowIsoDate() }
|
||||
if (isEditing.value && editingIndex.value >= 0) { mannschaften.value[editingIndex.value] = { ...updated } } else { mannschaften.value.push({ ...updated }) }
|
||||
|
||||
Reference in New Issue
Block a user