From bbcd1eb4e16e24688e4d83fd1fc4d9f7cea13d21 Mon Sep 17 00:00:00 2001 From: Adam Moussa <166072409+amoussa1229@users.noreply.github.com> Date: Mon, 20 Apr 2026 13:49:58 -0400 Subject: [PATCH] Wire Follow Ups list and form to backend Replaces the mock api with real endpoints, fixes list column mappings to match the backend payload, and realigns the form to the FollowUps model (Reason, ScheduleDate, ScheduleStartTime, EmployeeId, AccountId, LocationId, WorkOrderId) with real lookup dropdowns for Employee, Account, and Location. Also redesigns the list header to match the Work Orders toolbar + chip filter look. --- src/pages/followup/api.js | 74 +---- src/pages/followup/form/FollowUpFormPage.js | 324 ++++++++++++-------- src/pages/followup/list/List.js | 184 +++++------ src/pages/followup/list/Table.js | 73 ++++- src/pages/followup/list/api.js | 35 +-- src/pages/followup/list/styles.css | 122 ++++++++ 6 files changed, 485 insertions(+), 327 deletions(-) diff --git a/src/pages/followup/api.js b/src/pages/followup/api.js index c9cb47c6..5bc7c488 100644 --- a/src/pages/followup/api.js +++ b/src/pages/followup/api.js @@ -1,64 +1,20 @@ -// Mock follow-ups data -const mockFollowUps = [ - { - id: 1, - workOrder: "WO-001", - reason: "Quarterly maintenance check", - scheduledDate: "2024-01-22", - status: "Pending", - createdBy: "John Anderson", - createdDate: "2024-01-15", - notes: "Check all HVAC filters and oil levels", - }, - { - id: 2, - workOrder: "WO-002", - reason: "Verify electrical repairs", - scheduledDate: "2024-01-25", - status: "Pending", - createdBy: "Sarah Martinez", - createdDate: "2024-01-16", - notes: "Test all outlets to ensure proper operation", - }, - { - id: 3, - workOrder: "WO-003", - reason: "Post-repair inspection", - scheduledDate: "2024-01-20", - status: "Completed", - createdBy: "Mike Johnson", - createdDate: "2024-01-10", - notes: "Plumbing inspection completed successfully", - }, -]; +import { get, post } from "../../apiUtil"; +import { API_URL } from "../../constants"; -export const getFollowUps = async () => { - await new Promise(resolve => setTimeout(resolve, 300)); - return [...mockFollowUps]; -}; +export const getFollowUpById = id => + get(`${API_URL}/FollowUp/GetById?id=${id}`); -export const getFollowUpById = async (id) => { - await new Promise(resolve => setTimeout(resolve, 200)); - return mockFollowUps.find(f => f.id === parseInt(id)); -}; +export const addFollowUp = payload => + post(`${API_URL}/FollowUp/Create`, payload); -export const addFollowUp = async (followUp) => { - await new Promise(resolve => setTimeout(resolve, 500)); - const newFollowUp = { id: mockFollowUps.length + 1, ...followUp }; - mockFollowUps.push(newFollowUp); - return newFollowUp; -}; +export const editFollowUp = payload => + post(`${API_URL}/FollowUp/Update`, payload); -export const editFollowUp = async (followUp) => { - await new Promise(resolve => setTimeout(resolve, 500)); - const idx = mockFollowUps.findIndex(f => f.id === followUp.id); - if (idx >= 0) mockFollowUps[idx] = followUp; - return followUp; -}; +export const deleteFollowUp = id => + post(`${API_URL}/FollowUp/Delete?id=${id}`); -export const deleteFollowUp = async (id) => { - await new Promise(resolve => setTimeout(resolve, 500)); - const idx = mockFollowUps.findIndex(f => f.id === id); - if (idx >= 0) mockFollowUps.splice(idx, 1); - return true; -}; +export const completeFollowUp = id => + post(`${API_URL}/FollowUp/Complete?id=${id}`); + +export const cancelFollowUp = id => + post(`${API_URL}/FollowUp/Cancel?id=${id}`); diff --git a/src/pages/followup/form/FollowUpFormPage.js b/src/pages/followup/form/FollowUpFormPage.js index c4c0891f..6bbb2810 100644 --- a/src/pages/followup/form/FollowUpFormPage.js +++ b/src/pages/followup/form/FollowUpFormPage.js @@ -2,66 +2,127 @@ import React, { useState, useEffect } from "react"; import { useParams, useNavigate } from "react-router"; import { toast } from "react-toastify"; import { getFollowUpById, addFollowUp, editFollowUp } from "../api"; +import { get } from "../../../apiUtil"; +import { API_URL } from "../../../constants"; import "./FollowUpFormPage.css"; +function normalizeId(v) { + if (v === "" || v === null || v === undefined) return null; + const n = parseInt(v, 10); + return Number.isNaN(n) ? null : n; +} + +function toTimeString(t) { + if (!t) return "09:00"; + const m = String(t).match(/^(\d{2}:\d{2})/); + return m ? m[1] : "09:00"; +} + export default function FollowUpFormPage() { const { id } = useParams(); const navigate = useNavigate(); const isEdit = !!id; const [formData, setFormData] = useState({ - workOrder: "", reason: "", - scheduledDate: "", - status: "Pending", - createdBy: "", - createdDate: new Date().toISOString().split("T")[0], - notes: "", + scheduleDate: "", + scheduleStartTime: "09:00", + employeeId: "", + accountId: "", + locationId: "", + workOrderId: "", }); const [loading, setLoading] = useState(false); + const [employees, setEmployees] = useState([]); + const [accounts, setAccounts] = useState([]); + const [locations, setLocations] = useState([]); useEffect(() => { - if (isEdit && id) { - const loadFollowUp = async () => { - try { - const followUp = await getFollowUpById(id); - if (followUp) { - setFormData(prev => { - const merged = { ...prev }; - for (const key of Object.keys(merged)) { - if (followUp[key] != null) merged[key] = followUp[key]; - } - return merged; - }); - } - } catch (err) { - console.error("Failed to load follow up", err); - toast.error("Failed to load follow up"); - } - }; - loadFollowUp(); - } + get(`${API_URL}/Employee/GetEmployeeList?pageSize=500`) + .then((res) => { + const rows = res?.data || res?.Data || []; + setEmployees( + rows.map((e) => ({ + id: e.id ?? e.Id, + name: `${e.firstName ?? e.FirstName ?? ""} ${e.lastName ?? e.LastName ?? ""}`.trim(), + })) + ); + }) + .catch(() => {}); + + get(`${API_URL}/Account/GetAccountList?pageSize=500`) + .then((res) => { + const rows = res?.data || res?.Data || []; + setAccounts( + rows.map((a) => ({ + id: a.id ?? a.Id, + name: a.name ?? a.Name ?? "", + })) + ); + }) + .catch(() => {}); + + get(`${API_URL}/Contact/GetLocations`) + .then((data) => { + const rows = Array.isArray(data) ? data : []; + setLocations( + rows.map((l) => ({ + id: l.id ?? l.Id, + name: l.name ?? l.Name ?? "", + })) + ); + }) + .catch(() => {}); + }, []); + + useEffect(() => { + if (!isEdit || !id) return; + getFollowUpById(id) + .then((data) => { + if (!data) return; + setFormData({ + reason: data.reason ?? data.Reason ?? "", + scheduleDate: (data.scheduleDate ?? data.ScheduleDate ?? "").slice(0, 10), + scheduleStartTime: toTimeString(data.scheduleStartTime ?? data.ScheduleStartTime), + employeeId: data.employeeId ?? data.EmployeeId ?? "", + accountId: data.accountId ?? data.AccountId ?? "", + locationId: data.locationId ?? data.LocationId ?? "", + workOrderId: data.workOrderId ?? data.WorkOrderId ?? "", + }); + }) + .catch((err) => { + console.error("Failed to load follow up", err); + toast.error("Failed to load follow up"); + }); }, [id, isEdit]); const handleChange = (e) => { const { name, value } = e.target; - setFormData(prev => ({ - ...prev, - [name]: value, - })); + setFormData((prev) => ({ ...prev, [name]: value })); }; const handleSave = async (e) => { e.preventDefault(); setLoading(true); + const payload = { + Id: isEdit ? parseInt(id, 10) : 0, + Reason: formData.reason, + ScheduleDate: formData.scheduleDate, + ScheduleStartTime: `${toTimeString(formData.scheduleStartTime)}:00`, + EmployeeId: normalizeId(formData.employeeId), + AccountId: normalizeId(formData.accountId), + LocationId: normalizeId(formData.locationId), + WorkOrderId: normalizeId(formData.workOrderId), + }; + try { if (isEdit) { - await editFollowUp(formData); + await editFollowUp(payload); toast.success("Follow-up updated successfully!"); } else { - await addFollowUp(formData); + await addFollowUp(payload); toast.success("Follow-up created successfully!"); } navigate("/followups"); @@ -83,7 +144,13 @@ export default function FollowUpFormPage() {