diff --git a/app/Http/Controllers/Admin/RoleController.php b/app/Http/Controllers/Admin/RoleController.php index de3dd5b..9028512 100644 --- a/app/Http/Controllers/Admin/RoleController.php +++ b/app/Http/Controllers/Admin/RoleController.php @@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin; use App\Http\Controllers\Controller; use Illuminate\Http\Request; use App\Http\Requests\RoleRequest; +use Spatie\Permission\Models\Permission; use Spatie\Permission\Models\Role; class RoleController extends Controller @@ -18,4 +19,84 @@ class RoleController extends Controller // Kembalikan data ke komponen Inertia 'Admin/Roles/Index' return inertia('Admin/Roles/Index', compact('roles')); } + public function create() +{ + // Ambil semua permission + $permissions = Permission::all(); + + // Kembalikan ke Inertia 'Admin/Roles/Create' dengan data permissions + return inertia('Admin/Roles/Create', [ + 'permissions' => $permissions, + ]); +} + + public function store(RoleRequest $request) + { + // Validasi data input melalui RoleRequest + $validatedData = $request->validated(); + + // Buat role baru dengan data yang telah divalidasi + $role = Role::create($validatedData); + + // Tetapkan permissions pada role yang baru dibuat + $role->syncPermissions($request->input('permissions')); + + // Jika berhasil simpan, arahkan dengan pesan sukses, jika gagal dengan pesan error + if ($role) { + return redirect()->route('admin.roles.index')->with(['success' => 'Data Berhasil Disimpan!']); + } else { + return redirect()->route('admin.roles.index')->with(['error' => 'Data Gagal Disimpan!']); + } + } + + public function edit(Role $role) + { + // Ambil semua permission, diurutkan dari yang terbaru + $permissions = Permission::latest()->get(); + + // Ambil ID dari permissions yang dimiliki oleh role saat ini + $rolePermissions = $role->permissions->pluck('id')->toArray(); + + // Map permissions agar hanya menyertakan 'id' dan 'name' + $permissions = $permissions->map(function ($permission) { + return [ + 'id' => $permission->id, + 'name' => $permission->name, + ]; + }); + + // Kembalikan data ke Inertia 'Admin/Roles/Edit' dengan data role, permissions, dan rolePermissions + return inertia('Admin/Roles/Edit', [ + 'permissions' => $permissions, + 'role' => $role, + 'rolePermissions' => $rolePermissions, + ]); + } + + public function update(RoleRequest $request, Role $role) + { + // Update data role dengan data yang telah divalidasi + $role->name = $request->name; + + // Sync permissions + $role->permissions()->sync($request->permissions); + + // Simpan perubahan + $role->save(); + + // Arahkan kembali ke daftar role + return redirect()->route('admin.roles.index')->with('success', 'Role updated successfully.'); + } + + public function destroy($id) + { + // Cari role berdasarkan ID, error jika tidak ditemukan + $role = Role::findOrFail($id); + + // Hapus role dari database + $role->delete(); + + // Arahkan kembali dengan pesan bahwa role berhasil dihapus + return redirect()->route('admin.roles.index')->with('message', 'Role deleted successfully.'); + } } diff --git a/app/Http/Controllers/HistoryKegiatanController.php b/app/Http/Controllers/HistoryKegiatanController.php new file mode 100644 index 0000000..31a4c77 --- /dev/null +++ b/app/Http/Controllers/HistoryKegiatanController.php @@ -0,0 +1,66 @@ +get(); + return Inertia::render('admin/kegiatan/index_history_kegiatan', ['historykegiatan' => $historykegiatan]); + } catch (\Exception $e) { + Log::error('Error fetching History Kegiatan: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } + + public function store(HistoryKegiatanRequest $request) + { + try { + $historykegiatan = HistoryKegiatan::withTrashed() + ->where('NamaHistoryKegiatan', $request->NamaHistoryKegiatan) + ->first(); + + if ($historykegiatan) { + $historykegiatan->restore(); + return redirect()->route('admin.historykegiatan.index')->with('success', 'History Kegiatan berhasil dikembalikan.'); + } + + HistoryKegiatan::create($request->validated()); + + return redirect()->route('admin.historykegiatan.index')->with('success', 'History Kegiatan berhasil dibuat.'); + } catch (\Exception $e) { + Log::error('Error creating History Kegiatan: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } + + public function update(HistoryKegiatanRequest $request, HistoryKegiatan $historykegiatan) + { + try { + $historykegiatan->update($request->validated()); + return redirect()->route('admin.history$historykegiatan.index')->with('success', 'History Kegiatan berhasil diperbarui.'); + } catch (\Exception $e) { + Log::error('Error updating History Kegiatan: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } + + public function destroy(HistoryKegiatan $historykegiatan) + { + try { + $historykegiatan->delete(); + return redirect()->route('admin.histo$historykegiatan.index')->with('success', 'History Kegiatan berhasil dihapus.'); + } catch (\Exception $e) { + Log::error('Error deleting History Kegiatan: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } +} diff --git a/app/Http/Controllers/HomeController.php b/app/Http/Controllers/HomeController.php new file mode 100644 index 0000000..76d16f7 --- /dev/null +++ b/app/Http/Controllers/HomeController.php @@ -0,0 +1,66 @@ +where('IsPublish', true) + ->orderBy('created_at', 'desc') + ->get(); + + // Fetch Pengumuman + $pengumuman = Post::with(['kategori', 'subkategori']) + ->whereHas('kategori', function($query) { + $query->where('NamaKategori', 'Pengumuman'); + }) + ->where('IsPublish', true) + ->orderBy('created_at', 'desc') + ->take(4) + ->get(); + + // Fetch Undangan + $undangan = Post::with(['kategori', 'subkategori']) + ->whereHas('kategori', function($query) { + $query->where('NamaKategori', 'Undangan'); + }) + ->where('IsPublish', true) + ->orderBy('created_at', 'desc') + ->take(4) + ->get(); + + // Fetch Peraturan + $peraturan = Post::with(['kategori', 'subkategori']) + ->whereHas('kategori', function($query) { + $query->where('NamaKategori', 'Peraturan'); + }) + ->where('IsPublish', true) + ->orderBy('created_at', 'desc') + ->take(4) + ->get(); + + // Fetch Sekilas Info + $sekilasInfo = Post::with(['kategori', 'subkategori']) + ->whereHas('kategori', function($query) { + $query->where('NamaKategori', 'Sekilas Info'); + }) + ->where('IsPublish', true) + ->orderBy('created_at', 'desc') + ->take(3) + ->get(); + + return Inertia::render('welcome', [ + 'posts' => $pengumuman, + 'undangan' => $undangan, + 'peraturan' => $peraturan, + 'sekilasInfo' => $sekilasInfo, + 'runPosts' => $runPosts, + ]); + } +} diff --git a/app/Http/Controllers/HukumController.php b/app/Http/Controllers/HukumController.php new file mode 100644 index 0000000..a2a200d --- /dev/null +++ b/app/Http/Controllers/HukumController.php @@ -0,0 +1,20 @@ +getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } +} diff --git a/app/Http/Controllers/JenisDokILController.php b/app/Http/Controllers/JenisDokILController.php new file mode 100644 index 0000000..97b3fb3 --- /dev/null +++ b/app/Http/Controllers/JenisDokILController.php @@ -0,0 +1,56 @@ +get(); + return Inertia::render('admin/jenisdokil/index_jenisdokil', ['jenisdokil' => $jenisdokil]); + } catch (\Exception $e) { + Log::error('Error fetching Jenis Dokumen Ilmiah: ' . $e->getMessage()); + return back()->with('error', 'Failed to fetch Jenis Dokumen Ilmiah.'); + } + } + + public function store(JenisDokILRequest $request) + { + try { + JenisDokIL::create($request->validated()); + return redirect()->route('admin.jenisdokil.index')->with('success', 'Jenis Dokumen Ilmiah created successfully.'); + } catch (\Exception $e) { + Log::error('Error creating Jenis Dokumen Ilmiah: ' . $e->getMessage()); + return back()->with('error', 'Failed to create Jenis Dokumen Ilmiah.'); + } + } + + public function update(JenisDokILRequest $request, JenisDokIL $jenisdokil) + { + try { + $jenisdokil->update($request->validated()); + return redirect()->route('admin.jenisdokil.index')->with('success', 'Jenis Dokumen Ilmiah updated successfully.'); + } catch (\Exception $e) { + Log::error('Error updating Jenis Dokumen Ilmiah: ' . $e->getMessage()); + return back()->with('error', 'Failed to update Jenis Dokumen Ilmiah.'); + } + } + + public function destroy(JenisDokIL $jenisdokil) + { + try { + $jenisdokil->delete(); + return redirect()->route('admin.jenisdokil.index')->with('success', 'Jenis Dokumen Ilmiah deleted successfully.'); + } catch (\Exception $e) { + Log::error('Error deleting Jenis Dokumen Ilmiah: ' . $e->getMessage()); + return back()->with('error', 'Failed to delete Jenis Dokumen Ilmiah.'); + } + } +} diff --git a/app/Http/Controllers/JenisKegiatanController.php b/app/Http/Controllers/JenisKegiatanController.php new file mode 100644 index 0000000..342acbe --- /dev/null +++ b/app/Http/Controllers/JenisKegiatanController.php @@ -0,0 +1,66 @@ +get(); + return Inertia::render('admin/kegiatan/index_jenis_kegiatan', ['jeniskegiatan' => $jeniskegiatan]); + } catch (\Exception $e) { + Log::error('Error fetching Jenis Kegiatan: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } + + public function store(JenisKegiatanRequest $request) + { + try { + $jeniskegiatan = JenisKegiatan::withTrashed() + ->where('NamaJenisKegiatan', $request->NamaJenisKegiatan) + ->first(); + + if ($jeniskegiatan) { + $jeniskegiatan->restore(); + return redirect()->route('admin.jeniskegiatan.index')->with('success', 'Jenis Kegiatan berhasil dikembalikan.'); + } + + JenisKegiatan::create($request->validated()); + + return redirect()->route('admin.jeniskegiatan.index')->with('success', 'Jenis Kegiatan berhasil dibuat.'); + } catch (\Exception $e) { + Log::error('Error creating Jenis Kegiatan: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } + + public function update(JenisKegiatanRequest $request, JenisKegiatan $jeniskegiatan) + { + try { + $jeniskegiatan->update($request->validated()); + return redirect()->route('admin.jeniskegiatan.index')->with('success', 'Jenis Kegiatan berhasil diperbarui.'); + } catch (\Exception $e) { + Log::error('Error updating Jenis Kegiatan: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } + + public function destroy(JenisKegiatan $jeniskegiatan) + { + try { + $jeniskegiatan->delete(); + return redirect()->route('admin.jeniskegiatan.index')->with('success', 'Jenis Kegiatan berhasil dihapus.'); + } catch (\Exception $e) { + Log::error('Error deleting Jenis Kegiatan: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } +} diff --git a/app/Http/Controllers/JenisSanksiController.php b/app/Http/Controllers/JenisSanksiController.php new file mode 100644 index 0000000..d14057f --- /dev/null +++ b/app/Http/Controllers/JenisSanksiController.php @@ -0,0 +1,55 @@ +get(); + return Inertia::render('admin/jenissanksi/index_jenis_sanksi', ['jenissanksi' => $jenissanksi]); + } catch (\Exception $e) { + Log::error('Error fetching Jenis Sanksi: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } + + public function store(Request $request) + { + try { + JenisSanksi::create($request->all()); + return redirect()->route('admin.jenissanksi.index')->with('success', 'Jenis Sanksi berhasil dibuat.'); + } catch (\Exception $e) { + Log::error('Error creating Jenis Sanksi: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } + + public function update(Request $request, JenisSanksi $jenissanksi) + { + try { + $jenissanksi->update($request->all()); + return redirect()->route('admin.jenissanksi.index')->with('success', 'Jenis Sanksi berhasil diperbarui.'); + } catch (\Exception $e) { + Log::error('Error updating Jenis Sanksi: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } + + public function destroy(JenisSanksi $jenissanksi) + { + try { + $jenissanksi->delete(); + return redirect()->route('admin.jenissanksi.index')->with('success', 'Jenis Sanksi berhasil dihapus.'); + } catch (\Exception $e) { + Log::error('Error deleting Jenis Sanksi: ' . $e->getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } +} diff --git a/app/Http/Controllers/PelaporanController.php b/app/Http/Controllers/PelaporanController.php new file mode 100644 index 0000000..08ca839 --- /dev/null +++ b/app/Http/Controllers/PelaporanController.php @@ -0,0 +1,20 @@ +getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } +} diff --git a/app/Http/Controllers/PengumumanController.php b/app/Http/Controllers/PengumumanController.php new file mode 100644 index 0000000..b97d2d1 --- /dev/null +++ b/app/Http/Controllers/PengumumanController.php @@ -0,0 +1,48 @@ +whereHas('kategori', function($query) { + $query->where('NamaKategori', 'Pengumuman'); + }) + ->where('IsPublish', true) + ->orderBy('created_at', 'desc') + ->get(); + + Log::info('Posts data:', ['posts' => $posts->toArray()]); // Debug log + + return Inertia::render('Pengumuman', [ + 'posts' => $posts + ]); + } + + public function show($slug) + { + $article = Post::with(['kategori', 'subkategori']) + ->where('SlugPost', $slug) + ->firstOrFail(); + + $relatedArticles = Post::with(['kategori', 'subkategori']) + ->where('KategoriId', $article->KategoriId) + ->where('PostId', '!=', $article->PostId) + ->where('IsPublish', true) + ->latest() + ->take(5) + ->get(); + + return Inertia::render('detail/PegumumanDetail', [ + 'article' => $article, + 'relatedArticles' => $relatedArticles + ]); + } +} diff --git a/app/Http/Controllers/PeraturanController.php b/app/Http/Controllers/PeraturanController.php new file mode 100644 index 0000000..4ff037d --- /dev/null +++ b/app/Http/Controllers/PeraturanController.php @@ -0,0 +1,45 @@ +whereHas('kategori', function($query) { + $query->where('NamaKategori', 'Peraturan'); + }) + ->where('IsPublish', true) + ->orderBy('created_at', 'desc') + ->get(); + + return Inertia::render('Peraturan', [ + 'posts' => $posts + ]); + } + + public function show($slug) + { + $article = Post::with(['kategori', 'subkategori']) + ->where('SlugPost', $slug) + ->firstOrFail(); + + $relatedArticles = Post::with(['kategori', 'subkategori']) + ->where('KategoriId', $article->KategoriId) + ->where('PostId', '!=', $article->PostId) + ->where('IsPublish', true) + ->latest() + ->take(5) + ->get(); + + return Inertia::render('detail/PeraturanDetail', [ + 'article' => $article, + 'relatedArticles' => $relatedArticles + ]); + } +} diff --git a/app/Http/Controllers/PostController.php b/app/Http/Controllers/PostController.php index ef511e9..12836cb 100644 --- a/app/Http/Controllers/PostController.php +++ b/app/Http/Controllers/PostController.php @@ -10,6 +10,7 @@ use Illuminate\Http\Request; use Illuminate\Support\Facades\Log; use Illuminate\Support\Facades\Storage; use Inertia\Inertia; +use Illuminate\Support\Facades\DB; class PostController extends Controller { @@ -32,34 +33,68 @@ class PostController extends Controller $subkategori = SubKategori::all(); return Inertia::render('admin/post/add_post', [ - 'kategori' => $kategori, - 'subkategori' => $subkategori + 'kategori' => Kategori::all(), + 'subkategori' => SubKategori::all(), + 'existingPosts' => Post::select('JudulPost', 'KategoriId')->get() ]); } public function store(PostRequest $request) { - try { $data = $request->validated(); - $data['IsPublish'] = $request->has('IsPublish'); - if ($request->hasFile('ImagePost')) { - $data['ImagePost'] = $request->file('ImagePost')->store('images/posts'); + DB::beginTransaction(); + + try { + $data['IsPublish'] = $request->boolean('IsPublish'); + + if ($request->hasFile('ImagePost')) { + $file = $request->file('ImagePost'); + + if (!$file->isValid()) { + throw new \Exception('Invalid image file'); + } + + $data['ImagePost'] = $file->store('images/posts', 'public'); + + if ($data['ImagePost'] === false) { + throw new \Exception('Failed to store image'); + } + } + + $post = Post::create($data); + DB::commit(); + + return redirect() + ->route('admin.post.index') + ->with('success', 'Post berhasil ditambahkan'); + + } catch (\Exception $e) { + DB::rollBack(); + + if (isset($data['ImagePost']) && Storage::disk('public')->exists($data['ImagePost'])) { + Storage::disk('public')->delete($data['ImagePost']); + } + + Log::error('Error creating post: ' . $e->getMessage()); + + return redirect() + ->route('admin.post.index') + ->with('error', 'Post gagal ditambahkan: ' . $e->getMessage()); } - Post::create($data); - - return redirect()->route('admin.post.index')->with('success', 'Post berhasil ditambahkan'); } catch (\Exception $e) { - return redirect()->route('admin.post.index')->with('error', 'Post gagal ditambahkan'); - } + Log::error('Validation error: ' . $e->getMessage()); + return redirect() + ->route('admin.post.index') + ->with('error', 'Validasi gagal: ' . $e->getMessage()); + } } public function edit(Post $post) { - return Inertia::render('admin/post/edit_post', [ 'post' => $post, 'kategori' => Kategori::all(), @@ -70,19 +105,30 @@ class PostController extends Controller public function update(PostRequest $request, Post $post) { try { - $data = $request->validated(); + $data = array_filter($request->validated(), function($value) { + return $value !== null; + }); if ($request->hasFile('ImagePost')) { - Storage::disk('public')->delete($post->ImagePost); + if ($post->ImagePost && Storage::disk('public')->exists($post->ImagePost)) { + Storage::disk('public')->delete($post->ImagePost); + } $data['ImagePost'] = $request->file('ImagePost')->store('images/posts', 'public'); } + if (isset($data['IsPublish'])) { + $data['IsPublish'] = (bool) $data['IsPublish']; + } + $post->update($data); - 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) { Log::error('Error updating Post: ' . $e->getMessage()); - return back()->with('error', 'Something went wrong.'); + return back()->with('error', 'Terjadi kesalahan saat memperbarui post.'); } } @@ -98,6 +144,4 @@ class PostController extends Controller return back()->with('error', 'Something went wrong.'); } } - - } diff --git a/app/Http/Controllers/SearchController.php b/app/Http/Controllers/SearchController.php new file mode 100644 index 0000000..3f05d6f --- /dev/null +++ b/app/Http/Controllers/SearchController.php @@ -0,0 +1,29 @@ +input('q'); + + $searchResults = Post::with(['kategori', 'subkategori']) + ->where('IsPublish', true) + ->where(function($q) use ($query) { + $q->where('JudulPost', 'ILIKE', "%{$query}%") + ->orWhere('DescPost', 'ILIKE', "%{$query}%"); + }) + ->latest() + ->get(); + + return Inertia::render('Search', [ + 'searchResults' => $searchResults, + 'searchQuery' => $query + ]); + } +} diff --git a/app/Http/Controllers/SekilasInfoController.php b/app/Http/Controllers/SekilasInfoController.php new file mode 100644 index 0000000..3da878b --- /dev/null +++ b/app/Http/Controllers/SekilasInfoController.php @@ -0,0 +1,30 @@ +where('SlugPost', $slug) + ->firstOrFail(); + + $relatedArticles = Post::with(['kategori', 'subkategori']) + ->where('KategoriId', $article->KategoriId) + ->where('PostId', '!=', $article->PostId) + ->where('IsPublish', true) + ->latest() + ->take(5) + ->get(); + + return Inertia::render('detail/SekilasInfoDetail', [ + 'article' => $article, + 'relatedArticles' => $relatedArticles + ]); + } +} diff --git a/app/Http/Controllers/TentangController.php b/app/Http/Controllers/TentangController.php new file mode 100644 index 0000000..295d21e --- /dev/null +++ b/app/Http/Controllers/TentangController.php @@ -0,0 +1,20 @@ +getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } +} diff --git a/app/Http/Controllers/UndanganController.php b/app/Http/Controllers/UndanganController.php new file mode 100644 index 0000000..048663c --- /dev/null +++ b/app/Http/Controllers/UndanganController.php @@ -0,0 +1,45 @@ +whereHas('kategori', function($query) { + $query->where('NamaKategori', 'Undangan'); + }) + ->where('IsPublish', true) + ->orderBy('created_at', 'desc') + ->get(); + + return Inertia::render('Undangan', [ + 'posts' => $posts + ]); + } + + public function show($slug) + { + $article = Post::with(['kategori', 'subkategori']) + ->where('SlugPost', $slug) + ->firstOrFail(); + + $relatedArticles = Post::with(['kategori', 'subkategori']) + ->where('KategoriId', $article->KategoriId) + ->where('PostId', '!=', $article->PostId) + ->where('IsPublish', true) + ->latest() + ->take(5) + ->get(); + + return Inertia::render('detail/UndanganDetail', [ + 'article' => $article, + 'relatedArticles' => $relatedArticles + ]); + } +} diff --git a/app/Http/Controllers/VerifPelaporanController.php b/app/Http/Controllers/VerifPelaporanController.php new file mode 100644 index 0000000..f0b5c80 --- /dev/null +++ b/app/Http/Controllers/VerifPelaporanController.php @@ -0,0 +1,20 @@ +getMessage()); + return back()->with('error', 'Something went wrong.'); + } + } +} diff --git a/app/Http/Controllers/VerifikatorController.php b/app/Http/Controllers/VerifikatorController.php new file mode 100644 index 0000000..b062264 --- /dev/null +++ b/app/Http/Controllers/VerifikatorController.php @@ -0,0 +1,56 @@ +get(); + return Inertia::render('admin/verifikator/index_verifikator', ['verifikator' => $verifikator]); + } catch (\Exception $e) { + Log::error('Error fetching Verifikator: ' . $e->getMessage()); + return back()->with('error', 'Failed to fetch verifikator.'); + } + } + + public function store(VerifikatorRequest $request) + { + try { + Verifikator::create($request->validated()); + return redirect()->route('verifikator.index')->with('success', 'Verifikator created successfully.'); + } catch (\Exception $e) { + Log::error('Error creating verifikator: ' . $e->getMessage()); + return back()->with('error', 'Failed to create verifikator.'); + } + } + + public function update(VerifikatorRequest $request, Verifikator $verifikator) + { + try { + $verifikator->update($request->validated()); + return redirect()->route('verifikator.index')->with('success', 'Verifikator updated successfully.'); + } catch (\Exception $e) { + Log::error('Error updating verifikator: ' . $e->getMessage()); + return back()->with('error', 'Failed to update verifikator.'); + } + } + + public function destroy(Verifikator $verifikator) + { + try { + $verifikator->delete(); + return redirect()->route('verifikator.index')->with('success', 'Verifikator deleted successfully.'); + } catch (\Exception $e) { + Log::error('Error deleting verifikator: ' . $e->getMessage()); + return back()->with('error', 'Failed to delete verifikator.'); + } + } +} diff --git a/app/Http/Requests/HistoryKegiatanRequest.php b/app/Http/Requests/HistoryKegiatanRequest.php new file mode 100644 index 0000000..11d72db --- /dev/null +++ b/app/Http/Requests/HistoryKegiatanRequest.php @@ -0,0 +1,35 @@ +|string> + */ + public function rules(): array + { + return [ + 'NamaHistoryKegiatan' => [ + 'required', + 'string', + 'max:255', + Rule::unique('HistoryKegiatan', 'NamaHistoryKegiatan')->whereNull('deleted_at') // Abaikan data yang dihapus (soft delete) + ], + 'IsPublish' => 'boolean', + ]; + } +} diff --git a/app/Http/Requests/HukumRequest.php b/app/Http/Requests/HukumRequest.php new file mode 100644 index 0000000..4f351d2 --- /dev/null +++ b/app/Http/Requests/HukumRequest.php @@ -0,0 +1,58 @@ +|string> + */ + public function rules(): array + { + return [ + 'PerusahaanId' => 'nullable|integer|exists:RefPerusahaan,id', + 'JenisSanksiId' => 'nullable|integer|exists:RefJenisSanksi,id', + 'SanksiNumber' => 'nullable|string|max:100', + 'SanksiDate' => 'nullable|date', + 'SanksiFile' => 'nullable|file|mimes:pdf|max:2048', + 'StatusPenaatan' => 'required|integer|in:1,2,3', + 'PenaatanNumber' => 'nullable|string|max:100', + 'PenaatanDate' => 'nullable|date', + 'PenaatanFile' => 'nullable|file|mimes:pdf,jpg,png|max:2048', + 'IsDeleted' => 'nullable|boolean', + ]; + } + + public function messages(): array + { + return [ + 'PerusahaanId.integer' => 'Perusahaan harus berupa angka.', + 'PerusahaanId.exists' => 'Perusahaan tidak ditemukan.', + 'JenisSanksiId.integer' => 'Jenis Sanksi harus berupa angka.', + 'JenisSanksiId.exists' => 'Jenis Sanksi tidak ditemukan.', + 'SanksiNumber.max' => 'Nomor SK Sanksi maksimal 100 karakter.', + 'SanksiDate.date' => 'Tanggal SK Sanksi harus berupa format tanggal.', + 'SanksiFile.mimes' => 'File Sanksi harus berupa PDF', + 'SanksiFile.max' => 'Ukuran file maksimal 2MB.', + 'StatusPenaatan.required'=> 'Status Penaatan wajib diisi.', + 'StatusPenaatan.in' => 'Status Penaatan hanya boleh 1, 2, atau 3.', + 'PenaatanNumber.max' => 'Nomor SK Penaatan maksimal 100 karakter.', + 'PenaatanDate.date' => 'Tanggal SK Penaatan harus berupa format tanggal.', + 'PenaatanFile.mimes' => 'File Penaatan harus berupa PDF', + 'PenaatanFile.max' => 'Ukuran file maksimal 2MB.', + 'IsDeleted.boolean' => 'IsDeleted hanya boleh bernilai 0 atau 1.', + ]; + } +} diff --git a/app/Http/Requests/JenisDokILRequest.php b/app/Http/Requests/JenisDokILRequest.php new file mode 100644 index 0000000..5579d95 --- /dev/null +++ b/app/Http/Requests/JenisDokILRequest.php @@ -0,0 +1,29 @@ +|string> + */ + public function rules(): array + { + return [ + 'NamaJenisDokIL' => 'required|string|max:255', + 'KodeJenisDokIL' => 'required|string|max:20', + ]; + } +} diff --git a/app/Http/Requests/JenisKegiatanRequest.php b/app/Http/Requests/JenisKegiatanRequest.php new file mode 100644 index 0000000..c27ad5c --- /dev/null +++ b/app/Http/Requests/JenisKegiatanRequest.php @@ -0,0 +1,35 @@ +|string> + */ + public function rules(): array + { + return [ + 'NamaJenisKegiatan' => [ + 'required', + 'string', + 'max:255', + Rule::unique('JenisKegiatan', 'NamaJenisKegiatan')->whereNull('deleted_at') // Abaikan data yang dihapus (soft delete) + ], + 'IsPublish' => 'boolean', + ]; + } +} diff --git a/app/Http/Requests/JenisSanksiRequest.php b/app/Http/Requests/JenisSanksiRequest.php new file mode 100644 index 0000000..72d3267 --- /dev/null +++ b/app/Http/Requests/JenisSanksiRequest.php @@ -0,0 +1,34 @@ +|string> + */ + public function rules(): array + { + return [ + 'NamaJenisSanksi' => [ + 'required', + 'string', + 'max:255', + Rule::unique('JenisSanksi', 'NamaJenisSanksi')->whereNull('deleted_at') // Abaikan data yang dihapus (soft delete) + ], + ]; + } +} diff --git a/app/Http/Requests/PostRequest.php b/app/Http/Requests/PostRequest.php index ef6a44e..142da57 100644 --- a/app/Http/Requests/PostRequest.php +++ b/app/Http/Requests/PostRequest.php @@ -3,7 +3,7 @@ namespace App\Http\Requests; use Illuminate\Foundation\Http\FormRequest; -use Illuminate\Validation\Rule; +use Illuminate\Support\Facades\Log; class PostRequest extends FormRequest { @@ -22,15 +22,53 @@ class PostRequest extends FormRequest */ public function rules(): array { - return [ - 'KategoriId' => 'required|exists:Kategori,KategoriId', - 'SubKategoriId' => [ - 'required', - Rule::exists('SubKategori', 'SubKategoriId')->where('KategoriId', $this->KategoriId), - ], - 'JudulPost' => ['required', 'string', 'max:255'], - 'DescPost' => ['required', 'string'], - 'ImagePost' => ['nullable', 'image', 'mimes:jpg,png,webp', 'max:2048'], - ]; + 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]); + } + } } } diff --git a/app/Http/Requests/VerifikatorRequest.php b/app/Http/Requests/VerifikatorRequest.php new file mode 100644 index 0000000..003a6c0 --- /dev/null +++ b/app/Http/Requests/VerifikatorRequest.php @@ -0,0 +1,30 @@ +|string> + */ + public function rules(): array + { + return [ + 'NamaUnitKerja' => 'required|string|max:255', + 'NamaKepala' => 'required|string|max:255', + 'NIP' => 'required|string|max:255', + ]; + } +} diff --git a/app/Models/HistoryKegiatan.php b/app/Models/HistoryKegiatan.php new file mode 100644 index 0000000..4df4d34 --- /dev/null +++ b/app/Models/HistoryKegiatan.php @@ -0,0 +1,24 @@ +belongsTo(Perusahaan::class, 'PerusahaanId'); + } + + /** + * Relasi ke tabel jenis sanksi. + */ + public function jenisSanksi() + { + return $this->belongsTo(JenisSanksi::class, 'JenisSanksiId'); + } +} diff --git a/app/Models/JenisDokIL.php b/app/Models/JenisDokIL.php new file mode 100644 index 0000000..da7bebd --- /dev/null +++ b/app/Models/JenisDokIL.php @@ -0,0 +1,15 @@ +belongsTo(Kabupaten::class, 'KabupatenId', 'KabupatenId'); + } +} diff --git a/app/Models/Kelurahan.php b/app/Models/Kelurahan.php new file mode 100644 index 0000000..500a2ff --- /dev/null +++ b/app/Models/Kelurahan.php @@ -0,0 +1,19 @@ +belongsTo(Kecamatan::class, 'KecamatanId', 'KecamatanId'); + } +} diff --git a/app/Models/Perusahaan.php b/app/Models/Perusahaan.php new file mode 100644 index 0000000..0fbcc96 --- /dev/null +++ b/app/Models/Perusahaan.php @@ -0,0 +1,84 @@ + 'boolean', + 'IsPublish' => 'boolean', + 'ILPdlTanggal' => 'date', + ]; + + // Relationship with JenisKegiatan + public function jenisKegiatan(): BelongsTo + { + return $this->belongsTo(JenisKegiatan::class, 'JenisKegiatanId', 'JenisKegiatanId'); + } + + // Relationship with Kelurahan + public function kelurahan(): BelongsTo + { + return $this->belongsTo(Kelurahan::class, 'KelurahanId', 'KelurahanId'); + } + + // Relationship with JenisDokIL + public function jenisDokIL(): BelongsTo + { + return $this->belongsTo(JenisDokIL::class, 'JenisDokILId', 'JenisDokILId'); + } + + // Relationship with JenisDokIL for PDL + public function jenisDokILPdl(): BelongsTo + { + return $this->belongsTo(JenisDokIL::class, 'ILPdlJenis', 'JenisDokILId'); + } + + // Relationship with Verifikator + public function verifikator(): BelongsTo + { + return $this->belongsTo(Verifikator::class, 'VerifikatorId', 'VerifikatorId'); + } + + // Self-referential relationship for Kawasan + public function kawasan(): BelongsTo + { + return $this->belongsTo(Perusahaan::class, 'Kawasan', 'PerusahaanId'); + } +} diff --git a/app/Models/Post.php b/app/Models/Post.php index 0451a69..419d287 100644 --- a/app/Models/Post.php +++ b/app/Models/Post.php @@ -37,12 +37,12 @@ class Post extends Model return \Carbon\Carbon::parse($this->created_at)->translatedFormat('d F Y'); } - public static function boot() - { - parent::boot(); + // public static function boot() + // { + // parent::boot(); - static::creating(function ($post) { - $post->Slug = Str::slug($post->JudulPost); - }); - } + // static::creating(function ($post) { + // $post->SlugPost = Str::slug($post->JudulPost); + // }); + // } } diff --git a/app/Models/Verifikator.php b/app/Models/Verifikator.php new file mode 100644 index 0000000..1b6dcfc --- /dev/null +++ b/app/Models/Verifikator.php @@ -0,0 +1,17 @@ + fn () => Post::with(['kategori', 'subkategori']) + ->where('IsPublish', true) + ->orderBy('created_at', 'desc') + ->get(), + ]); } } diff --git a/config/filesystems.php b/config/filesystems.php index 44fe9c8..32114d8 100644 --- a/config/filesystems.php +++ b/config/filesystems.php @@ -13,7 +13,7 @@ return [ | */ - 'default' => env('FILESYSTEM_DISK', 'local'), + 'default' => env('FILESYSTEM_DISK', 'public'), /* |-------------------------------------------------------------------------- diff --git a/database/migrations/2024_01_01_create_kelurahans_table.php b/database/migrations/2024_01_01_create_kelurahans_table.php new file mode 100644 index 0000000..e69de29 diff --git a/database/migrations/2025_02_14_142819_create_historykegiatan_table.php b/database/migrations/2025_02_14_142819_create_historykegiatan_table.php new file mode 100644 index 0000000..ac485b3 --- /dev/null +++ b/database/migrations/2025_02_14_142819_create_historykegiatan_table.php @@ -0,0 +1,29 @@ +id('HistoryKegiatanId'); + $table->string('NamaHistoryKegiatan'); + $table->boolean('IsPublish')->default(0); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('historykegiatan'); + } +}; diff --git a/database/migrations/2025_02_14_153943_create_jeniskegiatan_table.php b/database/migrations/2025_02_14_153943_create_jeniskegiatan_table.php new file mode 100644 index 0000000..0f536d4 --- /dev/null +++ b/database/migrations/2025_02_14_153943_create_jeniskegiatan_table.php @@ -0,0 +1,29 @@ +id('JenisKegiatanId'); + $table->string('NamaJenisKegiatan'); + $table->boolean('IsPublish')->default(0); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('jeniskegiatan'); + } +}; diff --git a/database/migrations/2025_02_17_083731_add_softdeletes_to_jeniskegiatan_table.php b/database/migrations/2025_02_17_083731_add_softdeletes_to_jeniskegiatan_table.php new file mode 100644 index 0000000..2fced1d --- /dev/null +++ b/database/migrations/2025_02_17_083731_add_softdeletes_to_jeniskegiatan_table.php @@ -0,0 +1,28 @@ +softDeletes()->after('IsPublish'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('JenisKegiatan', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + } +}; diff --git a/database/migrations/2025_02_17_084617_add_softdeletes_to_historykegiatan_table.php b/database/migrations/2025_02_17_084617_add_softdeletes_to_historykegiatan_table.php new file mode 100644 index 0000000..03bb9b1 --- /dev/null +++ b/database/migrations/2025_02_17_084617_add_softdeletes_to_historykegiatan_table.php @@ -0,0 +1,28 @@ +softDeletes()->after('IsPublish'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('HistoryKegiatan', function (Blueprint $table) { + $table->dropSoftDeletes(); + }); + } +}; diff --git a/database/migrations/2025_02_17_100043_modify_nama_kategori_to_non_unique.php b/database/migrations/2025_02_17_100043_modify_nama_kategori_to_non_unique.php new file mode 100644 index 0000000..f8d0695 --- /dev/null +++ b/database/migrations/2025_02_17_100043_modify_nama_kategori_to_non_unique.php @@ -0,0 +1,28 @@ +dropUnique(['NamaKategori']); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('Kategori', function (Blueprint $table) { + $table->unique('NamaKategori'); + }); + } +}; diff --git a/database/migrations/2025_02_17_111029_create_periodepelaporan_table.php b/database/migrations/2025_02_17_111029_create_periodepelaporan_table.php new file mode 100644 index 0000000..a685da4 --- /dev/null +++ b/database/migrations/2025_02_17_111029_create_periodepelaporan_table.php @@ -0,0 +1,32 @@ +id('PeriodePelaporanId'); + $table->string('NamaPeriodePelaporan'); + $table->date('BulanAwal'); + $table->date('BulanSelesai'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('PeriodePelaporan', function (Blueprint $table) { + // + }); + } +}; diff --git a/database/migrations/2025_02_17_112004_create_perusahaan_table.php b/database/migrations/2025_02_17_112004_create_perusahaan_table.php new file mode 100644 index 0000000..830049b --- /dev/null +++ b/database/migrations/2025_02_17_112004_create_perusahaan_table.php @@ -0,0 +1,64 @@ +id('PerusahaanId'); + $table->string('NomorInduk')->nullable(); + $table->unsignedBigInteger('JenisKegiatanId'); + $table->foreign('JenisKegiatanId')->references('JenisKegiatanId')->on('JenisKegiatan')->onDelete('cascade'); + $table->string('NamaPerusahaan'); + $table->string('Alamat')->nullable(); + $table->unsignedBigInteger('KelurahanId'); + $table->foreign('KelurahanId')->references('KelurahanId')->on('Kelurahan')->onDelete('cascade'); + $table->string('KodePos')->nullable(); + $table->string('Telepon')->nullable(); + $table->string('Fax')->nullable(); + $table->string('Email')->nullable(); + $table->string('Lintang')->nullable(); + $table->string('Bujur')->nullable(); + $table->string('CPNama')->nullable(); + $table->string('CPTelepon')->nullable(); + $table->string('ILNomor')->nullable(); + $table->string('ILTanggal')->nullable(); + $table->unsignedBigInteger('JenisDokILId'); + $table->foreign('JenisDokILId')->references('JenisDokILId')->on('JenisDokIL')->onDelete('cascade'); + $table->unsignedBigInteger('VerifikatorId'); + $table->foreign('VerifikatorId')->references('VerifikatorId')->on('Verifikator')->onDelete('cascade'); + $table->boolean('ReportLocked')->default(1); + $table->boolean('IsPublish')->default(0); + $table->string('ILDokumen')->nullable(); + $table->string('ILPdlNomor')->default('n')->comment('Dok IL versi PDL'); + $table->unsignedInteger('ILPdlJenis')->nullable()->comment('id dari refjenisdokil'); + $table->foreign('ILPdlJenis')->references('JenisDokILId')->on('JenisDokIL')->onDelete('set null'); + $table->string('DocPdlOrig')->nullable()->comment('nama file original'); + $table->string('DocPdlHash')->nullable()->comment('nama file hash'); + $table->string('DocPdlPath')->nullable()->comment('upload path'); + $table->date('ILPdlTanggal')->nullable(); + $table->unsignedInteger('Kawasan')->nullable()->comment('id dari perusahaan, digunakan utk pengelola kawasan'); + $table->foreign('Kawasan')->references('PerusahaanId')->on('Perusahaan')->onDelete('set null'); + + + $table->timestamps(); + $table->index('NomorInduk', 'IndexNomorInduk'); + $table->index('KelurahanId', 'IndexKelurahanId'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Perusahaan'); + } +}; diff --git a/database/migrations/2025_02_17_112206_create_kelurahan_table.php b/database/migrations/2025_02_17_112206_create_kelurahan_table.php new file mode 100644 index 0000000..80fa63a --- /dev/null +++ b/database/migrations/2025_02_17_112206_create_kelurahan_table.php @@ -0,0 +1,30 @@ +id('KelurahanId'); + $table->unsignedBigInteger('KecamatanId'); + $table->foreign('KecamatanId')->references('KecamatanId')->on('Kecamatan')->onDelete('cascade'); + $table->string('NamaKelurahan'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Kelurahan'); + } +}; diff --git a/database/migrations/2025_02_17_112217_create_kecamatan_table.php b/database/migrations/2025_02_17_112217_create_kecamatan_table.php new file mode 100644 index 0000000..3ef994f --- /dev/null +++ b/database/migrations/2025_02_17_112217_create_kecamatan_table.php @@ -0,0 +1,30 @@ +id('KecamatanId'); + $table->unsignedBigInteger('KabupatenId'); + $table->foreign('KabupatenId')->references('KabupatenId')->on('Kabupaten')->onDelete('cascade'); + $table->string('NamaKecamatan'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Kecamatan'); + } +}; diff --git a/database/migrations/2025_02_17_112238_create_kabupaten_table.php b/database/migrations/2025_02_17_112238_create_kabupaten_table.php new file mode 100644 index 0000000..48f5afd --- /dev/null +++ b/database/migrations/2025_02_17_112238_create_kabupaten_table.php @@ -0,0 +1,28 @@ +id('KabupatenId'); + $table->string('NamaKabupaten'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Kabupaten'); + } +}; diff --git a/database/migrations/2025_02_17_124317_create_jenisdokumen__i_l_table.php b/database/migrations/2025_02_17_124317_create_jenisdokumen__i_l_table.php new file mode 100644 index 0000000..f046e02 --- /dev/null +++ b/database/migrations/2025_02_17_124317_create_jenisdokumen__i_l_table.php @@ -0,0 +1,29 @@ +id('JenisDokILId'); + $table->string('KodeJenisDokIL')->unique(); + $table->string('NamaJenisDokIL'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('JenisDokIL'); + } +}; diff --git a/database/migrations/2025_02_17_124903_create_verifikator_table.php b/database/migrations/2025_02_17_124903_create_verifikator_table.php new file mode 100644 index 0000000..3a9016b --- /dev/null +++ b/database/migrations/2025_02_17_124903_create_verifikator_table.php @@ -0,0 +1,30 @@ +id('VerifikatorId'); + $table->string('NamaUnitKerja'); + $table->string('NamaKepala'); + $table->string('NIP'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Verifikator'); + } +}; diff --git a/database/migrations/2025_02_17_150218_create_pelaporan_table.php b/database/migrations/2025_02_17_150218_create_pelaporan_table.php new file mode 100644 index 0000000..e302c3a --- /dev/null +++ b/database/migrations/2025_02_17_150218_create_pelaporan_table.php @@ -0,0 +1,55 @@ +id('PelaporanId'); + $table->unsignedInteger('PeriodePelaporanId'); + $table->foreign('PeriodePelaporanId')->references('PeriodePelaporanId')->on('PeriodePelaporan')->onDelete('cascade'); + $table->year('Tahun'); + $table->unsignedInteger('PerusahaanId'); + $table->foreign('PerusahaanId')->references('PerusahaanId')->on('Perusahaan')->onDelete('cascade'); + $table->float('SKL'); + $table->float('SPL'); + $table->float('SKL_IL'); + $table->float('SKL_AL'); + $table->float('SKL_LB3'); + $table->float('SKL_SB'); + $table->float('SKL_BS'); + $table->float('SKL_STB'); + $table->float('SKL_LP'); + $table->float('SKL_KDM'); + $table->float('SPL_AL'); + $table->float('SPL_LB3'); + $table->float('SPL_SB'); + $table->float('SPL_BS'); + $table->float('SPL_STB'); + $table->float('SPL_LP'); + $table->float('SPL_KDM'); + $table->string('IsChecked', 100)->nullable(); + + $table->timestamps(); + + $table->index('PeriodePelaporanId', 'IndexPeriodePelaporanId'); + $table->index('Tahun', 'IndexTahun'); + $table->index('PerusahaanId', 'IndexPerusahaanId'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Pelaporan'); + } +}; diff --git a/database/migrations/2025_02_18_091634_create_jenis_sanksi_table.php b/database/migrations/2025_02_18_091634_create_jenis_sanksi_table.php new file mode 100644 index 0000000..370d3ec --- /dev/null +++ b/database/migrations/2025_02_18_091634_create_jenis_sanksi_table.php @@ -0,0 +1,29 @@ +id('JenisSanksiId'); + $table->string('NamaJenisSanksi'); + $table->softDeletes(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('JenisSanksi'); + } +}; diff --git a/database/migrations/2025_02_18_135753_create__hukum_table.php b/database/migrations/2025_02_18_135753_create__hukum_table.php new file mode 100644 index 0000000..5b4e4fc --- /dev/null +++ b/database/migrations/2025_02_18_135753_create__hukum_table.php @@ -0,0 +1,42 @@ +id('HukumId'); + $table->unsignedInteger('PerusahaanId')->nullable()->comment('id dari Perusahaan'); + $table->foreign('PerusahaanId')->references('PerusahaanId')->on('Perusahaan')->onDelete('cascade'); + $table->unsignedInteger('JenisSanksiId')->nullable()->comment('id dari Jenis Sanksi'); + $table->foreign('JenisSanksiId')->references('JenisSanksiId')->on('JenisSanksi')->onDelete('cascade'); + $table->string('SanksiNumber', 100)->nullable()->comment('nomor SK Sanksi'); + $table->date('SanksiDate')->nullable()->comment('tanggal SK Sanksi'); + $table->string('SanksiFile', 255)->nullable()->comment('path + orig filename + id perusahaan + epoch time'); + $table->unsignedTinyInteger('StatusPenaatan')->default(1)->comment('1=pengawasan, 2=peningkatan sanksi, 3=taat'); + $table->string('PenaatanNumber', 100)->nullable()->comment('nomor SK penaatan'); + $table->date('PenaatanDate')->nullable()->comment('tanggal SK penaatan'); + $table->string('PenaatanFile', 255)->nullable()->comment('path + orig filename + id perusahaan + epoch time'); + $table->unsignedTinyInteger('IsDeleted')->default(0)->comment('0=false, 1=true'); + + $table->index('PerusahaanId'); + $table->index('JenisSanksiId'); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('Hukum'); + } +}; diff --git a/database/seeders/KabupatenTableSeeder.php b/database/seeders/KabupatenTableSeeder.php new file mode 100644 index 0000000..397e4f1 --- /dev/null +++ b/database/seeders/KabupatenTableSeeder.php @@ -0,0 +1,38 @@ +json() as $kabupaten) { + // Convert the id format and map the data + $cleanId = str_replace('.', '', $kabupaten['id']); + + Kabupaten::create([ + 'KabupatenId' => $cleanId, + 'NamaKabupaten' => $kabupaten['data'] + ]); + + // Log data kabupaten yang sedang di-seed + Log::info('Seeding kabupaten: ', [ + 'KabupatenId' => $cleanId, + 'NamaKabupaten' => $kabupaten['data'] + ]); + } + } +} diff --git a/database/seeders/KecamatanTableSeeder.php b/database/seeders/KecamatanTableSeeder.php new file mode 100644 index 0000000..548f80d --- /dev/null +++ b/database/seeders/KecamatanTableSeeder.php @@ -0,0 +1,45 @@ +KabupatenId, 0, 2) . '.' . + substr($kabupaten->KabupatenId, 2, 2); + + $response = Http::get("https://api-wilayah.dinaslhdki.id/api/kecamatan/search?kab={$kabId}"); + + foreach ($response->json() as $kecamatan) { + $cleanId = str_replace('.', '', $kecamatan['id']); + + Kecamatan::create([ + 'KecamatanId' => $cleanId, + 'KabupatenId' => $kabupaten->KabupatenId, + 'NamaKecamatan' => $kecamatan['data'] + ]); + + // Log the seeding process + Log::info('Seeding kecamatan: ', [ + 'KecamatanId' => $cleanId, + 'KabupatenId' => $kabupaten->KabupatenId, + 'NamaKecamatan' => $kecamatan['data'] + ]); + } + } + } +} diff --git a/database/seeders/KelurahanTableSeeder.php b/database/seeders/KelurahanTableSeeder.php new file mode 100644 index 0000000..213b8bc --- /dev/null +++ b/database/seeders/KelurahanTableSeeder.php @@ -0,0 +1,43 @@ +KecamatanId, 0, 2) . '.' . + substr($kecamatan->KecamatanId, 2, 2) . '.' . + substr($kecamatan->KecamatanId, 4, 2); + + $response = Http::get("https://api-wilayah.dinaslhdki.id/api/kelurahan/search?kec={$kecId}"); + + foreach ($response->json() as $kelurahan) { + $cleanId = str_replace('.', '', $kelurahan['id']); + $cleanName = str_replace("\n", ' ', $kelurahan['data']); + + Kelurahan::create([ + 'KelurahanId' => $cleanId, + 'KecamatanId' => $kecamatan->KecamatanId, + 'NamaKelurahan' => $cleanName + ]); + + Log::info('Seeding kelurahan: ', [ + 'KelurahanId' => $cleanId, + 'KecamatanId' => $kecamatan->KecamatanId, + 'NamaKelurahan' => $cleanName + ]); + } + } + } +} diff --git a/package-lock.json b/package-lock.json index da35c5a..9c4fdb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,20 +14,25 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-navigation-menu": "^1.2.4", "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-progress": "^1.1.2", "@radix-ui/react-scroll-area": "^1.0.5", "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-switch": "^1.1.2", + "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.1.3", "@reduxjs/toolkit": "^2.5.1", + "@tanstack/react-table": "^8.21.2", + "@tinymce/tinymce-react": "^5.1.1", "@types/react-redux": "^7.1.34", "apexcharts": "^4.4.0", "ckeditor4-react": "^4.1.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^3.6.0", + "framer-motion": "^12.4.3", "highlight.js": "^11.11.1", "i18next-browser-languagedetector": "^8.0.2", "i18next-http-backend": "^3.0.2", @@ -43,10 +48,12 @@ "react-redux": "^9.2.0", "react-resizable-panels": "^2.0.19", "react-router-dom": "^7.1.4", + "react-select": "^5.10.0", "react-type-animation": "^3.2.0", "recharts": "^2.15.1", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", + "tinymce": "^7.6.1", "vaul": "^1.1.2", "ziggy-js": "^2.5.0", "zod": "^3.23.8" @@ -101,7 +108,6 @@ "version": "7.26.2", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.25.9", @@ -157,7 +163,6 @@ "version": "7.26.5", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.26.5", @@ -191,7 +196,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.9", @@ -233,7 +237,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -243,7 +246,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -277,7 +279,6 @@ "version": "7.26.7", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.26.7" @@ -337,7 +338,6 @@ "version": "7.25.9", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.25.9", @@ -352,7 +352,6 @@ "version": "7.26.7", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.26.2", @@ -371,7 +370,6 @@ "version": "7.26.7", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.25.9", @@ -381,6 +379,126 @@ "node": ">=6.9.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -1729,6 +1847,71 @@ } } }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.2.tgz", + "integrity": "sha512-u1IgJFQ4zNAUTjGdDL5dcl/U8ntOR6jsnhxKb5RKp5Ozwl88xKR9EqRZOe/Mk8tnx0x5tNUe2F+MzsyjqMg0MA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-roving-focus": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz", @@ -2125,6 +2308,134 @@ } } }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.3.tgz", + "integrity": "sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-roving-focus": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-collection": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz", + "integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz", + "integrity": "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-toast": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.6.tgz", @@ -2839,6 +3150,26 @@ "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" } }, + "node_modules/@tanstack/react-table": { + "version": "8.21.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.2.tgz", + "integrity": "sha512-11tNlEDTdIhMJba2RBH+ecJ9l1zgS2kjmexDPAraulc8jeNA4xocSNeyzextT0XJyASil4XsCYlJmf5jEWAtYg==", + "license": "MIT", + "dependencies": { + "@tanstack/table-core": "8.21.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/@tanstack/react-virtual": { "version": "3.11.3", "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.11.3.tgz", @@ -2857,6 +3188,19 @@ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/@tanstack/table-core": { + "version": "8.21.2", + "resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.2.tgz", + "integrity": "sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@tanstack/virtual-core": { "version": "3.11.3", "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.3.tgz", @@ -2868,6 +3212,20 @@ "url": "https://github.com/sponsors/tannerlinsley" } }, + "node_modules/@tinymce/tinymce-react": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@tinymce/tinymce-react/-/tinymce-react-5.1.1.tgz", + "integrity": "sha512-DQ0wpvnf/9z8RsOEAmrWZ1DN1PKqcQHfU+DpM3llLze7FHmxVtzuN8O+FYh0oAAF4stzAXwiCIVacfqjMwRieQ==", + "license": "MIT", + "dependencies": { + "prop-types": "^15.6.2", + "tinymce": "^7.0.0 || ^6.0.0 || ^5.5.1" + }, + "peerDependencies": { + "react": "^18.0.0 || ^17.0.1 || ^16.7.0", + "react-dom": "^18.0.0 || ^17.0.1 || ^16.7.0" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3016,6 +3374,12 @@ "undici-types": "~5.26.4" } }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, "node_modules/@types/prop-types": { "version": "15.7.14", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", @@ -3078,6 +3442,15 @@ "@babel/runtime": "^7.9.2" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@types/use-sync-external-store": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", @@ -3242,6 +3615,21 @@ "proxy-from-env": "^1.1.0" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -3361,6 +3749,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/camelcase-css": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", @@ -3547,6 +3944,31 @@ "node": ">=18" } }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-fetch": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", @@ -3723,7 +4145,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -3878,6 +4299,15 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "license": "MIT" }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -3957,6 +4387,18 @@ "node": ">=6" } }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -4033,6 +4475,12 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/follow-redirects": { "version": "1.15.9", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", @@ -4099,6 +4547,33 @@ "url": "https://github.com/sponsors/rawify" } }, + "node_modules/framer-motion": { + "version": "12.4.3", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.4.3.tgz", + "integrity": "sha512-rsMeO7w3dKyNG09o3cGwSH49iHU+VgDmfSSfsX+wfkO3zDA6WWkh4sUsMXd155YROjZP+7FTIhDrBYfgZeHjKQ==", + "license": "MIT", + "dependencies": { + "motion-dom": "^12.0.0", + "motion-utils": "^12.0.0", + "tslib": "^2.4.0" + }, + "peerDependencies": { + "@emotion/is-prop-valid": "*", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/is-prop-valid": { + "optional": true + }, + "react": { + "optional": true + }, + "react-dom": { + "optional": true + } + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -4223,7 +4698,6 @@ "version": "11.12.0", "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -4379,6 +4853,22 @@ "url": "https://opencollective.com/immer" } }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/internmap": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", @@ -4404,6 +4894,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -4544,7 +5040,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, "license": "MIT", "bin": { "jsesc": "bin/jsesc" @@ -4553,6 +5048,12 @@ "node": ">=6" } }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -4658,6 +5159,12 @@ "node": ">= 0.4" } }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -4737,11 +5244,25 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/motion-dom": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.0.0.tgz", + "integrity": "sha512-CvYd15OeIR6kHgMdonCc1ihsaUG4MYh/wrkz8gZ3hBX/uamyZCXN9S9qJoYF03GqfTt7thTV/dxnHYX4+55vDg==", + "license": "MIT", + "dependencies": { + "motion-utils": "^12.0.0" + } + }, + "node_modules/motion-utils": { + "version": "12.0.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.0.0.tgz", + "integrity": "sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==", + "license": "MIT" + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/mz": { @@ -4904,6 +5425,36 @@ "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==", "license": "BSD-3-Clause" }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", @@ -4941,6 +5492,15 @@ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC" }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/perfect-scrollbar": { "version": "1.5.6", "resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.6.tgz", @@ -5487,6 +6047,27 @@ "react-dom": ">=18" } }, + "node_modules/react-select": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.0.tgz", + "integrity": "sha512-k96gw+i6N3ExgDwPIg0lUPmexl1ygPe6u5BdQFNBhkpbwroIgCNXdubtIzHfThYXYYTubwOBafoMnn7ruEP1xA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.0", + "@emotion/cache": "^11.4.0", + "@emotion/react": "^11.8.1", + "@floating-ui/dom": "^1.0.1", + "@types/react-transition-group": "^4.4.0", + "memoize-one": "^6.0.0", + "prop-types": "^15.6.0", + "react-transition-group": "^4.3.0", + "use-isomorphic-layout-effect": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/react-smooth": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", @@ -5677,6 +6258,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -5915,6 +6505,15 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6020,6 +6619,12 @@ "node": ">=8" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -6137,6 +6742,12 @@ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "license": "MIT" }, + "node_modules/tinymce": { + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/tinymce/-/tinymce-7.6.1.tgz", + "integrity": "sha512-5cHhaAoyyTHfAVTInNfoSp0KkUHmeVUbGSu37QKQbOFIPqxYPhqBiaLm1WVLgoNBYOHRProVc3xzxnNTeWHyoQ==", + "license": "GPL-2.0-or-later" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6246,6 +6857,20 @@ } } }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz", + "integrity": "sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/use-sidecar": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", diff --git a/package.json b/package.json index f36ec22..4b772e2 100644 --- a/package.json +++ b/package.json @@ -34,20 +34,25 @@ "@radix-ui/react-label": "^2.0.2", "@radix-ui/react-navigation-menu": "^1.2.4", "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-progress": "^1.1.2", "@radix-ui/react-scroll-area": "^1.0.5", "@radix-ui/react-select": "^2.1.6", "@radix-ui/react-separator": "^1.1.0", "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-switch": "^1.1.2", + "@radix-ui/react-tabs": "^1.1.3", "@radix-ui/react-toast": "^1.2.6", "@radix-ui/react-tooltip": "^1.1.3", "@reduxjs/toolkit": "^2.5.1", + "@tanstack/react-table": "^8.21.2", + "@tinymce/tinymce-react": "^5.1.1", "@types/react-redux": "^7.1.34", "apexcharts": "^4.4.0", "ckeditor4-react": "^4.1.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "date-fns": "^3.6.0", + "framer-motion": "^12.4.3", "highlight.js": "^11.11.1", "i18next-browser-languagedetector": "^8.0.2", "i18next-http-backend": "^3.0.2", @@ -63,10 +68,12 @@ "react-redux": "^9.2.0", "react-resizable-panels": "^2.0.19", "react-router-dom": "^7.1.4", + "react-select": "^5.10.0", "react-type-animation": "^3.2.0", "recharts": "^2.15.1", "tailwind-merge": "^2.6.0", "tailwindcss-animate": "^1.0.7", + "tinymce": "^7.6.1", "vaul": "^1.1.2", "ziggy-js": "^2.5.0", "zod": "^3.23.8" diff --git a/public/assets/cover-panduan-skl.png b/public/assets/cover-panduan-skl.png new file mode 100644 index 0000000..812575b Binary files /dev/null and b/public/assets/cover-panduan-skl.png differ diff --git a/public/assets/hero-second.jpg b/public/assets/hero-second.jpg index 7c4bc0c..c4d5f8a 100644 Binary files a/public/assets/hero-second.jpg and b/public/assets/hero-second.jpg differ diff --git a/resources/js/components/Card/CardPengumuman.tsx b/resources/js/components/Card/CardPengumuman.tsx index 439a95d..df77de4 100644 --- a/resources/js/components/Card/CardPengumuman.tsx +++ b/resources/js/components/Card/CardPengumuman.tsx @@ -2,72 +2,10 @@ import React from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { ArrowRight } from "lucide-react"; import { Link } from "@inertiajs/react"; -// const pengumumans = [ -// { -// id: 1, -// title_page: "Pengumuman", -// alt_image: "Pengumuman", -// date: "16 Januari 2025", -// title: "Pelatihan & Sertifikasi Online Bidang Pengendalian Pencemaran Air Dan Udara", -// description: -// "Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan Tersebut Bekerjasama Dengan Lembaga P...", -// image: "/assets/img1.jpg", -// }, -// { -// id: 2, -// title_page: "Pengumuman", -// alt_image: "Pengumuman", -// date: "12 Desember 2024", -// title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", -// description: -// "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", -// image: "/assets/img1.jpg", -// }, -// { -// id: 3, -// title_page: "Pengumuman", -// alt_image: "Pengumuman", -// date: "12 Desember 2024", -// title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", -// description: -// "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", -// image: "/assets/img1.jpg", -// }, -// { -// id: 4, -// title_page: "Pengumuman", -// alt_image: "Pengumuman", -// date: "12 Desember 2024", -// title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", -// description: -// "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", -// image: "/assets/img1.jpg", -// }, -// { -// id: 5, -// title_page: "Pengumuman", -// alt_image: "Pengumuman", -// date: "12 Desember 2024", -// title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", -// description: -// "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", -// image: "/assets/img1.jpg", -// }, -// { -// id: 6, -// title_page: "Pengumuman", -// alt_image: "Pengumuman", -// date: "12 Desember 2024", -// title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", -// description: -// "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", -// image: "/assets/img1.jpg", -// }, -// ]; - interface SubKategori { SubKategoriId: number; NamaSubKategori: string; @@ -95,55 +33,132 @@ interface CardPengumumanProps { } const CardPengumuman = ({ posts }: CardPengumumanProps) => { + const subcategories = Array.from( + new Set(posts.map((item) => item.subkategori?.NamaSubKategori)) + ).filter(Boolean); + + const filterPosts = (subkategori: string | null) => { + if (!subkategori) return posts; + return posts.filter( + (item) => item.subkategori?.NamaSubKategori === subkategori + ); + }; + + const [visibleItems, setVisibleItems] = React.useState(3); + const [activeTab, setActiveTab] = React.useState("all"); + + const loadMore = () => { + setVisibleItems((prev) => prev + 3); + }; + + const PostGrid = ({ items }: { items: Post[] }) => ( +
+ {items.slice(0, visibleItems).map((item) => ( + + {item.JudulPost} + + + {item.subkategori?.NamaSubKategori} + +

+ {new Date(item.created_at).toLocaleDateString( + "id-ID", + { + day: "numeric", + month: "long", + year: "numeric", + } + )} +

+

+ {item.JudulPost} +

+

+ {item.DescPost.replace(/<[^>]*>/g, "").slice( + 0, + 160 + )} + ... +

+ + + +
+
+ ))} +
+ ); + + const handleTabChange = (value: string) => { + setActiveTab(value); + setVisibleItems(3); // Reset visible items when changing tabs + }; return (
{/* List of Announcements */} -
- {posts.map((post) => ( - - {post.JudulPost} - - - {post.kategori?.NamaKategori} |{" "} - {post.subkategori?.NamaSubKategori} - -

- {new Date(post.created_at).toLocaleDateString( - "id-ID", - { - day: "numeric", - month: "long", - year: "numeric", - } - )} -

-

- {post.JudulPost} -

-

- {post.DescPost.replace(/<[^>]*>/g, "")} -

- +
+ +
+ + Semua + {subcategories.map((subcat) => ( + + {subcat} + + ))} + +
+ + + + {visibleItems < filterPosts(null).length && ( +
- - - - ))} -
-
- +
+ )} +
+ + {subcategories.map((subcat) => ( + + + {visibleItems < + filterPosts(subcat as string).length && ( +
+ +
+ )} +
+ ))} +
); diff --git a/resources/js/components/Card/CardPeraturan.tsx b/resources/js/components/Card/CardPeraturan.tsx index 68b4da9..bf34458 100644 --- a/resources/js/components/Card/CardPeraturan.tsx +++ b/resources/js/components/Card/CardPeraturan.tsx @@ -2,96 +2,89 @@ import React from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { ArrowRight } from "lucide-react"; +import { Link } from "@inertiajs/react"; -const peraturans = [ - { - id: 1, - title_page: "Peraturan", - alt_image: "Peraturan", - date: "16 Januari 2025", - title: "Pelatihan & Sertifikasi Online Bidang Pengendalian Pencemaran Air Dan Udara", - description: - "Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan Tersebut Bekerjasama Dengan Lembaga P...", - image: "/assets/img1.jpg", - }, - { - id: 2, - title_page: "Peraturan", - alt_image: "Peraturan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, - { - id: 3, - title_page: "Peraturan", - alt_image: "Peraturan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, - { - id: 4, - title_page: "Peraturan", - alt_image: "Peraturan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, - { - id: 5, - title_page: "Peraturan", - alt_image: "Peraturan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, - { - id: 6, - title_page: "Peraturan", - alt_image: "Peraturan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, -]; +interface SubKategori { + SubKategoriId: number; + NamaSubKategori: string; +} -const CardPeraturan = () => { - return ( -
- {/* List of Announcements */} -
- {peraturans.map((item) => ( - - {item.alt_image} - - - {item.title_page} - -

- {item.date} -

-

- {item.title} -

-

- {item.description} -

+interface Kategori { + KategoriId: number; + NamaKategori: string; +} + +interface Post { + PostId: number; + JudulPost: string; + DescPost: string; + SlugPost: string; + ImagePost: string; + IsPublish: boolean; + created_at: string; + kategori?: Kategori; + subkategori?: SubKategori; +} + +interface CardPeraturanProps { + posts: Post[]; +} + +const CardPeraturan = ({ posts }: CardPeraturanProps) => { + const subcategories = Array.from( + new Set(posts.map((item) => item.subkategori?.NamaSubKategori)) + ).filter(Boolean); + + const filterPosts = (subkategori: string | null) => { + if (!subkategori) return posts; + return posts.filter( + (item) => item.subkategori?.NamaSubKategori === subkategori + ); + }; + + const [visibleItems, setVisibleItems] = React.useState(3); + const [activeTab, setActiveTab] = React.useState("all"); + + const loadMore = () => { + setVisibleItems((prev) => prev + 3); + }; + + const PostGrid = ({ items }: { items: Post[] }) => ( +
+ {items.slice(0, visibleItems).map((item) => ( + + {item.JudulPost} + + + {item.subkategori?.NamaSubKategori} + +

+ {new Date(item.created_at).toLocaleDateString( + "id-ID", + { + day: "numeric", + month: "long", + year: "numeric", + } + )} +

+

+ {item.JudulPost} +

+

+ {item.DescPost.replace(/<[^>]*>/g, "").slice( + 0, + 160 + )} + ... +

+ -
-
- ))} -
-
- + + + + ))} +
+ ); + + const handleTabChange = (value: string) => { + setActiveTab(value); + setVisibleItems(3); // Reset visible items when changing tabs + }; + return ( +
+ {/* List of Peraturan */} +
+ +
+ + Semua + {subcategories.map((subcat) => ( + + {subcat} + + ))} + +
+ + + + {visibleItems < filterPosts(null).length && ( +
+ +
+ )} +
+ + {subcategories.map((subcat) => ( + + + {visibleItems < + filterPosts(subcat as string).length && ( +
+ +
+ )} +
+ ))} +
); diff --git a/resources/js/components/Card/CardUndangan.tsx b/resources/js/components/Card/CardUndangan.tsx index 4a1b0f5..0df48ea 100644 --- a/resources/js/components/Card/CardUndangan.tsx +++ b/resources/js/components/Card/CardUndangan.tsx @@ -2,96 +2,89 @@ import React from "react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { ArrowRight } from "lucide-react"; +import { Link } from "@inertiajs/react"; -const undangans = [ - { - id: 1, - title_page: "Undangan", - alt_image: "Undangan", - date: "16 Januari 2025", - title: "Pelatihan & Sertifikasi Online Bidang Pengendalian Pencemaran Air Dan Udara", - description: - "Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan Tersebut Bekerjasama Dengan Lembaga P...", - image: "/assets/img1.jpg", - }, - { - id: 2, - title_page: "Undangan", - alt_image: "Undangan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, - { - id: 3, - title_page: "Undangan", - alt_image: "Undangan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, - { - id: 4, - title_page: "Undangan", - alt_image: "Undangan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, - { - id: 5, - title_page: "Undangan", - alt_image: "Undangan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, - { - id: 6, - title_page: "Undangan", - alt_image: "Undangan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, -]; +interface SubKategori { + SubKategoriId: number; + NamaSubKategori: string; +} -const CardUndangan = () => { - return ( -
- {/* List of Announcements */} -
- {undangans.map((item) => ( - - {item.alt_image} - - - {item.title_page} - -

- {item.date} -

-

- {item.title} -

-

- {item.description} -

+interface Kategori { + KategoriId: number; + NamaKategori: string; +} + +interface Post { + PostId: number; + JudulPost: string; + DescPost: string; + SlugPost: string; + ImagePost: string; + IsPublish: boolean; + created_at: string; + kategori?: Kategori; + subkategori?: SubKategori; +} + +interface CardUndanganProps { + posts: Post[]; +} + +const CardUndangan = ({ posts }: CardUndanganProps) => { + const subcategories = Array.from( + new Set(posts.map((item) => item.subkategori?.NamaSubKategori)) + ).filter(Boolean); + + const filterPosts = (subkategori: string | null) => { + if (!subkategori) return posts; + return posts.filter( + (item) => item.subkategori?.NamaSubKategori === subkategori + ); + }; + + const [visibleItems, setVisibleItems] = React.useState(3); + const [activeTab, setActiveTab] = React.useState("all"); + + const loadMore = () => { + setVisibleItems((prev) => prev + 3); + }; + + const PostGrid = ({ items }: { items: Post[] }) => ( +
+ {items.slice(0, visibleItems).map((item) => ( + + {item.JudulPost} + + + {item.subkategori?.NamaSubKategori} + +

+ {new Date(item.created_at).toLocaleDateString( + "id-ID", + { + day: "numeric", + month: "long", + year: "numeric", + } + )} +

+

+ {item.JudulPost} +

+

+ {item.DescPost.replace(/<[^>]*>/g, "").slice( + 0, + 160 + )} + ... +

+ -
-
- ))} -
-
- + + + + ))} +
+ ); + + const handleTabChange = (value: string) => { + setActiveTab(value); + setVisibleItems(3); // Reset visible items when changing tabs + }; + + return ( +
+
+ +
+ + Semua + {subcategories.map((subcat) => ( + + {subcat} + + ))} + +
+ + + + {visibleItems < filterPosts(null).length && ( +
+ +
+ )} +
+ + {subcategories.map((subcat) => ( + + + {visibleItems < + filterPosts(subcat as string).length && ( +
+ +
+ )} +
+ ))} +
); diff --git a/resources/js/components/Card/HeroSecond.tsx b/resources/js/components/Card/HeroSecond.tsx index 9d82aef..07d9b76 100644 --- a/resources/js/components/Card/HeroSecond.tsx +++ b/resources/js/components/Card/HeroSecond.tsx @@ -17,10 +17,19 @@ export default function HeroSecond({ title }: HeroSecondProps) { /> {/* Overlay Content */} -
-

- {title} -

+
+
+ +
+
+

+ {title} +

+
diff --git a/resources/js/components/DetailArtikel/DetailArtikel.tsx b/resources/js/components/DetailArtikel/DetailArtikel.tsx new file mode 100644 index 0000000..8fe8ead --- /dev/null +++ b/resources/js/components/DetailArtikel/DetailArtikel.tsx @@ -0,0 +1,82 @@ +import React from "react"; +import { Link } from "@inertiajs/react"; + +import { DetailArtikelProps } from "./types"; + +export default function DetailArtikel({ + article, + relatedArticles, +}: DetailArtikelProps) { + const getRouteByCategory = (kategori: string, slug: string) => { + const routes: { [key: string]: string } = { + Pengumuman: `/pengumuman/${slug}`, + Peraturan: `/peraturan/${slug}`, + Undangan: `/undangan/${slug}`, + }; + + return routes[kategori] || `/sekilasinfo/${slug}`; + }; + + return ( +
+
+
+ + {article.kategori?.NamaKategori} + +
+ {new Date(article.created_at).toLocaleDateString( + "id-ID", + { + day: "numeric", + month: "long", + year: "numeric", + } + )} +
+
+

{article.JudulPost}

+ {article.JudulPost} +
+
+
+

Informasi Lainnya

+
+ {relatedArticles.slice(0, 5).map((related) => ( + +
+ {related.JudulPost} +
+ + {related.kategori?.NamaKategori} + +

+ {related.JudulPost} +

+
+
+ + ))} +
+
+
+ ); +} diff --git a/resources/js/components/DetailArtikel/types.ts b/resources/js/components/DetailArtikel/types.ts new file mode 100644 index 0000000..0b79534 --- /dev/null +++ b/resources/js/components/DetailArtikel/types.ts @@ -0,0 +1,26 @@ +export interface SubKategori { + SubKategoriId: number; + NamaSubKategori: string; +} + +export interface Kategori { + KategoriId: number; + NamaKategori: string; +} + +export interface Post { + PostId: number; + JudulPost: string; + DescPost: string; + SlugPost: string; + ImagePost: string; + IsPublish: boolean; + created_at: string; + kategori?: Kategori; + subkategori?: SubKategori; +} + +export interface DetailArtikelProps { + article: Post; + relatedArticles: Post[]; +} diff --git a/resources/js/components/DetailSearch/DetailSearch.tsx b/resources/js/components/DetailSearch/DetailSearch.tsx new file mode 100644 index 0000000..11291db --- /dev/null +++ b/resources/js/components/DetailSearch/DetailSearch.tsx @@ -0,0 +1,86 @@ +import React from "react"; +import { Link } from "@inertiajs/react"; +import { Post } from "@/components/DetailArtikel/types"; + +interface DetailSearchProps { + searchResults: Post[]; + searchQuery: string; +} + +export default function DetailSearch({ + searchResults, + searchQuery, +}: DetailSearchProps) { + const getRouteByCategory = (kategori: string, slug: string) => { + const routes: { [key: string]: string } = { + Pengumuman: `/pengumuman/${slug}`, + Peraturan: `/peraturan/${slug}`, + Undangan: `/undangan/${slug}`, + }; + + return routes[kategori] || `/sekilasinfo/${slug}`; + }; + + return ( +
+
+

Hasil Pencarian

+

+ Menampilkan hasil untuk "{searchQuery}" ( + {searchResults.length} hasil) +

+
+ +
+ {searchResults.map((result) => ( + +
+ {result.JudulPost} +
+
+ + {result.kategori?.NamaKategori} + + + {new Date( + result.created_at + ).toLocaleDateString("id-ID", { + day: "numeric", + month: "long", + year: "numeric", + })} + +
+

+ {result.JudulPost} +

+

+ {result.DescPost.replace(/<[^>]*>/g, "")} +

+
+
+ + ))} +
+ + {searchResults.length === 0 && ( +
+

+ Tidak ada hasil yang ditemukan +

+
+ )} +
+ ); +} diff --git a/resources/js/components/Partials/Hero.tsx b/resources/js/components/Partials/Hero.tsx index 09f259f..d8809c0 100644 --- a/resources/js/components/Partials/Hero.tsx +++ b/resources/js/components/Partials/Hero.tsx @@ -22,7 +22,7 @@ const HeroSection = () => {

void; } +interface CustomPageProps extends InertiaPageProps { + runPosts?: Post[]; + errors: Record; +} + const Navbar = () => { + const { runPosts = [] } = usePage().props; + // const runPosts = props.runPosts || []; const [isScrolled, setIsScrolled] = useState(false); const [isDrawerOpen, setIsDrawerOpen] = useState(false); // const [searchQuery, setSearchQuery] = useState(""); @@ -103,7 +112,7 @@ const Navbar = () => { return (
- +
{
- + */}
{ +interface Kategori { + KategoriId: number; + NamaKategori: string; +} + +interface Post { + PostId: number; + JudulPost: string; + DescPost: string; + SlugPost: string; + ImagePost: string; + IsPublish: boolean; + created_at: string; + kategori?: Kategori; + subkategori?: SubKategori; +} + +interface CardPengumumanProps { + posts: Post[]; +} + +const AnnouncementSection = ({ posts }: CardPengumumanProps) => { return (
+ + Informasi +

Pengumuman

- + + +
{/* Highlight Announcement */} -
- - {announcements[0].alt_image} - - - {announcements[0].title_page} - -

- {announcements[0].date} -

-

- {announcements[0].title} -

-

- {announcements[0].description} -

- -
-
-
- - {/* List of Announcements */} -
- {announcements.map((item) => ( - + {posts.length > 1 && ( +
+ {item.alt_image} - + - {item.title_page} + {posts[0].kategori?.NamaKategori}

- {item.date} + {new Date( + posts[0].created_at + ).toLocaleDateString("id-ID", { + day: "numeric", + month: "long", + year: "numeric", + })}

-

- {item.title} +

+ {posts[0].JudulPost}

- {item.description} + {posts[0].DescPost.replace( + /<[^>]*>/g, + "" + ).slice(0, 160)} + ...

- + + +
- ))} -
+
+ )} + + {/* List of Announcements */} + {posts.length > 1 && ( +
+ {posts.slice(1).map((post) => ( + + {post.JudulPost} + + + {post.kategori?.NamaKategori} + +

+ {new Date( + post.created_at + ).toLocaleDateString("id-ID", { + day: "numeric", + month: "long", + year: "numeric", + })} +

+

+ {post.JudulPost} +

+

+ {post.DescPost.replace( + /<[^>]*>/g, + "" + ).slice(0, 160)} + ... +

+ + + +
+
+ ))} +
+ )}
); }; diff --git a/resources/js/components/Partials/Peraturan.tsx b/resources/js/components/Partials/Peraturan.tsx index 16c42f5..371a225 100644 --- a/resources/js/components/Partials/Peraturan.tsx +++ b/resources/js/components/Partials/Peraturan.tsx @@ -3,31 +3,35 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; import { ArrowRight, FileText } from "lucide-react"; +import { Link } from "@inertiajs/react"; -const regulations = [ - { - id: 1, - title_page: "Peraturan", - alt_image: "Peraturan", - date: "16 Januari 2025", - title: "Pelatihan & Sertifikasi Online Bidang Pengendalian Pencemaran Air Dan Udara", - description: - "Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan Tersebut Bekerjasama Dengan Lembaga P...", - image: "/assets/img1.jpg", - }, - { - id: 2, - title_page: "Peraturan", - alt_image: "Peraturan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, -]; +interface SubKategori { + SubKategoriId: number; + NamaSubKategori: string; +} -const RegulationSection = () => { +interface Kategori { + KategoriId: number; + NamaKategori: string; +} + +interface Post { + PostId: number; + JudulPost: string; + DescPost: string; + SlugPost: string; + ImagePost: string; + IsPublish: boolean; + created_at: string; + kategori?: Kategori; + subkategori?: SubKategori; +} + +interface CardPeraturanProps { + peraturan: Post[]; +} + +const RegulationSection = ({ peraturan }: CardPeraturanProps) => { return (
{/* Background Green Box */} @@ -43,45 +47,59 @@ const RegulationSection = () => { Informasi

Peraturan

-

- Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan - Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan - Tersebut Bekerjasama Dengan Lembaga P... +

+ Aplikasi Sistem Ketaatan Lingkungan memastikan kepatuhan + terhadap perizinan, pengelolaan limbah, emisi, serta + pengawasan lingkungan.

- + + +
{/* List of Regulations */}
- {regulations.map((item) => ( - + {peraturan.map((item) => ( + {item.alt_image} - {item.title_page} + {item.kategori?.NamaKategori}

- {item.date} + {new Date( + item.created_at + ).toLocaleDateString("id-ID", { + day: "numeric", + month: "long", + year: "numeric", + })}

- {item.title} + {item.JudulPost}

- {item.description} + {item.DescPost.replace( + /<[^>]*>/g, + "" + ).slice(0, 160)} + ...

- + + +
))} diff --git a/resources/js/components/Partials/PopupModal.tsx b/resources/js/components/Partials/PopupModal.tsx index e7f6ef0..7580809 100644 --- a/resources/js/components/Partials/PopupModal.tsx +++ b/resources/js/components/Partials/PopupModal.tsx @@ -1,31 +1,26 @@ import React, { useState } from "react"; import { Button } from "@/components/ui/button"; +import { Progress } from "@/components/ui/progress"; import { X, ArrowLeft, ArrowRight } from "lucide-react"; +import { motion, AnimatePresence } from "framer-motion"; +import { usePage } from "@inertiajs/react"; +import { Link } from "@inertiajs/react"; const PopupModal = ({ onClose }: { onClose: () => void }) => { - const slides = [ - { - title: "Pelatihan dan Sertifikasi Online Bidang Pengendalian Pencemaran Udara", - image: "/assets/popup-1.jpeg", - description: - "Kegiatan ini diselenggarakan oleh DLH Provinsi DKI Jakarta bekerja sama dengan LSP Tersertifikasi BNSP.", - }, - { - title: "Workshop Peningkatan Kapasitas Pengendalian Pencemaran Udara", - image: "/assets/popup-2.jpeg", - description: - "Workshop ini bertujuan untuk meningkatkan pengetahuan dan keterampilan di bidang lingkungan hidup. Workshop ini bertujuan untuk meningkatkan pengetahuan dan keterampilan di bidang lingkungan hidup.", - }, - { - title: "Sosialisasi Program Ramah Lingkungan", - image: "/assets/popup-2.jpeg", - description: - "DLH DKI Jakarta mengajak masyarakat untuk lebih peduli terhadap lingkungan sekitar.", - }, - ]; - + const { sekilasInfo } = usePage().props as any; + const [isVisible, setIsVisible] = useState(true); const [currentSlide, setCurrentSlide] = useState(0); + const progressValue = ((currentSlide + 1) / sekilasInfo.length) * 100; + + const slides = sekilasInfo.map((info: any) => ({ + title: info.JudulPost, + image: `/storage/${info.ImagePost}`, + description: + info.DescPost.replace(/<[^>]*>/g, "").slice(0, 160) + "...", + slug: info.SlugPost, + })); + const nextSlide = () => { setCurrentSlide((prev) => (prev + 1) % slides.length); }; @@ -34,47 +29,109 @@ const PopupModal = ({ onClose }: { onClose: () => void }) => { setCurrentSlide((prev) => (prev - 1 + slides.length) % slides.length); }; + const handleClose = () => { + setIsVisible(false); + setTimeout(onClose, 500); + }; + return ( -
-
- -

- {slides[currentSlide].title} -

-
- Popup Slide Image - - -
- {/*

- {slides[currentSlide].title} -

*/} -

- {slides[currentSlide].description} -

-
- -
-
-
+ +

+ {slides[currentSlide].title} +

+
+ Popup Slide Image + + +
+
+ +
+ {currentSlide + 1} / {slides.length} +
+
+

+ {slides[currentSlide].description} +

+
+ + + +
+ + + )} + ); }; diff --git a/resources/js/components/Partials/RunningText.tsx b/resources/js/components/Partials/RunningText.tsx index 39bc7cd..652e270 100644 --- a/resources/js/components/Partials/RunningText.tsx +++ b/resources/js/components/Partials/RunningText.tsx @@ -1,43 +1,106 @@ import React, { useState, useEffect } from "react"; import { Megaphone } from "lucide-react"; +import { Link } from "@inertiajs/react"; +import { Post } from "@/components/DetailArtikel/types"; -const messages = [ - "Peraturan Menteri Lingkungan Hidup dan Kehutanan No. 14 Tahun 2024 Tentang Penyelenggaraan Pengawasan dan Sanksi Administratif Lingkungan Hidup", - "Workshop Peningkatan Kapasitas Pengendalian Pencemaran Udara akan diselenggarakan pada bulan depan.", - "DLH DKI Jakarta mengajak masyarakat untuk berpartisipasi dalam program lingkungan ramah sampah.", -]; +interface RunningTextProps { + posts: Post[]; +} -export default function RunningText() { +export default function RunningText({ posts }: RunningTextProps) { const [currentMessageIndex, setCurrentMessageIndex] = useState(0); const [isVisible, setIsVisible] = useState(true); + const [isPaused, setIsPaused] = useState(false); + + const getLatestPostsByCategory = () => { + const groupedPosts = posts.reduce((acc, post) => { + const category = post.kategori?.NamaKategori || "Uncategorized"; + if (!acc[category]) { + acc[category] = []; + } + acc[category].push(post); + return acc; + }, {} as Record); + + // Get latest 2 posts from each category and flatten + return Object.values(groupedPosts) + .map((categoryPosts) => + categoryPosts + .sort( + (a, b) => + new Date(b.created_at).getTime() - + new Date(a.created_at).getTime() + ) + .slice(0, 2) + ) + .flat(); + }; + + const messages = getLatestPostsByCategory(); useEffect(() => { + if (isPaused || messages.length === 0) return; + const interval = setInterval(() => { - setIsVisible(false); // Hide current message + setIsVisible(false); setTimeout(() => { setCurrentMessageIndex( (prevIndex) => (prevIndex + 1) % messages.length ); - setIsVisible(true); // Show new message - }, 500); // Wait for fade out animation - }, 3000); // Change message every 3 seconds + setIsVisible(true); + }, 500); + }, 3000); return () => clearInterval(interval); - }, []); + }, [isPaused, messages.length]); + + const getRouteByCategory = (kategori: string, slug: string) => { + const routes: { [key: string]: string } = { + Pengumuman: `/pengumuman/${slug}`, + Peraturan: `/peraturan/${slug}`, + Undangan: `/undangan/${slug}`, + }; + + return routes[kategori] || `/sekilasinfo/${slug}`; + }; + + if (messages.length === 0) return null; return ( -
+
setIsPaused(true)} + onMouseLeave={() => setIsPaused(false)} + >
-

- {messages[currentMessageIndex]} -

+

+ + { + messages[currentMessageIndex].kategori + ?.NamaKategori + } + + + {" "} + {messages[currentMessageIndex].JudulPost} + +

+
diff --git a/resources/js/components/Partials/SearchDialog.tsx b/resources/js/components/Partials/SearchDialog.tsx index 99d8554..9a3752b 100644 --- a/resources/js/components/Partials/SearchDialog.tsx +++ b/resources/js/components/Partials/SearchDialog.tsx @@ -17,7 +17,10 @@ const SearchDialog: React.FC = () => { const handleSearch = (e: React.FormEvent) => { e.preventDefault(); if (searchQuery.trim()) { - router.visit(`/search/${encodeURIComponent(searchQuery)}`); + router.visit(`/search?q=${encodeURIComponent(searchQuery)}`, { + preserveState: true, + preserveScroll: true, + }); setSearchQuery(""); // Clear input setelah search } }; @@ -56,14 +59,14 @@ const SearchDialog: React.FC = () => { - + {/* - + */} ); diff --git a/resources/js/components/Partials/Tentang.tsx b/resources/js/components/Partials/Tentang.tsx index 84b331a..9356a38 100644 --- a/resources/js/components/Partials/Tentang.tsx +++ b/resources/js/components/Partials/Tentang.tsx @@ -21,25 +21,19 @@ export default function Tentang() {

- Status Ketaatan Lingkungan + Sistem Ketaatan Lingkungan
(SKL)

- Lorem Ipsum Is Simply Dummy Text Of The Printing - And Typesetting Industry. Lorem Ipsum Has Been - The Industry's Standard Dummy Text Ever Since - The 1500s, When An Unknown Printer Took A Galley - Of Type And Scrambled It To Make A Type Specimen - Book. It Has Survived Not Only Five Centuries, - But Also The Leap Into Electronic Typesetting, - Remaining Essentially Unchanged. It Was - Popularised In The 1960s With The Release Of - Letraset Sheets Containing Lorem Ipsum Passages, - And More Recently With Desktop Publishing - Software Like Aldus PageMaker Including Versions - Of Lorem Ipsum. + SKL adalah Sistem Ketaatan Lingkungan yang + dinaungi oleh Dinas Lingkungan Hidup Provinsi + DKI Jakarta dengan menyediakan sistem yang dapat + memberikan pelayanan dalam pelaporan, analisa + dan evaluasi pengelolaan lingkungan dilakukan + oleh perusahaan atau kegiatan usaha yang berada + di Provinsi DKI Jakarta.

diff --git a/resources/js/components/Partials/Undangan.tsx b/resources/js/components/Partials/Undangan.tsx index 5542616..1c90e44 100644 --- a/resources/js/components/Partials/Undangan.tsx +++ b/resources/js/components/Partials/Undangan.tsx @@ -3,41 +3,35 @@ import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; import { ArrowRight } from "lucide-react"; +import { Link } from "@inertiajs/react"; -const undangans = [ - { - id: 1, - title_page: "Undangan", - alt_image: "Undangan", - date: "16 Januari 2025", - title: "Pelatihan & Sertifikasi Online Bidang Pengendalian Pencemaran Air Dan Udara", - description: - "Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan Tersebut Bekerjasama Dengan Lembaga P...", - image: "/assets/img1.jpg", - }, - { - id: 2, - title_page: "Undangan", - alt_image: "Undangan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, - { - id: 3, - title_page: "Undangan", - alt_image: "Undangan", - date: "12 Desember 2024", - title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)", - description: - "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...", - image: "/assets/img1.jpg", - }, -]; +interface SubKategori { + SubKategoriId: number; + NamaSubKategori: string; +} -const UndanganSection = () => { +interface Kategori { + KategoriId: number; + NamaKategori: string; +} + +interface Post { + PostId: number; + JudulPost: string; + DescPost: string; + SlugPost: string; + ImagePost: string; + IsPublish: boolean; + created_at: string; + kategori?: Kategori; + subkategori?: SubKategori; +} + +interface CardUndanganProps { + undangan: Post[]; +} + +const UndanganSection = ({ undangan }: CardUndanganProps) => { return (
@@ -49,40 +43,58 @@ const UndanganSection = () => { Undangan

- + + +
{/* List of Announcements */}
- {undangans.map((item) => ( - + {undangan.map((item) => ( + {item.alt_image} - {item.title_page} + {item.kategori?.NamaKategori}

- {item.date} + {new Date(item.created_at).toLocaleDateString( + "id-ID", + { + day: "numeric", + month: "long", + year: "numeric", + } + )}

- {item.title} + {item.JudulPost}

- {item.description} + {item.DescPost.replace(/<[^>]*>/g, "").slice( + 0, + 160 + )} + ...

- + + +
))} diff --git a/resources/js/components/app-sidebar.tsx b/resources/js/components/app-sidebar.tsx index 054962e..e6e4488 100644 --- a/resources/js/components/app-sidebar.tsx +++ b/resources/js/components/app-sidebar.tsx @@ -2,11 +2,22 @@ import * as React from "react"; import { + Archive, + Bold, + Bolt, + BookCheck, + BookMarked, Command, + DatabaseBackup, + FileKey2, + Flag, FolderArchive, Frame, + GalleryVertical, Home, LifeBuoy, + NotepadText, + Scale, Send, SquareTerminal, } from "lucide-react"; @@ -38,6 +49,64 @@ const data = { url: "/dashboard", icon: Home, }, + { + title: "Pelaporan", + url: "/admin/pelaporan", + icon: BookMarked, + }, + { + title: "Verifikasi Pelaporan", + url: "/admin/verifikasi", + icon: BookCheck, + }, + { + title: "Penegakan Hukum", + url: "/admin/hukum", + icon: Scale, + }, + { + title: "Perizinan Lingkungan", + url: "/admin/perizinan-lingkungan", + icon: FileKey2, + }, + { + title: "History Perusahaan", + url: "/admin/history-perusahaan", + icon: GalleryVertical, + }, + { + title: "Post", + url: "/admin/post", + icon: Frame, + }, + { + title: "Reporting", + url: "#", + icon: Flag, + isActive: true, + items: [ + { + title: "Rekapitulasi", + url: "/admin/rekap", + }, + { + title: "SKL & SPL", + url: "/admin/skl-spl", + }, + { + title: "Daftar Perusahaan", + url: "/admin/daftar-perusahaan", + }, + { + title: "SKL", + url: "/admin/skl", + }, + { + title: "SPL", + url: "/admin/spl", + }, + ], + }, { title: "Data Master", url: "#", @@ -52,28 +121,69 @@ const data = { title: "Sub Kategori Post", url: "/admin/subkategori", }, - // { - // title: "Menu", - // url: "/menus", - // }, + { + title: "Dinas LH", + url: "/admin/verifikator", + }, + { + title: "Jenis Kegiatan", + url: "/admin/jeniskegiatan", + }, + { + title: "Jenis Dokumen Izin", + url: "/admin/jenisdokil", + }, + { + title: "Perusahaan", + url: "/admin/perusahaan", + }, + { + title: "History Kegiatan", + url: "/admin/historykegiatan", + }, + { + title: "Jenis Sanksi", + url: "/admin/jenissanksi", + }, ], }, { - title: "Post", - url: "/admin/post", - icon: Frame, + title: "Pengaturan", + url: "#", + icon: Bolt, + isActive: true, + items: [ + { + title: "Pengguna", + url: "/admin/pengguna", + }, + { + title: "Role", + url: "/admin/role", + }, + ], }, ], navSecondary: [ { - title: "Support", - url: "#", + title: "Tentang", + url: "/admin/tentang", icon: LifeBuoy, }, { - title: "Feedback", + title: "Catatan Sistem", url: "#", - icon: Send, + icon: NotepadText, + }, + { + title: "Backup", + url: "#", + icon: Archive, + }, + { + title: "Restore", + url: "#", + icon: DatabaseBackup, }, ], }; diff --git a/resources/js/components/appearance-dropdown.tsx b/resources/js/components/appearance-dropdown.tsx index dda046b..81a0ad6 100644 --- a/resources/js/components/appearance-dropdown.tsx +++ b/resources/js/components/appearance-dropdown.tsx @@ -44,11 +44,11 @@ const AppearanceDropdown = () => { return ( <>
- + {formattedDate}
{formattedTime}
-
+ {/*
+
*/}
); diff --git a/resources/js/components/nav-main.tsx b/resources/js/components/nav-main.tsx index 09c9995..0fb1a15 100644 --- a/resources/js/components/nav-main.tsx +++ b/resources/js/components/nav-main.tsx @@ -46,10 +46,10 @@ export function NavMain({ > - +
{item.title}
-
+
{item.items?.length ? ( <> diff --git a/resources/js/components/nav-secondary.tsx b/resources/js/components/nav-secondary.tsx index 3b5e0e8..ae82194 100644 --- a/resources/js/components/nav-secondary.tsx +++ b/resources/js/components/nav-secondary.tsx @@ -8,6 +8,7 @@ import { SidebarMenuButton, SidebarMenuItem, } from "@/components/ui/sidebar"; +import { Link } from "@inertiajs/react"; export function NavSecondary({ items, @@ -26,10 +27,10 @@ export function NavSecondary({ {items.map((item) => ( - + {item.title} - + ))} diff --git a/resources/js/components/ui/alert.tsx b/resources/js/components/ui/alert.tsx new file mode 100644 index 0000000..5afd41d --- /dev/null +++ b/resources/js/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/resources/js/components/ui/data-table.tsx b/resources/js/components/ui/data-table.tsx new file mode 100644 index 0000000..b1afe78 --- /dev/null +++ b/resources/js/components/ui/data-table.tsx @@ -0,0 +1,209 @@ +"use client"; + +import * as React from "react"; +import { + ColumnDef, + SortingState, + ColumnFiltersState, + flexRender, + getCoreRowModel, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; + +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; + +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; + +interface DataTableProps { + columns: ColumnDef[]; + data: TData[]; + searchKey: string; + searchPlaceholder?: string; +} + +export function DataTable({ + columns, + data, + searchKey, + searchPlaceholder = "Filter records...", +}: DataTableProps) { + const [sorting, setSorting] = React.useState([]); + const [columnFilters, setColumnFilters] = + React.useState([]); + const [rowSelection, setRowSelection] = React.useState({}); + + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + getPaginationRowModel: getPaginationRowModel(), + onSortingChange: setSorting, + getSortedRowModel: getSortedRowModel(), + onColumnFiltersChange: setColumnFilters, + getFilteredRowModel: getFilteredRowModel(), + onRowSelectionChange: setRowSelection, + state: { + sorting, + columnFilters, + rowSelection, + }, + }); + + return ( +
+
+ + table + .getColumn(searchKey) + ?.setFilterValue(event.target.value) + } + className="max-w-sm" + /> +
+ +
+
+ + {/* Wrap table in a responsive container */} +
+
+
+ + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => ( + + {header.isPlaceholder + ? null + : flexRender( + header.column + .columnDef.header, + header.getContext() + )} + + ))} + + ))} + + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row) => ( + + {row + .getVisibleCells() + .map((cell) => ( + + {flexRender( + cell.column + .columnDef.cell, + cell.getContext() + )} + + ))} + + )) + ) : ( + + + No results. + + + )} + +
+
+
+
+ +
+
+ {table.getFilteredSelectedRowModel().rows.length} of{" "} + {table.getFilteredRowModel().rows.length} row(s) selected. +
+
+ + +
+
+
+ ); +} diff --git a/resources/js/components/ui/progress.tsx b/resources/js/components/ui/progress.tsx new file mode 100644 index 0000000..3fd47ad --- /dev/null +++ b/resources/js/components/ui/progress.tsx @@ -0,0 +1,26 @@ +import * as React from "react" +import * as ProgressPrimitive from "@radix-ui/react-progress" + +import { cn } from "@/lib/utils" + +const Progress = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, value, ...props }, ref) => ( + + + +)) +Progress.displayName = ProgressPrimitive.Root.displayName + +export { Progress } diff --git a/resources/js/components/ui/table.tsx b/resources/js/components/ui/table.tsx index 0c0898b..c28b88d 100644 --- a/resources/js/components/ui/table.tsx +++ b/resources/js/components/ui/table.tsx @@ -58,7 +58,7 @@ const TableRow = React.forwardRef< , + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsList.displayName = TabsPrimitive.List.displayName + +const TabsTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsTrigger.displayName = TabsPrimitive.Trigger.displayName + +const TabsContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +TabsContent.displayName = TabsPrimitive.Content.displayName + +export { Tabs, TabsList, TabsTrigger, TabsContent } diff --git a/resources/js/components/ui/toast.tsx b/resources/js/components/ui/toast.tsx index 77bc281..97f5ef2 100644 --- a/resources/js/components/ui/toast.tsx +++ b/resources/js/components/ui/toast.tsx @@ -27,7 +27,7 @@ const toastVariants = cva( { variants: { variant: { - default: "border bg-background text-foreground", + default: "border bg-green-100 text-green-800", destructive: "destructive group border-destructive bg-destructive text-destructive-foreground", }, diff --git a/resources/js/components/ui/toaster.tsx b/resources/js/components/ui/toaster.tsx index 6570bd3..0204427 100644 --- a/resources/js/components/ui/toaster.tsx +++ b/resources/js/components/ui/toaster.tsx @@ -12,7 +12,7 @@ export function Toaster() { const { toasts } = useToast(); return ( - + {toasts.map(function ({ id, title, diff --git a/resources/js/layouts/authenticated-layout.tsx b/resources/js/layouts/authenticated-layout.tsx index 4768f6a..83c0d7d 100644 --- a/resources/js/layouts/authenticated-layout.tsx +++ b/resources/js/layouts/authenticated-layout.tsx @@ -25,7 +25,7 @@ export default function AuthenticatedLayout({ - +
@@ -46,8 +46,10 @@ export default function AuthenticatedLayout({
-
{children}
-