refactor: Perbarui penanganan gambar dan boolean di PostController
parent
6bb5742623
commit
f413f8e4e0
|
@ -112,36 +112,42 @@ class PostController extends Controller
|
|||
}
|
||||
|
||||
public function update(PostRequest $request, Post $post)
|
||||
{
|
||||
{
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
$data = $request->validated();
|
||||
|
||||
// Only update image if new one is uploaded
|
||||
// Update gambar hanya jika ada file baru yang diupload
|
||||
if ($request->hasFile('ImagePost')) {
|
||||
// Delete old image if exists
|
||||
// Hapus gambar lama jika ada
|
||||
if ($post->ImagePost && Storage::disk('public')->exists($post->ImagePost)) {
|
||||
Storage::disk('public')->delete($post->ImagePost);
|
||||
}
|
||||
$data['ImagePost'] = $request->file('ImagePost')->store('images/posts', 'public');
|
||||
} else {
|
||||
// Keep existing image if no new one uploaded
|
||||
// Jika tidak ada file baru, jangan update field ImagePost
|
||||
unset($data['ImagePost']);
|
||||
}
|
||||
|
||||
$data['IsPublish'] = $request->boolean('IsPublish');
|
||||
// Pastikan nilai is_publish dikonversi ke boolean
|
||||
$data['IsPublish'] = filter_var($request->input('IsPublish'), FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
$post->update($data);
|
||||
|
||||
DB::commit();
|
||||
return redirect()->route('admin.post.index')->with('success', 'Post berhasil diperbarui.');
|
||||
|
||||
return redirect()->route('admin.post.index')->with('success', 'Post berhasil diperbarui.');
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
Log::error('Error updating Post: ' . $e->getMessage());
|
||||
Log::info('Form data received:', [
|
||||
'IsPublish' => $request->input('IsPublish'),
|
||||
'IsPublishType' => gettype($request->input('IsPublish')),
|
||||
'hasFile' => $request->hasFile('ImagePost')
|
||||
]);
|
||||
return back()->with('error', 'Terjadi kesalahan saat memperbarui post.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function destroy(Post $post)
|
||||
{
|
||||
|
|
|
@ -22,7 +22,6 @@ class PostRequest extends FormRequest
|
|||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
if ($this->isMethod('POST')) {
|
||||
return [
|
||||
'KategoriId' => 'required|integer|exists:Kategori,KategoriId',
|
||||
'SubKategoriId' => 'required|integer|exists:SubKategori,SubKategoriId',
|
||||
|
@ -33,42 +32,4 @@ class PostRequest extends FormRequest
|
|||
'IsPublish' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
if ($this->isMethod('PUT') || $this->isMethod('PATCH')) {
|
||||
return [
|
||||
'KategoriId' => 'nullable|integer|exists:Kategori,KategoriId',
|
||||
'SubKategoriId' => 'nullable|integer|exists:SubKategori,SubKategoriId',
|
||||
'JudulPost' => ['nullable', 'string', 'max:255'],
|
||||
'SlugPost' => ['nullable', 'string', 'max:255'],
|
||||
'DescPost' => ['nullable', 'string'],
|
||||
'ImagePost' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'],
|
||||
'IsPublish' => 'nullable|boolean',
|
||||
];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
// Add this method to handle validation before rules are applied
|
||||
protected function prepareForValidation()
|
||||
{
|
||||
if ($this->isMethod('PUT') || $this->isMethod('PATCH')) {
|
||||
// Only set these if they're not provided in the request
|
||||
if (!$this->has('KategoriId')) {
|
||||
$this->merge(['KategoriId' => $this->route('post')->KategoriId]);
|
||||
}
|
||||
if (!$this->has('SubKategoriId')) {
|
||||
$this->merge(['SubKategoriId' => $this->route('post')->SubKategoriId]);
|
||||
}
|
||||
if (!$this->has('JudulPost')) {
|
||||
$this->merge(['JudulPost' => $this->route('post')->JudulPost]);
|
||||
}
|
||||
if (!$this->has('SlugPost')) {
|
||||
$this->merge(['SlugPost' => $this->route('post')->SlugPost]);
|
||||
}
|
||||
if (!$this->has('DescPost')) {
|
||||
$this->merge(['DescPost' => $this->route('post')->DescPost]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -282,13 +282,13 @@ export default function AddPost({
|
|||
<div className="space-y-2">
|
||||
<div className="relative min-h-[300px]">
|
||||
<Editor
|
||||
apiKey="h471mb13phsh2c8rlp5msca6h0h8y0oy37llvpvzhqjymqq3"
|
||||
apiKey={import.meta.env.VITE_KEY_TINY_MCE}
|
||||
onEditorChange={(content) => {
|
||||
setData("DescPost", content);
|
||||
}}
|
||||
init={{
|
||||
plugins: [
|
||||
// Core editing features
|
||||
// Free plugins only
|
||||
"anchor",
|
||||
"autolink",
|
||||
"charmap",
|
||||
|
@ -302,60 +302,21 @@ export default function AddPost({
|
|||
"table",
|
||||
"visualblocks",
|
||||
"wordcount",
|
||||
"checklist",
|
||||
"mediaembed",
|
||||
"casechange",
|
||||
"export",
|
||||
"formatpainter",
|
||||
"pageembed",
|
||||
"a11ychecker",
|
||||
"tinymcespellchecker",
|
||||
"permanentpen",
|
||||
"powerpaste",
|
||||
"advtable",
|
||||
"advcode",
|
||||
"editimage",
|
||||
"advtemplate",
|
||||
"ai",
|
||||
"mentions",
|
||||
"tinycomments",
|
||||
"tableofcontents",
|
||||
"footnotes",
|
||||
"mergetags",
|
||||
"autocorrect",
|
||||
"typography",
|
||||
"inlinecss",
|
||||
"markdown",
|
||||
"importword",
|
||||
"exportword",
|
||||
"exportpdf",
|
||||
"code",
|
||||
"fullscreen",
|
||||
"preview",
|
||||
],
|
||||
toolbar:
|
||||
"undo redo | blocks fontfamily fontsize | bold italic underline strikethrough | link image media table mergetags | addcomment showcomments | spellcheckdialog a11ycheck typography | align lineheight | checklist numlist bullist indent outdent | emoticons charmap | removeformat",
|
||||
tinycomments_mode: "embedded",
|
||||
tinycomments_author: "Author name",
|
||||
mergetags_list: [
|
||||
{
|
||||
value: "First.Name",
|
||||
title: "First Name",
|
||||
},
|
||||
{ value: "Email", title: "Email" },
|
||||
],
|
||||
ai_request: (
|
||||
_request: any,
|
||||
respondWith: {
|
||||
string: (
|
||||
callback: () => Promise<string>
|
||||
) => void;
|
||||
}
|
||||
) =>
|
||||
respondWith.string(() =>
|
||||
Promise.reject(
|
||||
"See docs to implement AI Assistant"
|
||||
)
|
||||
),
|
||||
"undo redo | blocks | bold italic underline strikethrough | link image media table | align | bullist numlist | emoticons charmap | fullscreen preview code | removeformat",
|
||||
height: 500,
|
||||
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="Welcome to TinyMCE!"
|
||||
initialValue="Isi Artikel"
|
||||
/>
|
||||
</div>
|
||||
{errors.DescPost && (
|
||||
|
@ -436,10 +397,21 @@ export default function AddPost({
|
|||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
|
||||
<Button type="submit" className="bg-blue-600 text-white">
|
||||
<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"
|
||||
>
|
||||
Simpan
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
|
|
|
@ -26,7 +26,7 @@ interface SubKategori {
|
|||
NamaSubKategori: string;
|
||||
}
|
||||
|
||||
interface Post {
|
||||
interface Posting {
|
||||
PostId: number;
|
||||
KategoriId: number;
|
||||
SubKategoriId: number;
|
||||
|
@ -38,7 +38,7 @@ interface Post {
|
|||
}
|
||||
|
||||
interface EditPostProps {
|
||||
post: Post;
|
||||
posting: Posting;
|
||||
kategori: Kategori[];
|
||||
subkategori: SubKategori[];
|
||||
}
|
||||
|
@ -56,28 +56,29 @@ interface PostFormData {
|
|||
const slugify = (text: string) => text.toLowerCase().replace(/\s+/g, "-");
|
||||
|
||||
export default function EditPost({
|
||||
post,
|
||||
posting,
|
||||
kategori,
|
||||
subkategori,
|
||||
}: EditPostProps) {
|
||||
const { toast } = useToast();
|
||||
const [imagePreview, setImagePreview] = useState<string | null>(null);
|
||||
|
||||
const { data, setData, put, processing, errors } = useForm<PostFormData>({
|
||||
KategoriId: post.KategoriId.toString(),
|
||||
SubKategoriId: post.SubKategoriId.toString(),
|
||||
JudulPost: post.JudulPost,
|
||||
SlugPost: post.SlugPost,
|
||||
DescPost: post.DescPost,
|
||||
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: post.IsPublish,
|
||||
IsPublish: posting.IsPublish,
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (post.ImagePost) {
|
||||
setImagePreview(`/storage/${post.ImagePost}`);
|
||||
if (posting.ImagePost) {
|
||||
const path = `${posting.ImagePost}`;
|
||||
setImagePreview(path);
|
||||
}
|
||||
}, [post.ImagePost]);
|
||||
}, [posting.ImagePost]);
|
||||
|
||||
useEffect(() => {
|
||||
if (data.JudulPost && data.JudulPost.trim() !== "") {
|
||||
|
@ -107,13 +108,13 @@ export default function EditPost({
|
|||
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);
|
||||
}
|
||||
|
||||
put(`/admin/post/${post.PostId}`, {
|
||||
post(`/admin/post/${posting.PostId}`, {
|
||||
data: formData,
|
||||
headers: { "Content-Type": "multipart/form-data" },
|
||||
onSuccess: () => {
|
||||
|
@ -234,24 +235,60 @@ export default function EditPost({
|
|||
|
||||
<div>
|
||||
<Editor
|
||||
apiKey="h471mb13phsh2c8rlp5msca6h0h8y0oy37llvpvzhqjymqq3"
|
||||
apiKey={import.meta.env.VITE_KEY_TINY_MCE}
|
||||
value={data.DescPost}
|
||||
onEditorChange={(content) =>
|
||||
setData("DescPost", content)
|
||||
}
|
||||
init={{
|
||||
height: 300,
|
||||
menubar: false,
|
||||
plugins: [
|
||||
"advlist autolink lists link image charmap print preview anchor",
|
||||
"searchreplace visualblocks code fullscreen",
|
||||
"insertdatetime media table paste code help wordcount",
|
||||
// Free plugins only
|
||||
"anchor",
|
||||
"autolink",
|
||||
"charmap",
|
||||
"codesample",
|
||||
"emoticons",
|
||||
"image",
|
||||
"link",
|
||||
"lists",
|
||||
"media",
|
||||
"searchreplace",
|
||||
"table",
|
||||
"visualblocks",
|
||||
"wordcount",
|
||||
"code",
|
||||
"fullscreen",
|
||||
"preview",
|
||||
],
|
||||
toolbar:
|
||||
"undo redo | formatselect | bold italic backcolor | alignleft aligncenter alignright alignjustify | " +
|
||||
"bullist numlist outdent indent | removeformat | help",
|
||||
"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}
|
||||
|
@ -334,10 +371,10 @@ export default function EditPost({
|
|||
|
||||
<div>
|
||||
<Select
|
||||
value={data.IsPublish.toString()}
|
||||
onValueChange={(value) =>
|
||||
setData("IsPublish", value === "true")
|
||||
}
|
||||
value={data.IsPublish.toString()}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Pilih Status Publikasi" />
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect, useState } from "react";
|
||||
import { Link, useForm } from "@inertiajs/react";
|
||||
import { Link, useForm, usePage } from "@inertiajs/react";
|
||||
import { PageProps } from "@/types";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
|
@ -24,6 +24,7 @@ import {
|
|||
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||
import { Head } from "@inertiajs/react";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
import hasAnyPermission from "@/utils/hasAnyPermission";
|
||||
|
||||
interface SubKategori {
|
||||
SubKategoriId: number;
|
||||
|
@ -49,6 +50,8 @@ const ITEMS_PER_PAGE = 5;
|
|||
export default function PostIndex({
|
||||
posts = [],
|
||||
}: PageProps<{ posts: Posting[] }>) {
|
||||
const { auth } = usePage().props;
|
||||
const userPermissions = auth?.user?.permissions ?? [];
|
||||
const { toast } = useToast();
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
|
||||
|
|
Loading…
Reference in New Issue