124 lines
3.7 KiB
TypeScript
124 lines
3.7 KiB
TypeScript
|
|
import { create } from 'zustand'
|
||
|
|
import { persist } from 'zustand/middleware'
|
||
|
|
import type { MarathonResponse, AssignmentResponse } from '@shared/types'
|
||
|
|
|
||
|
|
interface MarathonState {
|
||
|
|
marathons: MarathonResponse[]
|
||
|
|
selectedMarathonId: number | null
|
||
|
|
currentAssignment: AssignmentResponse | null
|
||
|
|
isLoading: boolean
|
||
|
|
error: string | null
|
||
|
|
|
||
|
|
loadMarathons: () => Promise<void>
|
||
|
|
selectMarathon: (marathonId: number) => Promise<void>
|
||
|
|
loadCurrentAssignment: () => Promise<void>
|
||
|
|
syncTime: (minutes: number) => Promise<void>
|
||
|
|
reset: () => void
|
||
|
|
}
|
||
|
|
|
||
|
|
export const useMarathonStore = create<MarathonState>()(
|
||
|
|
persist(
|
||
|
|
(set, get) => ({
|
||
|
|
marathons: [],
|
||
|
|
selectedMarathonId: null,
|
||
|
|
currentAssignment: null,
|
||
|
|
isLoading: false,
|
||
|
|
error: null,
|
||
|
|
|
||
|
|
loadMarathons: async () => {
|
||
|
|
set({ isLoading: true, error: null })
|
||
|
|
|
||
|
|
const result = await window.electronAPI.apiRequest<MarathonResponse[]>('GET', '/marathons')
|
||
|
|
|
||
|
|
if (!result.success) {
|
||
|
|
set({ isLoading: false, error: result.error || 'Failed to load marathons' })
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
const marathons = result.data || []
|
||
|
|
const activeMarathons = marathons.filter(m => m.status === 'active')
|
||
|
|
|
||
|
|
set({ marathons: activeMarathons, isLoading: false })
|
||
|
|
|
||
|
|
// If we have a selected marathon, verify it's still valid
|
||
|
|
const { selectedMarathonId } = get()
|
||
|
|
if (selectedMarathonId) {
|
||
|
|
const stillExists = activeMarathons.some(m => m.id === selectedMarathonId)
|
||
|
|
if (!stillExists && activeMarathons.length > 0) {
|
||
|
|
// Select first available marathon
|
||
|
|
await get().selectMarathon(activeMarathons[0].id)
|
||
|
|
} else if (stillExists) {
|
||
|
|
// Reload assignment for current selection
|
||
|
|
await get().loadCurrentAssignment()
|
||
|
|
}
|
||
|
|
} else if (activeMarathons.length > 0) {
|
||
|
|
// No selection, select first marathon
|
||
|
|
await get().selectMarathon(activeMarathons[0].id)
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
selectMarathon: async (marathonId: number) => {
|
||
|
|
set({ selectedMarathonId: marathonId, currentAssignment: null })
|
||
|
|
await get().loadCurrentAssignment()
|
||
|
|
},
|
||
|
|
|
||
|
|
loadCurrentAssignment: async () => {
|
||
|
|
const { selectedMarathonId } = get()
|
||
|
|
if (!selectedMarathonId) {
|
||
|
|
set({ currentAssignment: null })
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
const result = await window.electronAPI.apiRequest<AssignmentResponse | null>(
|
||
|
|
'GET',
|
||
|
|
`/marathons/${selectedMarathonId}/current-assignment`
|
||
|
|
)
|
||
|
|
|
||
|
|
if (result.success) {
|
||
|
|
set({ currentAssignment: result.data ?? null })
|
||
|
|
} else {
|
||
|
|
// User might not be participant of this marathon
|
||
|
|
set({ currentAssignment: null, error: result.error })
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
syncTime: async (minutes: number) => {
|
||
|
|
const { currentAssignment } = get()
|
||
|
|
if (!currentAssignment || currentAssignment.status !== 'active') {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
const result = await window.electronAPI.apiRequest(
|
||
|
|
'PATCH',
|
||
|
|
`/assignments/${currentAssignment.id}/track-time`,
|
||
|
|
{ minutes }
|
||
|
|
)
|
||
|
|
|
||
|
|
if (result.success) {
|
||
|
|
// Update local assignment with new tracked time
|
||
|
|
set({
|
||
|
|
currentAssignment: {
|
||
|
|
...currentAssignment,
|
||
|
|
tracked_time_minutes: minutes
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
},
|
||
|
|
|
||
|
|
reset: () => {
|
||
|
|
set({
|
||
|
|
marathons: [],
|
||
|
|
selectedMarathonId: null,
|
||
|
|
currentAssignment: null,
|
||
|
|
isLoading: false,
|
||
|
|
error: null
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}),
|
||
|
|
{
|
||
|
|
name: 'marathon-storage',
|
||
|
|
partialize: (state) => ({ selectedMarathonId: state.selectedMarathonId })
|
||
|
|
}
|
||
|
|
)
|
||
|
|
)
|