196 lines
7.3 KiB
Swift
196 lines
7.3 KiB
Swift
import SwiftUI
|
|
|
|
struct BusinessSelectionScreen: View {
|
|
@EnvironmentObject var appState: AppState
|
|
@State private var businesses: [Employment] = []
|
|
@State private var isLoading = true
|
|
@State private var error: String?
|
|
|
|
var body: some View {
|
|
NavigationStack {
|
|
Group {
|
|
if isLoading {
|
|
ProgressView("Loading businesses...")
|
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
|
} else if let error = error {
|
|
VStack(spacing: 16) {
|
|
Image(systemName: "exclamationmark.triangle")
|
|
.font(.largeTitle)
|
|
.foregroundColor(.orange)
|
|
Text(error)
|
|
.foregroundColor(.secondary)
|
|
.multilineTextAlignment(.center)
|
|
Button("Retry") { loadBusinesses() }
|
|
.buttonStyle(.borderedProminent)
|
|
.tint(.payfritGreen)
|
|
}
|
|
.padding()
|
|
} else if businesses.isEmpty {
|
|
VStack(spacing: 16) {
|
|
Image(systemName: "building.2")
|
|
.font(.largeTitle)
|
|
.foregroundColor(.secondary)
|
|
Text("No businesses found")
|
|
.foregroundColor(.secondary)
|
|
}
|
|
} else {
|
|
ScrollView {
|
|
LazyVStack(spacing: 12) {
|
|
ForEach(businesses) { biz in
|
|
NavigationLink(value: biz) {
|
|
VStack(spacing: 0) {
|
|
BusinessHeaderImage(businessId: biz.businessId)
|
|
|
|
HStack {
|
|
VStack(alignment: .leading, spacing: 2) {
|
|
Text(biz.businessName)
|
|
.font(.subheadline.weight(.semibold))
|
|
.foregroundColor(.primary)
|
|
if !biz.businessCity.isEmpty {
|
|
Text(biz.businessCity)
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
}
|
|
Spacer()
|
|
Image(systemName: "chevron.right")
|
|
.font(.caption)
|
|
.foregroundColor(.secondary)
|
|
}
|
|
.padding(.horizontal, 12)
|
|
.padding(.vertical, 8)
|
|
}
|
|
.background(Color(.systemBackground))
|
|
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
|
|
.overlay(
|
|
RoundedRectangle(cornerRadius: 12, style: .continuous)
|
|
.stroke(Color(.systemGray4), lineWidth: 0.5)
|
|
)
|
|
.shadow(color: .black.opacity(0.06), radius: 4, x: 0, y: 2)
|
|
}
|
|
.buttonStyle(.plain)
|
|
}
|
|
}
|
|
.padding(.horizontal, 16)
|
|
.padding(.top, 8)
|
|
.padding(.bottom, 20)
|
|
}
|
|
}
|
|
}
|
|
.navigationTitle("Select Business")
|
|
.navigationDestination(for: Employment.self) { biz in
|
|
BeaconDashboard(business: biz)
|
|
}
|
|
.toolbar {
|
|
ToolbarItem(placement: .navigationBarTrailing) {
|
|
Button {
|
|
logout()
|
|
} label: {
|
|
Image(systemName: "rectangle.portrait.and.arrow.right")
|
|
}
|
|
}
|
|
}
|
|
}
|
|
.task { loadBusinesses() }
|
|
}
|
|
|
|
private func loadBusinesses() {
|
|
isLoading = true
|
|
error = nil
|
|
Task {
|
|
do {
|
|
businesses = try await APIService.shared.getMyBusinesses()
|
|
isLoading = false
|
|
} catch let apiError as APIError where apiError == .unauthorized {
|
|
await appState.handleUnauthorized()
|
|
} catch {
|
|
self.error = error.localizedDescription
|
|
isLoading = false
|
|
}
|
|
}
|
|
}
|
|
|
|
private func logout() {
|
|
Task {
|
|
await AuthStorage.shared.clearAuth()
|
|
await APIService.shared.logout()
|
|
appState.clearAuth()
|
|
}
|
|
}
|
|
}
|
|
|
|
// Make Employment Hashable for NavigationLink
|
|
extension Employment: Hashable {
|
|
static func == (lhs: Employment, rhs: Employment) -> Bool {
|
|
lhs.employeeId == rhs.employeeId && lhs.businessId == rhs.businessId
|
|
}
|
|
func hash(into hasher: inout Hasher) {
|
|
hasher.combine(employeeId)
|
|
hasher.combine(businessId)
|
|
}
|
|
}
|
|
|
|
// MARK: - Business Header Image
|
|
|
|
struct BusinessHeaderImage: View {
|
|
let businessId: Int
|
|
|
|
@State private var loadedImage: UIImage?
|
|
@State private var isLoading = true
|
|
|
|
private var imageURLs: [URL] {
|
|
[
|
|
"https://dev.payfrit.com/uploads/headers/\(businessId).png",
|
|
"https://dev.payfrit.com/uploads/headers/\(businessId).jpg",
|
|
].compactMap { URL(string: $0) }
|
|
}
|
|
|
|
var body: some View {
|
|
ZStack {
|
|
Color(.systemGray6)
|
|
|
|
if let image = loadedImage {
|
|
Image(uiImage: image)
|
|
.resizable()
|
|
.aspectRatio(contentMode: .fit)
|
|
.frame(maxWidth: .infinity)
|
|
} else if isLoading {
|
|
ProgressView()
|
|
.tint(.payfritGreen)
|
|
.frame(maxWidth: .infinity)
|
|
.frame(height: 100)
|
|
} else {
|
|
Image(systemName: "building.2")
|
|
.font(.system(size: 30))
|
|
.foregroundColor(.secondary)
|
|
.frame(maxWidth: .infinity)
|
|
.frame(height: 100)
|
|
}
|
|
}
|
|
.task {
|
|
await loadImage()
|
|
}
|
|
}
|
|
|
|
private func loadImage() async {
|
|
for url in imageURLs {
|
|
do {
|
|
let (data, response) = try await URLSession.shared.data(from: url)
|
|
if let httpResponse = response as? HTTPURLResponse,
|
|
httpResponse.statusCode == 200,
|
|
let image = UIImage(data: data) {
|
|
await MainActor.run {
|
|
loadedImage = image
|
|
isLoading = false
|
|
}
|
|
return
|
|
}
|
|
} catch {
|
|
continue
|
|
}
|
|
}
|
|
await MainActor.run {
|
|
isLoading = false
|
|
}
|
|
}
|
|
}
|