From c8aebd521a826fbda37826437ff7cf1707091346 Mon Sep 17 00:00:00 2001 From: anshux1 Date: Sun, 6 Oct 2024 00:57:51 +0530 Subject: [PATCH] Feat: Allow Users to Edit and Delete Their Own Comments in shifting and Resource --- src/Components/Common/components/Menu.tsx | 16 ++- src/Components/Resource/CommentSection.tsx | 128 +++++++++++++++++--- src/Components/Shifting/CommentsSection.tsx | 107 ++++++++++++++-- src/Redux/api.tsx | 22 ++++ 4 files changed, 240 insertions(+), 33 deletions(-) diff --git a/src/Components/Common/components/Menu.tsx b/src/Components/Common/components/Menu.tsx index 14dabfe9781..5080041e8a6 100644 --- a/src/Components/Common/components/Menu.tsx +++ b/src/Components/Common/components/Menu.tsx @@ -9,7 +9,8 @@ import { useIsAuthorized } from "../../../Common/hooks/useIsAuthorized"; interface DropdownMenuProps { id?: string; - title: string; + title?: string; + dropdownIconVisible?: boolean; variant?: ButtonVariant; size?: ButtonSize; icon?: JSX.Element | undefined; @@ -23,6 +24,7 @@ interface DropdownMenuProps { export default function DropdownMenu({ variant = "primary", size = "default", + dropdownIconVisible = true, ...props }: DropdownMenuProps) { return ( @@ -42,12 +44,14 @@ export default function DropdownMenu({ )} > {props.icon} - {props.title || "Dropdown"} + {props.title} - + {dropdownIconVisible && ( + + )} { const [commentBox, setCommentBox] = useState(""); @@ -80,26 +84,114 @@ const CommentSection = (props: { id: string }) => { export default CommentSection; export const Comment = ({ + id, comment, created_by_object, modified_date, -}: IComment) => ( -
-
-

{comment}

-
-
- - {formatDateTime(modified_date) || "-"} - -
-
-
- {created_by_object?.first_name?.charAt(0) || "U"} +}: IComment) => { + const [isCommentEditable, setIsCommentEditable] = useState(false); + const [openDeleteCommentDialog, setOpenDeleteCommentDialog] = useState(false); + const [commentBox, setCommentBox] = useState(comment); + const { user } = useAuthContext(); + const handleResourceDelete = async () => { + const { res, error } = await request(routes.deleteResourceComments, { + pathParams: { id }, + }); + if (res?.status === 204) { + Notification.Success({ + msg: "Comment deleted successfully", + }); + } else { + Notification.Error({ + msg: "Error while deleting comment: " + (error || ""), + }); + } + }; + const handleCommentUpdate = async () => { + const payload = { + comment: commentBox, + }; + if (!/\S+/.test(commentBox)) { + Notification.Error({ + msg: "Comment Should Contain At Least 1 Character", + }); + return; + } + const { res } = await request(routes.updateResourceComments, { + pathParams: { id }, + body: payload, + }); + if (res?.ok) { + Notification.Success({ msg: "Comment updated successfully" }); + } + setCommentBox(""); + setIsCommentEditable((prev) => !prev); + }; + return ( +
+
+ {isCommentEditable ? ( + setCommentBox(e.value)} + className="w-full" + /> + ) : ( +

{comment}

+ )} + {created_by_object.id === user?.id && ( + } + dropdownIconVisible={false} + className="w-fit bg-white px-4 hover:bg-transparent" + containerClassName="mr-4" + > + setIsCommentEditable((prev) => !prev)} + > + Edit + + setOpenDeleteCommentDialog(true)} + > + Delete + + + )}
- - {formatName(created_by_object)} - +
+ + {formatDateTime(modified_date) || "-"} + +
+
+
+
+ {created_by_object?.first_name?.charAt(0) || "U"} +
+ + {formatName(created_by_object)} + +
+ {isCommentEditable && ( + + Update + + )} +
+ setOpenDeleteCommentDialog(false)} + onConfirm={handleResourceDelete} + />
-
-); + ); +}; diff --git a/src/Components/Shifting/CommentsSection.tsx b/src/Components/Shifting/CommentsSection.tsx index ff826bec63f..3cccc31f1ff 100644 --- a/src/Components/Shifting/CommentsSection.tsx +++ b/src/Components/Shifting/CommentsSection.tsx @@ -8,6 +8,11 @@ import routes from "../../Redux/api"; import { IComment } from "../Resource/models"; import PaginatedList from "../../CAREUI/misc/PaginatedList"; import request from "../../Utils/request/request"; +import { useAuthContext } from "../../Common/hooks/useAuthUser"; +import DropdownMenu, { DropdownItem } from "../Common/components/Menu"; +import CareIcon from "../../CAREUI/icons/CareIcon"; +import TextAreaFormField from "../Form/FormFields/TextAreaFormField"; +import ConfirmDialog from "../Common/ConfirmDialog"; interface CommentSectionProps { id: string; @@ -88,28 +93,112 @@ export const Comment = ({ created_by_object, modified_date, }: IComment) => { - const { t } = useTranslation(); + const [isCommentEditable, setIsCommentEditable] = useState(false); + const [openDeleteCommentDialog, setOpenDeleteCommentDialog] = useState(false); + const [commentBox, setCommentBox] = useState(comment); + const { user } = useAuthContext(); + const handleResourceDelete = async () => { + const { res, error } = await request(routes.deleteShiftComments, { + pathParams: { id }, + }); + if (res?.status === 204) { + Notification.Success({ + msg: "Comment deleted successfully", + }); + } else { + Notification.Error({ + msg: "Error while deleting comment: " + (error || ""), + }); + } + }; + const handleCommentUpdate = async () => { + const payload = { + comment: commentBox, + }; + if (!/\S+/.test(commentBox)) { + Notification.Error({ + msg: "Comment Should Contain At Least 1 Character", + }); + return; + } + const { res } = await request(routes.updateShiftComments, { + pathParams: { id }, + body: payload, + }); + if (res?.ok) { + Notification.Success({ msg: "Comment updated successfully" }); + } + setCommentBox(""); + setIsCommentEditable((prev) => !prev); + }; return (
-
-

{comment}

+
+ {isCommentEditable ? ( + setCommentBox(e.value)} + className="w-full" + /> + ) : ( +

{comment}

+ )} + {created_by_object.id === user?.id && ( + } + dropdownIconVisible={false} + className="w-fit bg-white px-4 hover:bg-transparent" + containerClassName="mr-4" + > + setIsCommentEditable((prev) => !prev)} + > + Edit + + setOpenDeleteCommentDialog(true)} + > + Delete + + + )}
{modified_date ? formatDateTime(modified_date) : "-"}
-
-
- {created_by_object?.first_name?.charAt(0) || t("unknown")} +
+
+
+ {created_by_object?.first_name?.charAt(0) || "U"} +
+ + {formatName(created_by_object)} +
- - {formatName(created_by_object)} - + {isCommentEditable && ( + + Update + + )}
+ setOpenDeleteCommentDialog(false)} + onConfirm={handleResourceDelete} + />
); }; diff --git a/src/Redux/api.tsx b/src/Redux/api.tsx index a0c3271386d..a6479c1c12d 100644 --- a/src/Redux/api.tsx +++ b/src/Redux/api.tsx @@ -1138,6 +1138,17 @@ const routes = { TBody: Type>(), TRes: Type(), }, + updateShiftComments: { + path: "/api/v1/resource/{id}/comment/", + method: "PUT", + TRes: Type(), + TBody: Type>(), + }, + deleteShiftComments: { + path: "/api/v1/resource/{id}/comment/", + method: "DELETE", + TRes: Type>(), + }, // Notifications getNotifications: { @@ -1310,6 +1321,17 @@ const routes = { TRes: Type(), TBody: Type>(), }, + updateResourceComments: { + path: "/api/v1/resource/{id}/comment/", + method: "PUT", + TRes: Type(), + TBody: Type>(), + }, + deleteResourceComments: { + path: "/api/v1/resource/{id}/comment/", + method: "DELETE", + TRes: Type>(), + }, // Assets endpoints