Merge branch 'Project_Branch_Management' of https://git.marcoaiot.com/admin/marco.pms.web into Project_Branch_Management

This commit is contained in:
pramod.mahajan 2025-11-19 16:36:01 +05:30
commit f5d89f2bab
5 changed files with 396 additions and 0 deletions

View File

@ -0,0 +1,43 @@
import { z } from "zod";
export const BranchSchema = () =>
z.object({
projectId: z
.string()
.trim()
.min(1, { message: "Project is required" }),
branchName: z
.string()
.trim()
.min(1, { message: "Branch Name is required" }),
contactInformation: z
.string()
.trim()
.min(1, { message: "Contact Information is required" }),
address: z
.string()
.trim()
.min(1, { message: "Address is required" }),
branchType: z
.string()
.trim()
.min(1, { message: "Branch Type is required" }),
googleMapUrl: z
.string()
.trim()
.url({ message: "Enter a valid Google Map URL" }),
});
export const defaultBranches = {
branchName: "",
projectId: "",
contactInformation: "",
address: "",
branchType: "",
googleMapUrl: "",
};

View File

@ -0,0 +1,176 @@
import React from 'react'
import { useProjectName } from '../../hooks/useProjects';
import { BranchSchema, defaultBranches } from './BranchSchema';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import Label from '../common/Label';
const ManageBranch = ({ BranchToEdit = null }) => {
const schema = BranchSchema();
const {
register,
control,
watch,
handleSubmit,
setValue,
reset,
formState: { errors },
} = useForm({
resolver: zodResolver(schema),
defaultValues: defaultBranches,
});
const {
projectNames,
loading: projectLoading,
error,
isError: isProjectError,
} = useProjectName();
const handleClose = () => {
reset();
closeModal();
};
return (
<div className="container p-3">
<h5 className="m-0">
{BranchToEdit
? "Update Branch"
: "Create Branch"}
</h5>
<form onSubmit={handleSubmit(onsubmit)}>
<div className="row my-2 text-start">
<div className="col-md-6">
<Label className="form-label" required>
Select Project
</Label>
<select
className="form-select form-select-sm"
{...register("projectId")}
>
<option value="">Select Project</option>
{projectLoading ? (
<option>Loading...</option>
) : (
projectNames?.map((project) => (
<option key={project.id} value={project.id}>
{project.name}
</option>
))
)}
</select>
{errors.projectId && (
<small className="danger-text">{errors.projectId.message}</small>
)}
</div>
<div className="col-md-6">
<Label htmlFor="branchName" className="form-label" required>
Branch Name
</Label>
<input
type="text"
id="branchName"
className="form-control form-control-sm"
{...register("branchName")}
placeholder="Enter Branch"
/>
{errors.branchName && (
<small className="danger-text">{errors.branchName.message}</small>
)}
</div>
</div>
<div className="row my-2 text-start">
<div className="col-md-6">
<Label htmlFor="contactInformation" className="form-label" required>
Contact Information
</Label>
<input
type="text"
id="contactInformation"
className="form-control form-control-sm"
{...register("contactInformation")}
/>
{errors.contactInformation && (
<small className="danger-text">{errors.contactInformation.message}</small>
)}
</div>
<div className="col-md-6">
<Label htmlFor="address" className="form-label" required>
Address
</Label>
<input
type="text"
id="address"
className="form-control form-control-sm"
{...register("address")}
/>
{errors.address && (
<small className="danger-text">{errors.address.message}</small>
)}
</div>
</div>
<div className="row my-2 text-start">
<div className="col-md-6">
<Label htmlFor="branchType" className="form-label" required>
Branch Type
</Label>
<input
type="text"
id="branchType"
className="form-control form-control-sm"
{...register("branchType")}
/>
{errors.branchType && (
<small className="danger-text">{errors.branchType.message}</small>
)}
</div>
<div className="col-md-6">
<Label htmlFor="googleMapUrl" className="form-label" required>
Google Map URL
</Label>
<input
type="text"
id="googleMapUrl"
className="form-control form-control-sm"
{...register("googleMapUrl")}
/>
{errors.googleMapUrl && (
<small className="danger-text">{errors.googleMapUrl.message}</small>
)}
</div>
</div>
<div className="d-flex justify-content-end gap-3">
<button
type="reset"
onClick={handleClose}
className="btn btn-label-secondary btn-sm mt-3"
>
Cancel
</button>
<button type="submit" className="btn btn-primary btn-sm mt-3">
Submit
</button>
</div>
</form>
</div>
)
}
export default ManageBranch

