mirror of
https://github.com/Sea-Haven-Industries/shoc-frontend-new.git
synced 2026-10-01 05:03:13 +00:00
fix(vendors): preserve the selected edit state
This commit is contained in:
parent
ff2e04b30e
commit
ec203e1f5b
5 changed files with 82 additions and 22 deletions
|
|
@ -15,7 +15,7 @@ export interface VendorDeactivationState {
|
|||
confirm: () => void;
|
||||
}
|
||||
|
||||
export function useVendorDeactivation(): VendorDeactivationState {
|
||||
export function useVendorDeactivation(onSuccess?: () => void): VendorDeactivationState {
|
||||
const [target, setTarget] = useState<VendorListItem | null>(null);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
const deleteVendor = useDeleteVendor();
|
||||
|
|
@ -39,7 +39,10 @@ export function useVendorDeactivation(): VendorDeactivationState {
|
|||
if (!target || target.id == null) return;
|
||||
setError(null);
|
||||
deleteVendor.mutate(target.id, {
|
||||
onSuccess: () => setTarget(null),
|
||||
onSuccess: () => {
|
||||
setTarget(null);
|
||||
onSuccess?.();
|
||||
},
|
||||
onError: (err: Error) => setError(err.message || "Failed to deactivate vendor"),
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useState, type ReactNode } from "react";
|
||||
import { Controller } from "react-hook-form";
|
||||
import { Controller, useWatch } from "react-hook-form";
|
||||
import CloseIcon from "@mui/icons-material/Close";
|
||||
import EditOutlinedIcon from "@mui/icons-material/EditOutlined";
|
||||
import LaunchIcon from "@mui/icons-material/Launch";
|
||||
|
|
@ -250,13 +250,14 @@ function DrawerEditor({
|
|||
onSuccess: onSaved,
|
||||
});
|
||||
const roster = form.roster;
|
||||
const selectedIndex = Math.max(
|
||||
0,
|
||||
roster?.technicians.findIndex(
|
||||
(technician) => technician.id != null && String(technician.id) === String(vendor.id),
|
||||
) ?? 0,
|
||||
const technicians = useWatch({ control: form.control, name: "technicians" }) ?? [];
|
||||
const selectedIndex = technicians.findIndex(
|
||||
(technician) => technician.id != null && String(technician.id) === String(vendor.id),
|
||||
);
|
||||
const selected = roster?.technicians[selectedIndex];
|
||||
const selectedTotalJobs =
|
||||
roster?.technicians.find(
|
||||
(technician) => technician.id != null && String(technician.id) === String(vendor.id),
|
||||
)?.totalJobs ?? vendor.totalJobs;
|
||||
|
||||
if (form.isLoading) {
|
||||
return <CircularProgress size={28} sx={{ m: "auto" }} />;
|
||||
|
|
@ -289,12 +290,12 @@ function DrawerEditor({
|
|||
tradeOptions={form.trades}
|
||||
showTechnicianStatus={false}
|
||||
/>
|
||||
{selected != null && (
|
||||
{selectedIndex >= 0 && (
|
||||
<>
|
||||
<Divider />
|
||||
<Stack direction="row" sx={{ alignItems: "center", justifyContent: "space-between" }}>
|
||||
<Box>
|
||||
<Text variant="title">{selected.totalJobs ?? 0}</Text>
|
||||
<Text variant="title">{selectedTotalJobs}</Text>
|
||||
<Text variant="caption" tone="muted">
|
||||
Total Jobs
|
||||
</Text>
|
||||
|
|
|
|||
17
src/app/(protected)/vendors/index.tsx
vendored
17
src/app/(protected)/vendors/index.tsx
vendored
|
|
@ -116,7 +116,11 @@ export default function VendorsListPage() {
|
|||
const [createOpen, setCreateOpen] = useState(false);
|
||||
const [detailTarget, setDetailTarget] = useState<VendorListItem | null>(null);
|
||||
const [detailStartsInEdit, setDetailStartsInEdit] = useState(false);
|
||||
const deactivation = useVendorDeactivation();
|
||||
const closeDetail = () => {
|
||||
setDetailTarget(null);
|
||||
setDetailStartsInEdit(false);
|
||||
};
|
||||
const deactivation = useVendorDeactivation(closeDetail);
|
||||
|
||||
const { data: facets } = useVendorFacets(filters.isActive);
|
||||
const { data, isLoading, isFetching, error } = useVendorsList(filters.listParams);
|
||||
|
|
@ -213,15 +217,8 @@ export default function VendorsListPage() {
|
|||
open={Boolean(detailTarget)}
|
||||
vendor={detailTarget}
|
||||
startInEdit={detailStartsInEdit}
|
||||
onRequestDeactivation={(row) => {
|
||||
setDetailTarget(null);
|
||||
setDetailStartsInEdit(false);
|
||||
deactivation.open(row);
|
||||
}}
|
||||
onClose={() => {
|
||||
setDetailTarget(null);
|
||||
setDetailStartsInEdit(false);
|
||||
}}
|
||||
onRequestDeactivation={deactivation.open}
|
||||
onClose={closeDetail}
|
||||
/>
|
||||
|
||||
<VendorDeactivationDialog
|
||||
|
|
|
|||
|
|
@ -110,4 +110,27 @@ describe("VendorDetailDrawer selected-technician display", () => {
|
|||
expect(screen.getByText("0")).toBeInTheDocument();
|
||||
expect(screen.queryByText("999-999-9999")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("does not expose a status control for a different technician", () => {
|
||||
useVendorCompanyRoster.mockReturnValue(
|
||||
rosterWith([
|
||||
{
|
||||
id: 7,
|
||||
contactName: "Different Technician",
|
||||
phone: "314-555-0101",
|
||||
email: "",
|
||||
tradeSpecialties: "Plumbing",
|
||||
isActive: true,
|
||||
totalJobs: 10,
|
||||
},
|
||||
]),
|
||||
);
|
||||
|
||||
renderWithProviders(
|
||||
<VendorDetailDrawer open={true} vendor={vendor} startInEdit={true} onClose={vi.fn()} />,
|
||||
{ route: "/vendors", withAuth: false },
|
||||
);
|
||||
|
||||
expect(screen.queryByRole("switch", { name: "Active status" })).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { screen } from "@testing-library/react";
|
||||
import { screen, within } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import VendorsListPage from "@/app/(protected)/vendors/index";
|
||||
|
|
@ -220,6 +220,42 @@ describe("VendorsListPage", () => {
|
|||
expect(mutate).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("preserves inline edits when deactivation is cancelled", async () => {
|
||||
setupDefaults();
|
||||
useVendorCompanyRoster.mockReturnValue({
|
||||
data: activeRoster,
|
||||
isLoading: false,
|
||||
isError: false,
|
||||
error: null,
|
||||
refetch: vi.fn(),
|
||||
});
|
||||
useVendorDeactivationImpact.mockReturnValue({
|
||||
data: { vendorId: 1, canDeactivate: true, openWorkOrders: [] },
|
||||
isLoading: false,
|
||||
error: null,
|
||||
});
|
||||
useVendorsList.mockImplementation((params: { isActive?: boolean; pageSize?: number }) => {
|
||||
if (params.pageSize === 1) return result([], 1);
|
||||
return params.isActive ? result([activeVendor], 1) : result([], 0);
|
||||
});
|
||||
|
||||
renderWithProviders(<VendorsListPage />, { route: "/vendors", withAuth: false });
|
||||
|
||||
await userEvent.click(screen.getByRole("button", { name: "Edit vendor Gateway Plumbing" }));
|
||||
const company = screen.getByRole("textbox", { name: "Company" });
|
||||
await userEvent.clear(company);
|
||||
await userEvent.type(company, "Draft Company Name");
|
||||
await userEvent.click(screen.getByRole("switch", { name: "Active status" }));
|
||||
await userEvent.click(
|
||||
within(screen.getByRole("dialog", { name: "Deactivate Vendor" })).getByRole("button", {
|
||||
name: "Cancel",
|
||||
}),
|
||||
);
|
||||
|
||||
expect(company).toBeInTheDocument();
|
||||
expect(company).toHaveValue("Draft Company Name");
|
||||
});
|
||||
|
||||
it("opens inline edit when the table Edit control is clicked", async () => {
|
||||
setupDefaults();
|
||||
useVendorCompanyRoster.mockReturnValue({
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue