Migrate API endpoints from CFML to PHP
- Replace all .cfm endpoints with .php (PHP backend migration) - Update debug strings and test walkthrough documentation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
bbdc5a91c2
commit
8d9fa1caa6
6 changed files with 79 additions and 45 deletions
|
|
@ -9,6 +9,9 @@ struct OrderLineItem: Identifiable {
|
|||
let quantity: Int
|
||||
let remark: String
|
||||
let isModifier: Bool
|
||||
let isCheckedByDefault: Bool
|
||||
let isInvertedGroup: Bool
|
||||
let removedDefaults: [String] // Array of item names that were removed from inverted groups
|
||||
|
||||
var id: Int { lineItemId }
|
||||
|
||||
|
|
@ -26,5 +29,21 @@ struct OrderLineItem: Identifiable {
|
|||
if let b = json["IsModifier"] as? Bool { isModifier = b }
|
||||
else if let i = json["IsModifier"] as? Int { isModifier = i == 1 }
|
||||
else { isModifier = false }
|
||||
|
||||
// Inverted group fields
|
||||
if let b = json["IsCheckedByDefault"] as? Bool { isCheckedByDefault = b }
|
||||
else if let i = json["IsCheckedByDefault"] as? Int { isCheckedByDefault = i == 1 }
|
||||
else { isCheckedByDefault = false }
|
||||
|
||||
if let b = json["IsInvertedGroup"] as? Bool { isInvertedGroup = b }
|
||||
else if let i = json["IsInvertedGroup"] as? Int { isInvertedGroup = i == 1 }
|
||||
else { isInvertedGroup = false }
|
||||
|
||||
// Removed defaults array
|
||||
if let arr = json["RemovedDefaults"] as? [String] {
|
||||
removedDefaults = arr
|
||||
} else {
|
||||
removedDefaults = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import Foundation
|
||||
|
||||
// MARK: - Tier Status (from tierStatus.cfm)
|
||||
// MARK: - Tier Status (from tierStatus.php)
|
||||
|
||||
struct TierStatus: Codable {
|
||||
var tier: Int
|
||||
|
|
@ -98,7 +98,7 @@ struct TierStatus: Codable {
|
|||
}
|
||||
}
|
||||
|
||||
// MARK: - Ledger Response (from ledger.cfm)
|
||||
// MARK: - Ledger Response (from ledger.php)
|
||||
|
||||
struct LedgerResponse: Codable {
|
||||
var entries: [LedgerEntry]
|
||||
|
|
|
|||
|
|
@ -253,7 +253,7 @@ actor APIService {
|
|||
// MARK: - Auth
|
||||
|
||||
func login(username: String, password: String) async throws -> LoginResponse {
|
||||
let json = try await postJSON("/auth/login.cfm", payload: [
|
||||
let json = try await postJSON("/auth/login.php", payload: [
|
||||
"username": username,
|
||||
"password": password
|
||||
])
|
||||
|
|
@ -293,7 +293,7 @@ actor APIService {
|
|||
// MARK: - OTP Login
|
||||
|
||||
func sendLoginOtp(phone: String) async throws -> SendOtpResponse {
|
||||
let json = try await postJSON("/auth/loginOTP.cfm", payload: [
|
||||
let json = try await postJSON("/auth/loginOTP.php", payload: [
|
||||
"phone": phone
|
||||
])
|
||||
|
||||
|
|
@ -309,7 +309,7 @@ actor APIService {
|
|||
}
|
||||
|
||||
func verifyLoginOtp(uuid: String, otp: String) async throws -> LoginResponse {
|
||||
let json = try await postJSON("/auth/verifyLoginOTP.cfm", payload: [
|
||||
let json = try await postJSON("/auth/verifyLoginOTP.php", payload: [
|
||||
"uuid": uuid,
|
||||
"otp": otp
|
||||
])
|
||||
|
|
@ -363,7 +363,7 @@ actor APIService {
|
|||
throw APIError.serverError("User not logged in")
|
||||
}
|
||||
|
||||
let json = try await postJSON("/auth/profile.cfm", payload: [
|
||||
let json = try await postJSON("/auth/profile.php", payload: [
|
||||
"UserID": uid
|
||||
])
|
||||
|
||||
|
|
@ -393,7 +393,7 @@ actor APIService {
|
|||
if let em = email { payload["Email"] = em }
|
||||
if let ph = phone { payload["Phone"] = ph }
|
||||
|
||||
let json = try await postJSON("/auth/profile.cfm", payload: payload)
|
||||
let json = try await postJSON("/auth/profile.php", payload: payload)
|
||||
|
||||
guard ok(json) else {
|
||||
throw APIError.serverError("Failed to update profile: \(err(json))")
|
||||
|
|
@ -407,7 +407,7 @@ actor APIService {
|
|||
throw APIError.serverError("User not logged in")
|
||||
}
|
||||
|
||||
let json = try await postJSON("/workers/myBusinesses.cfm", payload: [
|
||||
let json = try await postJSON("/workers/myBusinesses.php", payload: [
|
||||
"UserID": uid
|
||||
])
|
||||
|
||||
|
|
@ -429,7 +429,7 @@ actor APIService {
|
|||
payload["CategoryID"] = cid
|
||||
}
|
||||
|
||||
let json = try await postJSON("/tasks/listPending.cfm", payload: payload)
|
||||
let json = try await postJSON("/tasks/listPending.php", payload: payload)
|
||||
guard ok(json) else {
|
||||
throw APIError.serverError("Failed to load tasks: \(err(json))")
|
||||
}
|
||||
|
|
@ -440,7 +440,7 @@ actor APIService {
|
|||
|
||||
func acceptTask(taskId: Int) async throws {
|
||||
// Flutter only sends TaskID + BusinessID (no UserID)
|
||||
let json = try await postJSON("/tasks/accept.cfm", payload: [
|
||||
let json = try await postJSON("/tasks/accept.php", payload: [
|
||||
"TaskID": taskId,
|
||||
"BusinessID": businessId
|
||||
])
|
||||
|
|
@ -463,7 +463,7 @@ actor APIService {
|
|||
payload["BusinessID"] = businessId
|
||||
}
|
||||
|
||||
let json = try await postJSON("/tasks/listMine.cfm", payload: payload)
|
||||
let json = try await postJSON("/tasks/listMine.php", payload: payload)
|
||||
guard ok(json) else {
|
||||
throw APIError.serverError("Failed to load my tasks: \(err(json))")
|
||||
}
|
||||
|
|
@ -483,7 +483,7 @@ actor APIService {
|
|||
if cancelOrder {
|
||||
payload["CancelOrder"] = true
|
||||
}
|
||||
let json = try await postJSON("/tasks/complete.cfm", payload: payload)
|
||||
let json = try await postJSON("/tasks/complete.php", payload: payload)
|
||||
guard ok(json) else {
|
||||
throw APIError.serverError("Failed to complete task: \(err(json))")
|
||||
}
|
||||
|
|
@ -493,14 +493,14 @@ actor APIService {
|
|||
let payload: [String: Any] = [
|
||||
"TaskID": taskId
|
||||
]
|
||||
let json = try await postJSON("/tasks/completeChat.cfm", payload: payload)
|
||||
let json = try await postJSON("/tasks/completeChat.php", payload: payload)
|
||||
guard ok(json) else {
|
||||
throw APIError.serverError("Close chat failed: \(err(json))")
|
||||
}
|
||||
}
|
||||
|
||||
func getTaskDetails(taskId: Int) async throws -> TaskDetails {
|
||||
let json = try await postJSON("/tasks/getDetails.cfm", payload: [
|
||||
let json = try await postJSON("/tasks/getDetails.php", payload: [
|
||||
"TaskID": taskId
|
||||
])
|
||||
guard ok(json) else {
|
||||
|
|
@ -553,7 +553,7 @@ actor APIService {
|
|||
payload["AfterMessageID"] = after
|
||||
}
|
||||
|
||||
let json = try await postJSON("/chat/getMessages.cfm", payload: payload)
|
||||
let json = try await postJSON("/chat/getMessages.php", payload: payload)
|
||||
guard ok(json) else {
|
||||
throw APIError.serverError("Failed to load chat messages")
|
||||
}
|
||||
|
|
@ -573,7 +573,7 @@ actor APIService {
|
|||
if let uid = userId { payload["UserID"] = uid }
|
||||
if let st = senderType { payload["SenderType"] = st }
|
||||
|
||||
let json = try await postJSON("/chat/sendMessage.cfm", payload: payload)
|
||||
let json = try await postJSON("/chat/sendMessage.php", payload: payload)
|
||||
guard ok(json) else {
|
||||
throw APIError.serverError("Failed to send message")
|
||||
}
|
||||
|
|
@ -582,7 +582,7 @@ actor APIService {
|
|||
}
|
||||
|
||||
func markChatMessagesRead(taskId: Int, readerType: String) async throws {
|
||||
let json = try await postJSON("/chat/markRead.cfm", payload: [
|
||||
let json = try await postJSON("/chat/markRead.php", payload: [
|
||||
"TaskID": taskId,
|
||||
"ReaderType": readerType
|
||||
])
|
||||
|
|
@ -594,7 +594,7 @@ actor APIService {
|
|||
// MARK: - Payout / Tier Endpoints
|
||||
|
||||
func getTierStatus() async throws -> TierStatus {
|
||||
let json = try await postJSON("/workers/tierStatus.cfm", payload: [
|
||||
let json = try await postJSON("/workers/tierStatus.php", payload: [
|
||||
"UserID": userId ?? 0
|
||||
])
|
||||
guard ok(json) else {
|
||||
|
|
@ -605,7 +605,7 @@ actor APIService {
|
|||
}
|
||||
|
||||
func createStripeAccount() async throws -> String {
|
||||
let json = try await postJSON("/workers/createAccount.cfm", payload: [
|
||||
let json = try await postJSON("/workers/createAccount.php", payload: [
|
||||
"UserID": userId ?? 0
|
||||
])
|
||||
guard ok(json) else {
|
||||
|
|
@ -615,7 +615,7 @@ actor APIService {
|
|||
}
|
||||
|
||||
func getOnboardingLink() async throws -> String {
|
||||
let json = try await postJSON("/workers/onboardingLink.cfm", payload: [
|
||||
let json = try await postJSON("/workers/onboardingLink.php", payload: [
|
||||
"UserID": userId ?? 0
|
||||
])
|
||||
guard ok(json) else {
|
||||
|
|
@ -625,7 +625,7 @@ actor APIService {
|
|||
}
|
||||
|
||||
func getEarlyUnlockUrl() async throws -> String {
|
||||
let json = try await postJSON("/workers/earlyUnlock.cfm", payload: [
|
||||
let json = try await postJSON("/workers/earlyUnlock.php", payload: [
|
||||
"UserID": userId ?? 0
|
||||
])
|
||||
guard ok(json) else {
|
||||
|
|
@ -635,7 +635,7 @@ actor APIService {
|
|||
}
|
||||
|
||||
func getLedger() async throws -> LedgerResponse {
|
||||
let json = try await postJSON("/workers/ledger.cfm", payload: [
|
||||
let json = try await postJSON("/workers/ledger.php", payload: [
|
||||
"UserID": userId ?? 0
|
||||
])
|
||||
guard ok(json) else {
|
||||
|
|
@ -648,7 +648,7 @@ actor APIService {
|
|||
// MARK: - Avatar
|
||||
|
||||
func getAvatarUrl() async throws -> String? {
|
||||
let json = try await getJSON("/auth/avatar.cfm")
|
||||
let json = try await getJSON("/auth/avatar.php")
|
||||
print("[Avatar] Response: \(json)")
|
||||
guard ok(json) else {
|
||||
print("[Avatar] Response not OK")
|
||||
|
|
@ -681,7 +681,7 @@ actor APIService {
|
|||
/// Get avatar URL for any user by their userId
|
||||
func getUserAvatarUrl(userId: Int) async throws -> String? {
|
||||
// Try the avatar endpoint with UserID
|
||||
let json = try await postJSON("/auth/avatar.cfm", payload: ["UserID": userId])
|
||||
let json = try await postJSON("/auth/avatar.php", payload: ["UserID": userId])
|
||||
print("[Avatar] getUserAvatarUrl(\(userId)) Response: \(json)")
|
||||
|
||||
let data = json["DATA"] as? [String: Any] ?? json
|
||||
|
|
@ -712,7 +712,7 @@ actor APIService {
|
|||
// MARK: - Beacon Sharding
|
||||
|
||||
func resolveServicePoint(uuid: String, major: Int, minor: Int) async throws -> Int {
|
||||
let json = try await postJSON("/beacon-sharding/resolve_servicepoint.cfm", payload: [
|
||||
let json = try await postJSON("/beacon-sharding/resolve_servicepoint.php", payload: [
|
||||
"UUID": uuid,
|
||||
"Major": major,
|
||||
"Minor": minor
|
||||
|
|
@ -732,7 +732,7 @@ actor APIService {
|
|||
}
|
||||
|
||||
func resolveBusiness(uuid: String, major: Int) async throws -> (businessId: Int, businessName: String) {
|
||||
let json = try await postJSON("/beacon-sharding/resolve_business.cfm", payload: [
|
||||
let json = try await postJSON("/beacon-sharding/resolve_business.php", payload: [
|
||||
"UUID": uuid,
|
||||
"Major": major
|
||||
])
|
||||
|
|
@ -774,7 +774,7 @@ actor APIService {
|
|||
// MARK: - About Info
|
||||
|
||||
func getAboutInfo() async throws -> AboutInfo {
|
||||
let json = try await getJSON("app/about.cfm")
|
||||
let json = try await getJSON("app/about.php")
|
||||
|
||||
guard ok(json) else {
|
||||
throw APIError.serverError(err(json))
|
||||
|
|
|
|||
|
|
@ -309,15 +309,15 @@ struct BusinessSelectionScreen: View {
|
|||
var text = "=== RAW API RESPONSES ===\n\n"
|
||||
text += "UserID: \(uid), BusinessID: \(bid)\n\n"
|
||||
|
||||
text += "--- /workers/myBusinesses.cfm ---\n"
|
||||
text += "--- /workers/myBusinesses.php ---\n"
|
||||
let bizRaw = await APIService.shared.debugRawJSON(
|
||||
"/workers/myBusinesses.cfm", payload: ["UserID": uid])
|
||||
"/workers/myBusinesses.php", payload: ["UserID": uid])
|
||||
text += bizRaw + "\n\n"
|
||||
|
||||
if bid > 0 {
|
||||
text += "--- /tasks/listPending.cfm ---\n"
|
||||
text += "--- /tasks/listPending.php ---\n"
|
||||
let taskRaw = await APIService.shared.debugRawJSON(
|
||||
"/tasks/listPending.cfm", payload: ["BusinessID": bid])
|
||||
"/tasks/listPending.php", payload: ["BusinessID": bid])
|
||||
text += taskRaw + "\n\n"
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -518,14 +518,29 @@ struct TaskDetailScreen: View {
|
|||
}
|
||||
|
||||
ForEach(modifiers) { mod in
|
||||
// For inverted groups: show removed defaults as "NO {name}"
|
||||
if mod.isInvertedGroup && !mod.removedDefaults.isEmpty {
|
||||
ForEach(mod.removedDefaults, id: \.self) { removedName in
|
||||
Text("- NO \(removedName)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.red)
|
||||
}
|
||||
}
|
||||
|
||||
let children = d.lineItems.filter { $0.parentLineItemId == mod.lineItemId }
|
||||
if !children.isEmpty {
|
||||
ForEach(children) { child in
|
||||
Text("- \(mod.itemName): \(child.itemName)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
// Skip default items in inverted groups (they're unchanged)
|
||||
if mod.isInvertedGroup && child.isCheckedByDefault {
|
||||
// Don't display - this is an unchanged default
|
||||
} else {
|
||||
Text("- \(mod.itemName): \(child.itemName)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
} else if !mod.isInvertedGroup {
|
||||
// Regular modifier (not inverted group)
|
||||
Text("- \(mod.itemName)")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
|
|
|
|||
|
|
@ -461,7 +461,7 @@ const sections = [
|
|||
preconditions: 'Logged in, user has businesses.',
|
||||
action: 'After login, observe Business Selection screen.',
|
||||
expected: 'Nav title "Select Business". List of businesses with: initial letter in colored square, business name, address/city, status badge (Pending/Active/Inactive), task count or green checkmark. Toolbar: refresh button and ellipsis menu.',
|
||||
onFail: 'Screenshot. Check API response from /workers/myBusinesses.cfm.'
|
||||
onFail: 'Screenshot. Check API response from /workers/myBusinesses.php.'
|
||||
},
|
||||
{
|
||||
id: 'BS02',
|
||||
|
|
@ -532,7 +532,7 @@ const sections = [
|
|||
title: 'Toolbar menu — Debug API',
|
||||
preconditions: 'Business Selection screen.',
|
||||
action: 'Tap ellipsis menu → "Debug API".',
|
||||
expected: 'Modal sheet appears showing raw JSON from /workers/myBusinesses.cfm and /tasks/listPending.cfm. Monospaced text, "Done" button to dismiss.',
|
||||
expected: 'Modal sheet appears showing raw JSON from /workers/myBusinesses.php and /tasks/listPending.php. Monospaced text, "Done" button to dismiss.',
|
||||
onFail: 'Screenshot.'
|
||||
},
|
||||
{
|
||||
|
|
@ -570,7 +570,7 @@ const sections = [
|
|||
preconditions: 'Selected a business with pending tasks.',
|
||||
action: 'Observe the task list screen.',
|
||||
expected: 'Nav title = business name. List of task cards with: color bar, category icon (chat = bubble icon), title, location (mappin or bicycle icon), category badge, chat badge if applicable, time ago label.',
|
||||
onFail: 'Screenshot. Check /tasks/listPending.cfm response.'
|
||||
onFail: 'Screenshot. Check /tasks/listPending.php response.'
|
||||
},
|
||||
{
|
||||
id: 'TL02',
|
||||
|
|
@ -655,7 +655,7 @@ const sections = [
|
|||
preconditions: 'Tapped a task from task list.',
|
||||
action: 'Observe the task detail screen.',
|
||||
expected: 'Loading spinner, then content: customer section (avatar/name/phone), location section (table or delivery), nav bar colored with category color. "Accept Task" button at bottom.',
|
||||
onFail: 'Screenshot. Check /tasks/getDetails.cfm response.'
|
||||
onFail: 'Screenshot. Check /tasks/getDetails.php response.'
|
||||
},
|
||||
{
|
||||
id: 'TD02',
|
||||
|
|
@ -750,7 +750,7 @@ const sections = [
|
|||
title: 'Accept Task — confirm',
|
||||
preconditions: 'Accept alert shown.',
|
||||
action: 'Tap "Accept".',
|
||||
expected: 'API call to /tasks/accept.cfm. On success, screen dismisses back to task list. Task removed from pending list.',
|
||||
expected: 'API call to /tasks/accept.php. On success, screen dismisses back to task list. Task removed from pending list.',
|
||||
onFail: 'Screenshot. Check API response.'
|
||||
},
|
||||
{
|
||||
|
|
@ -782,7 +782,7 @@ const sections = [
|
|||
title: 'Complete Task — confirm',
|
||||
preconditions: 'Complete alert shown.',
|
||||
action: 'Tap "Complete".',
|
||||
expected: 'API call to /tasks/complete.cfm. On success, dismisses back to My Tasks. Task moves to Completed filter.',
|
||||
expected: 'API call to /tasks/complete.php. On success, dismisses back to My Tasks. Task moves to Completed filter.',
|
||||
onFail: 'Screenshot.'
|
||||
},
|
||||
{
|
||||
|
|
@ -844,7 +844,7 @@ const sections = [
|
|||
preconditions: 'Worker has accepted tasks.',
|
||||
action: 'Select Active tab.',
|
||||
expected: 'Shows tasks with status Accepted/In Progress. Each card shows: color bar, title, location, category badge, time ago, status badge.',
|
||||
onFail: 'Screenshot. Verify /tasks/listMine.cfm?FilterType=active response.'
|
||||
onFail: 'Screenshot. Verify /tasks/listMine.php?FilterType=active response.'
|
||||
},
|
||||
{
|
||||
id: 'MT03',
|
||||
|
|
@ -1008,7 +1008,7 @@ const sections = [
|
|||
title: 'HTTP fallback when WebSocket disconnected',
|
||||
preconditions: 'WebSocket not connected.',
|
||||
action: 'Send a message.',
|
||||
expected: 'Message sent via HTTP POST to /chat/sendMessage.cfm. Messages poll via 3-second timer.',
|
||||
expected: 'Message sent via HTTP POST to /chat/sendMessage.php. Messages poll via 3-second timer.',
|
||||
onFail: 'Note if message fails or gets lost.'
|
||||
},
|
||||
{
|
||||
|
|
@ -1030,7 +1030,7 @@ const sections = [
|
|||
preconditions: 'Navigated to Account from menu.',
|
||||
action: 'Observe Account screen.',
|
||||
expected: 'Nav title "Account". Loading spinner, then: Payout Status card, Activation card, Earnings card, Logout button. FAB in bottom-right.',
|
||||
onFail: 'Screenshot. Check /workers/tierStatus.cfm response.'
|
||||
onFail: 'Screenshot. Check /workers/tierStatus.php response.'
|
||||
},
|
||||
{
|
||||
id: 'AC02',
|
||||
|
|
@ -1085,7 +1085,7 @@ const sections = [
|
|||
title: 'Early unlock button',
|
||||
preconditions: 'Activation not complete.',
|
||||
action: 'Tap "Complete activation now (optional)".',
|
||||
expected: 'API call to /workers/earlyUnlock.cfm. Opens URL in Safari. Reloads after return.',
|
||||
expected: 'API call to /workers/earlyUnlock.php. Opens URL in Safari. Reloads after return.',
|
||||
onFail: 'Screenshot.'
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue