Files
timetracker/ios/TimeTracker/TimeTracker/Features/Projects/ProjectsViewModel.swift
Simon Franken 39d6ea00d9 Fix iOS list response decoding to match plain-array backend responses
GET /clients and GET /projects return bare arrays, not wrapped objects.
Remove ClientListResponse and ProjectListResponse wrapper structs and
update ClientsViewModel, ProjectsViewModel, and TimerViewModel to decode
[Client] and [Project] directly.
2026-02-19 19:05:43 +01:00

103 lines
2.9 KiB
Swift

import Foundation
import SwiftUI
@MainActor
final class ProjectsViewModel: ObservableObject {
@Published var projects: [Project] = []
@Published var clients: [Client] = []
@Published var isLoading = false
@Published var error: String?
private let apiClient = APIClient()
private let database = DatabaseService.shared
func loadData() async {
isLoading = true
error = nil
do {
clients = try await apiClient.request(
endpoint: APIEndpoint.clients,
authenticated: true
)
projects = try await apiClient.request(
endpoint: APIEndpoint.projects,
authenticated: true
)
try await database.saveProjects(projects)
isLoading = false
} catch {
isLoading = false
self.error = error.localizedDescription
// Load from cache
projects = (try? await database.fetchProjects()) ?? []
}
}
func createProject(name: String, description: String?, color: String?, clientId: String) async {
isLoading = true
do {
let input = CreateProjectInput(
name: name,
description: description,
color: color,
clientId: clientId
)
try await apiClient.requestVoid(
endpoint: APIEndpoint.projects,
method: .post,
body: input,
authenticated: true
)
await loadData()
} catch {
isLoading = false
self.error = error.localizedDescription
}
}
func updateProject(id: String, name: String, description: String?, color: String?, clientId: String) async {
isLoading = true
do {
let input = UpdateProjectInput(
name: name,
description: description,
color: color,
clientId: clientId
)
try await apiClient.requestVoid(
endpoint: APIEndpoint.project(id: id),
method: .put,
body: input,
authenticated: true
)
await loadData()
} catch {
isLoading = false
self.error = error.localizedDescription
}
}
func deleteProject(_ project: Project) async {
do {
try await apiClient.requestVoid(
endpoint: APIEndpoint.project(id: project.id),
method: .delete,
authenticated: true
)
projects.removeAll { $0.id == project.id }
} catch {
self.error = error.localizedDescription
}
}
}