marco.pms.web/src/components/ServiceProject/ManageServiceProject.jsx

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;