Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management
This commit is contained in:
commit
e7a68aeab7
@ -4,6 +4,7 @@ import Label from "../common/Label";
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod";
|
import { zodResolver } from "@hookform/resolvers/zod";
|
||||||
import { defaultJobValue, jobSchema } from "./ServiceProjectSchema";
|
import { defaultJobValue, jobSchema } from "./ServiceProjectSchema";
|
||||||
import {
|
import {
|
||||||
|
useBranches,
|
||||||
useCreateServiceProjectJob,
|
useCreateServiceProjectJob,
|
||||||
useJobTags,
|
useJobTags,
|
||||||
useServiceProjectJobDetails,
|
useServiceProjectJobDetails,
|
||||||
@ -25,6 +26,7 @@ import { useParams } from "react-router-dom";
|
|||||||
import { useDispatch } from "react-redux";
|
import { useDispatch } from "react-redux";
|
||||||
import { useJobStatus } from "../../hooks/masterHook/useMaster";
|
import { useJobStatus } from "../../hooks/masterHook/useMaster";
|
||||||
import { useServiceProjectJobContext } from "./Jobs";
|
import { useServiceProjectJobContext } from "./Jobs";
|
||||||
|
import { SelectFieldSearch } from "../common/Forms/SelectFieldServerSide";
|
||||||
|
|
||||||
const ManageJob = ({ Job }) => {
|
const ManageJob = ({ Job }) => {
|
||||||
const { setManageJob, setSelectedJob } = useServiceProjectJobContext();
|
const { setManageJob, setSelectedJob } = useServiceProjectJobContext();
|
||||||
@ -39,7 +41,7 @@ const ManageJob = ({ Job }) => {
|
|||||||
control,
|
control,
|
||||||
watch,
|
watch,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
reset,
|
reset,setValue,
|
||||||
formState: { errors },
|
formState: { errors },
|
||||||
} = methods;
|
} = methods;
|
||||||
|
|
||||||
@ -199,7 +201,7 @@ const ManageJob = ({ Job }) => {
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-12 col-md-6 mb-2 mb-md-4">
|
<div className="col-12 col-md-6 mb-2 mb-md-4">
|
||||||
<Label required>Select Employee</Label>
|
<Label >Select Employee</Label>
|
||||||
<PmsEmployeeInputTag
|
<PmsEmployeeInputTag
|
||||||
control={control}
|
control={control}
|
||||||
name="assignees"
|
name="assignees"
|
||||||
@ -238,7 +240,21 @@ const ManageJob = ({ Job }) => {
|
|||||||
name="tags"
|
name="tags"
|
||||||
label="Tag"
|
label="Tag"
|
||||||
placeholder="Enter Tag"
|
placeholder="Enter Tag"
|
||||||
|
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="col-12 col-md-6 mb-2 mb-md-4">
|
||||||
|
<SelectFieldSearch
|
||||||
|
label="Select Branch"
|
||||||
|
placeholder="Select Branch"
|
||||||
required
|
required
|
||||||
|
value={watch("branchId")}
|
||||||
|
onChange={(val) => setValue("branchId", val)}
|
||||||
|
valueKey="id"
|
||||||
|
labelKey="branchName"
|
||||||
|
hookParams={[projectId,true,10,1]}
|
||||||
|
useFetchHook={useBranches}
|
||||||
|
isMultiple={false}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="col-12">
|
<div className="col-12">
|
||||||
|
|||||||
@ -70,6 +70,8 @@ export const jobSchema = z.object({
|
|||||||
tags: z.array(TagSchema).optional().default([]),
|
tags: z.array(TagSchema).optional().default([]),
|
||||||
|
|
||||||
statusId: z.string().optional().nullable(),
|
statusId: z.string().optional().nullable(),
|
||||||
|
|
||||||
|
branchId: z.string().optional().nullable(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
|
const MAX_FILE_SIZE = 5 * 1024 * 1024; // 5MB
|
||||||
@ -109,6 +111,7 @@ export const defaultJobValue = {
|
|||||||
startDate: null,
|
startDate: null,
|
||||||
dueDate: null,
|
dueDate: null,
|
||||||
tags: [],
|
tags: [],
|
||||||
|
branchId: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
//#endregion
|
//#endregion
|
||||||
|
|||||||
@ -361,3 +361,182 @@ export const SelectProjectField = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export const SelectFieldSearch = ({
|
||||||
|
label = "Select",
|
||||||
|
placeholder = "Select ",
|
||||||
|
required = false,
|
||||||
|
value = null,
|
||||||
|
onChange,
|
||||||
|
valueKey = "id",
|
||||||
|
labelKey = "name",
|
||||||
|
|
||||||
|
isFullObject = false,
|
||||||
|
isMultiple = false,
|
||||||
|
hookParams,
|
||||||
|
useFetchHook,
|
||||||
|
}) => {
|
||||||
|
const [searchText, setSearchText] = useState("");
|
||||||
|
const debounce = useDebounce(searchText, 300);
|
||||||
|
|
||||||
|
const { data, isLoading } = useFetchHook(...hookParams,debounce);
|
||||||
|
const options = data?.data ?? [];
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const dropdownRef = useRef(null);
|
||||||
|
|
||||||
|
const getDisplayName = (entity) => {
|
||||||
|
if (!entity) return "";
|
||||||
|
return `${entity[labelKey] || ""}`.trim();
|
||||||
|
};
|
||||||
|
|
||||||
|
/** -----------------------------
|
||||||
|
* SELECTED OPTION (SINGLE)
|
||||||
|
* ----------------------------- */
|
||||||
|
let selectedSingle = null;
|
||||||
|
|
||||||
|
if (!isMultiple) {
|
||||||
|
if (isFullObject && value) selectedSingle = value;
|
||||||
|
else if (!isFullObject && value)
|
||||||
|
selectedSingle = options.find((o) => o[valueKey] === value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** -----------------------------
|
||||||
|
* SELECTED OPTION (MULTIPLE)
|
||||||
|
* ----------------------------- */
|
||||||
|
let selectedList = [];
|
||||||
|
if (isMultiple && Array.isArray(value)) {
|
||||||
|
if (isFullObject) selectedList = value;
|
||||||
|
else {
|
||||||
|
selectedList = options.filter((opt) => value.includes(opt[valueKey]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Main button label */
|
||||||
|
const displayText = !isMultiple
|
||||||
|
? getDisplayName(selectedSingle) || placeholder
|
||||||
|
: selectedList.length > 0
|
||||||
|
? selectedList.map((e) => getDisplayName(e)).join(", ")
|
||||||
|
: placeholder;
|
||||||
|
|
||||||
|
/** -----------------------------
|
||||||
|
* HANDLE OUTSIDE CLICK
|
||||||
|
* ----------------------------- */
|
||||||
|
useEffect(() => {
|
||||||
|
const handleClickOutside = (e) => {
|
||||||
|
if (dropdownRef.current && !dropdownRef.current.contains(e.target)) {
|
||||||
|
setOpen(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
document.addEventListener("mousedown", handleClickOutside);
|
||||||
|
return () => document.removeEventListener("mousedown", handleClickOutside);
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
/** -----------------------------
|
||||||
|
* HANDLE SELECT
|
||||||
|
* ----------------------------- */
|
||||||
|
const handleSelect = (option) => {
|
||||||
|
if (!isMultiple) {
|
||||||
|
// SINGLE SELECT
|
||||||
|
if (isFullObject) onChange(option);
|
||||||
|
else onChange(option[valueKey]);
|
||||||
|
} else {
|
||||||
|
// MULTIPLE SELECT
|
||||||
|
let updated = [];
|
||||||
|
|
||||||
|
const exists = selectedList.some((e) => e[valueKey] === option[valueKey]);
|
||||||
|
|
||||||
|
if (exists) {
|
||||||
|
// remove
|
||||||
|
updated = selectedList.filter((e) => e[valueKey] !== option[valueKey]);
|
||||||
|
} else {
|
||||||
|
// add
|
||||||
|
updated = [...selectedList, option];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isFullObject) onChange(updated);
|
||||||
|
else onChange(updated.map((x) => x[valueKey]));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="mb-3 position-relative" ref={dropdownRef}>
|
||||||
|
{label && (
|
||||||
|
<Label className="form-label" required={required}>
|
||||||
|
{label}
|
||||||
|
</Label>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* MAIN BUTTON */}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`select2-icons form-select d-flex align-items-center justify-content-between ${
|
||||||
|
open ? "show" : ""
|
||||||
|
}`}
|
||||||
|
onClick={() => setOpen((prev) => !prev)}
|
||||||
|
>
|
||||||
|
<span className={`text-truncate ${!displayText ? "text-muted" : ""}`}>
|
||||||
|
{displayText}
|
||||||
|
</span>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
{/* DROPDOWN */}
|
||||||
|
{open && (
|
||||||
|
<ul
|
||||||
|
className="dropdown-menu w-100 shadow-sm show animate__fadeIn h-64 overflow-auto rounded"
|
||||||
|
style={{
|
||||||
|
position: "absolute",
|
||||||
|
top: "100%",
|
||||||
|
left: 0,
|
||||||
|
zIndex: 1050,
|
||||||
|
marginTop: "2px",
|
||||||
|
borderRadius: "0.375rem",
|
||||||
|
overflow: "hidden",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div className="p-1">
|
||||||
|
<input
|
||||||
|
type="search"
|
||||||
|
value={searchText}
|
||||||
|
onChange={(e) => setSearchText(e.target.value)}
|
||||||
|
className="form-control form-control-sm"
|
||||||
|
placeholder="Search..."
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{isLoading && (
|
||||||
|
<li className="dropdown-item text-muted text-center">Loading...</li>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isLoading && options.length === 0 && (
|
||||||
|
<li className="dropdown-item text-muted text-center">
|
||||||
|
No results found
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{!isLoading &&
|
||||||
|
options.map((option) => {
|
||||||
|
const isActive = isMultiple
|
||||||
|
? selectedList.some((x) => x[valueKey] === option[valueKey])
|
||||||
|
: selectedSingle &&
|
||||||
|
selectedSingle[valueKey] === option[valueKey];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<li key={option[valueKey]}>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`dropdown-item ${isActive ? "active" : ""}`}
|
||||||
|
onClick={() => handleSelect(option)}
|
||||||
|
>
|
||||||
|
{getDisplayName(option)}
|
||||||
|
</button>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
@ -308,12 +308,14 @@ export const useBranches = (
|
|||||||
pageNumber,
|
pageNumber,
|
||||||
searchString
|
searchString
|
||||||
);
|
);
|
||||||
|
console.log(resp)
|
||||||
return resp.data;
|
return resp.data;
|
||||||
},
|
},
|
||||||
enabled: !!projectId,
|
enabled: !!projectId,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const useBranch = (id)=>{
|
export const useBranch = (id)=>{
|
||||||
return useQuery({
|
return useQuery({
|
||||||
queryKey: ["branch", id],
|
queryKey: ["branch", id],
|
||||||
|
|||||||
@ -45,8 +45,8 @@ export const ServiceProjectRepository = {
|
|||||||
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) => {
|
||||||
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}`
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user