Merge branch 'Purchase_Invoice_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Adding_dropdown
This commit is contained in:
commit
5a8a8c4676
@ -103,6 +103,9 @@ const EmpAttendance = () => {
|
||||
<th className="border-top-1" colSpan={2}>
|
||||
Name
|
||||
</th>
|
||||
<th className="border-top-1" colSpan={2}>
|
||||
ProjectName
|
||||
</th>
|
||||
<th className="border-top-1">Date</th>
|
||||
<th>
|
||||
<i className="bx bxs-down-arrow-alt text-success"></i>{" "}
|
||||
@ -118,7 +121,7 @@ const EmpAttendance = () => {
|
||||
<tbody>
|
||||
{currentItems?.map((attendance, index) => (
|
||||
<tr key={index}>
|
||||
<td colSpan={2}>
|
||||
<td colSpan={3}>
|
||||
<div className="d-flex justify-content-start align-items-center">
|
||||
<Avatar
|
||||
firstName={attendance.firstName}
|
||||
@ -133,6 +136,7 @@ const EmpAttendance = () => {
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td>{attendance.projectName}</td>
|
||||
<td>
|
||||
{" "}
|
||||
{moment(attendance.checkInTime).format("DD-MMM-YYYY")}
|
||||
|
||||
@ -19,7 +19,7 @@ const taskSchema = z
|
||||
.object({
|
||||
activityID: z.string().min(1, "Activity is required"),
|
||||
workCategoryId: z.string().min(1, "Work Category is required"),
|
||||
plannedWork: z.number().min(1, "Planned Work must be greater than 0"),
|
||||
plannedWork: z.number().min(0.01, "Planned Work must be greater than 0"),
|
||||
completedWork: z.number().min(0, "Completed Work must be ≥ 0"),
|
||||
comment: z.string(),
|
||||
})
|
||||
@ -101,6 +101,7 @@ const EditActivityModal = ({
|
||||
const onSubmitForm = (data) => {
|
||||
const payload = {
|
||||
...data,
|
||||
plannedWork: Number(data.plannedWork.toFixed(2)),
|
||||
id: workItem?.workItem?.id ?? workItem?.id,
|
||||
buildingID: building?.id,
|
||||
floorId: floor?.id,
|
||||
@ -262,25 +263,90 @@ const EditActivityModal = ({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12 text-end mt-5">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary me-2"
|
||||
onClick={onClose}
|
||||
disabled={isPending}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary"
|
||||
disabled={isPending}
|
||||
>
|
||||
{isPending ? "Please Wait..." : "Edit Task"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</AppFormProvider>
|
||||
<div className="col-12 text-start">
|
||||
<label className="form-label">Select Work Category</label>
|
||||
<select
|
||||
{...register("workCategoryId")}
|
||||
className="form-select form-select-sm"
|
||||
>
|
||||
<option disabled>Select Category</option>
|
||||
{loadingCategories ? (
|
||||
<option>Loading...</option>
|
||||
) : (
|
||||
sortedCategories.map((c) => (
|
||||
<option key={c.id} value={c.id}>
|
||||
{c.name}
|
||||
</option>
|
||||
))
|
||||
)}
|
||||
</select>
|
||||
{errors.workCategoryId && (
|
||||
<p className="danger-text">{errors.workCategoryId.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-5 text-start">
|
||||
<label className="form-label">Planned Work</label>
|
||||
<input
|
||||
{...register("plannedWork", { valueAsNumber: true })}
|
||||
type="number"
|
||||
step="0.01" // <-- allows 2 decimal places
|
||||
className="form-control form-control-sm"
|
||||
/>
|
||||
|
||||
{errors.plannedWork && (
|
||||
<p className="danger-text">{errors.plannedWork.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-5 text-start">
|
||||
<label className="form-label">Completed Work</label>
|
||||
<input
|
||||
{...register("completedWork", { valueAsNumber: true })}
|
||||
type="number"
|
||||
disabled={getValues("completedWork") > 0}
|
||||
className="form-control form-control-sm"
|
||||
/>
|
||||
{errors.completedWork && (
|
||||
<p className="danger-text">{errors.completedWork.message}</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-2 text-start">
|
||||
<label className="form-label">Unit</label>
|
||||
<input
|
||||
className="form-control form-control-sm"
|
||||
disabled
|
||||
value={selectedActivity?.unitOfMeasurement || ""}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="col-12 text-start">
|
||||
<label className="form-label">Comment</label>
|
||||
<textarea {...register("comment")} rows="2" className="form-control" />
|
||||
{errors.comment && (
|
||||
<div className="danger-text">{errors.comment.message}</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="col-12 text-end mt-5">
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-label-secondary me-2"
|
||||
onClick={onClose}
|
||||
disabled={isPending}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
className="btn btn-sm btn-primary"
|
||||
disabled={isPending}
|
||||
>
|
||||
{isPending ? "Please Wait..." : "Edit Task"}
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@ -8,6 +8,7 @@ import { useParams } from "react-router-dom";
|
||||
import Pagination from "../../common/Pagination";
|
||||
import ConfirmModal from "../../common/ConfirmModal";
|
||||
import { SpinnerLoader } from "../../common/Loader";
|
||||
import ViewBranchDetails from "./ViewBranchDetails";
|
||||
|
||||
const ServiceBranch = () => {
|
||||
const { projectId } = useParams();
|
||||
@ -19,6 +20,7 @@ const ServiceBranch = () => {
|
||||
});
|
||||
const { mutate: DeleteBranch, isPending } = useDeleteBranch();
|
||||
const [deletingId, setDeletingId] = useState(null);
|
||||
const [ViewRequest, setViewRequest] = useState({ requestId: null, view: false });
|
||||
|
||||
const [search, setSearch] = useState("");
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
@ -84,7 +86,7 @@ const ServiceBranch = () => {
|
||||
<div className="col-md-4 col-sm-12 ms-n3 text-start ">
|
||||
<h5 className="mb-0">
|
||||
<i className="bx bx-buildings text-primary"></i>
|
||||
<span className="ms-2 fw-bold">Branchs</span>
|
||||
<span className="ms-2 fw-bold">Branches</span>
|
||||
</h5>
|
||||
</div>
|
||||
|
||||
@ -171,37 +173,78 @@ const ServiceBranch = () => {
|
||||
!isError &&
|
||||
data?.data?.length > 0 &&
|
||||
data.data.map((branch) => (
|
||||
<tr key={branch.id} style={{ height: "35px" }}>
|
||||
<tr
|
||||
key={branch.id}
|
||||
style={{ height: "35px", cursor: showInactive ? "default" : "pointer" }}
|
||||
onClick={(e) => {
|
||||
if (!showInactive && !e.target.closest(".dropdown") && !e.target.closest(".bx-show")) {
|
||||
setViewRequest({ branchId: branch.id, view: true });
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
||||
{columns.map((col) => (
|
||||
<td key={col.key} className={`${col.align} py-3`}>
|
||||
{col.getValue(branch)}
|
||||
</td>
|
||||
))}
|
||||
<td className="text-center">
|
||||
<div className="dropdown z-2">
|
||||
<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">
|
||||
{!showInactive ? (
|
||||
<>
|
||||
<li
|
||||
onClick={() =>
|
||||
setManageState({
|
||||
IsOpen: true,
|
||||
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>
|
||||
<div className="d-flex justify-content-center align-items-center gap-2">
|
||||
{/* View Icon */}
|
||||
{/* <i
|
||||
className="bx bx-show text-primary cursor-pointer"
|
||||
onClick={() =>
|
||||
setViewRequest({ branchId: branch.id, view: true })
|
||||
}
|
||||
></i> */}
|
||||
|
||||
<div className="dropdown z-2">
|
||||
<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">
|
||||
{!showInactive ? (
|
||||
<>
|
||||
<li
|
||||
onClick={() =>
|
||||
setManageState({
|
||||
IsOpen: true,
|
||||
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>
|
||||
<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>
|
||||
<li
|
||||
onClick={() =>
|
||||
setViewRequest({ branchId: branch.id, view: true })
|
||||
}
|
||||
>
|
||||
<a className="dropdown-item px-2 cursor-pointer py-1">
|
||||
<i className="bx bx-show text-primary cursor-pointer me-2"></i>
|
||||
View
|
||||
</a>
|
||||
</li>
|
||||
</>
|
||||
) : (
|
||||
<li
|
||||
onClick={() => {
|
||||
setIsDeleteModalOpen(true);
|
||||
@ -209,25 +252,13 @@ const ServiceBranch = () => {
|
||||
}}
|
||||
>
|
||||
<a className="dropdown-item px-2 cursor-pointer py-1">
|
||||
<i className="bx bx-trash text-danger bx-xs me-2"></i>
|
||||
Delete
|
||||
<i className="bx bx-undo text-danger me-2"></i>
|
||||
Restore
|
||||
</a>
|
||||
</li>
|
||||
</>
|
||||
) : (
|
||||
<li
|
||||
onClick={() => {
|
||||
setIsDeleteModalOpen(true);
|
||||
setDeletingId(branch.id);
|
||||
}}
|
||||
>
|
||||
<a className="dropdown-item px-2 cursor-pointer py-1">
|
||||
<i className="bx bx-undo text-danger me-2"></i>
|
||||
Restore
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
)}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@ -277,6 +308,17 @@ const ServiceBranch = () => {
|
||||
/>
|
||||
</GlobalModel>
|
||||
)}
|
||||
{ViewRequest.view && (
|
||||
<GlobalModel
|
||||
isOpen
|
||||
size="md"
|
||||
modalType="top"
|
||||
closeModal={() => setViewRequest({ branchId: null, view: false })}
|
||||
>
|
||||
<ViewBranchDetails BranchToEdit={ViewRequest.branchId} />
|
||||
</GlobalModel>
|
||||
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
|
||||
@ -0,0 +1,142 @@
|
||||
import React from "react";
|
||||
import { useBranchDetails } from "../../../hooks/useServiceProject";
|
||||
import Avatar from "../../common/Avatar";
|
||||
import { formatUTCToLocalTime } from "../../../utils/dateUtils";
|
||||
|
||||
const ViewBranchDetails = ({ BranchToEdit }) => {
|
||||
const { data, isLoading, isError, error: requestError } = useBranchDetails(BranchToEdit);
|
||||
|
||||
console.log("branch details:", data);
|
||||
|
||||
if (isLoading) return <p>Loading...</p>;
|
||||
if (isError) return <p>Error: {requestError?.message}</p>;
|
||||
|
||||
return (
|
||||
<form className="container px-3">
|
||||
<div className="col-12 mb-1">
|
||||
<h5 className="fw-semibold m-0">Branch Details</h5>
|
||||
</div>
|
||||
<div className="row mb-1 mt-5">
|
||||
<div className="col-md-12 mb-4">
|
||||
<div className="d-flex">
|
||||
<label
|
||||
className="form-label me-2 mb-0 fw-semibold text-start"
|
||||
style={{ minWidth: "130px" }}
|
||||
>
|
||||
Branch Name:
|
||||
</label>
|
||||
<div className="text-muted">{data?.branchName || "N/A"}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-12 mb-4">
|
||||
<div className="d-flex">
|
||||
<label
|
||||
className="form-label me-2 mb-0 fw-semibold text-start"
|
||||
style={{ minWidth: "130px" }}
|
||||
>
|
||||
Branch Type:
|
||||
</label>
|
||||
<div className="text-muted">{data?.branchName || "N/A"}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-12 mb-4">
|
||||
<div className="d-flex">
|
||||
<label
|
||||
className="form-label me-2 mb-0 fw-semibold text-start"
|
||||
style={{ minWidth: "130px" }}
|
||||
>
|
||||
Project:
|
||||
</label>
|
||||
<div className="text-muted text-start">{data?.project?.name || "N/A"}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="col-md-12 text-start mb-2">
|
||||
<div className="d-flex align-items-center">
|
||||
<label
|
||||
className="form-label me-2 mb-0 fw-semibold"
|
||||
style={{ minWidth: "125px" }}
|
||||
>
|
||||
Updated By :
|
||||
</label>
|
||||
<>
|
||||
<Avatar
|
||||
size="xs"
|
||||
classAvatar="m-0 me-1"
|
||||
firstName={data.updatedBy.firstName}
|
||||
lastName={data.updatedBy.lastName}
|
||||
/>
|
||||
<span className="text-muted">
|
||||
{`${data.updatedBy.firstName ?? ""} ${data.updatedBy.lastName ?? ""
|
||||
}`.trim() || "N/A"}
|
||||
</span>
|
||||
</>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-12 text-start mb-3">
|
||||
<div className="d-flex align-items-center">
|
||||
<label
|
||||
className="form-label me-2 mb-0 fw-semibold"
|
||||
style={{ minWidth: "125px" }}
|
||||
>
|
||||
Created By :
|
||||
</label>
|
||||
<Avatar
|
||||
size="xs"
|
||||
classAvatar="m-0 me-1"
|
||||
firstName={data?.createdBy?.firstName}
|
||||
lastName={data?.createdBy?.lastName}
|
||||
/>
|
||||
<span className="text-muted">
|
||||
{`${data?.createdBy?.firstName ?? ""} ${data?.createdBy?.lastName ?? ""
|
||||
}`.trim() || "N/A"}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-md-12 mb-3">
|
||||
<div className="d-flex">
|
||||
<label
|
||||
className="form-label me-2 mb-0 fw-semibold text-start"
|
||||
style={{ minWidth: "130px" }}
|
||||
>
|
||||
Created At :
|
||||
</label>
|
||||
<div className="text-muted">
|
||||
{data?.createdAt
|
||||
? formatUTCToLocalTime(data.createdAt, true)
|
||||
: "N/A"}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="col-12 mb-3 text-start">
|
||||
<label className="form-label mb-2 fw-semibold">Contact Information:</label>
|
||||
<div className="text-muted">
|
||||
{data?.contactInformation ? (
|
||||
JSON.parse(data.contactInformation).map((contact, index) => (
|
||||
<div key={index} className="mb-3">
|
||||
<div className="fw-semibold mb-1">Person {index + 1}:-</div>
|
||||
<div>
|
||||
<label className="fw-semibold mb-1">Person Name:</label> {contact.contactPerson || "N/A"}
|
||||
</div>
|
||||
<div>
|
||||
<label className="fw-semibold mb-1">Designation:</label> {contact.designation || "N/A"}
|
||||
</div>
|
||||
<div>
|
||||
<label className="fw-semibold mb-1">Emails:</label> {contact.contactEmails?.join(", ") || "N/A"}
|
||||
</div>
|
||||
<div>
|
||||
<label className="fw-semibold mb-1">Numbers:</label> {contact.contactNumbers?.join(", ") || "N/A"}
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
) : (
|
||||
"N/A"
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
);
|
||||
};
|
||||
|
||||
export default ViewBranchDetails;
|
||||
@ -21,6 +21,7 @@ import { useFab } from "../../Context/FabContext";
|
||||
import { useHasUserPermission } from "../../hooks/useHasUserPermission";
|
||||
import {
|
||||
CREATE_EXEPENSE,
|
||||
EXPENSE_STATUS,
|
||||
VIEW_ALL_EXPNESE,
|
||||
VIEW_SELF_EXPENSE,
|
||||
} from "../../utils/constants";
|
||||
@ -74,6 +75,30 @@ const ExpensePage = () => {
|
||||
const [filterData, setFilterdata] = useState(defaultFilter);
|
||||
const tableRef = useRef(null);
|
||||
const [filteredData, setFilteredData] = useState([]);
|
||||
const [showStatus, setShowStatus] = useState(false);
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
if (showStatus) {
|
||||
// ON → show only draft + payment_processed
|
||||
setFilters((prev) => ({
|
||||
...prev,
|
||||
statusIds: [
|
||||
EXPENSE_STATUS.daft,
|
||||
EXPENSE_STATUS.payment_processed,
|
||||
],
|
||||
}));
|
||||
} else {
|
||||
// OFF → show ALL (remove statusIds filter)
|
||||
setFilters((prev) => {
|
||||
const updated = { ...prev };
|
||||
delete updated.statusIds;
|
||||
return updated;
|
||||
});
|
||||
}
|
||||
}, [showStatus]);
|
||||
|
||||
|
||||
const removeFilterChip = (key, id) => {
|
||||
setFilters((prev) => {
|
||||
const updated = { ...prev };
|
||||
@ -136,7 +161,9 @@ const ExpensePage = () => {
|
||||
<div className="card-body py-2 px-3 me-n1">
|
||||
<div className="row align-items-center">
|
||||
<div className="col-md-8 col-sm-12 mb-2 mb-md-0">
|
||||
<div className="d-flex align-items-center flex-wrap gap-0">
|
||||
<div className="d-flex align-items-center flex-wrap gap-2">
|
||||
|
||||
{/* Search Input */}
|
||||
<input
|
||||
type="search"
|
||||
className="form-control form-control-sm w-auto"
|
||||
@ -144,9 +171,25 @@ const ExpensePage = () => {
|
||||
value={searchText}
|
||||
onChange={(e) => setSearchText(e.target.value)}
|
||||
/>
|
||||
|
||||
{/* Status Switch */}
|
||||
<div className="form-check form-switch ms-1 mt-2 d-flex align-items-center">
|
||||
<input
|
||||
className="form-check-input cursor-pointer"
|
||||
type="checkbox"
|
||||
id="statusSwitch"
|
||||
checked={showStatus}
|
||||
onChange={(e) => setShowStatus(e.target.checked)}
|
||||
/>
|
||||
<label className="form-check-label ms-2" htmlFor="statusSwitch">
|
||||
{showStatus ? "Showing: Draft + Payment Processed" : "Showing: All"}
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div className="col-md-4 col-sm-12 text-md-end text-end d-flex justify-content-end align-items-center gap-0">
|
||||
{IsCreatedAble && (
|
||||
<button
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user