Refactor iOS forms and integrate notification API with APILayer
- Refactored ContractorFormSheet to follow SwiftUI best practices - Moved Field enum outside struct and renamed to ContractorFormField - Extracted body into computed properties for better readability - Replaced deprecated NavigationView with NavigationStack - Fixed input field contrast in light mode by adding borders - Fixed force cast in loadContractorSpecialties - Refactored TaskFormView to eliminate screen flickering - Moved Field enum outside struct and renamed to TaskFormField - Fixed conditional view structure that caused flicker on load - Used ZStack with overlay instead of if/else for loading state - Changed to .task modifier for proper async initialization - Made loadLookups properly async and fixed force casts - Replaced deprecated NavigationView with NavigationStack - Integrated PushNotificationManager with APILayer - Updated registerDeviceWithBackend to use APILayer.shared.registerDevice() - Updated updateNotificationPreferences to use APILayer - Updated getNotificationPreferences to use APILayer - Added proper error handling with try-catch pattern - Added notification operations to APILayer - Added NotificationApi instance - Implemented registerDevice, unregisterDevice - Implemented getNotificationPreferences, updateNotificationPreferences - Implemented getNotificationHistory, markNotificationAsRead - Implemented markAllNotificationsAsRead, getUnreadCount - All methods follow consistent pattern with auth token handling 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,12 +1,18 @@
|
||||
import SwiftUI
|
||||
import ComposeApp
|
||||
|
||||
// MARK: - Field Focus Enum
|
||||
enum TaskFormField {
|
||||
case title, description, intervalDays, estimatedCost
|
||||
}
|
||||
|
||||
// MARK: - Task Form View
|
||||
struct TaskFormView: View {
|
||||
let residenceId: Int32?
|
||||
let residences: [Residence]?
|
||||
@Binding var isPresented: Bool
|
||||
@StateObject private var viewModel = TaskViewModel()
|
||||
@FocusState private var focusedField: Field?
|
||||
@FocusState private var focusedField: TaskFormField?
|
||||
|
||||
private var needsResidenceSelection: Bool {
|
||||
residenceId == nil
|
||||
@@ -17,7 +23,7 @@ struct TaskFormView: View {
|
||||
@State private var taskFrequencies: [TaskFrequency] = []
|
||||
@State private var taskPriorities: [TaskPriority] = []
|
||||
@State private var taskStatuses: [TaskStatus] = []
|
||||
@State private var isLoadingLookups: Bool = false
|
||||
@State private var isLoadingLookups: Bool = true
|
||||
|
||||
// Form fields
|
||||
@State private var selectedResidence: Residence?
|
||||
@@ -35,19 +41,9 @@ struct TaskFormView: View {
|
||||
@State private var titleError: String = ""
|
||||
@State private var residenceError: String = ""
|
||||
|
||||
enum Field {
|
||||
case title, description, intervalDays, estimatedCost
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
if isLoadingLookups {
|
||||
VStack(spacing: 16) {
|
||||
ProgressView()
|
||||
Text("Loading...")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
} else {
|
||||
NavigationStack {
|
||||
ZStack {
|
||||
Form {
|
||||
// Residence Picker (only if needed)
|
||||
if needsResidenceSelection, let residences = residences {
|
||||
@@ -138,49 +134,68 @@ struct TaskFormView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Add Task")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button("Cancel") {
|
||||
isPresented = false
|
||||
}
|
||||
}
|
||||
.disabled(isLoadingLookups)
|
||||
.blur(radius: isLoadingLookups ? 3 : 0)
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Save") {
|
||||
submitForm()
|
||||
}
|
||||
.disabled(viewModel.isLoading)
|
||||
if isLoadingLookups {
|
||||
VStack(spacing: 16) {
|
||||
ProgressView()
|
||||
.scaleEffect(1.5)
|
||||
Text("Loading...")
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(Color(uiColor: .systemBackground).opacity(0.8))
|
||||
}
|
||||
.onAppear {
|
||||
loadLookups()
|
||||
}
|
||||
.onChange(of: viewModel.taskCreated) { created in
|
||||
if created {
|
||||
}
|
||||
.navigationTitle("Add Task")
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button("Cancel") {
|
||||
isPresented = false
|
||||
}
|
||||
.disabled(isLoadingLookups)
|
||||
}
|
||||
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button("Save") {
|
||||
submitForm()
|
||||
}
|
||||
.disabled(viewModel.isLoading || isLoadingLookups)
|
||||
}
|
||||
}
|
||||
.task {
|
||||
await loadLookups()
|
||||
}
|
||||
.onChange(of: viewModel.taskCreated) { created in
|
||||
if created {
|
||||
isPresented = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadLookups() {
|
||||
Task {
|
||||
isLoadingLookups = true
|
||||
|
||||
// Load all lookups from DataCache
|
||||
await MainActor.run {
|
||||
self.taskCategories = DataCache.shared.taskCategories.value as! [TaskCategory]
|
||||
self.taskFrequencies = DataCache.shared.taskFrequencies.value as! [TaskFrequency]
|
||||
self.taskPriorities = DataCache.shared.taskPriorities.value as! [TaskPriority]
|
||||
self.taskStatuses = DataCache.shared.taskStatuses.value as! [TaskStatus]
|
||||
self.isLoadingLookups = false
|
||||
}
|
||||
|
||||
setDefaults()
|
||||
private func loadLookups() async {
|
||||
// Load all lookups from DataCache
|
||||
if let categories = DataCache.shared.taskCategories.value as? [TaskCategory] {
|
||||
taskCategories = categories
|
||||
}
|
||||
|
||||
if let frequencies = DataCache.shared.taskFrequencies.value as? [TaskFrequency] {
|
||||
taskFrequencies = frequencies
|
||||
}
|
||||
|
||||
if let priorities = DataCache.shared.taskPriorities.value as? [TaskPriority] {
|
||||
taskPriorities = priorities
|
||||
}
|
||||
|
||||
if let statuses = DataCache.shared.taskStatuses.value as? [TaskStatus] {
|
||||
taskStatuses = statuses
|
||||
}
|
||||
|
||||
setDefaults()
|
||||
isLoadingLookups = false
|
||||
}
|
||||
|
||||
private func setDefaults() {
|
||||
|
||||
Reference in New Issue
Block a user