From e643eda2c6242562aa7da2810de3345427915021 Mon Sep 17 00:00:00 2001 From: Anang Yusman Date: Sat, 18 Jan 2025 23:13:47 +0800 Subject: [PATCH] feat:schedule, detail konten,task,blog --- .../agenda-setting/event-modal.tsx | 23 +- .../contributor/blog/components/columns.tsx | 53 ++++- .../(protected)/shared/communication/page.tsx | 12 +- .../giat-routine/audio/audio.tsx | 17 +- .../giat-routine/video/audio-visual.tsx | 2 +- components/form/blog/blog--detail-form.tsx | 17 +- components/form/blog/blog--update-form.tsx | 196 +++++++++++++----- components/form/blog/blog-form.tsx | 118 +++++++---- .../communication/escalation-detail-form.tsx | 131 +++++++----- components/form/content/video-detail-form.tsx | 7 +- .../form/schedule/event-detail-form.tsx | 90 +++++++- components/form/schedule/event-form.tsx | 2 +- .../schedule/pers-release--detail-form.tsx | 90 +++++++- .../schedule/press-conference-detail-form.tsx | 91 +++++++- .../form/schedule/press-conference-form.tsx | 2 +- components/form/task/task-detail-form.tsx | 2 + components/form/task/task-edit-form.tsx | 3 + components/form/task/task-form.tsx | 3 + service/blog/blog.ts | 16 +- service/communication/communication.ts | 5 + service/schedule/schedule.ts | 22 +- 21 files changed, 707 insertions(+), 195 deletions(-) diff --git a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx index 67c3bb97..a0b9d18a 100644 --- a/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx +++ b/app/[locale]/(protected)/contributor/agenda-setting/event-modal.tsx @@ -4,7 +4,7 @@ import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { useForm, Controller } from "react-hook-form"; -import { cn } from "@/lib/utils"; +import { cn, getCookiesDecrypt } from "@/lib/utils"; import { format } from "date-fns"; import { Popover, @@ -84,6 +84,7 @@ const EventModal = ({ event: any; selectedDate: any; }) => { + const roleId = Number(getCookiesDecrypt("urie")) || 0; const [detail, setDetail] = useState(); const [startDate, setStartDate] = useState(new Date()); const [endDate, setEndDate] = useState(new Date()); @@ -1082,7 +1083,18 @@ const EventModal = ({
-
- {tab === "Komunikasi" && } + {tab === "Pertanyaan Internal" && } {tab === "Eskalasi" && } {tab === "Kolaborasi" && } diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/audio/audio.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/audio/audio.tsx index e18d163c..e187ea5d 100644 --- a/app/[locale]/(protected)/shared/curated-content/giat-routine/audio/audio.tsx +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/audio/audio.tsx @@ -7,6 +7,7 @@ import { CarouselNext, CarouselPrevious, } from "@/components/ui/carousel"; +import { listCuratedContent } from "@/service/curated-content/curated-content"; import { getListContent } from "@/service/landing/landing"; import { formatDateToIndonesian, @@ -21,10 +22,12 @@ const AudioSliderPage = () => { const [audioData, setAudioData] = useState(); const [displayAudio, setDisplayAudio] = useState([]); const [page, setPage] = useState(1); + const [limit, setLimit] = React.useState(10); + const [search, setSearch] = React.useState(""); useEffect(() => { initFetch(); - }, []); + }, [page, limit, search]); useEffect(() => { if (audioData?.length > 0) { @@ -35,14 +38,12 @@ const AudioSliderPage = () => { }, [audioData]); const initFetch = async () => { - const response = await getListContent({ - page: page - 1, - size: 12, - sortBy: "createdAt", - contentTypeId: "4", - }); + const response = await listCuratedContent(search, limit, page - 1, 4, "1"); console.log(response); - setAudioData(response?.data?.data?.content); + + const data = response?.data?.data; + const contentData = data?.content; + setAudioData(contentData); }; const shuffleAndSetVideos = () => { diff --git a/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx b/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx index 07070ec0..4d561a54 100644 --- a/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx +++ b/app/[locale]/(protected)/shared/curated-content/giat-routine/video/audio-visual.tsx @@ -28,7 +28,7 @@ const VideoSliderPage = () => { }, [allVideoData]); const fetchData = async () => { - const response = await listCuratedContent(search, limit, page - 1, 1, "2"); + const response = await listCuratedContent(search, limit, page - 1, 2, "1"); console.log(response); const data = response?.data?.data; diff --git a/components/form/blog/blog--detail-form.tsx b/components/form/blog/blog--detail-form.tsx index 17df42fc..876dcd4f 100644 --- a/components/form/blog/blog--detail-form.tsx +++ b/components/form/blog/blog--detail-form.tsx @@ -30,6 +30,7 @@ import { } from "@/service/content/content"; import { getBlog, postBlog } from "@/service/blog/blog"; import { Badge } from "@/components/ui/badge"; +import dynamic from "next/dynamic"; const taskSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -76,6 +77,13 @@ const initialCategories: Category[] = [ }, ]; +const ViewEditor = dynamic( + () => { + return import("@/components/editor/view-editor"); + }, + { ssr: false } +); + export default function FormBlogDetail() { const MySwal = withReactContent(Swal); const router = useRouter(); @@ -236,12 +244,7 @@ export default function FormBlogDetail() { control={control} name="narration" render={({ field: { onChange, value } }) => ( - + )} /> {errors.narration?.message && ( @@ -300,7 +303,7 @@ export default function FormBlogDetail() {
- +
{ + return import("@/components/editor/custom-editor"); + }, + { ssr: false } +); + export default function FormBlogUpdate() { const MySwal = withReactContent(Swal); const router = useRouter(); @@ -98,6 +107,9 @@ export default function FormBlogUpdate() { const [detail, setDetail] = useState(); const [refresh, setRefresh] = useState(false); + const inputRef = useRef(null); + const [thumbnail, setThumbnail] = useState(null); + const [preview, setPreview] = useState(null); const [unitSelection, setUnitSelection] = useState({ allUnit: false, @@ -117,18 +129,6 @@ export default function FormBlogUpdate() { resolver: zodResolver(taskSchema), }); - const handleRemoveTag = (index: any) => { - setTags((prevTags) => prevTags.filter((_, i) => i !== index)); - }; - - const handleImageChange = (event: ChangeEvent) => { - if (event.target.files) { - const files = Array.from(event.target.files); - setSelectedFiles((prevImages: any) => [...prevImages, ...files]); - console.log("DATAFILE::", selectedFiles); - } - }; - const handleRemoveImage = (index: number) => { setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index)); }; @@ -141,15 +141,20 @@ export default function FormBlogUpdate() { setDetail(details); - // Set categoryId dari API ke form dan Select - setValue("categoryName", details?.categoryName); - setSelectedTarget(details?.categoryId); // Untuk dropdown + if (details?.tags) { + setTags(details.tags.split(",").map((tag: string) => tag.trim())); + } + + setValue("categoryId", details?.categoryName); + setSelectedTarget(details?.categoryId); } } initState(); }, [refresh, setValue]); const save = async (data: TaskSchema) => { + loading(); + const finalTags = tags.join(", "); const requestData = { ...data, id: detail?.id, @@ -158,7 +163,7 @@ export default function FormBlogUpdate() { categoryId: selectedTarget, slug: data.slug, metadata: data.metadata, - tags: "siap", + tags: finalTags, isDraft, }; @@ -166,6 +171,30 @@ export default function FormBlogUpdate() { console.log("Form Data Submitted:", requestData); console.log("response", response); + if (response?.error) { + MySwal.fire("Error", response?.message, "error"); + return; + } + + const blogId = response?.data?.data.id; + if (blogId) { + if (thumbnail) { + const formMedia = new FormData(); + formMedia.append("file", thumbnail); // Tambahkan file ke FormData + console.log("FormMedia:", formMedia.get("file")); + + const responseThumbnail = await uploadThumbnailBlog(blogId, formMedia); + + if (responseThumbnail?.error) { + MySwal.fire("Error", responseThumbnail?.message, "error"); + return; + } + } else { + console.log("No thumbnail to upload"); + } + } + close(); + MySwal.fire({ title: "Sukses", text: "Data berhasil disimpan.", @@ -193,6 +222,17 @@ export default function FormBlogUpdate() { }); }; + const handleImageChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + setThumbnail(file); // Simpan file ke state + setPreview(URL.createObjectURL(file)); // Buat URL untuk preview + console.log("Selected Thumbnail:", file); + } else { + console.log("No file selected"); + } + }; + const handlePublish = () => { setIsDraft(false); }; @@ -201,6 +241,29 @@ export default function FormBlogUpdate() { setIsDraft(false); }; + const handleAddTag = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && e.currentTarget.value.trim()) { + e.preventDefault(); + const newTag = e.currentTarget.value.trim(); + if (!tags.includes(newTag)) { + setTags((prevTags) => [...prevTags, newTag]); // Tambahkan tag baru + if (inputRef.current) { + inputRef.current.value = ""; // Kosongkan input + } + } + } + }; + + const handleRemoveTag = (index: number) => { + setTags((prevTags) => prevTags.filter((_, i) => i !== index)); + }; + + const handleEditTag = (index: number, newValue: string) => { + setTags((prevTags) => + prevTags.map((tag, i) => (i === index ? newValue : tag)) + ); + }; + return (
{detail !== undefined ? ( @@ -238,11 +301,9 @@ export default function FormBlogUpdate() { control={control} name="narration" render={({ field: { onChange, value } }) => ( - )} /> @@ -302,31 +363,39 @@ export default function FormBlogUpdate() {
- +
- { - // const file = e.target.files[0]; - // if (file) { - // console.log("Selected File:", file); - // // Tambahkan logika jika diperlukan, misalnya upload file ke server - // } - // }} - className="" + onChange={handleImageChange} /> -
-
- - - Thumbnail Gambar Utama - + + {preview ? ( + // Menampilkan pratinjau gambar yang baru dipilih +
+ Thumbnail Preview +
+ ) : ( + // Menampilkan gambar dari `detail.thumbnailLink` jika tidak ada file yang dipilih + detail?.thumbnailLink && ( +
+ + + Thumbnail Gambar Utama + +
+ ) + )}
diff --git a/components/form/blog/blog-form.tsx b/components/form/blog/blog-form.tsx index 513e7401..693c0ab8 100644 --- a/components/form/blog/blog-form.tsx +++ b/components/form/blog/blog-form.tsx @@ -29,6 +29,9 @@ import { listEnableCategory, } from "@/service/content/content"; import { postBlog, uploadThumbnailBlog } from "@/service/blog/blog"; +import dynamic from "next/dynamic"; +import { error } from "console"; +import { loading } from "@/lib/swal"; const taskSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -37,7 +40,6 @@ const taskSchema = z.object({ narration: z .string() .min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }), - tags: z.string().min(1, { message: "Judul diperlukan" }), }); type Category = { @@ -64,6 +66,13 @@ const initialCategories: Category[] = [ }, ]; +const CustomEditor = dynamic( + () => { + return import("@/components/editor/custom-editor"); + }, + { ssr: false } +); + export default function FormBlog() { const MySwal = withReactContent(Swal); const router = useRouter(); @@ -82,6 +91,7 @@ export default function FormBlog() { const [isDraft, setIsDraft] = useState(false); const [thumbnail, setThumbnail] = useState(null); const [preview, setPreview] = useState(null); + const inputRef = useRef(null); const [unitSelection, setUnitSelection] = useState({ allUnit: false, @@ -156,6 +166,8 @@ export default function FormBlog() { // }; const save = async (data: TaskSchema) => { + loading(); + const finalTags = tags.join(", "); const requestData = { ...data, title: data.title, @@ -163,7 +175,7 @@ export default function FormBlog() { categoryId: selectedTarget, slug: data.slug, metadata: data.meta, - tags: data.tags, + tags: finalTags, isDraft, }; @@ -177,19 +189,33 @@ export default function FormBlog() { } const blogId = response?.data?.data.id; - if (blogId && thumbnail) { - const formMedia = new FormData(); - formMedia.append("file", thumbnail); + if (blogId) { + if (thumbnail) { + const formMedia = new FormData(); + formMedia.append("file", thumbnail); // Tambahkan file ke FormData + console.log("FormMedia:", formMedia.get("file")); - const responseThumbnail = await uploadThumbnailBlog(blogId, formMedia); + const responseThumbnail = await uploadThumbnailBlog(blogId, formMedia); - if (responseThumbnail?.error) { - MySwal.fire("Error", responseThumbnail?.message, "error"); - return; + if (responseThumbnail?.error) { + MySwal.fire("Error", responseThumbnail?.message, "error"); + return; + } + } else { + console.log("No thumbnail to upload"); } } + close(); - MySwal.fire("Sukses", "Data berhasil disimpan.", "success"); + MySwal.fire({ + title: "Sukses", + text: "Data berhasil disimpan.", + icon: "success", + confirmButtonColor: "#3085d6", + confirmButtonText: "OK", + }).then(() => { + router.push("/en/contributor/blog"); + }); }; const onSubmit = (data: TaskSchema) => { @@ -198,8 +224,9 @@ export default function FormBlog() { text: "Apakah Anda yakin ingin menyimpan data ini?", icon: "warning", showCancelButton: true, + cancelButtonColor: "#d33", + confirmButtonColor: "#3085d6", confirmButtonText: "Simpan", - cancelButtonText: "Batal", }).then((result) => { if (result.isConfirmed) { save(data); @@ -210,11 +237,11 @@ export default function FormBlog() { const handleImageChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { - setThumbnail(file); + setThumbnail(file); // Simpan file ke state + setPreview(URL.createObjectURL(file)); // Buat URL untuk preview console.log("Selected Thumbnail:", file); - } - if (file) { - setPreview(URL.createObjectURL(file)); + } else { + console.log("No file selected"); } }; @@ -226,6 +253,19 @@ export default function FormBlog() { setIsDraft(true); }; + const handleAddTag = (e: React.KeyboardEvent) => { + if (e.key === "Enter" && e.currentTarget.value.trim()) { + e.preventDefault(); + const newTag = e.currentTarget.value.trim(); + if (!tags.includes(newTag)) { + setTags((prevTags) => [...prevTags, newTag]); // Add new tag + if (inputRef.current) { + inputRef.current.value = ""; // Clear input field + } + } + } + }; + return (
@@ -260,12 +300,7 @@ export default function FormBlog() { control={control} name="narration" render={({ field: { onChange, value } }) => ( - + )} /> {errors.narration?.message && ( @@ -324,10 +359,10 @@ export default function FormBlog() {
- +
- - + +
{preview && (
@@ -366,19 +401,30 @@ export default function FormBlog() {
- ( - - )} + +
+ {tags.map((tag, index) => ( + + {tag}{" "} + + + ))} +
{/*
{tags.length === 0 && "Please add at least one tag."}
diff --git a/components/form/communication/escalation-detail-form.tsx b/components/form/communication/escalation-detail-form.tsx index bcddc3ec..4e579615 100644 --- a/components/form/communication/escalation-detail-form.tsx +++ b/components/form/communication/escalation-detail-form.tsx @@ -20,6 +20,7 @@ import { } from "@/components/ui/select"; import { Avatar, AvatarImage } from "@/components/ui/avatar"; import { + getEscalationDiscussion, getTicketingDetail, getTicketingInternalDetail, getTicketingInternalDiscussion, @@ -28,6 +29,8 @@ import { import { Textarea } from "@/components/ui/textarea"; import { Icon } from "@iconify/react/dist/iconify.js"; import { Link } from "@/i18n/routing"; +import { loading } from "@/lib/swal"; +import { id } from "date-fns/locale"; const taskSchema = z.object({ title: z.string().min(1, { message: "Judul diperlukan" }), @@ -57,9 +60,24 @@ export default function FormDetailEscalation() { const [detail, setDetail] = useState(); const [ticketReply, setTicketReply] = useState([]); const [replyVisible, setReplyVisible] = useState(false); - const [replyMessage, setReplyMessage] = useState(""); + const [listDiscussion, setListDiscussion] = useState(); + const [message, setMessage] = useState(""); const [selectedPriority, setSelectedPriority] = useState(""); - const [selectedStatus, setSelectedStatus] = useState(""); + const [replyMessage, setReplyMessage] = useState(""); + const [replies, setReplies] = useState([ + { + id: 1, + name: "Mabes Polri - Approver", + message: "test", + timestamp: "2024-12-20 00:56:10", + }, + { + id: 2, + name: "Mabes Polri - Approver", + message: "balas", + timestamp: "2025-01-18 17:42:48", + }, + ]); const { control, @@ -90,53 +108,32 @@ export default function FormDetailEscalation() { const handleReply = () => { setReplyVisible((prev) => !prev); // Toggle visibility }; + // useEffect(() => { + // async function initState() { + // if (id != undefined) { + // loading(); + // const responseGet = await getEscalationDiscussion(id); + // close(); - const handleSendReply = async () => { - if (replyMessage.trim() === "") { - MySwal.fire({ - title: "Error", - text: "Pesan tidak boleh kosong!", - icon: "error", - }); - return; - } + // console.log("escal data", responseGet?.data); + // setListDiscussion(responseGet?.data?.data); + // } + // } + // initState(); + // }, [id]); - const data = { - ticketId: id, + const handleSendReply = () => { + if (replyMessage.trim() === "") return; + + const newReply = { + id: replies.length + 1, + name: "Mabes Polri - Approver", // Sesuaikan dengan data dinamis jika ada message: replyMessage, + timestamp: new Date().toISOString().slice(0, 19).replace("T", " "), }; - try { - const response = await saveTicketInternalReply(data); - - // Tambahkan balasan baru ke daftar balasan - const newReply: replyDetail = { - id: response?.data?.id, - message: replyMessage, - createdAt: response?.data?.createdAt, - messageFrom: response?.data?.messageFrom, - messageTo: response?.data?.messageTo, - }; - - setTicketReply((prevReplies) => [newReply, ...prevReplies]); - - MySwal.fire({ - title: "Sukses", - text: "Pesan berhasil dikirim.", - icon: "success", - }); - - // Reset input dan sembunyikan form balasan - setReplyMessage(""); - setReplyVisible(false); - } catch (error) { - MySwal.fire({ - title: "Error", - text: "Gagal mengirim balasan.", - icon: "error", - }); - console.error("Error sending reply:", error); - } + setReplies([...replies, newReply]); + setReplyMessage(""); }; return ( @@ -244,26 +241,46 @@ export default function FormDetailEscalation() {

)} */}
-
- +
+

Tanggapan

+
+ {replies.map((reply) => ( +
+

{reply.name}

+

{reply.message}

+

{reply.timestamp}

+
+ ))} +
+
+ +
+