Update Kotlin models and iOS Swift to align with new Go API format
- Update all Kotlin API models to match Go API response structures - Fix Swift type aliases (TaskDetail→TaskResponse, Residence→ResidenceResponse, etc.) - Update TaskCompletionCreateRequest to simplified Go API format (taskId, notes, actualCost, photoUrl) - Fix optional handling for frequency, priority, category, status in task models - Replace isPrimaryOwner with ownerId comparison against current user - Update ResidenceUsersResponse to use owner.id instead of ownerId - Fix non-optional String fields to use isEmpty checks instead of optional binding - Add type aliases for backwards compatibility in Kotlin models 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -3,12 +3,13 @@ import PhotosUI
|
||||
import ComposeApp
|
||||
|
||||
struct CompleteTaskView: View {
|
||||
let task: TaskDetail
|
||||
let task: TaskResponse
|
||||
let onComplete: () -> Void
|
||||
|
||||
@Environment(\.dismiss) private var dismiss
|
||||
@StateObject private var taskViewModel = TaskViewModel()
|
||||
@StateObject private var contractorViewModel = ContractorViewModel()
|
||||
private let completionViewModel = ComposeApp.TaskCompletionViewModel()
|
||||
@State private var completedByName: String = ""
|
||||
@State private var actualCost: String = ""
|
||||
@State private var notes: String = ""
|
||||
@@ -32,7 +33,7 @@ struct CompleteTaskView: View {
|
||||
.font(.headline)
|
||||
|
||||
HStack {
|
||||
Label(task.category.name.capitalized, systemImage: "folder")
|
||||
Label((task.category?.name ?? "").capitalized, systemImage: "folder")
|
||||
.font(.subheadline)
|
||||
.foregroundStyle(.secondary)
|
||||
|
||||
@@ -303,66 +304,53 @@ struct CompleteTaskView: View {
|
||||
|
||||
isSubmitting = true
|
||||
|
||||
// Get current date in ISO format
|
||||
let dateFormatter = ISO8601DateFormatter()
|
||||
let currentDate = dateFormatter.string(from: Date())
|
||||
|
||||
// Create request
|
||||
// Create request with simplified Go API format
|
||||
// Note: completedAt defaults to now on server if not provided
|
||||
let request = TaskCompletionCreateRequest(
|
||||
task: task.id,
|
||||
completedByUser: nil,
|
||||
contractor: selectedContractor != nil ? KotlinInt(int: selectedContractor!.id) : nil,
|
||||
completedByName: completedByName.isEmpty ? nil : completedByName,
|
||||
completedByPhone: selectedContractor?.phone ?? "",
|
||||
completedByEmail: "",
|
||||
companyName: selectedContractor?.company ?? "",
|
||||
completionDate: currentDate,
|
||||
actualCost: actualCost.isEmpty ? nil : KotlinDouble(double: Double(actualCost) ?? 0.0),
|
||||
taskId: task.id,
|
||||
completedAt: nil,
|
||||
notes: notes.isEmpty ? nil : notes,
|
||||
rating: KotlinInt(int: Int32(rating))
|
||||
actualCost: actualCost.isEmpty ? nil : KotlinDouble(double: Double(actualCost) ?? 0.0),
|
||||
photoUrl: nil
|
||||
)
|
||||
|
||||
// Use TaskCompletionViewModel to create completion
|
||||
if !selectedImages.isEmpty {
|
||||
// Convert images to ImageData for Kotlin
|
||||
let imageDataList = selectedImages.compactMap { uiImage -> ComposeApp.ImageData? in
|
||||
guard let jpegData = uiImage.jpegData(compressionQuality: 0.8) else { return nil }
|
||||
let byteArray = KotlinByteArray(data: jpegData)
|
||||
return ComposeApp.ImageData(bytes: byteArray, fileName: "completion_image.jpg")
|
||||
}
|
||||
completionViewModel.createTaskCompletionWithImages(request: request, images: imageDataList)
|
||||
} else {
|
||||
completionViewModel.createTaskCompletion(request: request)
|
||||
}
|
||||
|
||||
// Observe the result
|
||||
Task {
|
||||
do {
|
||||
let result: ApiResult<TaskCompletion>
|
||||
|
||||
// If there are images, upload with images
|
||||
if !selectedImages.isEmpty {
|
||||
// Compress images to meet size requirements
|
||||
let imageDataArray = ImageCompression.compressImages(selectedImages)
|
||||
let imageByteArrays = imageDataArray.map { KotlinByteArray(data: $0) }
|
||||
let fileNames = (0..<imageDataArray.count).map { "image_\($0).jpg" }
|
||||
|
||||
result = try await APILayer.shared.createTaskCompletionWithImages(
|
||||
request: request,
|
||||
images: imageByteArrays,
|
||||
imageFileNames: fileNames
|
||||
)
|
||||
} else {
|
||||
// Upload without images
|
||||
result = try await APILayer.shared.createTaskCompletion(request: request)
|
||||
}
|
||||
|
||||
for await state in completionViewModel.createCompletionState {
|
||||
await MainActor.run {
|
||||
if result is ApiResultSuccess<TaskCompletion> {
|
||||
switch state {
|
||||
case is ApiResultSuccess<TaskCompletionResponse>:
|
||||
self.isSubmitting = false
|
||||
self.dismiss()
|
||||
self.onComplete()
|
||||
} else if let errorResult = result as? ApiResultError {
|
||||
self.errorMessage = errorResult.message
|
||||
self.showError = true
|
||||
self.isSubmitting = false
|
||||
} else {
|
||||
self.errorMessage = "Failed to complete task"
|
||||
case let error as ApiResultError:
|
||||
self.errorMessage = error.message
|
||||
self.showError = true
|
||||
self.isSubmitting = false
|
||||
case is ApiResultLoading:
|
||||
// Still loading, continue waiting
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
self.errorMessage = error.localizedDescription
|
||||
self.showError = true
|
||||
self.isSubmitting = false
|
||||
|
||||
// Break out of loop on terminal states
|
||||
if state is ApiResultSuccess<TaskCompletionResponse> || state is ApiResultError {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user