marco.pms.web/src/components/Project/ProjectPermission.jsx

225 lines
7.3 KiB
JavaScript

import React, { useCallback, useEffect } from "react";
import {
useProjectLevelEmployeePermission,
useProjectLevelModules,
useUpdateProjectLevelEmployeePermission,
} from "../../hooks/useProjects";
import { useSelectedProject } from "../../slices/apiDataManager";
import { useEmployeesByProject } from "../../hooks/useEmployees";
import { useForm, Controller } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import showToast from "../../services/toastService";
export const ProjectPermissionSchema = z.object({
employeeId: z.string().min(1, "Employee is required"),
selectedPermissions: z.array(z.string()).optional(),
});
const ProjectPermission = () => {
const selectedProject = useSelectedProject();
const { data: ProjectModules = [] } = useProjectLevelModules();
const { employees = [], loading } = useEmployeesByProject(selectedProject);
const {
register,
watch,
handleSubmit,
reset,
control,
setValue,
formState: { errors, isDirty },
} = useForm({
resolver: zodResolver(ProjectPermissionSchema),
defaultValues: {
employeeId: "",
selectedPermissions: [],
},
});
const selectedEmployee = watch("employeeId");
const { data: selectedEmpPermissions } = useProjectLevelEmployeePermission(
selectedEmployee || "",
selectedProject
);
useEffect(() => {
if (!selectedEmployee) return;
const enabledPerms =
selectedEmpPermissions?.permissions
?.filter((perm) => perm.isEnabled)
?.map((perm) => perm.id) || [];
setValue("selectedPermissions", enabledPerms, { shouldValidate: true });
}, [selectedEmpPermissions, setValue, selectedEmployee]);
const selectedPermissions = watch("selectedPermissions") || [];
const existingEnabledIds =
selectedEmpPermissions?.permissions
?.filter((p) => p.isEnabled)
?.map((p) => p.id) || [];
const hasChanges =
selectedPermissions.length !== existingEnabledIds.length ||
selectedPermissions.some((id) => !existingEnabledIds.includes(id));
const { mutate: updatePermission, isPending } =
useUpdateProjectLevelEmployeePermission();
const onSubmit = (formData) => {
if (!formData.employeeId) {
showToast("Please select an employee", "warn");
return;
}
const existingPermissions = selectedEmpPermissions?.permissions || [];
const existingEnabledIds = existingPermissions
.filter((p) => p.isEnabled)
.map((p) => p.id);
const newSelectedIds = formData.selectedPermissions || [];
const added = newSelectedIds
.filter((id) => !existingEnabledIds.includes(id))
.map((id) => ({ id, isEnabled: true }));
const removed = existingEnabledIds
.filter((id) => !newSelectedIds.includes(id))
.map((id) => ({ id, isEnabled: false }));
const payloadPermissions = [...added, ...removed];
if (payloadPermissions.length === 0) {
showToast("No changes detected", "info");
return;
}
const payload = {
employeeId: formData.employeeId,
projectId: selectedProject,
permission: payloadPermissions,
};
console.log("Final payload:", payload);
updatePermission(payload);
};
return (
<div className="w-100 p py-1 ">
<div className="text-start m-0">
<p className="fw-semibold fs-6">Project Permission</p>
</div>
<form className="row" onSubmit={handleSubmit(onSubmit)}>
<div className="d-flex justify-content-between align-items-end gap-2 mb-3">
<div className="text-start d-flex align-items-center gap-2">
<div className="d-block">
<label className="form-label">Select Employee</label>
</div>
<div className="d-block">
{" "}
<select
className="form-select form-select-sm"
{...register("employeeId")}
disabled={isPending}
>
{loading ? (
<option value="">Loading...</option>
) : (
<>
<option value="">-- Select Employee --</option>
{[...employees]
?.sort((a, b) =>
`${a?.firstName} ${a?.firstName}`?.localeCompare(
`${b?.firstName} ${b?.lastName}`
)
)
?.map((emp) => (
<option key={emp.id} value={emp.id}>
{emp.firstName} {emp.lastName}
</option>
))}
</>
)}
</select>
{errors.employeeId && (
<div className="d-block text-danger small">
{errors.employeeId.message}
</div>
)}
</div>
</div>
<div className="mt-3 text-end">
{hasChanges && (
<button
type="submit"
className="btn btn-sm btn-primary"
disabled={isPending || loading}
>
{isPending ? "Please Wait..." : "Save Permission"}
</button>
)}
</div>
</div>
{ProjectModules.map((feature) => (
<div
key={feature.id}
className="col-12 col-sm-6 col-md-4 col-lg-4 mb-4"
>
<div className="card text-start border-1 p-1">
<p className="card-title fs-6 fw-semibold">{feature.name}</p>
<div className="px-2">
<ul className="list-unstyled">
{feature.featurePermissions?.map((perm) => (
<div className="d-flex my-2" key={perm.id}>
<Controller
name="selectedPermissions"
control={control}
render={({ field }) => {
const value = field.value || [];
const isChecked = value.includes(perm.id);
return (
<label
className="form-check-label d-flex align-items-center"
htmlFor={perm.id}
>
<input
type="checkbox"
className="form-check-input me-2"
id={perm.id}
checked={isChecked}
onChange={(e) => {
if (e.target.checked) {
field.onChange([...value, perm.id]); // add
} else {
field.onChange(
value.filter((v) => v !== perm.id)
); // remove
}
}}
/>
{perm.name}
</label>
);
}}
/>
</div>
))}
</ul>
</div>
</div>
</div>
))}
</form>
</div>
);
};
export default ProjectPermission;