skl/resources/js/pages/admin/post/edit_post.tsx

415 lines
17 KiB
TypeScript

import React, { useEffect, useState } from "react";
import { useForm } from "@inertiajs/react";
import AuthenticatedLayout from "@/layouts/authenticated-layout";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { useToast } from "@/hooks/use-toast";
import { Head } from "@inertiajs/react";
import { Editor } from "@tinymce/tinymce-react";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
interface Kategori {
KategoriId: number;
NamaKategori: string;
}
interface SubKategori {
SubKategoriId: number;
KategoriId: number;
NamaSubKategori: string;
}
interface Posting {
PostId: number;
KategoriId: number;
SubKategoriId: number;
JudulPost: string;
ImagePost: string;
SlugPost: string;
DescPost: string;
IsPublish: boolean;
}
interface EditPostProps {
posting: Posting;
kategori: Kategori[];
subkategori: SubKategori[];
}
interface PostFormData {
KategoriId: string;
SubKategoriId: string;
JudulPost: string;
SlugPost: string;
DescPost: string;
ImagePost: File | null;
IsPublish: boolean;
}
const slugify = (text: string) => text.toLowerCase().replace(/\s+/g, "-");
export default function EditPost({
posting,
kategori,
subkategori,
}: EditPostProps) {
const { toast } = useToast();
const [imagePreview, setImagePreview] = useState<string | null>(null);
const { data, setData, post, processing, errors } = useForm<PostFormData>({
KategoriId: posting.KategoriId.toString(),
SubKategoriId: posting.SubKategoriId.toString(),
JudulPost: posting.JudulPost,
SlugPost: posting.SlugPost,
DescPost: posting.DescPost,
ImagePost: null,
IsPublish: posting.IsPublish,
});
useEffect(() => {
if (posting.ImagePost) {
const path = `${posting.ImagePost}`;
setImagePreview(path);
}
}, [posting.ImagePost]);
useEffect(() => {
if (data.JudulPost && data.JudulPost.trim() !== "") {
setData("SlugPost", slugify(data.JudulPost));
}
}, [data.JudulPost]);
const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
if (e.target.files && e.target.files[0]) {
const file = e.target.files[0];
setData("ImagePost", file);
const reader = new FileReader();
reader.onloadend = () => {
setImagePreview(reader.result as string);
};
reader.readAsDataURL(file);
}
};
const handleSubmit = (e: React.FormEvent) => {
e.preventDefault();
const formData = new FormData();
formData.append("KategoriId", data.KategoriId);
formData.append("SubKategoriId", data.SubKategoriId);
formData.append("JudulPost", data.JudulPost);
formData.append("SlugPost", data.SlugPost);
formData.append("DescPost", data.DescPost);
// Send the boolean value directly
formData.append("IsPublish", data.IsPublish.toString());
if (data.ImagePost instanceof File) {
formData.append("ImagePost", data.ImagePost);
}
post(`/admin/post/${posting.PostId}`, {
data: formData,
headers: { "Content-Type": "multipart/form-data" },
onSuccess: () => {
toast({
title: "Berhasil",
description: "Post berhasil diperbarui",
variant: "default",
});
},
onError: () => {
toast({
title: "Gagal",
description: "Terjadi kesalahan saat memperbarui Post",
variant: "destructive",
});
},
});
};
return (
<AuthenticatedLayout header="Edit Post">
<Head title="Edit Post" />
<div className="container mx-auto p-4">
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<Input
type="text"
placeholder="Judul Post"
value={data.JudulPost}
onChange={(e) =>
setData("JudulPost", e.target.value)
}
/>
{errors.JudulPost && (
<p className="text-red-500 text-sm">
{errors.JudulPost}
</p>
)}
</div>
<div>
<Input
type="text"
placeholder="Slug Post"
value={data.SlugPost}
readOnly
/>
{errors.SlugPost && (
<p className="text-red-500 text-sm">
{errors.SlugPost}
</p>
)}
</div>
<div>
<Select
onValueChange={(value) =>
setData("KategoriId", value)
}
value={data.KategoriId}
>
<SelectTrigger>
<SelectValue placeholder="Pilih Kategori" />
</SelectTrigger>
<SelectContent>
{kategori.map((kat) => (
<SelectItem
key={kat.KategoriId}
value={kat.KategoriId.toString()}
>
{kat.NamaKategori}
</SelectItem>
))}
</SelectContent>
</Select>
{errors.KategoriId && (
<p className="text-red-500 text-sm">
{errors.KategoriId}
</p>
)}
</div>
{data.KategoriId && (
<div>
<Select
onValueChange={(value) =>
setData("SubKategoriId", value)
}
value={data.SubKategoriId}
>
<SelectTrigger>
<SelectValue placeholder="Pilih SubKategori" />
</SelectTrigger>
<SelectContent>
{subkategori
.filter(
(sub) =>
sub.KategoriId ===
Number(data.KategoriId)
)
.map((sub) => (
<SelectItem
key={sub.SubKategoriId}
value={sub.SubKategoriId.toString()}
>
{sub.NamaSubKategori}
</SelectItem>
))}
</SelectContent>
</Select>
{errors.SubKategoriId && (
<p className="text-red-500 text-sm">
{errors.SubKategoriId}
</p>
)}
</div>
)}
<div>
<Editor
apiKey={import.meta.env.VITE_KEY_TINY_MCE}
value={data.DescPost}
onEditorChange={(content) =>
setData("DescPost", content)
}
init={{
plugins: [
// Free plugins only
"anchor",
"autolink",
"charmap",
"codesample",
"emoticons",
"image",
"link",
"lists",
"media",
"searchreplace",
"table",
"visualblocks",
"wordcount",
"code",
"fullscreen",
"preview",
],
toolbar:
"undo redo | blocks | bold italic underline strikethrough | link image media table | align | bullist numlist | emoticons charmap | fullscreen preview code | removeformat",
height: 300,
menubar:
"file edit view insert format tools table help",
image_caption: true,
quickbars_selection_toolbar:
"bold italic | quicklink h2 h3 blockquote",
contextmenu: "link image table",
}}
initialValue="Isi Artikel"
/>
{/* <div className="mt-2 p-2 bg-gray-50 text-xs text-gray-600 rounded">
<p className="font-medium">SEO Tips:</p>
<ul className="list-disc pl-4 mt-1">
<li>
Gunakan heading tags (H1, H2, H3) dengan
struktur yang tepat
</li>
<li>Tambahkan alt text pada gambar</li>
<li>
Gunakan link dengan atribut rel yang sesuai
</li>
<li>
Sertakan kata kunci utama dalam paragraf
awal
</li>
</ul>
</div> */}
{errors.DescPost && (
<p className="text-red-500 text-sm">
{errors.DescPost}
</p>
)}
</div>
<div className="space-y-4">
<div className="flex items-center justify-center w-full">
<label className="flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50 hover:bg-gray-100">
<div className="flex flex-col items-center justify-center pt-5 pb-6">
{imagePreview ? (
<img
src={imagePreview}
alt="Preview"
className="max-h-52 object-contain"
onError={() => {
console.error(
"Error loading image"
);
setImagePreview(null);
}}
/>
) : (
<>
<svg
className="w-8 h-8 mb-4 text-gray-500"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 20 16"
>
<path
stroke="currentColor"
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M13 13h3a3 3 0 0 0 0-6h-.025A5.56 5.56 0 0 0 16 6.5 5.5 5.5 0 0 0 5.207 5.021C5.137 5.017 5.071 5 5 5a4 4 0 0 0 0 8h2.167M10 15V6m0 0L8 8m2-2 2 2"
/>
</svg>
<p className="mb-2 text-sm text-gray-500">
<span className="font-semibold">
Klik untuk upload
</span>{" "}
atau drag and drop
</p>
<p className="text-xs text-gray-500">
PNG, JPG, JPEG atau WEBP (MAX.
800x400px)
</p>
</>
)}
</div>
<Input
type="file"
className="hidden"
accept="image/png, image/jpg, image/jpeg, image/webp"
onChange={handleImageChange}
/>
</label>
</div>
{errors.ImagePost && (
<p className="text-red-500 text-sm">
{errors.ImagePost}
</p>
)}
{imagePreview && (
<Button
type="button"
variant="destructive"
onClick={() => {
setData("ImagePost", null);
setImagePreview(null);
}}
>
Hapus Gambar
</Button>
)}
</div>
<div>
<Select
value={data.IsPublish.toString()}
onValueChange={(value) =>
setData("IsPublish", value === "true")
}
>
<SelectTrigger>
<SelectValue placeholder="Pilih Status Publikasi" />
</SelectTrigger>
<SelectContent>
<SelectItem value="true">Publish</SelectItem>
<SelectItem value="false">Draft</SelectItem>
</SelectContent>
</Select>
{errors.IsPublish && (
<p className="text-red-500 text-sm">
{errors.IsPublish}
</p>
)}
</div>
<div className="flex justify-start gap-4">
<Button
type="button"
className="bg-gray-600 text-white"
onClick={() => window.history.back()}
>
Kembali
</Button>
<Button
type="submit"
className="bg-blue-600 text-white"
disabled={processing}
>
{processing ? "Memperbarui..." : "Perbarui"}
</Button>
</div>
</form>
</div>
</AuthenticatedLayout>
);
}