mediahub-fe/components/form/content/spit-convert-form.tsx

861 lines
29 KiB
TypeScript

"use client";
import React, { ChangeEvent, useEffect, useRef, useState } from "react";
import { useForm, Controller } from "react-hook-form";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Card } from "@/components/ui/card";
import { zodResolver } from "@hookform/resolvers/zod";
import * as z from "zod";
import Swal from "sweetalert2";
import withReactContent from "sweetalert2-react-content";
import { useParams, useRouter } from "next/navigation";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Checkbox } from "@/components/ui/checkbox";
import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
import JoditEditor from "jodit-react";
import { register } from "module";
import { Switch } from "@/components/ui/switch";
import Cookies from "js-cookie";
import {
convertSPIT,
createMedia,
detailSPIT,
getTagsBySubCategoryId,
listCategory,
listEnableCategory,
} from "@/service/content/content";
import { detailMedia } from "@/service/curated-content/curated-content";
import { Badge } from "@/components/ui/badge";
import { MailIcon } from "lucide-react";
import { Swiper, SwiperSlide } from "swiper/react";
import "swiper/css";
import "swiper/css/free-mode";
import "swiper/css/navigation";
import "swiper/css/pagination";
import "swiper/css/thumbs";
import "swiper/css";
import "swiper/css/navigation";
import { FreeMode, Navigation, Pagination, Thumbs } from "swiper/modules";
import { request } from "http";
import { generateDataArticle, getDetailArticle } from "@/service/content/ai";
import { getCookiesDecrypt } from "@/lib/utils";
import dynamic from "next/dynamic";
const imageSchema = z.object({
contentTitle: z.string().min(1, { message: "Judul diperlukan" }),
contentDescription: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
contentCreator: z.string().min(1, { message: "Creator diperlukan" }),
// tags: z.string().min(1, { message: "Judul diperlukan" }),
contentRewriteDescription: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
});
type Category = {
id: number;
name: string;
};
type Detail = {
id: string;
contentTitle: string;
contentDescription: string;
slug: string;
content: {
id: number;
name: string;
};
contentCreator: string;
creatorName: string;
contentThumbnail: string;
contentTag: string;
};
type Option = {
id: string;
label: string;
};
interface FileData {
contentId: number;
placement?: string[];
[key: string]: any; // Extendable for additional properties
}
interface PlacementData {
mediaFileId: number;
placements: string;
}
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
export default function FormConvertSPIT() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type ImageSchema = z.infer<typeof imageSchema>;
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType");
const [categories, setCategories] = useState<Category[]>([]);
const [selectedCategoryId, setSelectedCategoryId] = useState<number | null>(
null
);
const [tags, setTags] = useState<any[]>([]);
const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false);
const [selectedPublishers, setSelectedPublishers] = useState<number[]>([]);
const [detailThumb, setDetailThumb] = useState<any>([]);
const [thumbsSwiper, setThumbsSwiper] = useState<any>(null);
const [selectedAdvConfig, setSelectedAdvConfig] = useState<string>("");
const [title, setTitle] = useState<string>("");
const [articleIds, setArticleIds] = useState<string[]>([]);
const [isGeneratedArticle, setIsGeneratedArticle] = useState(false);
const [articleBody, setArticleBody] = useState<string>("");
const [selectedArticleId, setSelectedArticleId] = useState<string | null>(
null
);
const [detailData, setDetailData] = useState<any>(null);
const [selectedFileType, setSelectedFileType] = useState("original");
const [isLoadingData, setIsLoadingData] = useState<boolean>(false);
const userLevelId = getCookiesDecrypt("ulie");
const userLevelNumber = getCookiesDecrypt("ulne");
const roleId = getCookiesDecrypt("urie");
const [isMabesApprover, setIsMabesApprover] = useState(false);
const [selectedTarget, setSelectedTarget] = useState("");
const [unitSelection, setUnitSelection] = useState({
allUnit: false,
mabes: false,
polda: false,
polres: false,
});
const [publishedFor, setPublishedFor] = useState<string[]>([]);
const [placementLength, setPlacementLength] = useState([]);
const options: Option[] = [
{ id: "all", label: "SEMUA" },
{ id: "5", label: "UMUM" },
{ id: "6", label: "JOURNALIS" },
{ id: "7", label: "POLRI" },
{ id: "8", label: "KSP" },
];
let fileTypeId = "1";
const {
control,
handleSubmit,
setValue,
formState: { errors },
} = useForm<ImageSchema>({
resolver: zodResolver(imageSchema),
});
const handleRemoveTag = (index: any) => {
setTags((prevTags) => prevTags.filter((_, i) => i !== index));
};
const handleImageChange = (event: ChangeEvent<HTMLInputElement>) => {
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));
};
// const handleCheckboxChange = (id: number) => {
// setSelectedPublishers((prev) =>
// prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
// );
// };
useEffect(() => {
async function initState() {
getCategories();
}
initState();
}, []);
useEffect(() => {
if (
userLevelId != undefined &&
roleId != undefined &&
userLevelId == "216" &&
roleId == "3"
) {
setIsMabesApprover(true);
}
}, [userLevelId, roleId]);
const getCategories = async () => {
try {
const category = await listCategory(fileTypeId);
const resCategory: Category[] = category?.data?.data?.content;
setCategories(resCategory);
console.log("data category", resCategory);
if (scheduleId && scheduleType === "3") {
const findCategory = resCategory.find((o) =>
o.name.toLowerCase().includes("pers rilis")
);
if (findCategory) {
// setValue("categoryId", findCategory.id);
setSelectedCategoryId(findCategory.id); // Set the selected category
const response = await getTagsBySubCategoryId(findCategory.id);
setTags(response?.data?.data);
}
}
} catch (error) {
console.error("Failed to fetch categories:", error);
}
};
useEffect(() => {
async function initState() {
if (id) {
const response = await detailSPIT(id);
const details = response?.data?.data;
setDetail(details);
const filesData = details.contentList || [];
const fileUrls = filesData.map((file: { contentFile: string }) =>
file.contentFile ? file.contentFile : "default-image.jpg"
);
setDetailThumb(fileUrls);
const matchingCategory = categories.find(
(category) => category.id === details.categoryId
);
if (matchingCategory) {
setSelectedTarget(matchingCategory.name);
}
// setSelectedTarget(details.categoryId); // Untuk dropdown
}
}
initState();
}, [refresh, setValue]);
const [tempFile, setTempFile] = useState(
detailThumb.map((data: any) => ({
contentId: data.id,
placement: [],
}))
);
const getPlacement = (): PlacementData[] => {
return tempFile
.filter((file: FileData) => (file.placement || []).length > 0) // Gunakan default array
.map((file: FileData) => ({
mediaFileId: Number(file.contentId),
placements: (file.placement || []).join(","), // Gunakan default array
}));
};
const setupPlacement = (value: string, id: number): void => {
const updatedFiles = tempFile.map((file: FileData) => {
if (file.contentId === id) {
const currentPlacement = file.placement || [];
if (currentPlacement.includes(value)) {
// Remove the placement value
file.placement = currentPlacement.filter((val) => val !== value);
} else {
// Add the placement value
file.placement =
value === "all"
? ["all", "mabes", "polda", "international"]
: [...currentPlacement, value];
if (file.placement.includes("all") && value !== "all") {
file.placement = file.placement.filter((val) => val !== "all");
}
}
}
return file;
});
const placementLength = updatedFiles.reduce(
(acc: any, file: any) => acc + (file.placement?.length || 0),
0
);
setTempFile(
updatedFiles.sort((a: any, b: any) => a.contentId - b.contentId)
);
setPlacementLength(placementLength);
console.log("Updated Files:", updatedFiles);
};
const handleCheckboxChangeFile = (contentId: number, value: string) => {
setTempFile((prevTempFile: any) => {
return prevTempFile.map((file: any) => {
if (file.contentId === contentId) {
const isChecked = file.placement?.includes(value);
return {
...file,
placement: isChecked
? file.placement.filter((v: any) => v !== value) // Uncheck
: [...(file.placement || []), value], // Check
};
}
return file;
});
});
};
const handleCheckboxChange = (id: string): void => {
if (id === "all") {
if (publishedFor.includes("all")) {
// Uncheck all checkboxes
setPublishedFor([]);
} else {
// Select all checkboxes
setPublishedFor(
options
.filter((opt: any) => opt.id !== "all")
.map((opt: any) => opt.id)
);
}
} else {
const updatedPublishedFor = publishedFor.includes(id)
? publishedFor.filter((item) => item !== id)
: [...publishedFor, id];
// Remove "all" if any checkbox is unchecked
if (publishedFor.includes("all") && id !== "all") {
setPublishedFor(updatedPublishedFor.filter((item) => item !== "all"));
} else {
setPublishedFor(updatedPublishedFor);
}
}
};
const save = async (data: {
contentTitle: string;
contentDescription: string;
contentRewriteDescription: string;
contentCreator: string;
}): Promise<void> => {
const description =
selectedFileType === "original"
? data.contentDescription
: data.contentRewriteDescription;
const requestData = {
spitId: id,
title: data.contentTitle,
description,
htmlDescription: description,
tags: "siap",
categoryId: selectedCategoryId,
publishedFor: publishedFor.join(","),
creator: data.contentCreator,
files: getPlacement(), // Include placement data
};
const response = await convertSPIT(requestData);
console.log("Form Data Submitted:", response);
MySwal.fire({
title: "Sukses",
text: "Data berhasil disimpan.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/en/contributor/content/spit");
});
};
const onSubmit = (data: ImageSchema) => {
MySwal.fire({
title: "Simpan Data",
text: "Apakah Anda yakin ingin menyimpan data ini?",
icon: "warning",
showCancelButton: true,
cancelButtonColor: "#d33",
confirmButtonColor: "#3085d6",
confirmButtonText: "Simpan",
}).then((result) => {
if (result.isConfirmed) {
save(data);
}
});
};
const [showRewriteEditor, setShowRewriteEditor] = useState(false);
// const handleRewriteClick = () => {
// setShowRewriteEditor(true);
// };
const handleRewriteClick = async () => {
const request = {
advConfig: selectedAdvConfig,
style: "friendly",
website: "None",
connectToWeb: true,
lang: "id",
pointOfView: "None",
title: detail?.contentTitle,
imageSource: "Web",
mainKeyword: detail?.contentTitle,
additionalKeywords: detail?.contentTitle,
targetCountry: null,
articleSize: "news",
projectId: 2,
createdBy: roleId,
clientId: "ngDLPPiorplznw2jTqVe3YFCz5xqKfUJ",
};
const res = await generateDataArticle(request);
close();
if (res?.error) {
console.error(res.message);
return false;
}
const newArticleId = res?.data?.data?.id;
setIsGeneratedArticle(true);
setArticleIds((prevIds: string[]) => {
if (prevIds.length < 3) {
return [...prevIds, newArticleId];
} else {
const updatedIds = [...prevIds];
updatedIds[2] = newArticleId;
return updatedIds;
}
});
Cookies.set("nulisAIArticleIdTemp", JSON.stringify(articleIds));
setShowRewriteEditor(true);
};
const handleArticleIdClick = async (id: string) => {
setIsLoadingData(true);
let retryCount = 0;
const maxRetries = 20;
try {
const waitForStatusUpdate = async () => {
while (retryCount < maxRetries) {
const res = await getDetailArticle(id);
const articleData = res?.data?.data;
if (articleData?.status === 2) {
return articleData;
}
retryCount++;
await new Promise((resolve) => setTimeout(resolve, 5000));
}
throw new Error("Timeout: Artikel belum selesai diproses.");
};
const articleData = await waitForStatusUpdate();
const cleanArticleBody = articleData?.articleBody?.replace(
/<img[^>]*>/g,
""
);
const articleImagesData = articleData?.imagesUrl?.split(",");
setArticleBody(cleanArticleBody || "");
setDetailData(articleData);
setSelectedArticleId(id);
// setArticleImages(articleImagesData || []);
} catch (error) {
console.error("Error fetching article details:", error);
} finally {
setIsLoadingData(false);
}
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{detail !== undefined ? (
<div className="flex lg:flex-row gap-10">
<Card className="w-full lg:w-8/12">
<div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">Form Konten Foto</p>
<div className="gap-5 mb-5">
{/* Input Title */}
<div className="space-y-2 py-3">
<Label>Judul</Label>
<Controller
control={control}
name="contentTitle"
render={({ field }) => (
<Input
size="md"
type="text"
defaultValue={detail?.contentTitle}
onChange={field.onChange}
placeholder="Enter contentTitle"
/>
)}
/>
{errors.contentTitle?.message && (
<p className="text-red-400 text-sm">
{errors.contentTitle.message}
</p>
)}
</div>
<div className="flex items-center">
<div className="py-3 w-full">
<Label>Kategori</Label>
<Select
defaultValue={detail?.content?.name}
onValueChange={(id) => {
console.log("Selected Category ID:", id);
setSelectedCategoryId(Number(id)); // Simpan ID kategori
}}
>
<SelectTrigger size="md">
<SelectValue placeholder="Pilih" />
</SelectTrigger>
<SelectContent>
{categories.map((category) => (
<SelectItem
key={category.id}
value={category.id.toString()} // ID kategori dikirim sebagai value
>
{category?.name}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
<div className="">
<RadioGroup
onValueChange={(value) => setSelectedFileType(value)}
value={selectedFileType}
className=" grid-cols-1"
>
<div className="">
<RadioGroupItem value="original" id="original-file" />
<Label htmlFor="original-file">
Select Original File
</Label>
</div>
<div className="py-3 ">
<Label>Deskripsi</Label>
<Controller
control={control}
name="contentDescription"
render={({ field: { onChange, value } }) => (
// <JoditEditor
// ref={editor}
// value={detail?.contentDescription}
// onChange={onChange}
// className="dark:text-black"
// />
<CustomEditor
onChange={onChange}
initialData={detail?.contentDescription}
/>
)}
/>
{errors.contentDescription?.message && (
<p className="text-red-400 text-sm">
{errors.contentDescription.message}
</p>
)}
</div>
<div className="my-2">
<Button
size="sm"
onClick={handleRewriteClick}
className="bg-blue-500 text-white py-2 px-4 rounded"
>
Content Rewrite
</Button>
</div>
{showRewriteEditor && (
<div>
{isGeneratedArticle && (
<div className="mt-3 pb-0 flex flex-row ">
{articleIds.map((id: string, index: number) => (
<button
key={index}
className={`mr-3 px-3 py-2 rounded-md ${
selectedArticleId === id
? "bg-green-500 text-white"
: "border-2 border-green-500 text-green-500"
}`}
onClick={() => handleArticleIdClick(id)}
>
{id}
</button>
))}
</div>
)}
<div className="flex items-center space-x-2 mt-3">
<RadioGroupItem value="rewrite" id="rewrite-file" />
<Label htmlFor="rewrite-file">
Select File Hasil Rewrite
</Label>
</div>
<div className="py-3 ">
<Label>File hasil Rewrite</Label>
<Controller
control={control}
name="contentRewriteDescription"
render={({ field: { onChange, value } }) =>
isLoadingData ? (
<div className="flex justify-center items-center h-40">
<p className="text-gray-500">
Loading Proses Data...
</p>
</div>
) : (
<CustomEditor
onChange={onChange}
initialData={articleBody || value}
/>
)
}
/>
{errors.contentRewriteDescription?.message && (
<p className="text-red-400 text-sm">
{errors.contentRewriteDescription.message}
</p>
)}
</div>
</div>
)}
</RadioGroup>
</div>
<div>
<Label className="text-xl text-black">File Media</Label>
<div className="w-full ">
<Swiper
thumbs={{ swiper: thumbsSwiper }}
modules={[FreeMode, Navigation, Thumbs]}
navigation={false}
className="w-full"
>
{detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}>
<img
className="object-fill h-full w-full"
src={data}
alt={` ${data.id}`}
/>
</SwiperSlide>
))}
</Swiper>
<div className=" mt-2 ">
<Swiper
onSwiper={setThumbsSwiper}
slidesPerView={6}
spaceBetween={8}
pagination={{
clickable: true,
}}
modules={[Pagination, Thumbs]}
// className="mySwiper2"
>
{detailThumb?.map((data: any) => (
<SwiperSlide key={data.id}>
<img
className="object-cover h-[60px] w-[80px]"
src={data}
alt={` ${data.id}`}
/>
</SwiperSlide>
))}
</Swiper>
</div>
</div>
</div>
{isMabesApprover ? (
<div className="mt-5">
<Label className="text-xl text-black">
Penempatan File
</Label>
{detailThumb.map((data: any) => (
<div
key={data.contentId}
className="flex items-center gap-3 mt-2"
>
<img
className="object-cover w-36 h-32"
src={data}
alt={`Thumbnail ${data.contentId}`}
/>
<div className="flex flex-row gap-3 items-center">
{[
"all",
"mabes",
"polda",
"satker",
"internasional",
].map((value) => (
<label
key={value}
className="text-blue-500 cursor-pointer flex items-center gap-1"
>
<input
type="checkbox"
name="placement"
value={value}
onChange={() =>
handleCheckboxChangeFile(
data.contentId,
value
)
}
checked={
tempFile
.find(
(file: FileData) =>
file.contentId === data.contentId
)
?.placement?.includes(value) || false
}
/>
{value.charAt(0).toUpperCase() + value.slice(1)}
</label>
))}
</div>
</div>
))}
</div>
) : (
""
)}
</div>
</div>
</Card>
<div className="w-4/12">
<Card className=" h-[800px]">
<div className="px-3 py-3">
<div className="space-y-2">
<Label>Kreator</Label>
<Controller
control={control}
name="contentCreator"
render={({ field }) => (
<Input
size="md"
type="text"
defaultValue={detail?.contentCreator}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{errors.contentCreator?.message && (
<p className="text-red-400 text-sm">
{errors.contentCreator.message}
</p>
)}
</div>
</div>
<div className="mt-3 px-3">
<Label>Pratinjau Gambar Utama</Label>
<Card className="mt-2">
<img
src={detail.contentThumbnail}
alt="Thumbnail Gambar Utama"
className="w-full h-auto rounded"
/>
</Card>
</div>
<div className="px-3 py-3">
<div className="space-y-2">
<Label>Tag</Label>
<div className="flex flex-wrap gap-2">
{detail?.contentTag?.split(",").map((tag, index) => (
<Badge
key={index}
className="border rounded-md px-2 py-2"
>
{tag.trim()}
</Badge>
))}
</div>
</div>
</div>
<div className="px-3 py-3">
<div className="flex flex-col gap-3">
<Label>Target Publish</Label>
{options.map((option) => (
<div key={option.id} className="flex gap-2 items-center">
<Checkbox
id={option.id}
checked={
option.id === "all"
? publishedFor.length ===
options.filter((opt: any) => opt.id !== "all")
.length
: publishedFor.includes(option.id)
}
onCheckedChange={() => handleCheckboxChange(option.id)}
/>
<Label htmlFor={option.id}>{option.label}</Label>
</div>
))}
</div>
</div>
<div className="px-3 py-3 flex flex-row items-center text-blue-500 gap-2 text-sm">
<MailIcon />
<p className="">Kotak Saran (0)</p>
</div>
<div className="px-3 py-3">
<p>Keterangan:</p>
{/* <p>{detail?.status}</p> */}
</div>
</Card>
<div className="flex flex-row justify-end gap-3">
<div className="mt-4">
<Button type="submit" color="primary">
Submit
</Button>
</div>
<div className="mt-4">
<Button type="submit" color="primary" variant="outline">
Cancel
</Button>
</div>
</div>
</div>
</div>
) : (
""
)}
</form>
);
}