Time tracker app
This commit is contained in:
123
desktop/src/renderer/store/marathon.ts
Normal file
123
desktop/src/renderer/store/marathon.ts
Normal file
@@ -0,0 +1,123 @@
|
||||
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 })
|
||||
}
|
||||
)
|
||||
)
|
||||
Reference in New Issue
Block a user