Some messy stuff cleaned up in Inventory UI

This commit is contained in:
2026-03-20 02:40:51 -05:00
parent 310d9faf33
commit 813031c823
4 changed files with 53 additions and 20 deletions

View File

@@ -1,6 +1,52 @@
import SwiftUI import SwiftUI
import LabWiseKit import LabWiseKit
private let isoDateFormatter: ISO8601DateFormatter = {
let f = ISO8601DateFormatter()
f.formatOptions = [.withFullDate]
return f
}()
private let isoTimestampFormatter: ISO8601DateFormatter = {
let f = ISO8601DateFormatter()
f.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
return f
}()
private let displayDateFormatter: DateFormatter = {
let f = DateFormatter()
f.dateStyle = .medium
f.timeStyle = .none
return f
}()
private let displayTimestampFormatter: DateFormatter = {
let f = DateFormatter()
f.dateStyle = .medium
f.timeStyle = .short
return f
}()
private func formatDate(_ iso: String?) -> String {
guard let iso else { return "" }
if let date = isoDateFormatter.date(from: iso) {
return displayDateFormatter.string(from: date)
}
// Fallback: try full timestamp (e.g. "2025-03-01T00:00:00.000Z")
if let date = isoTimestampFormatter.date(from: iso) {
return displayDateFormatter.string(from: date)
}
return iso
}
private func formatTimestamp(_ iso: String?) -> String {
guard let iso else { return "" }
if let date = isoTimestampFormatter.date(from: iso) {
return displayTimestampFormatter.string(from: date)
}
return iso
}
struct ChemicalDetailView: View { struct ChemicalDetailView: View {
@State private var chemical: Chemical @State private var chemical: Chemical
@State private var showEdit = false @State private var showEdit = false
@@ -56,8 +102,8 @@ struct ChemicalDetailView: View {
if let lot = chemical.lotNumber { if let lot = chemical.lotNumber {
LabeledContent("Lot #", value: lot) LabeledContent("Lot #", value: lot)
} }
if let exp = chemical.expirationDate { if let exp = chemical.expirationDate, !exp.isEmpty {
LabeledContent("Expiration", value: exp) LabeledContent("Expiration", value: formatDate(exp))
} }
if let barcode = chemical.barcode { if let barcode = chemical.barcode {
LabeledContent("Barcode", value: barcode) LabeledContent("Barcode", value: barcode)
@@ -76,10 +122,10 @@ struct ChemicalDetailView: View {
Section("Record") { Section("Record") {
if let created = chemical.createdAt { if let created = chemical.createdAt {
LabeledContent("Created", value: created) LabeledContent("Created", value: formatTimestamp(created))
} }
if let updated = chemical.updatedAt { if let updated = chemical.updatedAt {
LabeledContent("Updated", value: updated) LabeledContent("Updated", value: formatTimestamp(updated))
} }
} }
} }

View File

@@ -65,8 +65,7 @@ final class DashboardViewModel {
do { do {
chemicals = try await client.list() chemicals = try await client.list()
} catch { } catch {
print("[DashboardViewModel] load error: \(error)") errorMessage = "Failed to load dashboard"
errorMessage = "Failed to load: \(error)"
} }
} }

View File

@@ -16,8 +16,7 @@ final class InventoryViewModel {
do { do {
chemicals = try await client.list() chemicals = try await client.list()
} catch { } catch {
print("[InventoryViewModel] loadChemicals error: \(error)") errorMessage = "Failed to load chemicals"
errorMessage = "Failed to load chemicals: \(error)"
} }
} }
@@ -26,8 +25,7 @@ final class InventoryViewModel {
try await client.delete(id: chemical.id) try await client.delete(id: chemical.id)
chemicals.removeAll { $0.id == chemical.id } chemicals.removeAll { $0.id == chemical.id }
} catch { } catch {
print("[InventoryViewModel] delete error: \(error)") errorMessage = "Failed to delete"
errorMessage = "Failed to delete: \(error)"
} }
} }
} }

View File

@@ -45,9 +45,6 @@ public final class APIClient: Sendable {
req.httpMethod = method req.httpMethod = method
req.setValue(contentType, forHTTPHeaderField: "Content-Type") req.setValue(contentType, forHTTPHeaderField: "Content-Type")
let cookieNames = HTTPCookieStorage.shared.cookies(for: url)?.map(\.name) ?? []
print("[APIClient] \(method) \(path) — cookies: \(cookieNames)")
if let body { if let body {
do { do {
req.httpBody = try JSONEncoder.api.encode(body) req.httpBody = try JSONEncoder.api.encode(body)
@@ -60,27 +57,20 @@ public final class APIClient: Sendable {
do { do {
(data, response) = try await session.data(for: req) (data, response) = try await session.data(for: req)
} catch { } catch {
print("[APIClient] Network error on \(method) \(path): \(error)")
throw APIError.networkError(error) throw APIError.networkError(error)
} }
guard let http = response as? HTTPURLResponse else { guard let http = response as? HTTPURLResponse else {
print("[APIClient] Non-HTTP response on \(method) \(path)")
throw APIError.networkError(URLError(.badServerResponse)) throw APIError.networkError(URLError(.badServerResponse))
} }
print("[APIClient] \(method) \(path)\(http.statusCode)")
if http.statusCode == 401 { if http.statusCode == 401 {
print("[APIClient] 401 — clearing session and signalling unauthorized")
clearSessionCookies() clearSessionCookies()
onUnauthorized?() onUnauthorized?()
throw APIError.unauthorized throw APIError.unauthorized
} }
guard (200..<300).contains(http.statusCode) else { guard (200..<300).contains(http.statusCode) else {
let body = String(data: data, encoding: .utf8) ?? "<binary \(data.count)b>"
print("[APIClient] HTTP \(http.statusCode) on \(method) \(path): \(body)")
throw APIError.httpError(statusCode: http.statusCode, data: data) throw APIError.httpError(statusCode: http.statusCode, data: data)
} }