More UI Changes? Not sure why they didn't go previously
This commit is contained in:
147
LabWise/ChemicalsListView.swift
Normal file
147
LabWise/ChemicalsListView.swift
Normal file
@@ -0,0 +1,147 @@
|
||||
import SwiftUI
|
||||
import LabWiseKit
|
||||
|
||||
@Observable
|
||||
final class ChemicalsViewModel {
|
||||
var chemicals: [Chemical] = []
|
||||
var isLoading = false
|
||||
var errorMessage: String?
|
||||
|
||||
private let client = ChemicalsClient()
|
||||
|
||||
func loadChemicals() async {
|
||||
isLoading = true
|
||||
errorMessage = nil
|
||||
defer { isLoading = false }
|
||||
do {
|
||||
chemicals = try await client.list()
|
||||
} catch {
|
||||
errorMessage = "Failed to load chemicals."
|
||||
}
|
||||
}
|
||||
|
||||
func delete(chemical: Chemical) async {
|
||||
do {
|
||||
try await client.delete(id: chemical.id)
|
||||
chemicals.removeAll { $0.id == chemical.id }
|
||||
} catch {
|
||||
errorMessage = "Failed to delete chemical."
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ChemicalsListView: View {
|
||||
@State private var viewModel = ChemicalsViewModel()
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Group {
|
||||
if viewModel.isLoading && viewModel.chemicals.isEmpty {
|
||||
ProgressView("Loading chemicals...")
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
} else if viewModel.chemicals.isEmpty {
|
||||
ContentUnavailableView(
|
||||
"No Chemicals",
|
||||
systemImage: "flask",
|
||||
description: Text("Your chemical inventory is empty.")
|
||||
)
|
||||
} else {
|
||||
List {
|
||||
ForEach(viewModel.chemicals) { chemical in
|
||||
NavigationLink(destination: ChemicalDetailView(chemical: chemical)) {
|
||||
ChemicalRowView(chemical: chemical)
|
||||
}
|
||||
}
|
||||
.onDelete { indexSet in
|
||||
for index in indexSet {
|
||||
let chemical = viewModel.chemicals[index]
|
||||
Task { await viewModel.delete(chemical: chemical) }
|
||||
}
|
||||
}
|
||||
}
|
||||
.refreshable {
|
||||
await viewModel.loadChemicals()
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Chemicals")
|
||||
.alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) {
|
||||
Button("OK") { viewModel.errorMessage = nil }
|
||||
} message: {
|
||||
Text(viewModel.errorMessage ?? "")
|
||||
}
|
||||
}
|
||||
.task {
|
||||
await viewModel.loadChemicals()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ChemicalRowView: View {
|
||||
let chemical: Chemical
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
HStack {
|
||||
Text(chemical.chemicalName)
|
||||
.font(.headline)
|
||||
Spacer()
|
||||
PhysicalStateBadge(state: chemical.physicalState)
|
||||
}
|
||||
Text("CAS: \(chemical.casNumber)")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
if let pct = chemical.percentageFull {
|
||||
PercentageBar(value: pct / 100)
|
||||
.frame(height: 4)
|
||||
.padding(.top, 2)
|
||||
}
|
||||
}
|
||||
.padding(.vertical, 2)
|
||||
}
|
||||
}
|
||||
|
||||
struct PhysicalStateBadge: View {
|
||||
let state: String
|
||||
|
||||
var color: Color {
|
||||
switch state.lowercased() {
|
||||
case "liquid": return Color(.brandPrimary)
|
||||
case "solid": return Color(red: 0.42, green: 0.30, blue: 0.18)
|
||||
case "gas": return Color(red: 0.22, green: 0.56, blue: 0.52)
|
||||
default: return Color(.brandMutedForeground)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
Text(state.capitalized)
|
||||
.font(.caption2.weight(.semibold))
|
||||
.padding(.horizontal, 8)
|
||||
.padding(.vertical, 3)
|
||||
.background(color.opacity(0.15))
|
||||
.foregroundStyle(color)
|
||||
.clipShape(Capsule())
|
||||
}
|
||||
}
|
||||
|
||||
struct PercentageBar: View {
|
||||
let value: Double // 0.0 - 1.0
|
||||
|
||||
var body: some View {
|
||||
GeometryReader { geo in
|
||||
ZStack(alignment: .leading) {
|
||||
RoundedRectangle(cornerRadius: 2)
|
||||
.fill(Color.secondary.opacity(0.2))
|
||||
RoundedRectangle(cornerRadius: 2)
|
||||
.fill(barColor)
|
||||
.frame(width: geo.size.width * max(0, min(1, value)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var barColor: Color {
|
||||
if value > 0.6 { return .green }
|
||||
if value > 0.25 { return .yellow }
|
||||
return .red
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user