View File

@ -0,0 +1,170 @@
import React, { useState } from 'react'
import GlobalModel from '../common/GlobalModel';
import ManageBranch from './ManageBranch';
const ServiceBranch = () => {
const [ManageServiceBranch, setManageServiceBranch] = useState({
IsOpen: null,
branchId: null,
});
const ServiceBranch = [
{
key: "branchName",
label: "Branch Name",
align: "text-start",
getValue: (e) => e?.payee || "N/A",
},
];
return (
<div className="card h-100 table-responsive px-sm-4">
<div className="card-datatable" id="payment-request-table">
<div className="row align-items-center justify-content-end mt-3">
<div className="col-md-4 col-sm-12 text-md-end text-end">
<button
className="btn btn-sm btn-primary"
type="button"
onClick={() =>
setManageServiceBranch({
IsOpen: true,
branchId: null,
})
}
>
<i className="bx bx-plus-circle me-2"></i>
<span className="d-none d-md-inline-block">
Add Branch
</span>
</button>
</div>
</div>
<div className="mx-2">
{/* {Array.isArray(filteredData) && filteredData.length > 0 && ( */}
<table className="table border-top dataTable text-nowrap align-middle">
<thead>
<tr>
{ServiceBranch.map((col) => (
<th key={col.key} className={`sorting ${col.align}`}>
{col.label}
</th>
))}
<th className="text-center">Action</th>
</tr>
</thead>
{/* <tbody>
{filteredData?.length > 0 ? (
filteredData?.map((recurringExpense) => (
<tr
key={recurringExpense.id}
className="align-middle"
style={{ height: "40px" }}
>
{recurringExpenseColumns.map((col) => (
<td
key={col.key}
className={`d-table-cell ${col.align ?? ""} py-3`}
>
{col?.customRender
? col?.customRender(recurringExpense)
: col?.getValue(recurringExpense)}
</td>
))}
<td className="sticky-action-column bg-white">
<div className="d-flex flex-row gap-2 gap-0">
<i
className="bx bx-show text-primary cursor-pointer"
onClick={() =>
setViewRecurring({
recurringId: recurringExpense?.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">
<li
onClick={() =>
setManageServiceBranch({
IsOpen: true,
RecurringId: recurringExpense?.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(recurringExpense.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>
</div>
</td>
</tr>
))
) : (
<tr>
<td
colSpan={recurringExpenseColumns?.length + 1}
className="text-center border-0 py-8"
></td>
</tr>
)}
</tbody> */}
</table>
{/* )} */}
{/* {!filteredData ||
filteredData.length === 0
&& (
<div className="d-flex justify-content-center align-items-center h-64">
{isError ? (<p>{error.message}</p>) : (<p>No Recurring Expense Found</p>)}
</div>
)} */}
</div>
{ManageServiceBranch.IsOpen && (
<GlobalModel
isOpen
size="lg"
closeModal={() =>
setManageServiceBranch({ IsOpen: null, branchId: null })
}
>
<ManageBranch
key={ManageServiceBranch.branchId ?? "new"}
closeModal={() =>
setManageServiceBranch({ IsOpen: null, branchId: null })
}
// requestToEdit={ManageServiceBranch.branchId}
/>
</GlobalModel>
)}
</div>
</div>
)
}
export default ServiceBranch

View File

@ -5,6 +5,7 @@ import { formatUTCToLocalTime } from "../../utils/dateUtils";
import ManageServiceProject from "./ManageServiceProject";
import GlobalModel from "../common/GlobalModel";
import { SpinnerLoader } from "../common/Loader";
import ServiceBranch from "./ServiceBranch";
const ServiceProjectProfile = () => {
const { projectId } = useParams();
@ -114,6 +115,10 @@ const ServiceProjectProfile = () => {
</div>
</div>
<div className="col-md-6 col-lg-8 order-2 mb-6">
<ServiceBranch/>
</div>
</div>
</>

View File

@ -52,3 +52,5 @@ export const ServiceProjectRepository = {
GetBranchDetail: (id) => api.get(`/api/ServiceProject/branch/details/${id}`),
DeleteBranch: (id) => api.delete(`/api/ServiceProject/branch/delete/${id}`),
};