Times were always showing 0 because ISO8601DateFormatter with default options does not parse fractional seconds, but Prisma/Node.js serialises dates as "2026-02-20T09:00:00.000Z" (with .000). Every date(from:) call silently returned nil, so elapsedTime and duration always fell back to 0. - Date+Extensions: fromISO8601 now tries .withFractionalSeconds first, then falls back to whole seconds — single place to maintain - OngoingTimer.elapsedTime: use Date.fromISO8601() instead of bare formatter - TimeEntry.duration: use Date.fromISO8601() instead of bare formatters - TimerView: add TimerUnitLabels view showing h/min/sec column headers under the monospaced clock digits
90 lines
2.3 KiB
Swift
90 lines
2.3 KiB
Swift
import Foundation
|
|
|
|
struct TimeEntry: Codable, Identifiable, Equatable {
|
|
let id: String
|
|
let startTime: String
|
|
let endTime: String
|
|
let description: String?
|
|
let projectId: String
|
|
let project: ProjectReference
|
|
let createdAt: String
|
|
let updatedAt: String
|
|
|
|
var duration: TimeInterval {
|
|
guard let start = Date.fromISO8601(startTime),
|
|
let end = Date.fromISO8601(endTime) else { return 0 }
|
|
return end.timeIntervalSince(start)
|
|
}
|
|
}
|
|
|
|
struct ProjectReference: Codable, Equatable {
|
|
let id: String
|
|
let name: String
|
|
let color: String?
|
|
let client: ClientReference
|
|
}
|
|
|
|
struct TimeEntryListResponse: Codable {
|
|
let entries: [TimeEntry]
|
|
let pagination: Pagination
|
|
}
|
|
|
|
struct Pagination: Codable, Equatable {
|
|
let page: Int
|
|
let limit: Int
|
|
let total: Int
|
|
let totalPages: Int
|
|
}
|
|
|
|
struct TimeEntryFilters: Codable {
|
|
var startDate: String?
|
|
var endDate: String?
|
|
var projectId: String?
|
|
var clientId: String?
|
|
var page: Int?
|
|
var limit: Int?
|
|
|
|
init(
|
|
startDate: Date? = nil,
|
|
endDate: Date? = nil,
|
|
projectId: String? = nil,
|
|
clientId: String? = nil,
|
|
page: Int = 1,
|
|
limit: Int = 20
|
|
) {
|
|
let formatter = ISO8601DateFormatter()
|
|
formatter.formatOptions = [.withFullDate]
|
|
|
|
self.startDate = startDate.map { formatter.string(from: $0) }
|
|
self.endDate = endDate.map { formatter.string(from: $0) }
|
|
self.projectId = projectId
|
|
self.clientId = clientId
|
|
self.page = page
|
|
self.limit = limit
|
|
}
|
|
}
|
|
|
|
struct CreateTimeEntryInput: Codable {
|
|
let startTime: String
|
|
let endTime: String
|
|
let description: String?
|
|
let projectId: String
|
|
|
|
init(startTime: Date, endTime: Date, description: String? = nil, projectId: String) {
|
|
let formatter = ISO8601DateFormatter()
|
|
formatter.formatOptions = [.withInternetDateTime]
|
|
|
|
self.startTime = formatter.string(from: startTime)
|
|
self.endTime = formatter.string(from: endTime)
|
|
self.description = description
|
|
self.projectId = projectId
|
|
}
|
|
}
|
|
|
|
struct UpdateTimeEntryInput: Codable {
|
|
let startTime: String?
|
|
let endTime: String?
|
|
let description: String?
|
|
let projectId: String?
|
|
}
|