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(); | ||||
| { | ||||
|     try { | ||||
|         DB::beginTransaction(); | ||||
|         $data = $request->validated(); | ||||
| 
 | ||||
|             // Only update image if new one is uploaded
 | ||||
|             if ($request->hasFile('ImagePost')) { | ||||
|                 // Delete old image if exists
 | ||||
|                 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
 | ||||
|                 unset($data['ImagePost']); | ||||
|         // Update gambar hanya jika ada file baru yang diupload
 | ||||
|         if ($request->hasFile('ImagePost')) { | ||||
|             // Hapus gambar lama jika ada
 | ||||
|             if ($post->ImagePost && Storage::disk('public')->exists($post->ImagePost)) { | ||||
|                 Storage::disk('public')->delete($post->ImagePost); | ||||
|             } | ||||
| 
 | ||||
|             $data['IsPublish'] = $request->boolean('IsPublish'); | ||||
|             $post->update($data); | ||||
| 
 | ||||
|             DB::commit(); | ||||
|             return redirect()->route('admin.post.index')->with('success', 'Post berhasil diperbarui.'); | ||||
| 
 | ||||
|         } catch (\Exception $e) { | ||||
|             DB::rollBack(); | ||||
|             Log::error('Error updating Post: ' . $e->getMessage()); | ||||
|             return back()->with('error', 'Terjadi kesalahan saat memperbarui post.'); | ||||
|             $data['ImagePost'] = $request->file('ImagePost')->store('images/posts', 'public'); | ||||
|         } else { | ||||
|             // Jika tidak ada file baru, jangan update field ImagePost
 | ||||
|             unset($data['ImagePost']); | ||||
|         } | ||||
| 
 | ||||
|         // 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.'); | ||||
|     } 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,53 +22,14 @@ 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', | ||||
|                 'JudulPost' => ['required', 'string', 'max:255'], | ||||
|                 'SlugPost' => ['required', 'string', 'max:255'], | ||||
|                 'DescPost' => ['required', 'string'], | ||||
|                 'ImagePost' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'], | ||||
|                 '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]); | ||||
|             } | ||||
|         } | ||||
|         return [ | ||||
|             'KategoriId' => 'required|integer|exists:Kategori,KategoriId', | ||||
|             'SubKategoriId' => 'required|integer|exists:SubKategori,SubKategoriId', | ||||
|             'JudulPost' => ['required', 'string', 'max:255'], | ||||
|             'SlugPost' => ['required', 'string', 'max:255'], | ||||
|             'DescPost' => ['required', 'string'], | ||||
|             'ImagePost' => ['nullable', 'image', 'mimes:jpg,jpeg,png,webp', 'max:2048'], | ||||
|             'IsPublish' => 'boolean', | ||||
|         ]; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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"> | ||||
|                         Simpan | ||||
|                     </Button> | ||||
|                     <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