marco.pms.web/src/components/Directory/NotesDirectory.jsx
2025-07-26 12:07:12 +05:30

231 lines
7.3 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, { useEffect, useState } from "react";
import Editor from "../common/TextEditor/Editor";
import Avatar from "../common/Avatar";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
import { DirectoryRepository } from "../../repositories/DirectoryRepository";
import moment from "moment";
import { cacheData, getCachedData } from "../../slices/apiDataManager";
import NoteCardDirectory from "./NoteCardDirectory";
import showToast from "../../services/toastService";
import { useContactNotes } from "../../hooks/useDirectory";
const schema = z.object({
note: z.string().min(1, { message: "Note is required" }),
});
const NotesDirectory = ({
refetchProfile,
isLoading,
contactProfile, // This contactProfile now reliably includes firstName, middleName, lastName, and fullName
setProfileContact,
}) => {
const [IsActive, setIsActive] = useState(true);
const { contactNotes, refetch } = useContactNotes(
contactProfile?.id,
IsActive
);
const [IsSubmitting, setIsSubmitting] = useState(false);
const [showEditor, setShowEditor] = useState(false);
const {
register,
handleSubmit,
setValue,
watch,
formState: { errors },
} = useForm({
resolver: zodResolver(schema),
defaultValues: {
note: "",
},
});
const noteValue = watch("note");
const handleEditorChange = (value) => {
setValue("note", value, { shouldValidate: true });
};
const onSubmit = async (data) => {
const newNote = { ...data, contactId: contactProfile?.id };
try {
setIsSubmitting(true);
const response = await DirectoryRepository.CreateNote(newNote);
const createdNote = response.data;
setProfileContact((prev) => ({
...prev,
notes: [...(prev.notes || []), createdNote],
}));
const cached_contactProfile = getCachedData("Contact Profile");
if (
cached_contactProfile &&
cached_contactProfile.contactId === contactProfile?.id
) {
const updatedProfile = {
...cached_contactProfile.data,
notes: [...(cached_contactProfile.data.notes || []), createdNote],
};
cacheData("Contact Profile", {
contactId: contactProfile?.id,
data: updatedProfile,
});
}
setValue("note", "");
setIsSubmitting(false);
showToast("Note added successfully!", "success");
setShowEditor(false);
setIsActive(true);
refetch(contactProfile?.id, true);
} catch (error) {
setIsSubmitting(false);
const msg =
error.response?.data?.message ||
error.message ||
"Error occurred during API calling";
showToast(msg, "error");
}
};
const onCancel = () => {
setValue("note", "");
setShowEditor(false);
};
const handleSwitch = () => {
setIsActive((prevIsActive) => {
const newState = !prevIsActive;
refetch(contactProfile?.id, newState);
return newState;
});
};
// Use the fullName from contactProfile, which now includes middle and last names if available
const contactName =
contactProfile?.fullName || contactProfile?.firstName || "Contact";
const noNotesMessage = `Be the first to share your insights! ${contactName} currently has no notes.`;
const notesToDisplay = IsActive
? contactProfile?.notes || []
: contactNotes || [];
return (
<div className="text-start mt-10">
<div className="d-flex align-items-center justify-content-between">
<div className="row w-100 align-items-center">
<div className="col col-2">
<p className="fw-semibold m-0 ms-3">Notes :</p>
</div>
<div className="col d-flex justify-content-end gap-2 pe-0">
{" "}
<div className="d-flex align-items-center justify-content-between">
<label
className="switch switch-primary"
style={{
visibility:
contactProfile?.notes?.length > 0 ||
contactNotes?.length > 0
? "visible"
: "hidden",
}}
>
<input
type="checkbox"
className="switch-input"
checked={!IsActive} // checked when showing *in*active notes (i.e., when IsActive is false)
onChange={handleSwitch}
/>
<span className="switch-toggle-slider">
<span className="switch-on"></span>
<span className="switch-off"></span>
</span>
<span className="switch-label">Include Deleted Notes</span>
</label>
{!showEditor && (
<div className="d-flex justify-content-end">
<button
type="button"
className="btn btn-sm d-flex align-items-center"
onClick={() => setShowEditor(true)}
style={{
color: "#6c757d",
backgroundColor: "transparent",
boxShadow: "none",
border: "none",
}}
>
<i
className="bx bx-plus-circle me-0 text-primary"
style={{ fontSize: "1.5rem", color: "#6c757d" }}
></i>
Add a Note
</button>
</div>
)}
</div>
</div>
</div>
</div>
{showEditor && (
<div className="card m-2 mb-5 position-relative">
<span
type="button"
className="position-absolute top-0 end-0  mt-3 bg-secondary rounded-circle"
aria-label="Close"
onClick={() => setShowEditor(false)}
>
<i className="bx bx-x fs-5  p-1 text-white"></i>
</span>
<form onSubmit={handleSubmit(onSubmit)}>
<Editor
value={noteValue}
loading={IsSubmitting}
onChange={handleEditorChange}
onCancel={onCancel}
onSubmit={handleSubmit(onSubmit)}
/>
{errors.note && (
<p className="text-danger small mt-1">{errors.note.message}</p>
)}
</form>
</div>
)}
<div className=" justify-content-start px-1 mt-1">
{isLoading && (
<div className="text-center">
{" "}
<p>Loading...</p>{" "}
</div>
)}
{!isLoading && notesToDisplay.length > 0
? notesToDisplay
.slice()
.reverse()
.map((noteItem) => (
<NoteCardDirectory
refetchProfile={refetchProfile}
refetchNotes={refetch}
refetchContact={refetch}
noteItem={noteItem}
contactId={contactProfile?.id}
setProfileContact={setProfileContact}
key={noteItem.id}
/>
))
: !isLoading &&
!showEditor && (
<div className="text-center mt-5">{noNotesMessage}</div>
)}
</div>
</div>
);
};
export default NotesDirectory;