326 lines
10 KiB
JavaScript
326 lines
10 KiB
JavaScript
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import React, { useEffect, useState } from "react";
|
|
import { defaultProjectValues } from "./ServiceProjectSchema";
|
|
import Label from "../common/Label";
|
|
import { FormProvider, useForm } from "react-hook-form";
|
|
import {
|
|
useGlobalServices,
|
|
useServices,
|
|
} from "../../hooks/masterHook/useMaster";
|
|
import { ITEMS_PER_PAGE, PROJECT_STATUS } from "../../utils/constants";
|
|
import DatePicker from "../common/DatePicker";
|
|
|
|
import SelectMultiple from "../common/SelectMultiple";
|
|
import { projectSchema } from "./ServiceProjectSchema";
|
|
import {
|
|
useOrganization,
|
|
useOrganizationModal,
|
|
useOrganizationsList,
|
|
} from "../../hooks/useOrganization";
|
|
import { error } from "pdf-lib";
|
|
import {
|
|
useCreateServiceProject,
|
|
useServiceProject,
|
|
useUpdateServiceProject,
|
|
} from "../../hooks/useServiceProject";
|
|
|
|
const ManageServiceProject = ({ serviceProjectId, onClose }) => {
|
|
const {
|
|
data: projectdata,
|
|
isLoading: isProjectLoading,
|
|
isProjectError,
|
|
} = useServiceProject(serviceProjectId);
|
|
const [searchText, setSearchText] = useState("");
|
|
const methods = useForm({
|
|
resolver: zodResolver(projectSchema),
|
|
defaultValues: defaultProjectValues,
|
|
});
|
|
const {
|
|
register,
|
|
handleSubmit,
|
|
control,
|
|
reset,
|
|
formState: { errors },
|
|
} = methods;
|
|
const { data, isError, isLoading } = useServices();
|
|
const { data: organization, isLoading: isLoadingOrganization } =
|
|
useOrganizationsList(ITEMS_PER_PAGE, 1, true);
|
|
|
|
const { mutate: CreateServiceProject, isPending } = useCreateServiceProject(
|
|
() => {
|
|
reset();
|
|
onClose();
|
|
}
|
|
);
|
|
const { mutate: UpdateServiceProject, isPending: isUpdating } =
|
|
useUpdateServiceProject(() => {
|
|
reset();
|
|
onClose();
|
|
});
|
|
const onSubmit = (formData) => {
|
|
|
|
if (serviceProjectId) {
|
|
let existingServiceIds = projectdata?.services?.map((s) => s.id) || [];
|
|
|
|
const oldServices = projectdata.services.map((service) => ({
|
|
serviceId: service.id,
|
|
isActive: formData.services.includes(service.id),
|
|
}));
|
|
|
|
const newServices = formData.services
|
|
.filter((s) => !existingServiceIds.includes(s))
|
|
.map((service) => ({ serviceId: service, isActive: true }));
|
|
|
|
formData.services = [...oldServices, ...newServices];
|
|
} else {
|
|
formData.services = formData.services.map((service) => ({
|
|
serviceId: service,
|
|
isActive: true,
|
|
}));
|
|
}
|
|
|
|
if (serviceProjectId) {
|
|
let payload = { ...formData, id: serviceProjectId };
|
|
|
|
UpdateServiceProject({ id: serviceProjectId, payload });
|
|
} else {
|
|
CreateServiceProject(formData);
|
|
}
|
|
};
|
|
|
|
useEffect(() => {
|
|
if (projectdata) {
|
|
const activeServiceIds = (projectdata.services || [])
|
|
.map((s) => s.serviceId || s.id)
|
|
.filter(Boolean);
|
|
reset({
|
|
name: projectdata.name,
|
|
shortName: projectdata.shortName,
|
|
clientId: projectdata.client.id,
|
|
assignedDate: projectdata.assignedDate,
|
|
address: projectdata.address,
|
|
statusId: projectdata.status.id,
|
|
contactEmail: projectdata.contactEmail,
|
|
contactPhone: projectdata.contactPhone,
|
|
contactName: projectdata.contactName,
|
|
services: activeServiceIds,
|
|
});
|
|
}
|
|
}, [projectdata]);
|
|
|
|
const { onOpen: openOrgModal } = useOrganizationModal();
|
|
return (
|
|
<FormProvider {...methods}>
|
|
<form className="px-3 py-2" onSubmit={handleSubmit(onSubmit)}>
|
|
<div className=" text-center">
|
|
<h5>{serviceProjectId ? "Update Service Project" : "Create Service Project"}</h5>
|
|
</div>
|
|
<div className="row text-start">
|
|
<div className="col-12 mb-2">
|
|
<Label htmlFor="name" required>
|
|
Client
|
|
</Label>
|
|
<div className="d-flex align-items-center gap-2">
|
|
<select
|
|
className="select2 form-select form-select-sm flex-grow-1"
|
|
aria-label="Default select example"
|
|
{...register("clientId", {
|
|
required: "Client is required",
|
|
valueAsNumber: false,
|
|
})}
|
|
>
|
|
{isLoading ? (
|
|
<option>Loading...</option>
|
|
) : (
|
|
<>
|
|
<option value="">Select Client</option>
|
|
{organization?.data?.map((org) => (
|
|
<option key={org.id} value={org.id}>
|
|
{org.name}
|
|
</option>
|
|
))}
|
|
</>
|
|
)}
|
|
</select>
|
|
<i
|
|
className="bx bx-plus-circle bx-xs cursor-pointer text-primary"
|
|
onClick={() => {
|
|
onClose();
|
|
openOrgModal({ startStep: 2 }); // Step 4 = ManagOrg
|
|
}}
|
|
/>
|
|
</div>
|
|
{errors?.clientId && (
|
|
<span className="danger-text">{errors.clientId.message}</span>
|
|
)}
|
|
</div>
|
|
|
|
<div className="col-12 mb-2">
|
|
<Label htmlFor="name" required>
|
|
Project Name
|
|
</Label>
|
|
<input
|
|
type="text"
|
|
className="form-control form-control-sm"
|
|
{...register("name")}
|
|
placeholder="Enter Project Name.."
|
|
/>
|
|
{errors?.name && (
|
|
<span className="danger-text">{errors.name.message}</span>
|
|
)}
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-2">
|
|
<Label htmlFor="name" required>
|
|
Short Name (Short Project Name)
|
|
</Label>
|
|
<input
|
|
type="text"
|
|
className="form-control form-control-sm"
|
|
{...register("shortName")}
|
|
placeholder="Enter Project Short Name.."
|
|
/>
|
|
{errors?.shortName && (
|
|
<span className="danger-text">{errors.shortName.message}</span>
|
|
)}
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-2">
|
|
<Label htmlFor="name" required>
|
|
Select Status
|
|
</Label>
|
|
<select
|
|
className="form-select form-select-sm"
|
|
{...register("statusId")}
|
|
>
|
|
<option>Select Service</option>
|
|
{PROJECT_STATUS?.map((status) => (
|
|
<option key={status.id} value={status.id}>{status.label}</option>
|
|
))}
|
|
</select>
|
|
{errors?.statusId && (
|
|
<span className="danger-text">{errors.statusId.message}</span>
|
|
)}
|
|
</div>
|
|
<div className="col-12 mb-2">
|
|
<SelectMultiple
|
|
options={data?.data}
|
|
isLoading={isLoading}
|
|
name="services"
|
|
labelKey="name"
|
|
valueKey="id"
|
|
label="Select Service"
|
|
/>
|
|
{errors?.services && (
|
|
<span className="danger-text">{errors.services.message}</span>
|
|
)}
|
|
</div>
|
|
|
|
<div className="col-12 col-md-6 mb-2">
|
|
<Label htmlFor="name" required>
|
|
Contact Person
|
|
</Label>
|
|
<input
|
|
type="text"
|
|
className="form-control form-control-sm"
|
|
{...register("contactName")}
|
|
placeholder="Enter Employee name.."
|
|
/>
|
|
{errors?.contactName && (
|
|
<span className="danger-text">{errors.contactName.message}</span>
|
|
)}
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-2">
|
|
<Label htmlFor="name" required>
|
|
Contact Email
|
|
</Label>
|
|
<input
|
|
type="text"
|
|
className="form-control form-control-sm"
|
|
{...register("contactEmail")}
|
|
placeholder="Enter Employee Email.."
|
|
/>
|
|
{errors?.contactEmail && (
|
|
<span className="danger-text">{errors.contactEmail.message}</span>
|
|
)}
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-2">
|
|
<Label htmlFor="name" required>
|
|
Contact Number
|
|
</Label>
|
|
<input
|
|
type="text"
|
|
maxLength={10}
|
|
className="form-control form-control-sm"
|
|
{...register("contactPhone")}
|
|
placeholder="Enter Employee Contact.."
|
|
onInput={(e) => {
|
|
e.target.value = e.target.value.replace(/[^0-9]/g, "");
|
|
}}
|
|
/>
|
|
{errors?.contactPhone && (
|
|
<span className="danger-text">{errors.contactPhone.message}</span>
|
|
)}
|
|
</div>
|
|
<div className="col-12 col-md-6 mb-2">
|
|
<Label htmlFor="name" required>
|
|
Assign Date
|
|
</Label>
|
|
<DatePicker
|
|
name="assignedDate"
|
|
className="w-100"
|
|
control={control}
|
|
/>
|
|
</div>
|
|
<div className="col-12 col-md-12 mb-2">
|
|
<Label htmlFor="address" required>
|
|
Project Address
|
|
</Label>
|
|
<div className="input-group">
|
|
<textarea
|
|
name="address"
|
|
className="form-control"
|
|
// maxLength={maxAddressLength}
|
|
{...register("address")}
|
|
placeholder="Enter Project Address.."
|
|
// onChange={(e) => {
|
|
// setAddressLength(e.target.value.length);
|
|
// }}
|
|
/>
|
|
</div>
|
|
|
|
<div className="text-end" style={{ fontSize: "12px" }}>
|
|
{/* {maxAddressLength - addressLength} characters left */}
|
|
</div>
|
|
{errors.address && (
|
|
<div className="danger-text text-start">
|
|
{errors.address.message}
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
<div className="d-flex justify-content-end gap-4">
|
|
<button
|
|
className="btn btn-sm btn-outline-secondary"
|
|
disabled={isPending || isUpdating}
|
|
onClick={onClose}
|
|
>
|
|
Cancel
|
|
</button>
|
|
<button
|
|
type="submit"
|
|
className="btn btn-sm btn-primary"
|
|
disabled={isPending || isUpdating}
|
|
>
|
|
{isPending || isUpdating
|
|
? "Please wait..."
|
|
: serviceProjectId
|
|
? "Update"
|
|
: "Submit"}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</FormProvider>
|
|
);
|
|
};
|
|
|
|
export default ManageServiceProject;
|