mediahub-fe/components/form/blog/blog--update-form.tsx

514 lines
16 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client";
import React, { ChangeEvent, useEffect, useRef, useState } from "react";
import { useForm, Controller, useWatch } 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 {
createMedia,
getTagsBySubCategoryId,
listEnableCategory,
} from "@/service/content/content";
import { getBlog, postBlog, uploadThumbnailBlog } from "@/service/blog/blog";
import { Badge } from "@/components/ui/badge";
import dynamic from "next/dynamic";
import { loading } from "@/lib/swal";
const taskSchema = z.object({
title: z.string().min(1, { message: "Judul diperlukan" }),
slug: z.string().min(1, { message: "Judul diperlukan" }),
metadata: z.string().min(1, { message: "Judul diperlukan" }),
narration: z
.string()
.min(2, { message: "Narasi Penugasan harus lebih dari 2 karakter." }),
categoryId: z.string().min(1, { message: "Kategori diperlukan" }),
});
type Category = {
id: string;
categoryName: string;
};
type Detail = {
id: number;
title: string;
narration: string;
slug: string;
metadata: string;
categoryName: string;
categoryId: string;
thumbnailLink: string;
tags: string;
};
const initialCategories: Category[] = [
{
id: "1",
categoryName: "Giat Polri",
},
{
id: "2",
categoryName: "Giat Pimpinan",
},
{
id: "3",
categoryName: "Liputan Kegiatan",
},
{
id: "4",
categoryName: "Seputar Prestasi",
},
];
const CustomEditor = dynamic(
() => {
return import("@/components/editor/custom-editor");
},
{ ssr: false }
);
export default function FormBlogUpdate() {
const MySwal = withReactContent(Swal);
const router = useRouter();
const { id } = useParams() as { id: string };
console.log(id);
const editor = useRef(null);
type TaskSchema = z.infer<typeof taskSchema>;
const [selectedFiles, setSelectedFiles] = useState<File[]>([]);
const taskId = Cookies.get("taskId");
const scheduleId = Cookies.get("scheduleId");
const scheduleType = Cookies.get("scheduleType");
const [categories] = useState<Category[]>(initialCategories);
const [selectedTarget, setSelectedTarget] = useState<string>("");
const [selectedCategory, setSelectedCategory] = useState<any>();
const [tags, setTags] = useState<any[]>([]);
const [isDraft, setIsDraft] = useState(false);
const [detail, setDetail] = useState<Detail>();
const [refresh, setRefresh] = useState(false);
const inputRef = useRef<HTMLInputElement>(null);
const [thumbnail, setThumbnail] = useState<File | null>(null);
const [preview, setPreview] = useState<string | null>(null);
const [unitSelection, setUnitSelection] = useState({
allUnit: false,
mabes: false,
polda: false,
polres: false,
});
let fileTypeId = "1";
const {
control,
handleSubmit,
setValue,
formState: { errors },
} = useForm<TaskSchema>({
resolver: zodResolver(taskSchema),
});
const handleRemoveImage = (index: number) => {
setSelectedFiles((prevImages) => prevImages.filter((_, i) => i !== index));
};
const titleValue = useWatch({ control, name: "title" });
useEffect(() => {
if (titleValue) {
const slugified = titleValue
.toLowerCase()
.trim()
.replace(/\s+/g, "-")
.replace(/[^\w-]+/g, ""); // optional: hapus karakter non-alfanumerik
setValue("slug", slugified);
}
}, [titleValue, setValue]);
useEffect(() => {
async function initState() {
if (id) {
const response = await getBlog(id);
const details = response?.data?.data;
setDetail(details);
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,
title: data.title,
narration: data.narration,
categoryId: selectedTarget,
slug: data.slug,
metadata: data.metadata,
tags: finalTags,
isDraft,
};
const response = await postBlog(requestData);
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.",
icon: "success",
confirmButtonColor: "#3085d6",
confirmButtonText: "OK",
}).then(() => {
router.push("/in/contributor/blog");
});
};
const onSubmit = (data: TaskSchema) => {
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 handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
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);
};
const handleSave = () => {
setIsDraft(false);
};
const handleAddTag = (e: React.KeyboardEvent<HTMLInputElement>) => {
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 (
<form onSubmit={handleSubmit(onSubmit)}>
{detail !== undefined ? (
<div className="flex flex-col lg:flex-row gap-3 sm:gap-0 lg:gap-3">
<Card className="w-full lg:w-8/12">
<div className="px-6 py-6">
<p className="text-lg font-semibold mb-3">Update Indeks</p>
<div className="gap-5 mb-5">
{/* Input Title */}
<div className="space-y-2 py-3">
<Label>Judul</Label>
<Controller
control={control}
name="title"
render={({ field }) => (
<Input
size="md"
type="text"
defaultValue={detail?.title}
onChange={field.onChange}
placeholder="Enter Title"
/>
)}
/>
{errors.title?.message && (
<p className="text-red-400 text-sm">
{errors.title.message}
</p>
)}
</div>
<div className="py-3">
<Label>Deskripsi</Label>
<Controller
control={control}
name="narration"
render={({ field: { onChange, value } }) => (
<CustomEditor
onChange={onChange}
initialData={detail?.narration || value}
/>
)}
/>
{errors.narration?.message && (
<p className="text-red-400 text-sm">
{errors.narration.message}
</p>
)}
</div>
<div className=" py-3">
<div className="space-y-2">
<Label>Slug</Label>
<Controller
control={control}
name="slug"
render={({ field }) => (
<Input
size="md"
type="text"
defaultValue={detail?.slug || field?.value}
onChange={field.onChange}
placeholder="Enter Slug"
/>
)}
/>
{errors.slug?.message && (
<p className="text-red-400 text-sm">
{errors.slug.message}
</p>
)}
</div>
</div>
<div className=" py-3">
<div className="space-y-2">
<Label>Meta</Label>
<Controller
control={control}
name="metadata"
render={({ field }) => (
<Input
size="md"
type="text"
defaultValue={detail?.metadata}
onChange={field.onChange}
placeholder="Enter Meta"
/>
)}
/>
{errors.metadata?.message && (
<p className="text-red-400 text-sm">
{errors.metadata.message}
</p>
)}
</div>
</div>
</div>
</div>
</Card>
<div className="w-full lg:w-4/12">
<Card className="h-[600px] sm:h-[850px] lg:h-[700px] px-3 py-3">
<div className="px-3 py-3">
<Label htmlFor="fileInput">Gambar Utama</Label>
<input
id="fileInput"
type="file"
onChange={handleImageChange}
/>
{preview ? (
// Menampilkan pratinjau gambar yang baru dipilih
<div className="mt-3 px-3">
<img
src={preview}
alt="Thumbnail Preview"
className="w-full h-auto rounded"
/>
</div>
) : (
// Menampilkan gambar dari `detail.thumbnailLink` jika tidak ada file yang dipilih
detail?.thumbnailLink && (
<div className="mt-3 px-3">
<Label>Pratinjau Gambar Utama</Label>
<Card className="mt-2">
<img
src={detail.thumbnailLink}
alt="Thumbnail Gambar Utama"
className="w-full h-auto rounded"
/>
</Card>
</div>
)
)}
</div>
<div className="px-3 py-3 mt-6">
<label
htmlFor="kategori"
className="block text-sm font-medium text-gray-700"
>
Kategori
</label>
<Controller
name="categoryId"
control={control}
render={({ field }) => (
<Select
defaultValue={detail?.categoryName}
onValueChange={(value) => {
setSelectedTarget(value);
field.onChange(value); // Sinkronisasi dengan react-hook-form
}}
>
<SelectTrigger size="md">
<SelectValue placeholder="Pilih" />
</SelectTrigger>
<SelectContent>
{categories.map((category: any) => (
<SelectItem key={category.id} value={category.id}>
{category.categoryName}
</SelectItem>
))}
</SelectContent>
</Select>
)}
/>
</div>
<div className="px-3 py-3">
<div className="space-y-2">
<Label>Tag</Label>
<Input
type="text"
id="tags"
placeholder="Add a tag and press Enter"
onKeyDown={handleAddTag}
ref={inputRef}
/>
<div className="mt-3 flex flex-wrap gap-2">
{tags.map((tag, index) => (
<span
key={index}
className="flex items-center gap-2 px-2 py-1 rounded-lg bg-black text-white text-sm"
>
<input
type="text"
value={tag}
onChange={(e) => handleEditTag(index, e.target.value)}
className="bg-black text-white border-none focus:outline-none w-auto"
/>
<button
value={tag}
type="button"
onClick={() => handleRemoveTag(index)}
className="remove-tag-button text-white"
>
×
</button>
</span>
))}
</div>
</div>
</div>
</Card>
<div className="flex flex-row justify-end gap-3">
{/* <div className="mt-4">
<Button
type="submit"
color="success"
onClick={() => handlePublish()}
>
Publish
</Button>
</div> */}
<div className="mt-4">
<Button
type="submit"
color="primary"
onClick={() => handleSave()}
>
Submit
</Button>
</div>
<div className="mt-4">
<Button type="submit" color="primary" variant="outline">
Cancel
</Button>
</div>
</div>
</div>
</div>
) : (
""
)}
</form>
);
}