From a05368520e1c4c60bafd3230737c395cba23bb7f Mon Sep 17 00:00:00 2001 From: Bagasbanuna02 Date: Wed, 23 Apr 2025 17:47:16 +0800 Subject: [PATCH] fix stiker forum --- .../forum/detail/main-detail/[id]/page.tsx | 5 +- src/app/zCoba/text_editor2/page.tsx | 8 +- .../component/new/comp_set_inner_html.tsx | 23 +- .../forum/component/detail_one_komentar.tsx | 4 +- .../admin/forum/sub_menu/table_posting.tsx | 4 +- .../comp_V3_create.comment.tsx | 348 ++++++++++++++++++ .../detail_component/detail_list_komentar.tsx | 30 +- .../forum/detail/v3_main_detail.tsx | 193 ++++++++++ 8 files changed, 602 insertions(+), 13 deletions(-) create mode 100644 src/app_modules/forum/component/detail_component/comp_V3_create.comment.tsx create mode 100644 src/app_modules/forum/detail/v3_main_detail.tsx diff --git a/src/app/dev/forum/detail/main-detail/[id]/page.tsx b/src/app/dev/forum/detail/main-detail/[id]/page.tsx index 325f4bb7..0a8c52f1 100644 --- a/src/app/dev/forum/detail/main-detail/[id]/page.tsx +++ b/src/app/dev/forum/detail/main-detail/[id]/page.tsx @@ -1,12 +1,13 @@ import { funGetUserIdByToken } from "@/app_modules/_global/fun/get"; import Forum_MainDetail from "@/app_modules/forum/detail/main_detail"; +import Forum_V3_MainDetail from "@/app_modules/forum/detail/v3_main_detail"; export default async function Page() { const userLoginId = await funGetUserIdByToken(); return ( <> - - {/* */} + {/* */} + ); } diff --git a/src/app/zCoba/text_editor2/page.tsx b/src/app/zCoba/text_editor2/page.tsx index b868b363..e2bc55b6 100644 --- a/src/app/zCoba/text_editor2/page.tsx +++ b/src/app/zCoba/text_editor2/page.tsx @@ -70,12 +70,12 @@ export default function Page() { const style = document.createElement("style"); style.textContent = ` .ql-editor img { - max-width: 40px !important; - max-height: 40px !important; + max-width: 100px !important; + max-height: 100px !important; } .chat-content img { - max-width: 40px !important; - max-height: 40px !important; + max-width: 70px !important; + max-height: 70px !important; } `; document.head.appendChild(style); diff --git a/src/app_modules/_global/component/new/comp_set_inner_html.tsx b/src/app_modules/_global/component/new/comp_set_inner_html.tsx index a2c24945..36c43239 100644 --- a/src/app_modules/_global/component/new/comp_set_inner_html.tsx +++ b/src/app_modules/_global/component/new/comp_set_inner_html.tsx @@ -1,19 +1,38 @@ import React from "react"; import { MainColor } from "../../color"; +import { useShallowEffect } from "@mantine/hooks"; -export function Component_SetInnerHtml({ +export function Comp_DangerouslySetInnerHTML({ props, color, }: { - props: string + props: string; color?: string; }) { + // useShallowEffect(() => { + // // Add custom style for stickers inside Quill editor + // const style = document.createElement("style"); + // style.textContent = ` + // .chat-content img { + // max-width: 100px !important; + // max-height: 100px !important; + // } + // `; + // document.head.appendChild(style); + + // return () => { + // // Clean up when component unmounts + // document.head.removeChild(style); + // }; + // }, []); + return ( <>
- + diff --git a/src/app_modules/admin/forum/sub_menu/table_posting.tsx b/src/app_modules/admin/forum/sub_menu/table_posting.tsx index e8712bbf..cac44ce3 100644 --- a/src/app_modules/admin/forum/sub_menu/table_posting.tsx +++ b/src/app_modules/admin/forum/sub_menu/table_posting.tsx @@ -27,7 +27,7 @@ import { ComponentAdminGlobal_TitlePage } from "../../_admin_global/_component"; import { Admin_V3_ComponentPaginationBreakpoint } from "../../_components_v3/comp_pagination_breakpoint"; import ComponentAdminForum_ButtonDeletePosting from "../component/button_delete"; import { apiGetAdminForumPublish } from "../lib/api_fetch_admin_forum"; -import { Component_SetInnerHtml } from "@/app_modules/_global/component/new/comp_set_inner_html"; +import { Comp_DangerouslySetInnerHTML } from "@/app_modules/_global/component/new/comp_set_inner_html"; export default function AdminForum_TablePosting() { return ( @@ -134,7 +134,7 @@ function TablePublish() { hideLabel="sembunyikan" showLabel="tampilkan" > - + diff --git a/src/app_modules/forum/component/detail_component/comp_V3_create.comment.tsx b/src/app_modules/forum/component/detail_component/comp_V3_create.comment.tsx new file mode 100644 index 00000000..e25a4c71 --- /dev/null +++ b/src/app_modules/forum/component/detail_component/comp_V3_create.comment.tsx @@ -0,0 +1,348 @@ +"use client"; + +import { MainColor } from "@/app_modules/_global/color/color_pallet"; +import ComponentGlobal_InputCountDown from "@/app_modules/_global/component/input_countdown"; +import { ComponentGlobal_NotifikasiBerhasil } from "@/app_modules/_global/notif_global/notifikasi_berhasil"; +import { ComponentGlobal_NotifikasiGagal } from "@/app_modules/_global/notif_global/notifikasi_gagal"; +import notifikasiToUser_funCreate from "@/app_modules/notifikasi/fun/create/create_notif_to_user"; +import { clientLogger } from "@/util/clientLogger"; +import mqtt_client from "@/util/mqtt_client"; +import { + ActionIcon, + Box, + Button, + Group, + Image, + Modal, + Paper, + ScrollArea, + SimpleGrid, + Stack, + Text, + Tooltip, +} from "@mantine/core"; +import { useRouter } from "next/navigation"; +import React, { useState } from "react"; +import { forum_funCreateKomentar } from "../../fun/create/fun_create_komentar"; +import { MODEL_FORUM_POSTING } from "../../model/interface"; +import dynamic from "next/dynamic"; +import { useDisclosure, useShallowEffect } from "@mantine/hooks"; +import { listStiker } from "@/app_modules/_global/lib/stiker"; +import CustomSkeleton from "@/app_modules/components/CustomSkeleton"; +import { maxInputLength } from "@/app_modules/_global/lib/maximal_setting"; +import { funReplaceHtml } from "@/app_modules/_global/fun/fun_replace_html"; +import { IconMoodSmileFilled, IconX } from "@tabler/icons-react"; + +const ReactQuill = dynamic( + async () => { + const { default: RQ } = await import("react-quill"); + // Tidak perlu import CSS dengan import statement + return function comp({ forwardedRef, ...props }: any) { + return ; + }; + }, + { + ssr: false, + loading: () => ( + + Ketik pesan di sini atau tambahkan stiker... + + ), + } +); + +type ChatItem = { + content: string; // HTML content including text and stickers +}; +export default function Forum_V3_CreateKomentar({ + postingId, + data, + userLoginId, + onSetNewKomentar, +}: { + postingId: string; + data: MODEL_FORUM_POSTING; + userLoginId: string; + onSetNewKomentar: (val: boolean) => void; +}) { + const [loading, setLoading] = useState(false); + const [isComment, setIsComment] = useState(false); + + // New State + const [editorContent, setEditorContent] = useState(""); + const [opened, { open, close }] = useDisclosure(false); + const quillRef = React.useRef(null); + const [quillLoaded, setQuillLoaded] = useState(false); + + async function onComment() { + if (editorContent.length > 500) { + return null; + } + + try { + setLoading(true); + const createComment = await forum_funCreateKomentar( + postingId, + editorContent + ); + if (createComment.status === 201) { + onSetNewKomentar(true); + setEditorContent(""); + ComponentGlobal_NotifikasiBerhasil(createComment.message, 2000); + + if (userLoginId !== data.Author.id) { + const dataNotif = { + appId: data.id, + userId: data.authorId, + pesan: editorContent, + kategoriApp: "FORUM", + title: "Komentar baru", + }; + + const createNotifikasi = await notifikasiToUser_funCreate({ + data: dataNotif as any, + }); + + if (createNotifikasi.status === 201) { + mqtt_client.publish( + "USER", + JSON.stringify({ + userId: dataNotif.userId, + count: 1, + }) + ); + } + } + } else { + setLoading(false); + ComponentGlobal_NotifikasiGagal(createComment.message); + } + } catch (error) { + setLoading(false); + clientLogger.error("Error create komentar forum", error); + } + } + + useShallowEffect(() => { + // Add Quill CSS via tag + const link = document.createElement("link"); + link.href = "https://cdn.quilljs.com/1.3.6/quill.snow.css"; + link.rel = "stylesheet"; + document.head.appendChild(link); + + // Add custom style for stickers inside Quill editor + const style = document.createElement("style"); + style.textContent = ` + .ql-editor img { + max-width: 70px !important; + max-height: 70px !important; + } + // .chat-content img { + // max-width: 70px !important; + // max-height: 70px !important; + // } + `; + document.head.appendChild(style); + + setQuillLoaded(true); + + return () => { + // Clean up when component unmounts + document.head.removeChild(link); + document.head.removeChild(style); + }; + }, []); + + // Custom toolbar options for ReactQuill + const modules = { + toolbar: [ + [{ header: [1, 2, false] }], + ["bold", "italic", "underline", "strike", "blockquote"], + [{ list: "ordered" }, { list: "bullet" }], + ["link"], + ["clean"], + ], + }; + + const formats = [ + "header", + "bold", + "italic", + "underline", + "strike", + "blockquote", + "list", + "bullet", + "link", + "image", + ]; + + const insertSticker = (stickerUrl: string) => { + if (!quillRef.current) return; + + const quill = quillRef.current.getEditor(); + const range = quill.getSelection(true); + + // Custom image insertion with size + // Use custom blot or HTML string with size attributes + const stickerHtml = `sticker`; + + // Insert HTML at cursor position + quill.clipboard.dangerouslyPasteHTML(range.index, stickerHtml); + + // Move cursor after inserted sticker + quill.setSelection(range.index + 1, 0); + + // Focus back on editor + quill.focus(); + + // Close sticker modal + close(); + }; + + // // Function to send message + // const sendMessage = () => { + // if (editorContent.trim() !== "") { + // setChat((prev) => [...prev, { content: editorContent }]); + // setEditorContent(""); // Clear after sending + // } + // }; + + return ( + <> + + {isComment ? ( + + + + setIsComment(false)} variant="transparent"> + + + + + {quillLoaded && ( + + )} + + + + + + + + + + + + + + + ) : ( + setIsComment(true)} + py="sm" + px={"xl"} + withBorder + shadow="lg" + bg={MainColor.white} + > + + Ketik pesan di sini atau tambahkan stiker... + + + )} + + {/* {JSON.stringify(editorContent, null, 2)} */} + + {/* Sticker Modal */} + + + {listStiker.map((item) => ( + + + {item.name} insertSticker(item.url)} + /> + + + ))} + + + + {/* + + + + */} + + + ); +} diff --git a/src/app_modules/forum/component/detail_component/detail_list_komentar.tsx b/src/app_modules/forum/component/detail_component/detail_list_komentar.tsx index 1232555b..ed672fe1 100644 --- a/src/app_modules/forum/component/detail_component/detail_list_komentar.tsx +++ b/src/app_modules/forum/component/detail_component/detail_list_komentar.tsx @@ -4,6 +4,7 @@ import { Card, Divider, Spoiler, Stack, Text } from "@mantine/core"; import { MODEL_FORUM_KOMENTAR } from "../../model/interface"; import ComponentForum_KomentarAuthorNameOnHeader from "../komentar_component/komentar_author_header_name"; import { ComponentGlobal_CardStyles } from "@/app_modules/_global/component"; +import { useShallowEffect } from "@mantine/hooks"; export default function ComponentForum_KomentarView({ data, @@ -16,6 +17,30 @@ export default function ComponentForum_KomentarView({ postingId: string; userLoginId: string; }) { + useShallowEffect(() => { + + // Add custom style for stickers inside Quill editor + const style = document.createElement("style"); + style.textContent = ` + // .ql-editor img { + // max-width: 40px !important; + // max-height: 40px !important; + // } + .chat-content img { + max-width: 70px !important; + max-height: 70px !important; + } + `; + document.head.appendChild(style); + + // setQuillLoaded(true); + + return () => { + // Clean up when component unmounts + document.head.removeChild(style); + }; + }, []); + return ( <> @@ -38,7 +63,10 @@ export default function ComponentForum_KomentarView({ maxHeight={100} showLabel="tampilkan" > -
+
) : ( "" diff --git a/src/app_modules/forum/detail/v3_main_detail.tsx b/src/app_modules/forum/detail/v3_main_detail.tsx new file mode 100644 index 00000000..a8d9a5f8 --- /dev/null +++ b/src/app_modules/forum/detail/v3_main_detail.tsx @@ -0,0 +1,193 @@ +"use client"; + +import ComponentGlobal_IsEmptyData from "@/app_modules/_global/component/is_empty_data"; +import CustomSkeleton from "@/app_modules/components/CustomSkeleton"; +import { clientLogger } from "@/util/clientLogger"; +import mqtt_client from "@/util/mqtt_client"; +import { Box, Center, Loader, Stack } from "@mantine/core"; +import { useShallowEffect } from "@mantine/hooks"; +import _ from "lodash"; +import { ScrollOnly } from "next-scroll-loader"; +import { useParams } from "next/navigation"; +import { useState } from "react"; +import "react-quill/dist/quill.bubble.css"; +import { + apiGetKomentarForumById, + apiGetOneForumById, +} from "../component/api_fetch_forum"; +import Forum_V3_CreateKomentar from "../component/detail_component/comp_V3_create.comment"; +import ComponentForum_KomentarView from "../component/detail_component/detail_list_komentar"; +import ComponentForum_DetailForumView from "../component/detail_component/detail_view"; +import { + Forum_SkeletonKomentar, + Forum_SkeletonListKomentar, +} from "../component/skeleton_view"; +import { MODEL_FORUM_KOMENTAR, MODEL_FORUM_POSTING } from "../model/interface"; + +export default function Forum_V3_MainDetail({ + userLoginId, +}: { + userLoginId: string; +}) { + const param = useParams<{ id: string }>(); + const [dataPosting, setDataPosting] = useState( + null + ); + const [listKomentar, setListKomentar] = useState< + MODEL_FORUM_KOMENTAR[] | null + >(null); + const [activePage, setActivePage] = useState(1); + const [newKomentar, setNewKomentar] = useState(false); + const [isLoading, setIsLoading] = useState(false); + + useShallowEffect(() => { + handleLoadData(); + }, []); + + const handleLoadData = async () => { + try { + setIsLoading(true); + const response = await apiGetOneForumById({ + id: param.id, + }); + + if (response) { + setDataPosting(response.data); + } else { + setDataPosting(null); + } + } catch (error) { + clientLogger.error("Error get data forum", error); + setDataPosting(null); + } finally { + setIsLoading(false); + } + }; + + useShallowEffect(() => { + handleLoadDataKomentar(); + }, [newKomentar]); + + const handleLoadDataKomentar = async () => { + try { + const response = await apiGetKomentarForumById({ + id: param.id, + page: `${activePage}`, + }); + + if (response.success) { + setListKomentar(response.data); + } else { + setListKomentar([]); + } + } catch (error) { + clientLogger.error("Error get data komentar forum", error); + setListKomentar([]); + } + }; + + const handleMoreDataKomentar = async () => { + try { + const nextPage = activePage + 1; + const response = await apiGetKomentarForumById({ + id: param.id, + page: `${nextPage}`, + }); + + if (response.success) { + setActivePage(nextPage); + return response.data; + } else { + return null; + } + } catch (error) { + clientLogger.error("Error get data komentar forum", error); + return null; + } + }; + + useShallowEffect(() => { + mqtt_client.subscribe("Forum_detail_ganti_status"); + + mqtt_client.on("message", (topic: any, message: any) => { + const newData = JSON.parse(message.toString()); + if (newData.id === dataPosting?.id) { + const cloneData = _.clone(dataPosting); + + // console.log(newData.data); + const updateData = { + ...cloneData, + ForumMaster_StatusPosting: { + id: newData.data.id, + status: newData.data.status, + }, + }; + + setDataPosting(updateData as any); + } + }); + }, [dataPosting]); + + return ( + <> + + {!dataPosting ? ( + + ) : ( + { + setDataPosting(val); + }} + /> + )} + + {!dataPosting ? ( + + ) : ( + (dataPosting?.ForumMaster_StatusPosting?.id as any) === 1 && ( + { + setNewKomentar(val); + }} + /> + ) + )} + + {!listKomentar ? ( + + ) : _.isEmpty(listKomentar) ? ( + + ) : ( + + ( +
+ +
+ )} + data={listKomentar} + setData={setListKomentar as any} + moreData={handleMoreDataKomentar} + > + {(item) => ( + + )} +
+
+ )} +
+ + ); +}