feat: Menambahkan controller, request, dan model Penaatan
							parent
							
								
									db45e28350
								
							
						
					
					
						commit
						e37be6bc2d
					
				|  | @ -0,0 +1,56 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Http\Controllers; | ||||||
|  | 
 | ||||||
|  | use App\Http\Requests\PenaatanRequest; | ||||||
|  | use App\Models\Penaatan; | ||||||
|  | use Illuminate\Http\Request; | ||||||
|  | use Illuminate\Support\Facades\Log; | ||||||
|  | use Inertia\Inertia; | ||||||
|  | 
 | ||||||
|  | class PenaatanController extends Controller | ||||||
|  | { | ||||||
|  |     public function index() | ||||||
|  |     { | ||||||
|  |         try { | ||||||
|  |             $penaatan = Penaatan::latest()->get(); | ||||||
|  |             return Inertia::render('admin/penaatan/index_penaatan', ['penaatan' => $penaatan]); | ||||||
|  |         } catch (\Exception $e) { | ||||||
|  |             Log::error('Error fetching Status Penaatan: ' . $e->getMessage()); | ||||||
|  |             return back()->with('error', 'Something went wrong.'); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function store(PenaatanRequest $request) | ||||||
|  |     { | ||||||
|  |         try { | ||||||
|  |             Penaatan::create($request->validated()); | ||||||
|  |             return redirect()->route('admin.penataan.index')->with('success', 'Penaatan created successfully.'); | ||||||
|  |         } catch (\Exception $e) { | ||||||
|  |             Log::error('Error creating Penaatan: ' . $e->getMessage()); | ||||||
|  |             return back()->with('error', 'Failed to create Penaatan.'); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function update(PenaatanRequest $request, Penaatan $penaatan) | ||||||
|  |     { | ||||||
|  |         try { | ||||||
|  |             $penaatan->update($request->validated()); | ||||||
|  |             return redirect()->route('admin.penaatan.index')->with('success', 'Penaatan berhasil diperbarui.'); | ||||||
|  |         } catch (\Exception $e) { | ||||||
|  |             Log::error('Error updating Penaatan: ' . $e->getMessage()); | ||||||
|  |             return back()->with('error', 'Something went wrong.'); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     public function destroy(Penaatan $penaatan) | ||||||
|  |     { | ||||||
|  |         try { | ||||||
|  |             $penaatan->delete(); | ||||||
|  |             return redirect()->route('admin.penaatan.index')->with('success', 'Penaatan berhasil dihapus.'); | ||||||
|  |         } catch (\Exception $e) { | ||||||
|  |             Log::error('Error deleting Penaatan: ' . $e->getMessage()); | ||||||
|  |             return back()->with('error', 'Something went wrong.'); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,28 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Http\Requests; | ||||||
|  | 
 | ||||||
|  | use Illuminate\Foundation\Http\FormRequest; | ||||||
|  | 
 | ||||||
|  | class PenaatanRequest extends FormRequest | ||||||
|  | { | ||||||
|  |     /** | ||||||
|  |      * Determine if the user is authorized to make this request. | ||||||
|  |      */ | ||||||
|  |     public function authorize(): bool | ||||||
|  |     { | ||||||
|  |         return true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     /** | ||||||
|  |      * Get the validation rules that apply to the request. | ||||||
|  |      * | ||||||
|  |      * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string> | ||||||
|  |      */ | ||||||
|  |     public function rules(): array | ||||||
|  |     { | ||||||
|  |         return [ | ||||||
|  |             'NamaPenaatan' => ['required', 'string', 'max:255'], | ||||||
|  |         ]; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | @ -0,0 +1,22 @@ | ||||||
|  | <?php | ||||||
|  | 
 | ||||||
|  | namespace App\Models; | ||||||
|  | 
 | ||||||
|  | use Illuminate\Database\Eloquent\Factories\HasFactory; | ||||||
|  | use Illuminate\Database\Eloquent\Model; | ||||||
|  | 
 | ||||||
|  | class Penaatan extends Model | ||||||
|  | { | ||||||
|  | 
 | ||||||
|  |     protected $table = 'Penaatan'; | ||||||
|  | 
 | ||||||
|  |     protected $primaryKey = 'PenaatanId'; | ||||||
|  | 
 | ||||||
|  |     protected $fillable = ['NamaPenaatan']; | ||||||
|  | 
 | ||||||
|  |     public function hukum() | ||||||
|  |     { | ||||||
|  |         return $this->hasMany(Hukum::class, 'PenaatanId'); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -0,0 +1,208 @@ | ||||||
|  | import { Button } from "@/components/ui/button"; | ||||||
|  | import { | ||||||
|  |     Dialog, | ||||||
|  |     DialogContent, | ||||||
|  |     DialogHeader, | ||||||
|  |     DialogTitle, | ||||||
|  |     DialogFooter, | ||||||
|  | } from "@/components/ui/dialog"; | ||||||
|  | import { Input } from "@/components/ui/input"; | ||||||
|  | import { Label } from "@/components/ui/label"; | ||||||
|  | import Select from "react-select"; | ||||||
|  | import { useState, useEffect } from "react"; | ||||||
|  | import { useForm } from "@inertiajs/react"; | ||||||
|  | import { useToast } from "@/hooks/use-toast"; | ||||||
|  | 
 | ||||||
|  | import { HukumType } from "@/types/perusahaan"; | ||||||
|  | 
 | ||||||
|  | interface AddPenaatanModalProps { | ||||||
|  |     open: boolean; | ||||||
|  |     onClose: () => void; | ||||||
|  |     onSuccess: () => void; | ||||||
|  |     editingData: HukumType | null; | ||||||
|  |     penaatan: { PenaatanId: number; NamaPenaatan: string }[]; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export function AddPenaatanModal({ | ||||||
|  |     open, | ||||||
|  |     onClose, | ||||||
|  |     onSuccess, | ||||||
|  |     editingData, | ||||||
|  |     penaatan, | ||||||
|  | }: AddPenaatanModalProps) { | ||||||
|  |     const { toast } = useToast(); | ||||||
|  |     const [loading, setLoading] = useState(false); | ||||||
|  | 
 | ||||||
|  |     const { data, setData, post, reset } = useForm<{ | ||||||
|  |         PenaatanId: string; | ||||||
|  |         PenaatanNumber: string; | ||||||
|  |         PenaatanDate: string; | ||||||
|  |         PenaatanFile: File | null; | ||||||
|  |         currentPenaatanFile?: string; | ||||||
|  |     }>({ | ||||||
|  |         PenaatanId: "", | ||||||
|  |         PenaatanNumber: "", | ||||||
|  |         PenaatanDate: "", | ||||||
|  |         PenaatanFile: null, | ||||||
|  |         currentPenaatanFile: editingData?.PenaatanFile, | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     const [isPenaatanModalOpen, setIsPenaatanModalOpen] = useState(false); | ||||||
|  |     const [selectedHukum, setSelectedHukum] = useState<HukumType | null>(null); | ||||||
|  | 
 | ||||||
|  |     // Handle Open Modal for Penaatan
 | ||||||
|  |     const handleOpenPenaatanModal = (hukum: HukumType) => { | ||||||
|  |         setSelectedHukum(hukum); | ||||||
|  |         setIsPenaatanModalOpen(true); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     useEffect(() => { | ||||||
|  |         if (editingData) { | ||||||
|  |             setData({ | ||||||
|  |                 PenaatanId: editingData.PenaatanId?.toString() || "", | ||||||
|  |                 PenaatanNumber: editingData.PenaatanNumber || "", | ||||||
|  |                 PenaatanDate: editingData.PenaatanDate || "", | ||||||
|  |                 PenaatanFile: null, | ||||||
|  |                 currentPenaatanFile: "", | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  |             reset(); | ||||||
|  |         } | ||||||
|  |     }, [editingData, open]); | ||||||
|  | 
 | ||||||
|  |     const penaatanOptions = penaatan.map((p) => ({ | ||||||
|  |         value: p.PenaatanId.toString(), | ||||||
|  |         label: p.NamaPenaatan, | ||||||
|  |     })); | ||||||
|  | 
 | ||||||
|  |     const handleSubmit = (e: React.FormEvent) => { | ||||||
|  |         e.preventDefault(); | ||||||
|  |         setLoading(true); | ||||||
|  | 
 | ||||||
|  |         const formData = new FormData(); | ||||||
|  |         formData.append("PenaatanId", data.PenaatanId); | ||||||
|  |         formData.append("PenaatanNumber", data.PenaatanNumber); | ||||||
|  |         formData.append("PenaatanDate", data.PenaatanDate); | ||||||
|  |         if (data.PenaatanFile) | ||||||
|  |             formData.append("PenaatanFile", data.PenaatanFile); | ||||||
|  | 
 | ||||||
|  |         if (!editingData) return; | ||||||
|  | 
 | ||||||
|  |         post(`/admin/hukum/${editingData.HukumId}/penaatan`, { | ||||||
|  |             data: formData, | ||||||
|  |             forceFormData: true, | ||||||
|  |             onSuccess: () => { | ||||||
|  |                 toast({ | ||||||
|  |                     title: "Berhasil", | ||||||
|  |                     description: "Data Penaatan berhasil diperbarui", | ||||||
|  |                     variant: "default", | ||||||
|  |                 }); | ||||||
|  |                 reset(); | ||||||
|  |                 setLoading(false); | ||||||
|  |                 onSuccess(); | ||||||
|  |                 onClose(); | ||||||
|  |             }, | ||||||
|  |             onError: () => { | ||||||
|  |                 toast({ | ||||||
|  |                     title: "Gagal", | ||||||
|  |                     description: "Terjadi kesalahan saat memperbarui data", | ||||||
|  |                     variant: "destructive", | ||||||
|  |                 }); | ||||||
|  |                 setLoading(false); | ||||||
|  |             }, | ||||||
|  |         }); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     return ( | ||||||
|  |         <Dialog open={open} onOpenChange={onClose}> | ||||||
|  |             <DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto"> | ||||||
|  |                 <DialogHeader> | ||||||
|  |                     <DialogTitle>Perbarui Data Penaatan</DialogTitle> | ||||||
|  |                 </DialogHeader> | ||||||
|  |                 <form onSubmit={handleSubmit} className="space-y-6"> | ||||||
|  |                     <div className="space-y-4"> | ||||||
|  |                         <Label>Status Penaatan</Label> | ||||||
|  |                         <Select | ||||||
|  |                             id="PenaatanId" | ||||||
|  |                             options={penaatanOptions} | ||||||
|  |                             placeholder="Pilih Status Penaatan" | ||||||
|  |                             value={penaatanOptions.find( | ||||||
|  |                                 (p) => p.value === data.PenaatanId | ||||||
|  |                             )} | ||||||
|  |                             onChange={(option) => | ||||||
|  |                                 setData("PenaatanId", option?.value || "") | ||||||
|  |                             } | ||||||
|  |                         /> | ||||||
|  | 
 | ||||||
|  |                         <Label>Nomor SK Penaatan</Label> | ||||||
|  |                         <Input | ||||||
|  |                             value={data.PenaatanNumber} | ||||||
|  |                             onChange={(e) => | ||||||
|  |                                 setData("PenaatanNumber", e.target.value) | ||||||
|  |                             } | ||||||
|  |                         /> | ||||||
|  | 
 | ||||||
|  |                         <Label>Tanggal SK Penaatan</Label> | ||||||
|  |                         <Input | ||||||
|  |                             type="date" | ||||||
|  |                             value={data.PenaatanDate} | ||||||
|  |                             onChange={(e) => | ||||||
|  |                                 setData("PenaatanDate", e.target.value) | ||||||
|  |                             } | ||||||
|  |                         /> | ||||||
|  | 
 | ||||||
|  |                         <Label>Dokumen SK Penaatan</Label> | ||||||
|  |                         <div className="flex flex-col gap-2"> | ||||||
|  |                             <Input | ||||||
|  |                                 type="file" | ||||||
|  |                                 onChange={(e) => | ||||||
|  |                                     setData( | ||||||
|  |                                         "PenaatanFile", | ||||||
|  |                                         e.target.files | ||||||
|  |                                             ? e.target.files[0] | ||||||
|  |                                             : null | ||||||
|  |                                     ) | ||||||
|  |                                 } | ||||||
|  |                             /> | ||||||
|  |                             {editingData?.PenaatanFile && ( | ||||||
|  |                                 <div className="flex items-center gap-2 text-sm text-muted-foreground"> | ||||||
|  |                                     <span> | ||||||
|  |                                         File saat ini:{" "} | ||||||
|  |                                         {editingData.PenaatanFile} | ||||||
|  |                                     </span> | ||||||
|  |                                     <Button | ||||||
|  |                                         type="button" | ||||||
|  |                                         variant="outline" | ||||||
|  |                                         size="sm" | ||||||
|  |                                         onClick={() => | ||||||
|  |                                             window.open( | ||||||
|  |                                                 `/storage/${editingData.PenaatanFile}`, | ||||||
|  |                                                 "_blank" | ||||||
|  |                                             ) | ||||||
|  |                                         } | ||||||
|  |                                     > | ||||||
|  |                                         Lihat File | ||||||
|  |                                     </Button> | ||||||
|  |                                 </div> | ||||||
|  |                             )} | ||||||
|  |                         </div> | ||||||
|  |                     </div> | ||||||
|  | 
 | ||||||
|  |                     <DialogFooter> | ||||||
|  |                         <Button | ||||||
|  |                             type="button" | ||||||
|  |                             variant="outline" | ||||||
|  |                             onClick={onClose} | ||||||
|  |                             disabled={loading} | ||||||
|  |                         > | ||||||
|  |                             Batal | ||||||
|  |                         </Button> | ||||||
|  |                         <Button type="submit" disabled={loading}> | ||||||
|  |                             {loading ? "Menyimpan..." : "Simpan"} | ||||||
|  |                         </Button> | ||||||
|  |                     </DialogFooter> | ||||||
|  |                 </form> | ||||||
|  |             </DialogContent> | ||||||
|  |         </Dialog> | ||||||
|  |     ); | ||||||
|  | } | ||||||
|  | @ -179,7 +179,7 @@ AddPerusahaanModalProps) { | ||||||
| 
 | 
 | ||||||
|     const perusahaanOptions = perusahaan.map((per) => ({ |     const perusahaanOptions = perusahaan.map((per) => ({ | ||||||
|         value: per.PerusahaanId, |         value: per.PerusahaanId, | ||||||
|         label: per.PerusahaanId, |         label: per.NamaPerusahaan, | ||||||
|     })); |     })); | ||||||
| 
 | 
 | ||||||
|     const kecamatanOptions = kecamatan |     const kecamatanOptions = kecamatan | ||||||
|  | @ -439,7 +439,7 @@ AddPerusahaanModalProps) { | ||||||
|                                         onChange={(option) => |                                         onChange={(option) => | ||||||
|                                             setData({ |                                             setData({ | ||||||
|                                                 ...data, |                                                 ...data, | ||||||
|                                                 PerusahaanId: |                                                 NamaPerusahaan: | ||||||
|                                                     option?.value?.toString() || |                                                     option?.value?.toString() || | ||||||
|                                                     "", |                                                     "", | ||||||
|                                             }) |                                             }) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,461 @@ | ||||||
|  | import React, { useEffect, useState } from "react"; | ||||||
|  | import { useForm } from "@inertiajs/react"; | ||||||
|  | import { PageProps } from "@/types"; | ||||||
|  | import { Button } from "@/components/ui/button"; | ||||||
|  | import { Input } from "@/components/ui/input"; | ||||||
|  | import { | ||||||
|  |     Table, | ||||||
|  |     TableHeader, | ||||||
|  |     TableRow, | ||||||
|  |     TableHead, | ||||||
|  |     TableBody, | ||||||
|  |     TableCell, | ||||||
|  | } from "@/components/ui/table"; | ||||||
|  | // import { toast } from "react-toastify";
 | ||||||
|  | import { useToast } from "@/hooks/use-toast"; | ||||||
|  | import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; | ||||||
|  | import { | ||||||
|  |     Dialog, | ||||||
|  |     DialogTrigger, | ||||||
|  |     DialogContent, | ||||||
|  |     DialogHeader, | ||||||
|  |     DialogTitle, | ||||||
|  |     DialogFooter, | ||||||
|  | } from "@/components/ui/dialog"; | ||||||
|  | import { | ||||||
|  |     Search, | ||||||
|  |     Plus, | ||||||
|  |     Pencil, | ||||||
|  |     Trash2, | ||||||
|  |     ChevronLeft, | ||||||
|  |     ChevronRight, | ||||||
|  | } from "lucide-react"; | ||||||
|  | import AuthenticatedLayout from "@/layouts/authenticated-layout"; | ||||||
|  | import { Head } from "@inertiajs/react"; | ||||||
|  | import { Toaster } from "@/components/ui/toaster"; | ||||||
|  | import { | ||||||
|  |     Select, | ||||||
|  |     SelectContent, | ||||||
|  |     SelectItem, | ||||||
|  |     SelectTrigger, | ||||||
|  |     SelectValue, | ||||||
|  | } from "@/components/ui/select"; | ||||||
|  | 
 | ||||||
|  | interface Penaatan { | ||||||
|  |     PenaatanId: number | null; | ||||||
|  |     NamaPenaatan: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | const ITEMS_PER_PAGE = 5; | ||||||
|  | 
 | ||||||
|  | export default function PenaatanIndex({ | ||||||
|  |     penaatan, | ||||||
|  | }: PageProps<{ penaatan: Penaatan[] }>) { | ||||||
|  |     const { | ||||||
|  |         data, | ||||||
|  |         setData, | ||||||
|  |         post, | ||||||
|  |         put, | ||||||
|  |         delete: destroy, | ||||||
|  |         reset, | ||||||
|  |     } = useForm<Penaatan>({ | ||||||
|  |         PenaatanId: null, | ||||||
|  |         NamaPenaatan: "", | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     const { toast } = useToast(); | ||||||
|  | 
 | ||||||
|  |     const [editing, setEditing] = useState(false); | ||||||
|  |     const [isModalOpen, setIsModalOpen] = useState(false); | ||||||
|  |     const [deleteConfirm, setDeleteConfirm] = useState<Penaatan | null>(null); | ||||||
|  |     const [search, setSearch] = useState(""); | ||||||
|  | 
 | ||||||
|  |     const [filteredPenaatan, setFilteredPenaatan] = useState(penaatan); | ||||||
|  |     const [currentPage, setCurrentPage] = useState(1); | ||||||
|  | 
 | ||||||
|  |     useEffect(() => { | ||||||
|  |         let filtered = penaatan; | ||||||
|  | 
 | ||||||
|  |         if (search) { | ||||||
|  |             filtered = filtered.filter((penaatan) => | ||||||
|  |                 penaatan.NamaPenaatan.toLowerCase().includes( | ||||||
|  |                     search.toLowerCase() | ||||||
|  |                 ) | ||||||
|  |             ); | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         setFilteredPenaatan(filtered); | ||||||
|  |         setCurrentPage(1); | ||||||
|  |     }, [penaatan, search]); | ||||||
|  | 
 | ||||||
|  |     const totalPages = Math.ceil(filteredPenaatan.length / ITEMS_PER_PAGE); | ||||||
|  |     const startIndex = (currentPage - 1) * ITEMS_PER_PAGE; | ||||||
|  |     const endIndex = startIndex + ITEMS_PER_PAGE; | ||||||
|  |     const currentItems = filteredPenaatan.slice(startIndex, endIndex); | ||||||
|  | 
 | ||||||
|  |     const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => { | ||||||
|  |         setSearch(e.target.value); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const handlePageChange = (page: number) => { | ||||||
|  |         setCurrentPage(page); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const handleSubmit = (e: React.FormEvent) => { | ||||||
|  |         e.preventDefault(); | ||||||
|  | 
 | ||||||
|  |         if (editing) { | ||||||
|  |             put(`/admin/penaatan/${data.PenaatanId}`, { | ||||||
|  |                 onSuccess: () => { | ||||||
|  |                     toast({ | ||||||
|  |                         title: "Berhasil", | ||||||
|  |                         description: "Penaatan berhasil diperbarui", | ||||||
|  |                         variant: "default", | ||||||
|  |                     }); | ||||||
|  |                     setIsModalOpen(false); | ||||||
|  |                     reset(); | ||||||
|  |                     setEditing(false); | ||||||
|  |                 }, | ||||||
|  |                 onError: () => { | ||||||
|  |                     toast({ | ||||||
|  |                         title: "Gagal", | ||||||
|  |                         description: | ||||||
|  |                             "Terjadi kesalahan saat memperbarui Penaatan", | ||||||
|  |                         variant: "destructive", | ||||||
|  |                     }); | ||||||
|  |                 }, | ||||||
|  |             }); | ||||||
|  |         } else { | ||||||
|  |             post("/admin/penaatan", { | ||||||
|  |                 onSuccess: () => { | ||||||
|  |                     toast({ | ||||||
|  |                         title: "Berhasil", | ||||||
|  |                         description: "Penaatan berhasil dibuat", | ||||||
|  |                         variant: "default", | ||||||
|  |                     }); | ||||||
|  |                     setIsModalOpen(false); | ||||||
|  |                     reset(); | ||||||
|  |                 }, | ||||||
|  |                 onError: () => { | ||||||
|  |                     toast({ | ||||||
|  |                         title: "Gagal", | ||||||
|  |                         description: "Terjadi kesalahan saat membuat Penaatan", | ||||||
|  |                         variant: "destructive", | ||||||
|  |                     }); | ||||||
|  |                 }, | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const handleEdit = (penaatan: Penaatan) => { | ||||||
|  |         setData({ ...penaatan }); | ||||||
|  |         setEditing(true); | ||||||
|  |         setIsModalOpen(true); | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     const handleDelete = () => { | ||||||
|  |         if (deleteConfirm) { | ||||||
|  |             destroy(`/admin/penaatan/${deleteConfirm.PenaatanId}`, { | ||||||
|  |                 onSuccess: () => { | ||||||
|  |                     toast({ | ||||||
|  |                         title: "Berhasil", | ||||||
|  |                         description: "Penaatan berhasil dihapus", | ||||||
|  |                         variant: "default", | ||||||
|  |                     }); | ||||||
|  |                     setDeleteConfirm(null); | ||||||
|  |                 }, | ||||||
|  |                 onError: () => { | ||||||
|  |                     toast({ | ||||||
|  |                         title: "Gagal", | ||||||
|  |                         description: | ||||||
|  |                             "Terjadi kesalahan saat menghapus Penaatan", | ||||||
|  |                         variant: "destructive", | ||||||
|  |                     }); | ||||||
|  |                     setDeleteConfirm(null); | ||||||
|  |                 }, | ||||||
|  |             }); | ||||||
|  |         } | ||||||
|  |     }; | ||||||
|  |     return ( | ||||||
|  |         <AuthenticatedLayout header={"Status Penaatan"}> | ||||||
|  |             <Head title="Status Penaatan" /> | ||||||
|  |             <div className="container mx-auto p-4"> | ||||||
|  |                 <Card className="shadow-lg"> | ||||||
|  |                     <CardHeader> | ||||||
|  |                         <div className="flex flex-col space-y-4"> | ||||||
|  |                             <div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4"> | ||||||
|  |                                 <div className="relative w-full md:w-96"> | ||||||
|  |                                     <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 h-4 w-4" /> | ||||||
|  |                                     <Input | ||||||
|  |                                         type="text" | ||||||
|  |                                         placeholder="Cari Penaatan..." | ||||||
|  |                                         value={search} | ||||||
|  |                                         onChange={handleSearch} | ||||||
|  |                                         className="pl-10 pr-4 w-full border-gray-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 rounded-lg" | ||||||
|  |                                     /> | ||||||
|  |                                 </div> | ||||||
|  |                                 <Dialog | ||||||
|  |                                     open={isModalOpen} | ||||||
|  |                                     onOpenChange={setIsModalOpen} | ||||||
|  |                                 > | ||||||
|  |                                     <DialogTrigger asChild> | ||||||
|  |                                         <Button | ||||||
|  |                                             onClick={() => { | ||||||
|  |                                                 setEditing(false); | ||||||
|  |                                                 setIsModalOpen(true); | ||||||
|  |                                             }} | ||||||
|  |                                             className="bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded-lg flex items-center gap-2 transition-colors duration-200" | ||||||
|  |                                         > | ||||||
|  |                                             <Plus className="h-4 w-4" /> | ||||||
|  |                                             Buat Penaatan | ||||||
|  |                                         </Button> | ||||||
|  |                                     </DialogTrigger> | ||||||
|  | 
 | ||||||
|  |                                     <DialogContent className="sm:max-w-md"> | ||||||
|  |                                         <DialogHeader> | ||||||
|  |                                             <DialogTitle className="text-xl font-semibold"> | ||||||
|  |                                                 {editing | ||||||
|  |                                                     ? "Ubah Penaatan" | ||||||
|  |                                                     : "Buat Penaatan"} | ||||||
|  |                                             </DialogTitle> | ||||||
|  |                                         </DialogHeader> | ||||||
|  |                                         <form | ||||||
|  |                                             onSubmit={handleSubmit} | ||||||
|  |                                             className="flex flex-col gap-4 mt-4" | ||||||
|  |                                         > | ||||||
|  |                                             <div className="space-y-2"> | ||||||
|  |                                                 <label className="text-sm font-medium text-gray-700"> | ||||||
|  |                                                     Name | ||||||
|  |                                                 </label> | ||||||
|  |                                                 <Input | ||||||
|  |                                                     type="text" | ||||||
|  |                                                     placeholder="Masukkan nama Penaatan" | ||||||
|  |                                                     value={data.NamaPenaatan} | ||||||
|  |                                                     onChange={(e) => | ||||||
|  |                                                         setData( | ||||||
|  |                                                             "NamaPenaatan", | ||||||
|  |                                                             e.target.value | ||||||
|  |                                                         ) | ||||||
|  |                                                     } | ||||||
|  |                                                     className="w-full" | ||||||
|  |                                                 /> | ||||||
|  |                                                 {/* <div className="space-y-2"> | ||||||
|  |                                                     <label className="text-sm font-medium text-gray-700"> | ||||||
|  |                                                         Publish | ||||||
|  |                                                     </label> | ||||||
|  |                                                     <Select | ||||||
|  |                                                         value={ | ||||||
|  |                                                             data.is_publish | ||||||
|  |                                                                 ? "true" | ||||||
|  |                                                                 : "false" | ||||||
|  |                                                         } | ||||||
|  |                                                         onValueChange={( | ||||||
|  |                                                             value | ||||||
|  |                                                         ) => | ||||||
|  |                                                             setData( | ||||||
|  |                                                                 "is_publish", | ||||||
|  |                                                                 value === "true" | ||||||
|  |                                                             ) | ||||||
|  |                                                         } | ||||||
|  |                                                     > | ||||||
|  |                                                         <SelectTrigger className="w-full"> | ||||||
|  |                                                             <SelectValue placeholder="Select status" /> | ||||||
|  |                                                         </SelectTrigger> | ||||||
|  |                                                         <SelectContent> | ||||||
|  |                                                             <SelectItem value="true"> | ||||||
|  |                                                                 Published | ||||||
|  |                                                             </SelectItem> | ||||||
|  |                                                             <SelectItem value="false"> | ||||||
|  |                                                                 Unpublished | ||||||
|  |                                                             </SelectItem> | ||||||
|  |                                                         </SelectContent> | ||||||
|  |                                                     </Select> | ||||||
|  |                                                 </div> */} | ||||||
|  |                                             </div> | ||||||
|  |                                             <DialogFooter className="mt-6"> | ||||||
|  |                                                 <Button | ||||||
|  |                                                     type="submit" | ||||||
|  |                                                     className="w-full bg-blue-600 hover:bg-blue-700 text-white" | ||||||
|  |                                                 > | ||||||
|  |                                                     {editing ? "Ubah" : "Buat"} | ||||||
|  |                                                 </Button> | ||||||
|  |                                             </DialogFooter> | ||||||
|  |                                         </form> | ||||||
|  |                                     </DialogContent> | ||||||
|  |                                 </Dialog> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </CardHeader> | ||||||
|  |                     <CardContent> | ||||||
|  |                         <div className="rounded-lg border border-gray-200 overflow-hidden"> | ||||||
|  |                             <Table> | ||||||
|  |                                 <TableHeader> | ||||||
|  |                                     <TableRow className="bg-gray-50 dark:bg-black/50"> | ||||||
|  |                                         {/* <TableHead className="font-semibold"> | ||||||
|  |                                             ID | ||||||
|  |                                         </TableHead> */} | ||||||
|  |                                         <TableHead className="font-semibold"> | ||||||
|  |                                             No | ||||||
|  |                                         </TableHead> | ||||||
|  |                                         <TableHead className="font-semibold"> | ||||||
|  |                                             Name | ||||||
|  |                                         </TableHead> | ||||||
|  |                                         <TableHead className="font-semibold"> | ||||||
|  |                                             Actions | ||||||
|  |                                         </TableHead> | ||||||
|  |                                     </TableRow> | ||||||
|  |                                 </TableHeader> | ||||||
|  |                                 <TableBody> | ||||||
|  |                                     {currentItems.map((penaatan, index) => ( | ||||||
|  |                                         <TableRow | ||||||
|  |                                             key={penaatan.PenaatanId || "new"} | ||||||
|  |                                             className="hover:bg-gray-50 transition-colors duration-150" | ||||||
|  |                                         > | ||||||
|  |                                             {/* <TableCell>{category.id}</TableCell> */} | ||||||
|  |                                             <TableCell> | ||||||
|  |                                                 {startIndex + index + 1} | ||||||
|  |                                             </TableCell> | ||||||
|  |                                             <TableCell> | ||||||
|  |                                                 {penaatan.NamaPenaatan} | ||||||
|  |                                             </TableCell> | ||||||
|  |                                             <TableCell className="flex gap-2"> | ||||||
|  |                                                 <Button | ||||||
|  |                                                     onClick={() => | ||||||
|  |                                                         handleEdit(penaatan) | ||||||
|  |                                                     } | ||||||
|  |                                                     variant="outline" | ||||||
|  |                                                     className="flex items-center gap-2" | ||||||
|  |                                                 > | ||||||
|  |                                                     <Pencil className="h-4 w-4" /> | ||||||
|  |                                                     Edit | ||||||
|  |                                                 </Button> | ||||||
|  |                                                 <Button | ||||||
|  |                                                     onClick={() => | ||||||
|  |                                                         setDeleteConfirm( | ||||||
|  |                                                             penaatan | ||||||
|  |                                                         ) | ||||||
|  |                                                     } | ||||||
|  |                                                     variant="destructive" | ||||||
|  |                                                     className="flex items-center gap-2" | ||||||
|  |                                                 > | ||||||
|  |                                                     <Trash2 className="h-4 w-4" /> | ||||||
|  |                                                     Delete | ||||||
|  |                                                 </Button> | ||||||
|  |                                             </TableCell> | ||||||
|  |                                         </TableRow> | ||||||
|  |                                     ))} | ||||||
|  |                                     {currentItems.length === 0 && ( | ||||||
|  |                                         <TableRow> | ||||||
|  |                                             <TableCell | ||||||
|  |                                                 colSpan={4} | ||||||
|  |                                                 className="text-center py-4 text-gray-500" | ||||||
|  |                                             > | ||||||
|  |                                                 Tidak ada data | ||||||
|  |                                             </TableCell> | ||||||
|  |                                         </TableRow> | ||||||
|  |                                     )} | ||||||
|  |                                 </TableBody> | ||||||
|  |                             </Table> | ||||||
|  |                         </div> | ||||||
|  |                         {/* <div className="flex flex-row mt-3"> | ||||||
|  |                             <div className="text-sm w-1/2"> | ||||||
|  |                                 1 : Pengumuman <br /> 2 : Undangan | ||||||
|  |                             </div> | ||||||
|  |                             <div className="text-sm w-1/2"> | ||||||
|  |                                 3 : Peraturan <br /> 4 : Popup Home | ||||||
|  |                             </div> | ||||||
|  |                         </div> */} | ||||||
|  |                         <div className="flex items-center justify-between mt-4"> | ||||||
|  |                             <div className="text-sm text-gray-500"> | ||||||
|  |                                 Showing {startIndex + 1} to{" "} | ||||||
|  |                                 {Math.min(endIndex, filteredPenaatan.length)} of{" "} | ||||||
|  |                                 {filteredPenaatan.length} entries | ||||||
|  |                             </div> | ||||||
|  |                             <div className="flex items-center gap-2"> | ||||||
|  |                                 <Button | ||||||
|  |                                     variant="outline" | ||||||
|  |                                     size="sm" | ||||||
|  |                                     onClick={() => | ||||||
|  |                                         handlePageChange(currentPage - 1) | ||||||
|  |                                     } | ||||||
|  |                                     disabled={currentPage === 1} | ||||||
|  |                                 > | ||||||
|  |                                     <ChevronLeft className="h-4 w-4" /> | ||||||
|  |                                 </Button> | ||||||
|  |                                 {Array.from( | ||||||
|  |                                     { length: totalPages }, | ||||||
|  |                                     (_, i) => i + 1 | ||||||
|  |                                 ).map((page) => ( | ||||||
|  |                                     <Button | ||||||
|  |                                         key={page} | ||||||
|  |                                         variant={ | ||||||
|  |                                             currentPage === page | ||||||
|  |                                                 ? "default" | ||||||
|  |                                                 : "outline" | ||||||
|  |                                         } | ||||||
|  |                                         size="sm" | ||||||
|  |                                         onClick={() => handlePageChange(page)} | ||||||
|  |                                     > | ||||||
|  |                                         {page} | ||||||
|  |                                     </Button> | ||||||
|  |                                 ))} | ||||||
|  |                                 <Button | ||||||
|  |                                     variant="outline" | ||||||
|  |                                     size="sm" | ||||||
|  |                                     onClick={() => | ||||||
|  |                                         handlePageChange(currentPage + 1) | ||||||
|  |                                     } | ||||||
|  |                                     disabled={currentPage === totalPages} | ||||||
|  |                                 > | ||||||
|  |                                     <ChevronRight className="h-4 w-4" /> | ||||||
|  |                                 </Button> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                     </CardContent> | ||||||
|  |                 </Card> | ||||||
|  |             </div> | ||||||
|  | 
 | ||||||
|  |             {deleteConfirm && ( | ||||||
|  |                 <Dialog open={true} onOpenChange={() => setDeleteConfirm(null)}> | ||||||
|  |                     <DialogContent className="sm:max-w-md"> | ||||||
|  |                         <DialogHeader> | ||||||
|  |                             <DialogTitle className="text-xl font-semibold"> | ||||||
|  |                                 Konfirmasi Penghapusan | ||||||
|  |                             </DialogTitle> | ||||||
|  |                         </DialogHeader> | ||||||
|  |                         <div className="py-4"> | ||||||
|  |                             <p className="text-gray-600"> | ||||||
|  |                                 Apakah Anda yakin ingin menghapus " | ||||||
|  |                                 <span className="font-medium"> | ||||||
|  |                                     {deleteConfirm.NamaPenaatan} | ||||||
|  |                                 </span> | ||||||
|  |                                 "? | ||||||
|  |                                 <br /> | ||||||
|  |                                 <span className="text-red-500"> | ||||||
|  |                                     Tindakan ini tidak dapat dibatalkan. | ||||||
|  |                                 </span> | ||||||
|  |                             </p> | ||||||
|  |                         </div> | ||||||
|  |                         <DialogFooter className="gap-2"> | ||||||
|  |                             <Button | ||||||
|  |                                 onClick={() => setDeleteConfirm(null)} | ||||||
|  |                                 variant="outline" | ||||||
|  |                             > | ||||||
|  |                                 Batal | ||||||
|  |                             </Button> | ||||||
|  |                             <Button | ||||||
|  |                                 onClick={handleDelete} | ||||||
|  |                                 variant="destructive" | ||||||
|  |                                 className="flex items-center gap-2" | ||||||
|  |                             > | ||||||
|  |                                 <Trash2 className="h-4 w-4" /> | ||||||
|  |                                 Ya, hapus | ||||||
|  |                             </Button> | ||||||
|  |                         </DialogFooter> | ||||||
|  |                     </DialogContent> | ||||||
|  |                 </Dialog> | ||||||
|  |             )} | ||||||
|  |             <Toaster /> | ||||||
|  |         </AuthenticatedLayout> | ||||||
|  |     ); | ||||||
|  | } | ||||||
		Loading…
	
		Reference in New Issue