Files
LabWiseiOS/LabWise/ProfileView.swift

145 lines
4.7 KiB
Swift

import SwiftUI
import LabWiseKit
@Observable
final class ProfileViewModel {
var profile: UserProfile?
var isLoading = false
var isSaving = false
var isEditing = false
var errorMessage: String?
// Editable fields
var piFirstName = ""
var bldgCode = ""
var lab = ""
var contact = ""
private let profileClient = ProfileClient()
private let authClient = AuthClient()
func load() async {
isLoading = true
defer { isLoading = false }
do {
let p = try await profileClient.get()
profile = p
populateFields(from: p)
} catch {
// Profile may not exist yet that's OK
}
}
func save() async {
isSaving = true
defer { isSaving = false }
do {
let body = UserProfileUpsertBody(
piFirstName: piFirstName,
bldgCode: bldgCode,
lab: lab,
contact: contact.isEmpty ? nil : contact
)
let updated = try await profileClient.upsert(body)
profile = updated
populateFields(from: updated)
isEditing = false
} catch {
errorMessage = "Failed to save profile."
}
}
func signOut() async {
try? await authClient.signOut()
await MainActor.run {
AppState.shared.signedOut()
}
}
private func populateFields(from p: UserProfile) {
piFirstName = p.piFirstName
bldgCode = p.bldgCode
lab = p.lab
contact = p.contact ?? ""
}
}
struct ProfileView: View {
@Environment(AppState.self) private var appState
@State private var viewModel = ProfileViewModel()
var body: some View {
NavigationStack {
Form {
if let user = appState.currentUser {
Section("Account") {
LabeledContent("Email", value: user.email)
}
}
Section("Lab Info") {
if viewModel.isEditing {
TextField("PI First Name", text: $viewModel.piFirstName)
TextField("Building Code", text: $viewModel.bldgCode)
TextField("Lab", text: $viewModel.lab)
TextField("Contact", text: $viewModel.contact)
} else {
LabeledContent("PI First Name", value: viewModel.piFirstName.isEmpty ? "" : viewModel.piFirstName)
LabeledContent("Building Code", value: viewModel.bldgCode.isEmpty ? "" : viewModel.bldgCode)
LabeledContent("Lab", value: viewModel.lab.isEmpty ? "" : viewModel.lab)
LabeledContent("Contact", value: viewModel.contact.isEmpty ? "" : viewModel.contact)
}
}
Section {
Button(role: .destructive) {
Task { await viewModel.signOut() }
} label: {
HStack {
Spacer()
Text("Sign Out")
Spacer()
}
}
}
}
.navigationTitle("Profile")
.toolbar {
ToolbarItem(placement: .topBarTrailing) {
if viewModel.isEditing {
Button("Save") {
Task { await viewModel.save() }
}
.disabled(viewModel.isSaving)
} else {
Button("Edit") {
viewModel.isEditing = true
}
}
}
if viewModel.isEditing {
ToolbarItem(placement: .topBarLeading) {
Button("Cancel") {
viewModel.isEditing = false
if let p = viewModel.profile {
viewModel.piFirstName = p.piFirstName
viewModel.bldgCode = p.bldgCode
viewModel.lab = p.lab
viewModel.contact = p.contact ?? ""
}
}
}
}
}
.alert("Error", isPresented: .constant(viewModel.errorMessage != nil)) {
Button("OK") { viewModel.errorMessage = nil }
} message: {
Text(viewModel.errorMessage ?? "")
}
}
.task {
await viewModel.load()
}
}
}