Implementing edit api for branches.

This commit is contained in:
Kartik Sharma 2025-11-20 09:51:23 +05:30
parent e7fddd41c2
commit dd716187da
4 changed files with 255 additions and 143 deletions

View File

@ -4,6 +4,7 @@ import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import Label from "../../common/Label"; import Label from "../../common/Label";
import { import {
useBranchDetails,
useCreateBranch, useCreateBranch,
useServiceProjects, useServiceProjects,
useUpdateBranch, useUpdateBranch,
@ -13,7 +14,13 @@ import { useParams } from "react-router-dom";
import { BranchSchema, defaultBranches } from "../ServiceProjectSchema"; import { BranchSchema, defaultBranches } from "../ServiceProjectSchema";
const ManageBranch = ({ closeModal, BranchToEdit = null }) => { const ManageBranch = ({ closeModal, BranchToEdit = null }) => {
const { data } = {}; const {
data,
isLoading,
isError,
error: requestError,
} = useBranchDetails(BranchToEdit);
const { projectId } = useParams(); const { projectId } = useParams();
const schema = BranchSchema(); const schema = BranchSchema();
const { const {

View File

@ -1,19 +1,24 @@
import React, { useState } from "react"; import React, { useState } from "react";
import GlobalModel from "../../common/GlobalModel"; import GlobalModel from "../../common/GlobalModel";
import ManageBranch from "./ManageBranch"; import ManageBranch from "./ManageBranch";
import { useBranches } from "../../../hooks/useServiceProject"; import { useBranches, useDeleteBranch } from "../../../hooks/useServiceProject";
import { ITEMS_PER_PAGE } from "../../../utils/constants"; import { ITEMS_PER_PAGE } from "../../../utils/constants";
import { useDebounce } from "../../../utils/appUtils"; import { useDebounce } from "../../../utils/appUtils";
import { useParams } from "react-router-dom"; import { useParams } from "react-router-dom";
import Pagination from "../../common/Pagination"; import Pagination from "../../common/Pagination";
import ConfirmModal from "../../common/ConfirmModal";
import { SpinnerLoader } from "../../common/Loader";
const ServiceBranch = () => { const ServiceBranch = () => {
const { projectId } = useParams(); const { projectId } = useParams();
const [IsDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
const [showInactive, setShowInactive] = useState(false);
const [manageState, setManageState] = useState({ const [manageState, setManageState] = useState({
IsOpen: false, IsOpen: false,
branchId: null, branchId: null,
}); });
const { mutate: DeleteBranch, isPending } = useDeleteBranch();
const [deletingId, setDeletingId] = useState(null);
const [search, setSearch] = useState(""); const [search, setSearch] = useState("");
const [currentPage, setCurrentPage] = useState(1); const [currentPage, setCurrentPage] = useState(1);
@ -21,7 +26,8 @@ const ServiceBranch = () => {
const { data, isLoading, isError, error } = useBranches( const { data, isLoading, isError, error } = useBranches(
projectId, projectId,
true, // true,
!showInactive,
ITEMS_PER_PAGE - 10, ITEMS_PER_PAGE - 10,
currentPage, currentPage,
debouncedSearch debouncedSearch
@ -42,19 +48,62 @@ const ServiceBranch = () => {
}, },
]; ];
const handleDelete = (id) => {
setDeletingId(id);
DeleteBranch(
{ id, isActive: false },
{
onSettled: () => {
setDeletingId(null);
setIsDeleteModalOpen(false);
},
}
);
};
return ( return (
<>
{IsDeleteModalOpen && (
<ConfirmModal
isOpen={IsDeleteModalOpen}
type="delete"
header="Delete Expense"
message="Are you sure you want delete?"
onSubmit={handleDelete}
onClose={() => setIsDeleteModalOpen(false)}
loading={isPending}
paramData={deletingId}
/>
)}
<div className="card h-100 table-responsive px-sm-4"> <div className="card h-100 table-responsive px-sm-4">
<div className="card-datatable" id="payment-request-table"> <div className="card-datatable" id="payment-request-table">
{/* Header Section */} {/* Header Section */}
<div className="row align-items-center justify-content-between mt-3 mx-1"> <div className="row align-items-center justify-content-between mt-3 mx-1">
<div className="col-md-6 col-sm-12 text-start"> <div className="col-md-6 col-sm-12 ms-n3 text-start ">
<h5 className="mb-0"> <h5 className="mb-0">
<i className="bx bx-buildings text-primary me-2"></i> <i className="bx bx-buildings text-primary"></i>
Branches <span className="ms-2 fw-bold">Branch</span>
</h5> </h5>
</div> </div>
<div className="col-md-3 col-sm-12 text-end"> {/* Flex container for toggle + button */}
<div className="col-md-6 col-sm-12 text-end">
<div className="d-flex flex-column flex-md-row align-items-md-center justify-content-md-end gap-2">
{/* Toggle Switch */}
<div className="form-check form-switch d-inline-flex align-items-center">
<input
type="checkbox"
className="form-check-input mt-1"
id="inactiveEmployeesCheckbox"
checked={showInactive}
onChange={() => setShowInactive(!showInactive)}
/>
<label htmlFor="inactiveEmployeesCheckbox" className="ms-2 mt-1">
Show Deleted Branches
</label>
</div>
{/* Add Branch Button */}
<button <button
className="btn btn-sm btn-primary" className="btn btn-sm btn-primary"
type="button" type="button"
@ -68,9 +117,12 @@ const ServiceBranch = () => {
<i className="bx bx-sm bx-plus-circle me-2"></i> <i className="bx bx-sm bx-plus-circle me-2"></i>
<span className="d-none d-md-inline-block">Add Branch</span> <span className="d-none d-md-inline-block">Add Branch</span>
</button> </button>
</div> </div>
</div> </div>
</div>
<div className="mx-2 mt-3"> <div className="mx-2 mt-3">
<table className="table border-top text-nowrap align-middle table-borderless"> <table className="table border-top text-nowrap align-middle table-borderless">
<thead> <thead>
@ -87,12 +139,22 @@ const ServiceBranch = () => {
<tbody> <tbody>
{isLoading && ( {isLoading && (
<tr> <tr>
<td colSpan={columns.length + 1} className="text-center py-5"> <td
Loading... colSpan={columns.length + 1}
className="text-center py-5"
style={{
height: "200px",
verticalAlign: "middle",
}}
>
<div className="d-flex justify-content-center align-items-center w-100 h-100">
<SpinnerLoader />
</div>
</td> </td>
</tr> </tr>
)} )}
{isError && ( {isError && (
<tr> <tr>
<td <td
@ -115,16 +177,47 @@ const ServiceBranch = () => {
</td> </td>
))} ))}
<td className="text-center"> <td className="text-center">
<i <div className="dropdown z-2">
className="bx bx-edit text-primary cursor-pointer" <button
type="button"
className="btn btn-xs btn-icon btn-text-secondary rounded-pill dropdown-toggle hide-arrow p-0 m-0"
data-bs-toggle="dropdown"
>
<i className="bx bx-dots-vertical-rounded text-muted p-0"></i>
</button>
<ul className="dropdown-menu dropdown-menu-end w-auto">
{/* Modify */}
<li
onClick={() => onClick={() =>
setManageState({ setManageState({
IsOpen: true, IsOpen: true,
branchId: branch.id, branchId: branch.id,
}) })
} }
/> >
<a className="dropdown-item px-2 cursor-pointer py-1">
<i className="bx bx-edit text-primary bx-xs me-2"></i>
Modify
</a>
</li>
{/* Delete */}
<li
onClick={() => {
setIsDeleteModalOpen(true);
setDeletingId(branch.id);
}}
>
<a className="dropdown-item px-2 cursor-pointer py-1">
<i className="bx bx-trash text-danger bx-xs me-2"></i>
Delete
</a>
</li>
</ul>
</div>
</td> </td>
</tr> </tr>
))} ))}
@ -160,14 +253,17 @@ const ServiceBranch = () => {
> >
<ManageBranch <ManageBranch
key={manageState.branchId ?? "new"} key={manageState.branchId ?? "new"}
BranchToEdit={manageState.branchId}
closeModal={() => closeModal={() =>
setManageState({ IsOpen: false, branchId: null }) setManageState({ IsOpen: false, branchId: null })
} }
/> />
</GlobalModel> </GlobalModel>
)} )}
</div> </div>
</div> </div>
</>
); );
}; };

View File

@ -315,12 +315,12 @@ export const useBranches = (
}; };
export const useBranch = (id)=>{ export const useBranchDetails = (id) => {
return useQuery({ return useQuery({
queryKey: ["branch", id], queryKey: ["branch", id],
queryFn: async () => { queryFn: async () => {
const resp = await ServiceProjectRepository.GetBranchDetail(id); const resp = await ServiceProjectRepository.GetBranchDetail(id);
return resp.data ?? resp; return resp.data;
}, },
enabled: !!id enabled: !!id
}) })
@ -346,37 +346,42 @@ export const useCreateBranch = (onSuccessCallBack) => {
}, },
}); });
}; };
export const useUpdateBranch = (onSuccessCallBack) => {
export const useUpdateBranch = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async ({ id, payload }) => await ServiceProjectRepository.UpdateBranch(id, payload), mutationFn: async ({ id, payload }) =>
await ServiceProjectRepository.UpdateBranch(id, payload),
onSuccess: (_, variables) => { onSuccess: (_, variables) => {
// remove old single-branch cache
queryClient.removeQueries({ queryKey: ["branch", variables.id] });
// refresh list
queryClient.invalidateQueries({ queryKey: ["branches"] }); queryClient.invalidateQueries({ queryKey: ["branches"] });
queryClient.invalidateQueries({ queryKey: ["branch", variables.id] });
if (onSuccessCallback) onSuccessCallback(); showToast("Branch updated successfully", "success");
showToast("Branch Updated successfully", "success"); onSuccessCallBack?.();
}, },
onError: (error) => {
showToast( onError: () => {
error?.response?.data?.message || showToast("Something went wrong. Please try again later.", "error");
error.message ||
"Failed to update branch",
"error"
);
}, },
}) });
} };
export const useDeleteBranch = () => { export const useDeleteBranch = () => {
const queryClient = useQueryClient(); const queryClient = useQueryClient();
return useMutation({ return useMutation({
mutationFn: async (id) => await ServiceProjectRepository.DeleteBranch(id), mutationFn: async ({ id, isActive}) =>
onSuccess: (_, variables) => { await ServiceProjectRepository.DeleteBranch(id, isActive),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ["branches"] }); queryClient.invalidateQueries({ queryKey: ["branches"] });
if (onSuccessCallback) onSuccessCallback(); showToast("Branch deleted successfully", "success");
showToast("Branch Deleted successfully", "success");
}, },
onError: (error) => { onError: (error) => {
showToast( showToast(
error?.response?.data?.message || error?.response?.data?.message ||
@ -385,5 +390,5 @@ export const useDeleteBranch = () => {
"error" "error"
); );
}, },
}) });
} };

View File

@ -1,3 +1,4 @@
import { isAction } from "@reduxjs/toolkit";
import { api } from "../utils/axiosClient"; import { api } from "../utils/axiosClient";
export const ServiceProjectRepository = { export const ServiceProjectRepository = {
@ -43,7 +44,8 @@ export const ServiceProjectRepository = {
//#region Project Branch //#region Project Branch
CreateBranch: (data) => api.post(`/api/ServiceProject/branch/create`, data), CreateBranch: (data) => api.post(`/api/ServiceProject/branch/create`, data),
UpdateBranch: (id, data) => UpdateBranch: (id, data) =>
api.put("/api/ServiceProject/branch/edit/${id}", data), api.put(`/api/ServiceProject/branch/edit/${id}`, data),
GetBranchList: (projectId, isActive, pageSize, pageNumber, searchString) => { GetBranchList: (projectId, isActive, pageSize, pageNumber, searchString) => {
return api.get( return api.get(
`/api/ServiceProject/branch/list/${projectId}?isActive=${isActive}&pageSize=${pageSize}&pageNumber=${pageNumber}&searchString=${searchString}` `/api/ServiceProject/branch/list/${projectId}?isActive=${isActive}&pageSize=${pageSize}&pageNumber=${pageNumber}&searchString=${searchString}`
@ -51,6 +53,8 @@ export const ServiceProjectRepository = {
}, },
GetBranchDetail: (id) => api.get(`/api/ServiceProject/branch/details/${id}`), GetBranchDetail: (id) => api.get(`/api/ServiceProject/branch/details/${id}`),
DeleteBranch: (id) => api.delete(`/api/ServiceProject/branch/delete/${id}`), DeleteBranch: (id, isActive = false) =>
api.delete(`/api/ServiceProject/branch/delete/${id}?isActive=${isActive}`),
}; };