05/04 Update the app functionalities
This commit is contained in:
@@ -1,7 +1,12 @@
|
||||
// Views/Inspection/FlagIssueView.swift
|
||||
// -------------------------------------
|
||||
// Views/Dashboard/FlagIssueView.swift
|
||||
// ------------------------------------
|
||||
// Sheet for flagging an issue during an inspection.
|
||||
// Saves locally immediately; syncs to server when online.
|
||||
//
|
||||
// CHANGED: Area picker removed. Facility is derived directly from the
|
||||
// inspection (inspection.facilityServerId) and displayed as read-only info,
|
||||
// matching the web app's flag_issue.html behaviour where facility_id is
|
||||
// a hidden field populated from the inspection context.
|
||||
|
||||
import SwiftUI
|
||||
import SwiftData
|
||||
@@ -14,42 +19,55 @@ struct FlagIssueView: View {
|
||||
|
||||
let inspection: LocalInspection
|
||||
|
||||
@State private var selectedAreaId: Int?
|
||||
@State private var severity = "medium"
|
||||
@State private var description = ""
|
||||
@State private var severity = "medium"
|
||||
@State private var description = ""
|
||||
@State private var selectedImage: UIImage?
|
||||
@State private var photoLocalPath: String?
|
||||
@State private var showImagePicker = false
|
||||
@State private var showChoice = false
|
||||
@State private var showCamera = false
|
||||
@State private var showLibrary = false
|
||||
|
||||
private var cameraAvailable: Bool {
|
||||
UIImagePickerController.isSourceTypeAvailable(.camera)
|
||||
}
|
||||
|
||||
private let severities = ["low", "medium", "high", "critical"]
|
||||
|
||||
private var areas: [LocalArea] {
|
||||
let facilityId = inspection.facilityServerId
|
||||
let results = try? context.fetch(
|
||||
FetchDescriptor<LocalArea>(
|
||||
predicate: #Predicate { $0.facilityServerId == facilityId },
|
||||
sortBy: [SortDescriptor(\.name)]
|
||||
private var facility: LocalFacility? {
|
||||
let id = inspection.facilityServerId
|
||||
return try? context.fetch(
|
||||
FetchDescriptor<LocalFacility>(
|
||||
predicate: #Predicate { $0.serverId == id }
|
||||
)
|
||||
)
|
||||
return results ?? []
|
||||
).first
|
||||
}
|
||||
|
||||
private var canSubmit: Bool {
|
||||
selectedAreaId != nil && !description.trimmingCharacters(in: .whitespaces).isEmpty
|
||||
!description.trimmingCharacters(in: .whitespaces).isEmpty
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationStack {
|
||||
Form {
|
||||
// ── Area ───────────────────────────────────────────────────
|
||||
Section("Area") {
|
||||
Picker("Area", selection: $selectedAreaId) {
|
||||
Text("Select area…").tag(Optional<Int>(nil))
|
||||
ForEach(areas) { area in
|
||||
Text(area.name).tag(Optional(area.serverId))
|
||||
// ── Facility (read-only) — matches web alert banner ────────
|
||||
Section {
|
||||
HStack(spacing: 10) {
|
||||
Image(systemName: "building.2")
|
||||
.foregroundStyle(.secondary)
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text("Facility")
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
Text(facility?.name ?? "—")
|
||||
.font(.body)
|
||||
}
|
||||
}
|
||||
.pickerStyle(.navigationLink)
|
||||
.padding(.vertical, 2)
|
||||
} header: {
|
||||
Text("Inspection Context")
|
||||
} footer: {
|
||||
Text("Issue will be logged against this facility.")
|
||||
.font(.caption)
|
||||
}
|
||||
|
||||
// ── Severity ───────────────────────────────────────────────
|
||||
@@ -78,7 +96,7 @@ struct FlagIssueView: View {
|
||||
.clipShape(RoundedRectangle(cornerRadius: 8))
|
||||
}
|
||||
Button {
|
||||
showImagePicker = true
|
||||
if cameraAvailable { showChoice = true } else { showLibrary = true }
|
||||
} label: {
|
||||
Label(selectedImage == nil ? "Attach Photo" : "Replace Photo",
|
||||
systemImage: "camera")
|
||||
@@ -107,10 +125,17 @@ struct FlagIssueView: View {
|
||||
.fontWeight(.semibold)
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showImagePicker) {
|
||||
ImagePickerView(image: $selectedImage) { img in
|
||||
savePhoto(img)
|
||||
}
|
||||
.confirmationDialog("Add Photo", isPresented: $showChoice, titleVisibility: .visible) {
|
||||
Button("Take Photo") { showCamera = true }
|
||||
Button("Photo Library") { showLibrary = true }
|
||||
Button("Cancel", role: .cancel) {}
|
||||
}
|
||||
.fullScreenCover(isPresented: $showCamera) {
|
||||
CameraPickerView(image: $selectedImage, onSelected: savePhoto)
|
||||
.ignoresSafeArea()
|
||||
}
|
||||
.sheet(isPresented: $showLibrary) {
|
||||
LibraryPickerView(image: $selectedImage, onSelected: savePhoto)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -129,11 +154,9 @@ struct FlagIssueView: View {
|
||||
}
|
||||
|
||||
private func submitIssue() {
|
||||
guard let areaId = selectedAreaId else { return }
|
||||
|
||||
let issue = LocalIssue(
|
||||
inspectionLocalId: inspection.localId,
|
||||
areaServerId: areaId,
|
||||
facilityServerId: inspection.facilityServerId,
|
||||
severity: severity,
|
||||
description: description.trimmingCharacters(in: .whitespaces)
|
||||
)
|
||||
@@ -142,7 +165,6 @@ struct FlagIssueView: View {
|
||||
inspection.localIssues.append(issue)
|
||||
context.insert(issue)
|
||||
|
||||
// Create PendingPhoto if a photo was attached
|
||||
if let path = photoLocalPath {
|
||||
let photo = PendingPhoto(
|
||||
localFilePath: path,
|
||||
|
||||
Reference in New Issue
Block a user