versi presentasi
parent
f00612339c
commit
0562099ca0
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use App\Http\Requests\RoleRequest;
|
use App\Http\Requests\RoleRequest;
|
||||||
|
use Spatie\Permission\Models\Permission;
|
||||||
use Spatie\Permission\Models\Role;
|
use Spatie\Permission\Models\Role;
|
||||||
|
|
||||||
class RoleController extends Controller
|
class RoleController extends Controller
|
||||||
|
@ -18,4 +19,84 @@ class RoleController extends Controller
|
||||||
// Kembalikan data ke komponen Inertia 'Admin/Roles/Index'
|
// Kembalikan data ke komponen Inertia 'Admin/Roles/Index'
|
||||||
return inertia('Admin/Roles/Index', compact('roles'));
|
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.');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\HistoryKegiatanRequest;
|
||||||
|
use App\Models\HistoryKegiatan;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class HistoryKegiatanController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$historykegiatan = HistoryKegiatan::latest()->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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Post;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class HomeController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$runPosts = Post::with(['kategori', 'subkategori'])
|
||||||
|
->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,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class HukumController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return Inertia::render('admin/hukum/index_hukum');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Error rendering view: ' . $e->getMessage());
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\JenisDokILRequest;
|
||||||
|
use App\Models\JenisDokIL;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class JenisDokILController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$jenisdokil = JenisDokIL::latest()->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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\JenisKegiatanRequest;
|
||||||
|
use App\Models\JenisKegiatan;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class JenisKegiatanController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$jeniskegiatan = JenisKegiatan::latest()->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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\JenisSanksi;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class JenisSanksiController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$jenissanksi = JenisSanksi::latest()->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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class PelaporanController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return Inertia::render('admin/pelaporan/index_pelaporan');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Error rendering view: ' . $e->getMessage());
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Post;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class PengumumanController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$posts = Post::with(['kategori', 'subkategori'])
|
||||||
|
->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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Post;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class PeraturanController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$posts = Post::with(['kategori', 'subkategori'])
|
||||||
|
->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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,6 +10,7 @@ use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Log;
|
use Illuminate\Support\Facades\Log;
|
||||||
use Illuminate\Support\Facades\Storage;
|
use Illuminate\Support\Facades\Storage;
|
||||||
use Inertia\Inertia;
|
use Inertia\Inertia;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
|
||||||
class PostController extends Controller
|
class PostController extends Controller
|
||||||
{
|
{
|
||||||
|
@ -32,34 +33,68 @@ class PostController extends Controller
|
||||||
$subkategori = SubKategori::all();
|
$subkategori = SubKategori::all();
|
||||||
|
|
||||||
return Inertia::render('admin/post/add_post', [
|
return Inertia::render('admin/post/add_post', [
|
||||||
'kategori' => $kategori,
|
'kategori' => Kategori::all(),
|
||||||
'subkategori' => $subkategori
|
'subkategori' => SubKategori::all(),
|
||||||
|
'existingPosts' => Post::select('JudulPost', 'KategoriId')->get()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function store(PostRequest $request)
|
public function store(PostRequest $request)
|
||||||
{
|
{
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$data = $request->validated();
|
$data = $request->validated();
|
||||||
$data['IsPublish'] = $request->has('IsPublish');
|
|
||||||
|
DB::beginTransaction();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$data['IsPublish'] = $request->boolean('IsPublish');
|
||||||
|
|
||||||
if ($request->hasFile('ImagePost')) {
|
if ($request->hasFile('ImagePost')) {
|
||||||
$data['ImagePost'] = $request->file('ImagePost')->store('images/posts');
|
$file = $request->file('ImagePost');
|
||||||
|
|
||||||
|
if (!$file->isValid()) {
|
||||||
|
throw new \Exception('Invalid image file');
|
||||||
}
|
}
|
||||||
|
|
||||||
Post::create($data);
|
$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');
|
||||||
|
|
||||||
return redirect()->route('admin.post.index')->with('success', 'Post berhasil ditambahkan');
|
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
return redirect()->route('admin.post.index')->with('error', 'Post gagal ditambahkan');
|
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());
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Validation error: ' . $e->getMessage());
|
||||||
|
|
||||||
|
return redirect()
|
||||||
|
->route('admin.post.index')
|
||||||
|
->with('error', 'Validasi gagal: ' . $e->getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function edit(Post $post)
|
public function edit(Post $post)
|
||||||
{
|
{
|
||||||
|
|
||||||
return Inertia::render('admin/post/edit_post', [
|
return Inertia::render('admin/post/edit_post', [
|
||||||
'post' => $post,
|
'post' => $post,
|
||||||
'kategori' => Kategori::all(),
|
'kategori' => Kategori::all(),
|
||||||
|
@ -70,19 +105,30 @@ class PostController extends Controller
|
||||||
public function update(PostRequest $request, Post $post)
|
public function update(PostRequest $request, Post $post)
|
||||||
{
|
{
|
||||||
try {
|
try {
|
||||||
$data = $request->validated();
|
$data = array_filter($request->validated(), function($value) {
|
||||||
|
return $value !== null;
|
||||||
|
});
|
||||||
|
|
||||||
if ($request->hasFile('ImagePost')) {
|
if ($request->hasFile('ImagePost')) {
|
||||||
|
if ($post->ImagePost && Storage::disk('public')->exists($post->ImagePost)) {
|
||||||
Storage::disk('public')->delete($post->ImagePost);
|
Storage::disk('public')->delete($post->ImagePost);
|
||||||
|
}
|
||||||
$data['ImagePost'] = $request->file('ImagePost')->store('images/posts', 'public');
|
$data['ImagePost'] = $request->file('ImagePost')->store('images/posts', 'public');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($data['IsPublish'])) {
|
||||||
|
$data['IsPublish'] = (bool) $data['IsPublish'];
|
||||||
|
}
|
||||||
|
|
||||||
$post->update($data);
|
$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) {
|
} catch (\Exception $e) {
|
||||||
Log::error('Error updating Post: ' . $e->getMessage());
|
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.');
|
return back()->with('error', 'Something went wrong.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Post;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class SearchController extends Controller
|
||||||
|
{
|
||||||
|
public function index(Request $request)
|
||||||
|
{
|
||||||
|
$query = $request->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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Post;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class SekilasInfoController extends Controller
|
||||||
|
{
|
||||||
|
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/SekilasInfoDetail', [
|
||||||
|
'article' => $article,
|
||||||
|
'relatedArticles' => $relatedArticles
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class TentangController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return Inertia::render('admin/navBottom/index_tentang');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Error rendering view: ' . $e->getMessage());
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Models\Post;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class UndanganController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
$posts = Post::with(['kategori', 'subkategori'])
|
||||||
|
->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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class VerifPelaporanController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
return Inertia::render('admin/pelaporan/index_verif_pelaporan');
|
||||||
|
} catch (\Exception $e) {
|
||||||
|
Log::error('Error rendering view: ' . $e->getMessage());
|
||||||
|
return back()->with('error', 'Something went wrong.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Requests\VerifikatorRequest;
|
||||||
|
use App\Models\Verifikator;
|
||||||
|
use Illuminate\Http\Request;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
|
class VerifikatorController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
$verifikator = Verifikator::latest()->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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class HistoryKegiatanRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'NamaHistoryKegiatan' => [
|
||||||
|
'required',
|
||||||
|
'string',
|
||||||
|
'max:255',
|
||||||
|
Rule::unique('HistoryKegiatan', 'NamaHistoryKegiatan')->whereNull('deleted_at') // Abaikan data yang dihapus (soft delete)
|
||||||
|
],
|
||||||
|
'IsPublish' => 'boolean',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class HukumRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'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.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class JenisDokILRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'NamaJenisDokIL' => 'required|string|max:255',
|
||||||
|
'KodeJenisDokIL' => 'required|string|max:20',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class JenisKegiatanRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'NamaJenisKegiatan' => [
|
||||||
|
'required',
|
||||||
|
'string',
|
||||||
|
'max:255',
|
||||||
|
Rule::unique('JenisKegiatan', 'NamaJenisKegiatan')->whereNull('deleted_at') // Abaikan data yang dihapus (soft delete)
|
||||||
|
],
|
||||||
|
'IsPublish' => 'boolean',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
|
||||||
|
class JenisSanksiRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'NamaJenisSanksi' => [
|
||||||
|
'required',
|
||||||
|
'string',
|
||||||
|
'max:255',
|
||||||
|
Rule::unique('JenisSanksi', 'NamaJenisSanksi')->whereNull('deleted_at') // Abaikan data yang dihapus (soft delete)
|
||||||
|
],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,7 @@
|
||||||
namespace App\Http\Requests;
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
use Illuminate\Foundation\Http\FormRequest;
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
use Illuminate\Validation\Rule;
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
class PostRequest extends FormRequest
|
class PostRequest extends FormRequest
|
||||||
{
|
{
|
||||||
|
@ -22,15 +22,53 @@ class PostRequest extends FormRequest
|
||||||
*/
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
|
if ($this->isMethod('POST')) {
|
||||||
return [
|
return [
|
||||||
'KategoriId' => 'required|exists:Kategori,KategoriId',
|
'KategoriId' => 'required|integer|exists:Kategori,KategoriId',
|
||||||
'SubKategoriId' => [
|
'SubKategoriId' => 'required|integer|exists:SubKategori,SubKategoriId',
|
||||||
'required',
|
|
||||||
Rule::exists('SubKategori', 'SubKategoriId')->where('KategoriId', $this->KategoriId),
|
|
||||||
],
|
|
||||||
'JudulPost' => ['required', 'string', 'max:255'],
|
'JudulPost' => ['required', 'string', 'max:255'],
|
||||||
|
'SlugPost' => ['required', 'string', 'max:255'],
|
||||||
'DescPost' => ['required', 'string'],
|
'DescPost' => ['required', 'string'],
|
||||||
'ImagePost' => ['nullable', 'image', 'mimes:jpg,png,webp', 'max:2048'],
|
'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]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
|
||||||
|
class VerifikatorRequest extends FormRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Determine if the user is authorized to make this request.
|
||||||
|
*/
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the validation rules that apply to the request.
|
||||||
|
*
|
||||||
|
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'NamaUnitKerja' => 'required|string|max:255',
|
||||||
|
'NamaKepala' => 'required|string|max:255',
|
||||||
|
'NIP' => 'required|string|max:255',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class HistoryKegiatan extends Model
|
||||||
|
{
|
||||||
|
use HasFactory; use SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'HistoryKegiatan';
|
||||||
|
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
protected $primaryKey = 'HistoryKegiatanId';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'HistoryKegiatanId',
|
||||||
|
'NamaHistoryKegiatan',
|
||||||
|
'IsPublish',
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,39 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Hukum extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'Hukum';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'PerusahaanId',
|
||||||
|
'JenisSanksiId',
|
||||||
|
'SanksiNumber',
|
||||||
|
'SanksiDate',
|
||||||
|
'SanksiFile',
|
||||||
|
'StatusPenaatan',
|
||||||
|
'PenaatanNumber',
|
||||||
|
'PenaatanDate',
|
||||||
|
'PenaatanFile',
|
||||||
|
'IsDeleted',
|
||||||
|
];
|
||||||
|
|
||||||
|
public function perusahaan()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Perusahaan::class, 'PerusahaanId');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Relasi ke tabel jenis sanksi.
|
||||||
|
*/
|
||||||
|
public function jenisSanksi()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(JenisSanksi::class, 'JenisSanksiId');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class JenisDokIL extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'JenisDokIL';
|
||||||
|
protected $primaryKey = 'JenisDokILId';
|
||||||
|
protected $fillable = [
|
||||||
|
'KodeJenisDokIL',
|
||||||
|
'NamaJenisDokIL'
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class JenisKegiatan extends Model
|
||||||
|
{
|
||||||
|
use HasFactory; use SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'JenisKegiatan';
|
||||||
|
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
|
||||||
|
protected $primaryKey = 'JenisKegiatanId';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'NamaJenisKegiatan',
|
||||||
|
'IsPublish',
|
||||||
|
];
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||||
|
|
||||||
|
class JenisSanksi extends Model
|
||||||
|
{
|
||||||
|
use HasFactory; use SoftDeletes;
|
||||||
|
|
||||||
|
protected $table = 'JenisSanksi';
|
||||||
|
|
||||||
|
protected $primaryKey = 'JenisSanksiId';
|
||||||
|
|
||||||
|
protected $fillable = ['NamaJenisSanksi'];
|
||||||
|
|
||||||
|
protected $dates = ['deleted_at'];
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Kabupaten extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'Kabupaten';
|
||||||
|
protected $primaryKey = 'KabupatenId';
|
||||||
|
protected $fillable = [
|
||||||
|
'KabupatenId',
|
||||||
|
'NamaKabupaten',
|
||||||
|
];
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Kecamatan extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'Kecamatan';
|
||||||
|
protected $primaryKey = 'KecamatanId';
|
||||||
|
public $incrementing = false;
|
||||||
|
protected $keyType = 'string';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'KecamatanId',
|
||||||
|
'KabupatenId',
|
||||||
|
'NamaKecamatan'
|
||||||
|
];
|
||||||
|
|
||||||
|
public function kabupaten()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Kabupaten::class, 'KabupatenId', 'KabupatenId');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Kelurahan extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'Kelurahan';
|
||||||
|
protected $primaryKey = 'KelurahanId';
|
||||||
|
public $incrementing = false;
|
||||||
|
protected $keyType = 'string';
|
||||||
|
protected $fillable = ['KelurahanId', 'KecamatanId', 'NamaKelurahan'];
|
||||||
|
|
||||||
|
public function kecamatan()
|
||||||
|
{
|
||||||
|
return $this->belongsTo(Kecamatan::class, 'KecamatanId', 'KecamatanId');
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,84 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||||
|
|
||||||
|
class Perusahaan extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'Perusahaan';
|
||||||
|
protected $primaryKey = 'PerusahaanId';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'NomorInduk',
|
||||||
|
'JenisKegiatanId',
|
||||||
|
'NamaPerusahaan',
|
||||||
|
'Alamat',
|
||||||
|
'KelurahanId',
|
||||||
|
'KodePos',
|
||||||
|
'Telepon',
|
||||||
|
'Fax',
|
||||||
|
'Email',
|
||||||
|
'Lintang',
|
||||||
|
'Bujur',
|
||||||
|
'CPNama',
|
||||||
|
'CPTelepon',
|
||||||
|
'ILNomor',
|
||||||
|
'ILTanggal',
|
||||||
|
'JenisDokILId',
|
||||||
|
'VerifikatorId',
|
||||||
|
'ReportLocked',
|
||||||
|
'IsPublish',
|
||||||
|
'ILDokumen',
|
||||||
|
'ILPdlNomor',
|
||||||
|
'ILPdlJenis',
|
||||||
|
'DocPdlOrig',
|
||||||
|
'DocPdlHash',
|
||||||
|
'DocPdlPath',
|
||||||
|
'ILPdlTanggal',
|
||||||
|
'Kawasan'
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'ReportLocked' => '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');
|
||||||
|
}
|
||||||
|
}
|
|
@ -37,12 +37,12 @@ class Post extends Model
|
||||||
return \Carbon\Carbon::parse($this->created_at)->translatedFormat('d F Y');
|
return \Carbon\Carbon::parse($this->created_at)->translatedFormat('d F Y');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function boot()
|
// public static function boot()
|
||||||
{
|
// {
|
||||||
parent::boot();
|
// parent::boot();
|
||||||
|
|
||||||
static::creating(function ($post) {
|
// static::creating(function ($post) {
|
||||||
$post->Slug = Str::slug($post->JudulPost);
|
// $post->SlugPost = Str::slug($post->JudulPost);
|
||||||
});
|
// });
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
class Verifikator extends Model
|
||||||
|
{
|
||||||
|
use HasFactory;
|
||||||
|
|
||||||
|
protected $table = 'Verifikator';
|
||||||
|
|
||||||
|
protected $primaryKey = 'VerifikatorId';
|
||||||
|
|
||||||
|
protected $fillable = ['NamaUnitKerja', 'NamaKepala', 'NIP'];
|
||||||
|
}
|
|
@ -2,8 +2,9 @@
|
||||||
|
|
||||||
namespace App\Providers;
|
namespace App\Providers;
|
||||||
|
|
||||||
|
use App\Models\Post;
|
||||||
use Illuminate\Support\ServiceProvider;
|
use Illuminate\Support\ServiceProvider;
|
||||||
|
use Inertia\Inertia;
|
||||||
|
|
||||||
class AppServiceProvider extends ServiceProvider
|
class AppServiceProvider extends ServiceProvider
|
||||||
{
|
{
|
||||||
|
@ -20,6 +21,11 @@ class AppServiceProvider extends ServiceProvider
|
||||||
*/
|
*/
|
||||||
public function boot(): void
|
public function boot(): void
|
||||||
{
|
{
|
||||||
//
|
Inertia::share([
|
||||||
|
'runPosts' => fn () => Post::with(['kategori', 'subkategori'])
|
||||||
|
->where('IsPublish', true)
|
||||||
|
->orderBy('created_at', 'desc')
|
||||||
|
->get(),
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ return [
|
||||||
|
|
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'default' => env('FILESYSTEM_DISK', 'local'),
|
'default' => env('FILESYSTEM_DISK', 'public'),
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|--------------------------------------------------------------------------
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('historykegiatan', function (Blueprint $table) {
|
||||||
|
$table->id('HistoryKegiatanId');
|
||||||
|
$table->string('NamaHistoryKegiatan');
|
||||||
|
$table->boolean('IsPublish')->default(0);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('historykegiatan');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('jeniskegiatan', function (Blueprint $table) {
|
||||||
|
$table->id('JenisKegiatanId');
|
||||||
|
$table->string('NamaJenisKegiatan');
|
||||||
|
$table->boolean('IsPublish')->default(0);
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('jeniskegiatan');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('JenisKegiatan', function (Blueprint $table) {
|
||||||
|
$table->softDeletes()->after('IsPublish');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('JenisKegiatan', function (Blueprint $table) {
|
||||||
|
$table->dropSoftDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('HistoryKegiatan', function (Blueprint $table) {
|
||||||
|
$table->softDeletes()->after('IsPublish');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('HistoryKegiatan', function (Blueprint $table) {
|
||||||
|
$table->dropSoftDeletes();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::table('Kategori', function (Blueprint $table) {
|
||||||
|
$table->dropUnique(['NamaKategori']);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::table('Kategori', function (Blueprint $table) {
|
||||||
|
$table->unique('NamaKategori');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,32 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('PeriodePelaporan', function (Blueprint $table) {
|
||||||
|
$table->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) {
|
||||||
|
//
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,64 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('Perusahaan', function (Blueprint $table) {
|
||||||
|
$table->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');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('Kelurahan', function (Blueprint $table) {
|
||||||
|
$table->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');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('Kecamatan', function (Blueprint $table) {
|
||||||
|
$table->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');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('Kabupaten', function (Blueprint $table) {
|
||||||
|
$table->id('KabupatenId');
|
||||||
|
$table->string('NamaKabupaten');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('Kabupaten');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('JenisDokIL', function (Blueprint $table) {
|
||||||
|
$table->id('JenisDokILId');
|
||||||
|
$table->string('KodeJenisDokIL')->unique();
|
||||||
|
$table->string('NamaJenisDokIL');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('JenisDokIL');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,30 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('Verifikator', function (Blueprint $table) {
|
||||||
|
$table->id('VerifikatorId');
|
||||||
|
$table->string('NamaUnitKerja');
|
||||||
|
$table->string('NamaKepala');
|
||||||
|
$table->string('NIP');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('Verifikator');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,55 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('Pelaporan', function (Blueprint $table) {
|
||||||
|
$table->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');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('JenisSanksi', function (Blueprint $table) {
|
||||||
|
$table->id('JenisSanksiId');
|
||||||
|
$table->string('NamaJenisSanksi');
|
||||||
|
$table->softDeletes();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('JenisSanksi');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,42 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('Hukum', function (Blueprint $table) {
|
||||||
|
$table->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');
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use App\Models\Kabupaten;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class KabupatenTableSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
// Ambil data kabupaten dari API
|
||||||
|
$response = Http::get('https://api-wilayah.dinaslhdki.id/api/kabupaten/dkj/search');
|
||||||
|
|
||||||
|
// Loop data kabupaten yang diterima dari API
|
||||||
|
foreach ($response->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']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Kecamatan;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
use App\Models\Kabupaten;
|
||||||
|
|
||||||
|
class KecamatanTableSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$kabupatens = Kabupaten::all();
|
||||||
|
|
||||||
|
foreach ($kabupatens as $kabupaten) {
|
||||||
|
$kabId = substr($kabupaten->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']
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use App\Models\Kecamatan;
|
||||||
|
use App\Models\Kelurahan;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
use Illuminate\Support\Facades\Http;
|
||||||
|
use Illuminate\Support\Facades\Log;
|
||||||
|
|
||||||
|
class KelurahanTableSeeder extends Seeder
|
||||||
|
{
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$kecamatans = Kecamatan::all();
|
||||||
|
|
||||||
|
foreach ($kecamatans as $kecamatan) {
|
||||||
|
$kecId = substr($kecamatan->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
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,20 +14,25 @@
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-navigation-menu": "^1.2.4",
|
"@radix-ui/react-navigation-menu": "^1.2.4",
|
||||||
"@radix-ui/react-popover": "^1.0.7",
|
"@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-scroll-area": "^1.0.5",
|
||||||
"@radix-ui/react-select": "^2.1.6",
|
"@radix-ui/react-select": "^2.1.6",
|
||||||
"@radix-ui/react-separator": "^1.1.0",
|
"@radix-ui/react-separator": "^1.1.0",
|
||||||
"@radix-ui/react-slot": "^1.1.1",
|
"@radix-ui/react-slot": "^1.1.1",
|
||||||
"@radix-ui/react-switch": "^1.1.2",
|
"@radix-ui/react-switch": "^1.1.2",
|
||||||
|
"@radix-ui/react-tabs": "^1.1.3",
|
||||||
"@radix-ui/react-toast": "^1.2.6",
|
"@radix-ui/react-toast": "^1.2.6",
|
||||||
"@radix-ui/react-tooltip": "^1.1.3",
|
"@radix-ui/react-tooltip": "^1.1.3",
|
||||||
"@reduxjs/toolkit": "^2.5.1",
|
"@reduxjs/toolkit": "^2.5.1",
|
||||||
|
"@tanstack/react-table": "^8.21.2",
|
||||||
|
"@tinymce/tinymce-react": "^5.1.1",
|
||||||
"@types/react-redux": "^7.1.34",
|
"@types/react-redux": "^7.1.34",
|
||||||
"apexcharts": "^4.4.0",
|
"apexcharts": "^4.4.0",
|
||||||
"ckeditor4-react": "^4.1.2",
|
"ckeditor4-react": "^4.1.2",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
|
"framer-motion": "^12.4.3",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"i18next-browser-languagedetector": "^8.0.2",
|
"i18next-browser-languagedetector": "^8.0.2",
|
||||||
"i18next-http-backend": "^3.0.2",
|
"i18next-http-backend": "^3.0.2",
|
||||||
|
@ -43,10 +48,12 @@
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"react-resizable-panels": "^2.0.19",
|
"react-resizable-panels": "^2.0.19",
|
||||||
"react-router-dom": "^7.1.4",
|
"react-router-dom": "^7.1.4",
|
||||||
|
"react-select": "^5.10.0",
|
||||||
"react-type-animation": "^3.2.0",
|
"react-type-animation": "^3.2.0",
|
||||||
"recharts": "^2.15.1",
|
"recharts": "^2.15.1",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"tinymce": "^7.6.1",
|
||||||
"vaul": "^1.1.2",
|
"vaul": "^1.1.2",
|
||||||
"ziggy-js": "^2.5.0",
|
"ziggy-js": "^2.5.0",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
|
@ -101,7 +108,6 @@
|
||||||
"version": "7.26.2",
|
"version": "7.26.2",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
|
||||||
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
|
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-validator-identifier": "^7.25.9",
|
"@babel/helper-validator-identifier": "^7.25.9",
|
||||||
|
@ -157,7 +163,6 @@
|
||||||
"version": "7.26.5",
|
"version": "7.26.5",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz",
|
||||||
"integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==",
|
"integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/parser": "^7.26.5",
|
"@babel/parser": "^7.26.5",
|
||||||
|
@ -191,7 +196,6 @@
|
||||||
"version": "7.25.9",
|
"version": "7.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
|
||||||
"integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
|
"integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/traverse": "^7.25.9",
|
"@babel/traverse": "^7.25.9",
|
||||||
|
@ -233,7 +237,6 @@
|
||||||
"version": "7.25.9",
|
"version": "7.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
|
"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==",
|
"integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
|
@ -243,7 +246,6 @@
|
||||||
"version": "7.25.9",
|
"version": "7.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
|
||||||
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
|
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
|
@ -277,7 +279,6 @@
|
||||||
"version": "7.26.7",
|
"version": "7.26.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
|
||||||
"integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
|
"integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.26.7"
|
"@babel/types": "^7.26.7"
|
||||||
|
@ -337,7 +338,6 @@
|
||||||
"version": "7.25.9",
|
"version": "7.25.9",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
|
||||||
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
|
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.25.9",
|
"@babel/code-frame": "^7.25.9",
|
||||||
|
@ -352,7 +352,6 @@
|
||||||
"version": "7.26.7",
|
"version": "7.26.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz",
|
||||||
"integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==",
|
"integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.26.2",
|
"@babel/code-frame": "^7.26.2",
|
||||||
|
@ -371,7 +370,6 @@
|
||||||
"version": "7.26.7",
|
"version": "7.26.7",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz",
|
||||||
"integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==",
|
"integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.25.9",
|
"@babel/helper-string-parser": "^7.25.9",
|
||||||
|
@ -381,6 +379,126 @@
|
||||||
"node": ">=6.9.0"
|
"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": {
|
"node_modules/@esbuild/aix-ppc64": {
|
||||||
"version": "0.21.5",
|
"version": "0.21.5",
|
||||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
"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": {
|
"node_modules/@radix-ui/react-roving-focus": {
|
||||||
"version": "1.1.1",
|
"version": "1.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz",
|
"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": {
|
"node_modules/@radix-ui/react-toast": {
|
||||||
"version": "1.2.6",
|
"version": "1.2.6",
|
||||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.6.tgz",
|
"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"
|
"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": {
|
"node_modules/@tanstack/react-virtual": {
|
||||||
"version": "3.11.3",
|
"version": "3.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.11.3.tgz",
|
"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"
|
"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": {
|
"node_modules/@tanstack/virtual-core": {
|
||||||
"version": "3.11.3",
|
"version": "3.11.3",
|
||||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.3.tgz",
|
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.3.tgz",
|
||||||
|
@ -2868,6 +3212,20 @@
|
||||||
"url": "https://github.com/sponsors/tannerlinsley"
|
"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": {
|
"node_modules/@types/babel__core": {
|
||||||
"version": "7.20.5",
|
"version": "7.20.5",
|
||||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||||
|
@ -3016,6 +3374,12 @@
|
||||||
"undici-types": "~5.26.4"
|
"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": {
|
"node_modules/@types/prop-types": {
|
||||||
"version": "15.7.14",
|
"version": "15.7.14",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
|
||||||
|
@ -3078,6 +3442,15 @@
|
||||||
"@babel/runtime": "^7.9.2"
|
"@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": {
|
"node_modules/@types/use-sync-external-store": {
|
||||||
"version": "0.0.6",
|
"version": "0.0.6",
|
||||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
"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"
|
"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": {
|
"node_modules/balanced-match": {
|
||||||
"version": "1.0.2",
|
"version": "1.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||||
|
@ -3361,6 +3749,15 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/camelcase-css": {
|
||||||
"version": "2.0.1",
|
"version": "2.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||||
|
@ -3547,6 +3944,31 @@
|
||||||
"node": ">=18"
|
"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": {
|
"node_modules/cross-fetch": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||||
|
@ -3723,7 +4145,6 @@
|
||||||
"version": "4.4.0",
|
"version": "4.4.0",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"ms": "^2.1.3"
|
"ms": "^2.1.3"
|
||||||
|
@ -3878,6 +4299,15 @@
|
||||||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/es-define-property": {
|
||||||
"version": "1.0.1",
|
"version": "1.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||||
|
@ -3957,6 +4387,18 @@
|
||||||
"node": ">=6"
|
"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": {
|
"node_modules/eventemitter3": {
|
||||||
"version": "4.0.7",
|
"version": "4.0.7",
|
||||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||||
|
@ -4033,6 +4475,12 @@
|
||||||
"node": ">=8"
|
"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": {
|
"node_modules/follow-redirects": {
|
||||||
"version": "1.15.9",
|
"version": "1.15.9",
|
||||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||||
|
@ -4099,6 +4547,33 @@
|
||||||
"url": "https://github.com/sponsors/rawify"
|
"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": {
|
"node_modules/fsevents": {
|
||||||
"version": "2.3.3",
|
"version": "2.3.3",
|
||||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||||
|
@ -4223,7 +4698,6 @@
|
||||||
"version": "11.12.0",
|
"version": "11.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=4"
|
"node": ">=4"
|
||||||
|
@ -4379,6 +4853,22 @@
|
||||||
"url": "https://opencollective.com/immer"
|
"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": {
|
"node_modules/internmap": {
|
||||||
"version": "2.0.3",
|
"version": "2.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||||
|
@ -4404,6 +4894,12 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/is-binary-path": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||||
|
@ -4544,7 +5040,6 @@
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
||||||
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
"jsesc": "bin/jsesc"
|
"jsesc": "bin/jsesc"
|
||||||
|
@ -4553,6 +5048,12 @@
|
||||||
"node": ">=6"
|
"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": {
|
"node_modules/json5": {
|
||||||
"version": "2.2.3",
|
"version": "2.2.3",
|
||||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||||
|
@ -4658,6 +5159,12 @@
|
||||||
"node": ">= 0.4"
|
"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": {
|
"node_modules/merge2": {
|
||||||
"version": "1.4.1",
|
"version": "1.4.1",
|
||||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||||
|
@ -4737,11 +5244,25 @@
|
||||||
"node": ">=16 || 14 >=14.17"
|
"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": {
|
"node_modules/ms": {
|
||||||
"version": "2.1.3",
|
"version": "2.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||||
"dev": true,
|
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
"node_modules/mz": {
|
"node_modules/mz": {
|
||||||
|
@ -4904,6 +5425,36 @@
|
||||||
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==",
|
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==",
|
||||||
"license": "BSD-3-Clause"
|
"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": {
|
"node_modules/path-key": {
|
||||||
"version": "3.1.1",
|
"version": "3.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||||
|
@ -4941,6 +5492,15 @@
|
||||||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||||
"license": "ISC"
|
"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": {
|
"node_modules/perfect-scrollbar": {
|
||||||
"version": "1.5.6",
|
"version": "1.5.6",
|
||||||
"resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.6.tgz",
|
"resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.6.tgz",
|
||||||
|
@ -5487,6 +6047,27 @@
|
||||||
"react-dom": ">=18"
|
"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": {
|
"node_modules/react-smooth": {
|
||||||
"version": "4.0.4",
|
"version": "4.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
|
||||||
|
@ -5677,6 +6258,15 @@
|
||||||
"url": "https://github.com/sponsors/ljharb"
|
"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": {
|
"node_modules/reusify": {
|
||||||
"version": "1.0.4",
|
"version": "1.0.4",
|
||||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||||
|
@ -5915,6 +6505,15 @@
|
||||||
"url": "https://github.com/sponsors/isaacs"
|
"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": {
|
"node_modules/source-map-js": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||||
|
@ -6020,6 +6619,12 @@
|
||||||
"node": ">=8"
|
"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": {
|
"node_modules/sucrase": {
|
||||||
"version": "3.35.0",
|
"version": "3.35.0",
|
||||||
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
|
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
|
||||||
|
@ -6137,6 +6742,12 @@
|
||||||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||||
"license": "MIT"
|
"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": {
|
"node_modules/to-regex-range": {
|
||||||
"version": "5.0.1",
|
"version": "5.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
"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": {
|
"node_modules/use-sidecar": {
|
||||||
"version": "1.1.3",
|
"version": "1.1.3",
|
||||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
||||||
|
|
|
@ -34,20 +34,25 @@
|
||||||
"@radix-ui/react-label": "^2.0.2",
|
"@radix-ui/react-label": "^2.0.2",
|
||||||
"@radix-ui/react-navigation-menu": "^1.2.4",
|
"@radix-ui/react-navigation-menu": "^1.2.4",
|
||||||
"@radix-ui/react-popover": "^1.0.7",
|
"@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-scroll-area": "^1.0.5",
|
||||||
"@radix-ui/react-select": "^2.1.6",
|
"@radix-ui/react-select": "^2.1.6",
|
||||||
"@radix-ui/react-separator": "^1.1.0",
|
"@radix-ui/react-separator": "^1.1.0",
|
||||||
"@radix-ui/react-slot": "^1.1.1",
|
"@radix-ui/react-slot": "^1.1.1",
|
||||||
"@radix-ui/react-switch": "^1.1.2",
|
"@radix-ui/react-switch": "^1.1.2",
|
||||||
|
"@radix-ui/react-tabs": "^1.1.3",
|
||||||
"@radix-ui/react-toast": "^1.2.6",
|
"@radix-ui/react-toast": "^1.2.6",
|
||||||
"@radix-ui/react-tooltip": "^1.1.3",
|
"@radix-ui/react-tooltip": "^1.1.3",
|
||||||
"@reduxjs/toolkit": "^2.5.1",
|
"@reduxjs/toolkit": "^2.5.1",
|
||||||
|
"@tanstack/react-table": "^8.21.2",
|
||||||
|
"@tinymce/tinymce-react": "^5.1.1",
|
||||||
"@types/react-redux": "^7.1.34",
|
"@types/react-redux": "^7.1.34",
|
||||||
"apexcharts": "^4.4.0",
|
"apexcharts": "^4.4.0",
|
||||||
"ckeditor4-react": "^4.1.2",
|
"ckeditor4-react": "^4.1.2",
|
||||||
"class-variance-authority": "^0.7.1",
|
"class-variance-authority": "^0.7.1",
|
||||||
"clsx": "^2.1.1",
|
"clsx": "^2.1.1",
|
||||||
"date-fns": "^3.6.0",
|
"date-fns": "^3.6.0",
|
||||||
|
"framer-motion": "^12.4.3",
|
||||||
"highlight.js": "^11.11.1",
|
"highlight.js": "^11.11.1",
|
||||||
"i18next-browser-languagedetector": "^8.0.2",
|
"i18next-browser-languagedetector": "^8.0.2",
|
||||||
"i18next-http-backend": "^3.0.2",
|
"i18next-http-backend": "^3.0.2",
|
||||||
|
@ -63,10 +68,12 @@
|
||||||
"react-redux": "^9.2.0",
|
"react-redux": "^9.2.0",
|
||||||
"react-resizable-panels": "^2.0.19",
|
"react-resizable-panels": "^2.0.19",
|
||||||
"react-router-dom": "^7.1.4",
|
"react-router-dom": "^7.1.4",
|
||||||
|
"react-select": "^5.10.0",
|
||||||
"react-type-animation": "^3.2.0",
|
"react-type-animation": "^3.2.0",
|
||||||
"recharts": "^2.15.1",
|
"recharts": "^2.15.1",
|
||||||
"tailwind-merge": "^2.6.0",
|
"tailwind-merge": "^2.6.0",
|
||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
|
"tinymce": "^7.6.1",
|
||||||
"vaul": "^1.1.2",
|
"vaul": "^1.1.2",
|
||||||
"ziggy-js": "^2.5.0",
|
"ziggy-js": "^2.5.0",
|
||||||
"zod": "^3.23.8"
|
"zod": "^3.23.8"
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 315 KiB |
Binary file not shown.
Before Width: | Height: | Size: 340 KiB After Width: | Height: | Size: 130 KiB |
|
@ -2,72 +2,10 @@ import React from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { ArrowRight } from "lucide-react";
|
import { ArrowRight } from "lucide-react";
|
||||||
import { Link } from "@inertiajs/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 {
|
interface SubKategori {
|
||||||
SubKategoriId: number;
|
SubKategoriId: number;
|
||||||
NamaSubKategori: string;
|
NamaSubKategori: string;
|
||||||
|
@ -95,24 +33,39 @@ interface CardPengumumanProps {
|
||||||
}
|
}
|
||||||
|
|
||||||
const CardPengumuman = ({ posts }: CardPengumumanProps) => {
|
const CardPengumuman = ({ posts }: CardPengumumanProps) => {
|
||||||
return (
|
const subcategories = Array.from(
|
||||||
<section className="container max-w-7xl py-8 px-6">
|
new Set(posts.map((item) => item.subkategori?.NamaSubKategori))
|
||||||
{/* List of Announcements */}
|
).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[] }) => (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
{posts.map((post) => (
|
{items.slice(0, visibleItems).map((item) => (
|
||||||
<Card key={post.PostId} className="md:p-4">
|
<Card key={item.PostId} className="md:p-4">
|
||||||
<img
|
<img
|
||||||
src={`/storage/${post.ImagePost}`}
|
src={`/storage/${item.ImagePost}`}
|
||||||
alt={post.JudulPost}
|
alt={item.JudulPost}
|
||||||
className="rounded-md"
|
className="rounded-md"
|
||||||
/>
|
/>
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
<Badge className="bg-red-600 text-white">
|
<Badge className="bg-red-600 text-white">
|
||||||
{post.kategori?.NamaKategori} |{" "}
|
{item.subkategori?.NamaSubKategori}
|
||||||
{post.subkategori?.NamaSubKategori}
|
|
||||||
</Badge>
|
</Badge>
|
||||||
<p className="text-gray-500 text-sm mt-2">
|
<p className="text-gray-500 text-sm mt-2">
|
||||||
{new Date(post.created_at).toLocaleDateString(
|
{new Date(item.created_at).toLocaleDateString(
|
||||||
"id-ID",
|
"id-ID",
|
||||||
{
|
{
|
||||||
day: "numeric",
|
day: "numeric",
|
||||||
|
@ -122,12 +75,16 @@ const CardPengumuman = ({ posts }: CardPengumumanProps) => {
|
||||||
)}
|
)}
|
||||||
</p>
|
</p>
|
||||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||||
{post.JudulPost}
|
{item.JudulPost}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600 mt-2">
|
<p className="text-sm text-gray-600 mt-2">
|
||||||
{post.DescPost.replace(/<[^>]*>/g, "")}
|
{item.DescPost.replace(/<[^>]*>/g, "").slice(
|
||||||
|
0,
|
||||||
|
160
|
||||||
|
)}
|
||||||
|
...
|
||||||
</p>
|
</p>
|
||||||
<Link href={`/post/${post.SlugPost}`}>
|
<Link href={route("pengumuman.show", item.SlugPost)}>
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
className="text-red-600 mt-2 pl-0"
|
className="text-red-600 mt-2 pl-0"
|
||||||
|
@ -140,11 +97,69 @@ const CardPengumuman = ({ posts }: CardPengumumanProps) => {
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleTabChange = (value: string) => {
|
||||||
|
setActiveTab(value);
|
||||||
|
setVisibleItems(3); // Reset visible items when changing tabs
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<section className="container max-w-7xl py-8 px-6">
|
||||||
|
{/* List of Announcements */}
|
||||||
|
<div className="flex justify-center items-center w-full">
|
||||||
|
<Tabs
|
||||||
|
defaultValue="all"
|
||||||
|
className="w-full max-w-6xl"
|
||||||
|
onValueChange={handleTabChange}
|
||||||
|
>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<TabsList className="mb-6">
|
||||||
|
<TabsTrigger value="all">Semua</TabsTrigger>
|
||||||
|
{subcategories.map((subcat) => (
|
||||||
|
<TabsTrigger
|
||||||
|
key={subcat}
|
||||||
|
value={subcat as string}
|
||||||
|
>
|
||||||
|
{subcat}
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TabsContent value="all">
|
||||||
|
<PostGrid items={filterPosts(null)} />
|
||||||
|
{visibleItems < filterPosts(null).length && (
|
||||||
<div className="flex justify-center mt-8">
|
<div className="flex justify-center mt-8">
|
||||||
<Button variant="link" className="text-green-700">
|
<Button
|
||||||
|
variant="link"
|
||||||
|
className="text-green-700"
|
||||||
|
onClick={loadMore}
|
||||||
|
>
|
||||||
Lihat Lebih Banyak
|
Lihat Lebih Banyak
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
{subcategories.map((subcat) => (
|
||||||
|
<TabsContent key={subcat} value={subcat as string}>
|
||||||
|
<PostGrid items={filterPosts(subcat as string)} />
|
||||||
|
{visibleItems <
|
||||||
|
filterPosts(subcat as string).length && (
|
||||||
|
<div className="flex justify-center mt-8">
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
className="text-green-700"
|
||||||
|
onClick={loadMore}
|
||||||
|
>
|
||||||
|
Lihat Lebih Banyak
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,96 +2,89 @@ import React from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { ArrowRight } from "lucide-react";
|
import { ArrowRight } from "lucide-react";
|
||||||
|
import { Link } from "@inertiajs/react";
|
||||||
|
|
||||||
const peraturans = [
|
interface SubKategori {
|
||||||
{
|
SubKategoriId: number;
|
||||||
id: 1,
|
NamaSubKategori: string;
|
||||||
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",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const CardPeraturan = () => {
|
interface Kategori {
|
||||||
return (
|
KategoriId: number;
|
||||||
<section className="container max-w-7xl py-8 px-6">
|
NamaKategori: string;
|
||||||
{/* List of Announcements */}
|
}
|
||||||
|
|
||||||
|
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[] }) => (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
{peraturans.map((item) => (
|
{items.slice(0, visibleItems).map((item) => (
|
||||||
<Card key={item.id} className="md:p-4">
|
<Card key={item.PostId} className="md:p-4">
|
||||||
<img
|
<img
|
||||||
src={item.image}
|
src={`/storage/${item.ImagePost}`}
|
||||||
alt={item.alt_image}
|
alt={item.JudulPost}
|
||||||
className="rounded-md"
|
className="rounded-md"
|
||||||
/>
|
/>
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
<Badge className="bg-red-600 text-white">
|
<Badge className="bg-red-600 text-white">
|
||||||
{item.title_page}
|
{item.subkategori?.NamaSubKategori}
|
||||||
</Badge>
|
</Badge>
|
||||||
<p className="text-gray-500 text-sm mt-2">
|
<p className="text-gray-500 text-sm mt-2">
|
||||||
{item.date}
|
{new Date(item.created_at).toLocaleDateString(
|
||||||
|
"id-ID",
|
||||||
|
{
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
}
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||||
{item.title}
|
{item.JudulPost}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600 mt-2">
|
<p className="text-sm text-gray-600 mt-2">
|
||||||
{item.description}
|
{item.DescPost.replace(/<[^>]*>/g, "").slice(
|
||||||
|
0,
|
||||||
|
160
|
||||||
|
)}
|
||||||
|
...
|
||||||
</p>
|
</p>
|
||||||
|
<Link href={route("peraturan.show", item.SlugPost)}>
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
className="text-red-600 mt-2 pl-0"
|
className="text-red-600 mt-2 pl-0"
|
||||||
|
@ -99,15 +92,74 @@ const CardPeraturan = () => {
|
||||||
Baca Selengkapnya{" "}
|
Baca Selengkapnya{" "}
|
||||||
<ArrowRight className="ml-2 w-4 h-4" />
|
<ArrowRight className="ml-2 w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleTabChange = (value: string) => {
|
||||||
|
setActiveTab(value);
|
||||||
|
setVisibleItems(3); // Reset visible items when changing tabs
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<section className="container max-w-7xl py-8 px-6">
|
||||||
|
{/* List of Peraturan */}
|
||||||
|
<div className="flex justify-center items-center w-full">
|
||||||
|
<Tabs
|
||||||
|
defaultValue="all"
|
||||||
|
className="w-full max-w-6xl"
|
||||||
|
onValueChange={handleTabChange}
|
||||||
|
>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<TabsList className="mb-6">
|
||||||
|
<TabsTrigger value="all">Semua</TabsTrigger>
|
||||||
|
{subcategories.map((subcat) => (
|
||||||
|
<TabsTrigger
|
||||||
|
key={subcat}
|
||||||
|
value={subcat as string}
|
||||||
|
>
|
||||||
|
{subcat}
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TabsContent value="all">
|
||||||
|
<PostGrid items={filterPosts(null)} />
|
||||||
|
{visibleItems < filterPosts(null).length && (
|
||||||
<div className="flex justify-center mt-8">
|
<div className="flex justify-center mt-8">
|
||||||
<Button variant="link" className="text-green-700">
|
<Button
|
||||||
|
variant="link"
|
||||||
|
className="text-green-700"
|
||||||
|
onClick={loadMore}
|
||||||
|
>
|
||||||
Lihat Lebih Banyak
|
Lihat Lebih Banyak
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
{subcategories.map((subcat) => (
|
||||||
|
<TabsContent key={subcat} value={subcat as string}>
|
||||||
|
<PostGrid items={filterPosts(subcat as string)} />
|
||||||
|
{visibleItems <
|
||||||
|
filterPosts(subcat as string).length && (
|
||||||
|
<div className="flex justify-center mt-8">
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
className="text-green-700"
|
||||||
|
onClick={loadMore}
|
||||||
|
>
|
||||||
|
Lihat Lebih Banyak
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -2,96 +2,89 @@ import React from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { ArrowRight } from "lucide-react";
|
import { ArrowRight } from "lucide-react";
|
||||||
|
import { Link } from "@inertiajs/react";
|
||||||
|
|
||||||
const undangans = [
|
interface SubKategori {
|
||||||
{
|
SubKategoriId: number;
|
||||||
id: 1,
|
NamaSubKategori: string;
|
||||||
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",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const CardUndangan = () => {
|
interface Kategori {
|
||||||
return (
|
KategoriId: number;
|
||||||
<section className="container max-w-7xl py-8 px-6">
|
NamaKategori: string;
|
||||||
{/* List of Announcements */}
|
}
|
||||||
|
|
||||||
|
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[] }) => (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
{undangans.map((item) => (
|
{items.slice(0, visibleItems).map((item) => (
|
||||||
<Card key={item.id} className="md:p-4">
|
<Card key={item.PostId} className="md:p-4">
|
||||||
<img
|
<img
|
||||||
src={item.image}
|
src={`/storage/${item.ImagePost}`}
|
||||||
alt={item.alt_image}
|
alt={item.JudulPost}
|
||||||
className="rounded-md"
|
className="rounded-md"
|
||||||
/>
|
/>
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
<Badge className="bg-red-600 text-white">
|
<Badge className="bg-red-600 text-white">
|
||||||
{item.title_page}
|
{item.subkategori?.NamaSubKategori}
|
||||||
</Badge>
|
</Badge>
|
||||||
<p className="text-gray-500 text-sm mt-2">
|
<p className="text-gray-500 text-sm mt-2">
|
||||||
{item.date}
|
{new Date(item.created_at).toLocaleDateString(
|
||||||
|
"id-ID",
|
||||||
|
{
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
}
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||||
{item.title}
|
{item.JudulPost}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600 mt-2">
|
<p className="text-sm text-gray-600 mt-2">
|
||||||
{item.description}
|
{item.DescPost.replace(/<[^>]*>/g, "").slice(
|
||||||
|
0,
|
||||||
|
160
|
||||||
|
)}
|
||||||
|
...
|
||||||
</p>
|
</p>
|
||||||
|
<Link href={route("undangan.show", item.SlugPost)}>
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
className="text-red-600 mt-2 pl-0"
|
className="text-red-600 mt-2 pl-0"
|
||||||
|
@ -99,15 +92,74 @@ const CardUndangan = () => {
|
||||||
Baca Selengkapnya{" "}
|
Baca Selengkapnya{" "}
|
||||||
<ArrowRight className="ml-2 w-4 h-4" />
|
<ArrowRight className="ml-2 w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
const handleTabChange = (value: string) => {
|
||||||
|
setActiveTab(value);
|
||||||
|
setVisibleItems(3); // Reset visible items when changing tabs
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<section className="container max-w-7xl py-8 px-6">
|
||||||
|
<div className="flex justify-center items-center w-full">
|
||||||
|
<Tabs
|
||||||
|
defaultValue="all"
|
||||||
|
className="w-full max-w-6xl"
|
||||||
|
onValueChange={handleTabChange}
|
||||||
|
>
|
||||||
|
<div className="flex justify-center">
|
||||||
|
<TabsList className="mb-6">
|
||||||
|
<TabsTrigger value="all">Semua</TabsTrigger>
|
||||||
|
{subcategories.map((subcat) => (
|
||||||
|
<TabsTrigger
|
||||||
|
key={subcat}
|
||||||
|
value={subcat as string}
|
||||||
|
>
|
||||||
|
{subcat}
|
||||||
|
</TabsTrigger>
|
||||||
|
))}
|
||||||
|
</TabsList>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<TabsContent value="all">
|
||||||
|
<PostGrid items={filterPosts(null)} />
|
||||||
|
{visibleItems < filterPosts(null).length && (
|
||||||
<div className="flex justify-center mt-8">
|
<div className="flex justify-center mt-8">
|
||||||
<Button variant="link" className="text-green-700">
|
<Button
|
||||||
|
variant="link"
|
||||||
|
className="text-green-700"
|
||||||
|
onClick={loadMore}
|
||||||
|
>
|
||||||
Lihat Lebih Banyak
|
Lihat Lebih Banyak
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
|
||||||
|
{subcategories.map((subcat) => (
|
||||||
|
<TabsContent key={subcat} value={subcat as string}>
|
||||||
|
<PostGrid items={filterPosts(subcat as string)} />
|
||||||
|
{visibleItems <
|
||||||
|
filterPosts(subcat as string).length && (
|
||||||
|
<div className="flex justify-center mt-8">
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
className="text-green-700"
|
||||||
|
onClick={loadMore}
|
||||||
|
>
|
||||||
|
Lihat Lebih Banyak
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</TabsContent>
|
||||||
|
))}
|
||||||
|
</Tabs>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,13 +17,22 @@ export default function HeroSecond({ title }: HeroSecondProps) {
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* Overlay Content */}
|
{/* Overlay Content */}
|
||||||
<div className="absolute inset-0 flex flex-col items-center justify-center">
|
<div className="absolute inset-0 flex flex-col items-center justify-center md:flex-row md:items-center md:justify-between md:p-8">
|
||||||
<h1 className="text-2xl md:text-5xl font-bold text-gray-800 mb-4 text-center md:text-right absolute md:right-24 top-1/2 -translate-y-1/2">
|
<div className="hidden md:flex md:w-1/3 md:justify-center">
|
||||||
|
<img
|
||||||
|
src="/assets/dlh-logo.svg"
|
||||||
|
alt=""
|
||||||
|
className="md:max-w-[170px] ml-28"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="md:w-1/2 md:flex md:justify-center">
|
||||||
|
<h1 className="text-2xl md:text-5xl font-bold text-[#43b311] text-center">
|
||||||
{title}
|
{title}
|
||||||
</h1>
|
</h1>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 (
|
||||||
|
<div className="container flex flex-col md:flex-row gap-8 p-4 max-w-6xl mx-auto">
|
||||||
|
<div className="w-full md:w-7/12">
|
||||||
|
<div className="mb-4 flex flex-row gap-5 items-center">
|
||||||
|
<span className="bg-red-500 text-white px-2 py-1 text-sm rounded">
|
||||||
|
{article.kategori?.NamaKategori}
|
||||||
|
</span>
|
||||||
|
<div className="text-gray-500 text-sm ">
|
||||||
|
{new Date(article.created_at).toLocaleDateString(
|
||||||
|
"id-ID",
|
||||||
|
{
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
}
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<h1 className="text-2xl font-bold mb-6">{article.JudulPost}</h1>
|
||||||
|
<img
|
||||||
|
src={`/storage/${article.ImagePost}`}
|
||||||
|
alt={article.JudulPost}
|
||||||
|
className="w-full h-auto rounded-lg mb-6"
|
||||||
|
/>
|
||||||
|
<div
|
||||||
|
className="text-justify prose prose-lg max-w-none"
|
||||||
|
dangerouslySetInnerHTML={{ __html: article.DescPost }}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="w-full md:w-5/12">
|
||||||
|
<h1 className="text-xl font-bold mb-6">Informasi Lainnya</h1>
|
||||||
|
<div className="flex flex-col gap-6">
|
||||||
|
{relatedArticles.slice(0, 5).map((related) => (
|
||||||
|
<Link
|
||||||
|
key={related.PostId}
|
||||||
|
href={getRouteByCategory(
|
||||||
|
related.kategori?.NamaKategori || "",
|
||||||
|
related.SlugPost
|
||||||
|
)}
|
||||||
|
className="group"
|
||||||
|
>
|
||||||
|
<div className="flex flex-row gap-4">
|
||||||
|
<img
|
||||||
|
src={`/storage/${related.ImagePost}`}
|
||||||
|
alt={related.JudulPost}
|
||||||
|
className="w-24 h-24 object-cover rounded-lg"
|
||||||
|
/>
|
||||||
|
<div className="flex flex-col">
|
||||||
|
<span className="bg-red-500 text-white px-2 py-1 text-sm rounded w-fit">
|
||||||
|
{related.kategori?.NamaKategori}
|
||||||
|
</span>
|
||||||
|
<p className="font-medium group-hover:text-red-600 transition-colors mt-2">
|
||||||
|
{related.JudulPost}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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[];
|
||||||
|
}
|
|
@ -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 (
|
||||||
|
<div className="container mx-auto px-4 py-8">
|
||||||
|
<div className="mb-8">
|
||||||
|
<h1 className="text-2xl font-bold mb-2">Hasil Pencarian</h1>
|
||||||
|
<p className="text-gray-600">
|
||||||
|
Menampilkan hasil untuk "{searchQuery}" (
|
||||||
|
{searchResults.length} hasil)
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
|
{searchResults.map((result) => (
|
||||||
|
<Link
|
||||||
|
key={result.PostId}
|
||||||
|
href={getRouteByCategory(
|
||||||
|
result.kategori?.NamaKategori || "",
|
||||||
|
result.SlugPost
|
||||||
|
)}
|
||||||
|
className="group"
|
||||||
|
>
|
||||||
|
<div className="border rounded-lg overflow-hidden shadow-sm hover:shadow-md transition-shadow h-full">
|
||||||
|
<img
|
||||||
|
src={`/storage/${result.ImagePost}`}
|
||||||
|
alt={result.JudulPost}
|
||||||
|
className="w-full h-48 object-cover"
|
||||||
|
/>
|
||||||
|
<div className="p-4">
|
||||||
|
<div className="flex items-center gap-2 mb-2">
|
||||||
|
<span className="bg-red-500 text-white px-2 py-1 text-xs rounded">
|
||||||
|
{result.kategori?.NamaKategori}
|
||||||
|
</span>
|
||||||
|
<span className="text-sm text-gray-500">
|
||||||
|
{new Date(
|
||||||
|
result.created_at
|
||||||
|
).toLocaleDateString("id-ID", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
})}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<h2 className="font-semibold mb-2 group-hover:text-red-600 transition-colors">
|
||||||
|
{result.JudulPost}
|
||||||
|
</h2>
|
||||||
|
<p className="text-sm text-gray-600 line-clamp-2">
|
||||||
|
{result.DescPost.replace(/<[^>]*>/g, "")}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Link>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{searchResults.length === 0 && (
|
||||||
|
<div className="text-center py-12">
|
||||||
|
<p className="text-gray-500">
|
||||||
|
Tidak ada hasil yang ditemukan
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -22,7 +22,7 @@ const HeroSection = () => {
|
||||||
<h1 className="text-4xl md:text-5xl font-bold text-gray-800 mb-4 text-center">
|
<h1 className="text-4xl md:text-5xl font-bold text-gray-800 mb-4 text-center">
|
||||||
<TypeAnimation
|
<TypeAnimation
|
||||||
sequence={[
|
sequence={[
|
||||||
"Status Ketaatan Lingkungan",
|
"Sistem Ketaatan Lingkungan",
|
||||||
1000,
|
1000,
|
||||||
"",
|
"",
|
||||||
500,
|
500,
|
||||||
|
|
|
@ -6,7 +6,7 @@ import {
|
||||||
NavigationMenuItem,
|
NavigationMenuItem,
|
||||||
} from "@/components/ui/navigation-menu";
|
} from "@/components/ui/navigation-menu";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Link, router } from "@inertiajs/react";
|
import { Link, router, usePage } from "@inertiajs/react";
|
||||||
import {
|
import {
|
||||||
Drawer,
|
Drawer,
|
||||||
DrawerClose,
|
DrawerClose,
|
||||||
|
@ -18,13 +18,22 @@ import {
|
||||||
import RunningText from "./RunningText";
|
import RunningText from "./RunningText";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import SearchDialog from "./SearchDialog";
|
import SearchDialog from "./SearchDialog";
|
||||||
|
import { Post } from "../DetailArtikel/types";
|
||||||
|
import { PageProps as InertiaPageProps } from "@inertiajs/core";
|
||||||
|
|
||||||
interface NavItemsProps {
|
interface NavItemsProps {
|
||||||
mobile?: boolean;
|
mobile?: boolean;
|
||||||
onClose?: () => void;
|
onClose?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface CustomPageProps extends InertiaPageProps {
|
||||||
|
runPosts?: Post[];
|
||||||
|
errors: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
|
const { runPosts = [] } = usePage<CustomPageProps>().props;
|
||||||
|
// const runPosts = props.runPosts || [];
|
||||||
const [isScrolled, setIsScrolled] = useState(false);
|
const [isScrolled, setIsScrolled] = useState(false);
|
||||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||||
// const [searchQuery, setSearchQuery] = useState("");
|
// const [searchQuery, setSearchQuery] = useState("");
|
||||||
|
@ -103,7 +112,7 @@ const Navbar = () => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="w-full">
|
<div className="w-full">
|
||||||
<RunningText />
|
<RunningText posts={runPosts} />
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={`w-full rounded-br-2xl rounded-bl-2xl text-white transition-all duration-300 ${
|
className={`w-full rounded-br-2xl rounded-bl-2xl text-white transition-all duration-300 ${
|
||||||
|
@ -184,7 +193,7 @@ const Navbar = () => {
|
||||||
</Drawer>
|
</Drawer>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Button
|
{/* <Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
onClick={toggleTheme}
|
onClick={toggleTheme}
|
||||||
className="p-3 bg-[#2d7448] hover:bg-[#16a34a] rounded-full text-white "
|
className="p-3 bg-[#2d7448] hover:bg-[#16a34a] rounded-full text-white "
|
||||||
|
@ -194,7 +203,7 @@ const Navbar = () => {
|
||||||
) : (
|
) : (
|
||||||
<Sun className="h-6 w-6" />
|
<Sun className="h-6 w-6" />
|
||||||
)}
|
)}
|
||||||
</Button>
|
</Button> */}
|
||||||
|
|
||||||
<div className="hidden md:block">
|
<div className="hidden md:block">
|
||||||
<Link
|
<Link
|
||||||
|
|
|
@ -3,73 +3,84 @@ import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { ArrowRight } from "lucide-react";
|
import { ArrowRight } from "lucide-react";
|
||||||
|
import { Link } from "@inertiajs/react";
|
||||||
|
|
||||||
const announcements = [
|
interface SubKategori {
|
||||||
{
|
SubKategoriId: number;
|
||||||
id: 1,
|
NamaSubKategori: string;
|
||||||
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",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
const AnnouncementSection = () => {
|
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 (
|
return (
|
||||||
<section className="container max-w-7xl py-8 px-6">
|
<section className="container max-w-7xl py-8 px-6">
|
||||||
|
<Badge className="bg-black text-white hover:bg-green-600 cursor-pointer">
|
||||||
|
Informasi
|
||||||
|
</Badge>
|
||||||
<div className="flex items-center justify-between mb-6">
|
<div className="flex items-center justify-between mb-6">
|
||||||
<h2 className="text-2xl font-bold text-green-800">
|
<h2 className="text-2xl font-bold text-green-800">
|
||||||
Pengumuman
|
Pengumuman
|
||||||
</h2>
|
</h2>
|
||||||
|
<Link href="/pengumuman">
|
||||||
<Button variant="link" className="text-green-700">
|
<Button variant="link" className="text-green-700">
|
||||||
Selengkapnya
|
Selengkapnya
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Highlight Announcement */}
|
{/* Highlight Announcement */}
|
||||||
|
{posts.length > 1 && (
|
||||||
<div className="grid grid-cols-1 gap-6 mb-8">
|
<div className="grid grid-cols-1 gap-6 mb-8">
|
||||||
<Card className="p-4 flex flex-col md:flex-row items-start">
|
<Card className="p-4 flex flex-col md:flex-row items-start">
|
||||||
<img
|
<img
|
||||||
src={announcements[0].image}
|
src={`/storage/${posts[0].ImagePost}`}
|
||||||
alt={announcements[0].alt_image}
|
alt={posts[0].JudulPost}
|
||||||
className="rounded-md w-full md:w-1/2"
|
className="rounded-md w-full md:w-1/2"
|
||||||
/>
|
/>
|
||||||
<CardContent className="p-4 w-full md:w-1/2">
|
<CardContent className="p-4 w-full md:w-1/2">
|
||||||
<Badge className="bg-red-600 text-white">
|
<Badge className="bg-red-600 text-white">
|
||||||
{announcements[0].title_page}
|
{posts[0].kategori?.NamaKategori}
|
||||||
</Badge>
|
</Badge>
|
||||||
<p className="text-gray-500 text-sm mt-2">
|
<p className="text-gray-500 text-sm mt-2">
|
||||||
{announcements[0].date}
|
{new Date(
|
||||||
|
posts[0].created_at
|
||||||
|
).toLocaleDateString("id-ID", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
<h3 className="text-lg font-semibold mt-2 text-gray-900">
|
<h3 className="text-lg font-semibold mt-2 text-gray-900">
|
||||||
{announcements[0].title}
|
{posts[0].JudulPost}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600 mt-2">
|
<p className="text-sm text-gray-600 mt-2">
|
||||||
{announcements[0].description}
|
{posts[0].DescPost.replace(
|
||||||
|
/<[^>]*>/g,
|
||||||
|
""
|
||||||
|
).slice(0, 160)}
|
||||||
|
...
|
||||||
</p>
|
</p>
|
||||||
|
<Link href={`/pengumuman/${posts[0].SlugPost}`}>
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
className="text-red-600 mt-2 pl-0"
|
className="text-red-600 mt-2 pl-0"
|
||||||
|
@ -77,32 +88,46 @@ const AnnouncementSection = () => {
|
||||||
Baca Selengkapnya{" "}
|
Baca Selengkapnya{" "}
|
||||||
<ArrowRight className="ml-2 w-4 h-4" />
|
<ArrowRight className="ml-2 w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* List of Announcements */}
|
{/* List of Announcements */}
|
||||||
|
{posts.length > 1 && (
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
{announcements.map((item) => (
|
{posts.slice(1).map((post) => (
|
||||||
<Card key={item.id} className="p-4">
|
<Card key={post.PostId} className="p-4">
|
||||||
<img
|
<img
|
||||||
src={item.image}
|
src={`/storage/${post.ImagePost}`}
|
||||||
alt={item.alt_image}
|
alt={post.JudulPost}
|
||||||
className="rounded-md"
|
className="rounded-md"
|
||||||
/>
|
/>
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
<Badge className="bg-red-600 text-white">
|
<Badge className="bg-red-600 text-white">
|
||||||
{item.title_page}
|
{post.kategori?.NamaKategori}
|
||||||
</Badge>
|
</Badge>
|
||||||
<p className="text-gray-500 text-sm mt-2">
|
<p className="text-gray-500 text-sm mt-2">
|
||||||
{item.date}
|
{new Date(
|
||||||
|
post.created_at
|
||||||
|
).toLocaleDateString("id-ID", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||||
{item.title}
|
{post.JudulPost}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600 mt-2">
|
<p className="text-sm text-gray-600 mt-2">
|
||||||
{item.description}
|
{post.DescPost.replace(
|
||||||
|
/<[^>]*>/g,
|
||||||
|
""
|
||||||
|
).slice(0, 160)}
|
||||||
|
...
|
||||||
</p>
|
</p>
|
||||||
|
<Link href={`/pengumuman/${post.SlugPost}`}>
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
className="text-red-600 mt-2 pl-0"
|
className="text-red-600 mt-2 pl-0"
|
||||||
|
@ -110,10 +135,12 @@ const AnnouncementSection = () => {
|
||||||
Baca Selengkapnya{" "}
|
Baca Selengkapnya{" "}
|
||||||
<ArrowRight className="ml-2 w-4 h-4" />
|
<ArrowRight className="ml-2 w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
)}
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
@ -3,31 +3,35 @@ import { Button } from "@/components/ui/button";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { ArrowRight, FileText } from "lucide-react";
|
import { ArrowRight, FileText } from "lucide-react";
|
||||||
|
import { Link } from "@inertiajs/react";
|
||||||
|
|
||||||
const regulations = [
|
interface SubKategori {
|
||||||
{
|
SubKategoriId: number;
|
||||||
id: 1,
|
NamaSubKategori: string;
|
||||||
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",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<section className="relative w-full py-8 px-6">
|
<section className="relative w-full py-8 px-6">
|
||||||
{/* Background Green Box */}
|
{/* Background Green Box */}
|
||||||
|
@ -43,38 +47,51 @@ const RegulationSection = () => {
|
||||||
Informasi
|
Informasi
|
||||||
</Badge>
|
</Badge>
|
||||||
<h2 className="text-2xl font-bold">Peraturan</h2>
|
<h2 className="text-2xl font-bold">Peraturan</h2>
|
||||||
<p className="text-sm text-[#94bb98] mt-2 text-center md:text-left">
|
<p className="text-sm text-[#94bb98] mt-2 text-center md:text-left pr-3">
|
||||||
Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan
|
Aplikasi Sistem Ketaatan Lingkungan memastikan kepatuhan
|
||||||
Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan
|
terhadap perizinan, pengelolaan limbah, emisi, serta
|
||||||
Tersebut Bekerjasama Dengan Lembaga P...
|
pengawasan lingkungan.
|
||||||
</p>
|
</p>
|
||||||
|
<Link href="/peraturan">
|
||||||
<Button variant="link" className="text-white mt-4 pl-0">
|
<Button variant="link" className="text-white mt-4 pl-0">
|
||||||
Selengkapnya
|
Selengkapnya
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* List of Regulations */}
|
{/* List of Regulations */}
|
||||||
<div className="col-span-2 grid grid-cols-1 md:grid-cols-2 gap-6 relative z-10">
|
<div className="col-span-2 grid grid-cols-1 md:grid-cols-2 gap-6 relative z-10">
|
||||||
{regulations.map((item) => (
|
{peraturan.map((item) => (
|
||||||
<Card key={item.id} className="md:p-4">
|
<Card key={item.PostId} className="md:p-4">
|
||||||
<img
|
<img
|
||||||
src={item.image}
|
src={`/storage/${item.ImagePost}`}
|
||||||
alt={item.alt_image}
|
alt={item.JudulPost}
|
||||||
className="rounded-md"
|
className="rounded-md"
|
||||||
/>
|
/>
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
<Badge className="bg-red-600 text-white">
|
<Badge className="bg-red-600 text-white">
|
||||||
{item.title_page}
|
{item.kategori?.NamaKategori}
|
||||||
</Badge>
|
</Badge>
|
||||||
<p className="text-gray-500 text-sm mt-2">
|
<p className="text-gray-500 text-sm mt-2">
|
||||||
{item.date}
|
{new Date(
|
||||||
|
item.created_at
|
||||||
|
).toLocaleDateString("id-ID", {
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
})}
|
||||||
</p>
|
</p>
|
||||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||||
{item.title}
|
{item.JudulPost}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600 mt-2">
|
<p className="text-sm text-gray-600 mt-2">
|
||||||
{item.description}
|
{item.DescPost.replace(
|
||||||
|
/<[^>]*>/g,
|
||||||
|
""
|
||||||
|
).slice(0, 160)}
|
||||||
|
...
|
||||||
</p>
|
</p>
|
||||||
|
<Link href={`/peraturan/${item.SlugPost}`}>
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
className="text-red-600 mt-2 pl-0"
|
className="text-red-600 mt-2 pl-0"
|
||||||
|
@ -82,6 +99,7 @@ const RegulationSection = () => {
|
||||||
Baca Selengkapnya{" "}
|
Baca Selengkapnya{" "}
|
||||||
<ArrowRight className="ml-2 w-4 h-4" />
|
<ArrowRight className="ml-2 w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -1,31 +1,26 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Progress } from "@/components/ui/progress";
|
||||||
import { X, ArrowLeft, ArrowRight } from "lucide-react";
|
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 PopupModal = ({ onClose }: { onClose: () => void }) => {
|
||||||
const slides = [
|
const { sekilasInfo } = usePage().props as any;
|
||||||
{
|
const [isVisible, setIsVisible] = useState(true);
|
||||||
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 [currentSlide, setCurrentSlide] = useState(0);
|
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 = () => {
|
const nextSlide = () => {
|
||||||
setCurrentSlide((prev) => (prev + 1) % slides.length);
|
setCurrentSlide((prev) => (prev + 1) % slides.length);
|
||||||
};
|
};
|
||||||
|
@ -34,13 +29,63 @@ const PopupModal = ({ onClose }: { onClose: () => void }) => {
|
||||||
setCurrentSlide((prev) => (prev - 1 + slides.length) % slides.length);
|
setCurrentSlide((prev) => (prev - 1 + slides.length) % slides.length);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
setIsVisible(false);
|
||||||
|
setTimeout(onClose, 500);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50 container md:max-w-none">
|
<AnimatePresence mode="wait">
|
||||||
<div className="bg-white p-6 rounded-lg relative md:max-w-2xl mx-auto w-full h-50 md:h-5/6 flex flex-col justify-between md:justify-around">
|
{isVisible && sekilasInfo.length > 0 && (
|
||||||
<button className="absolute top-2 right-2" onClick={onClose}>
|
<motion.div
|
||||||
|
initial={{ opacity: 0 }}
|
||||||
|
animate={{
|
||||||
|
opacity: 1,
|
||||||
|
transition: {
|
||||||
|
duration: 0.3,
|
||||||
|
delay: 0.1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
exit={{
|
||||||
|
opacity: 0,
|
||||||
|
transition: {
|
||||||
|
duration: 0.3,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50 container md:max-w-none"
|
||||||
|
>
|
||||||
|
<motion.div
|
||||||
|
initial={{ opacity: 0, y: -50 }}
|
||||||
|
animate={{
|
||||||
|
opacity: 1,
|
||||||
|
y: 0,
|
||||||
|
transition: {
|
||||||
|
type: "spring",
|
||||||
|
damping: 70,
|
||||||
|
stiffness: 500,
|
||||||
|
delay: 0.2,
|
||||||
|
duration: 0.5,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
exit={{
|
||||||
|
opacity: 0,
|
||||||
|
y: 50,
|
||||||
|
transition: {
|
||||||
|
type: "spring",
|
||||||
|
damping: 25,
|
||||||
|
stiffness: 300,
|
||||||
|
duration: 0.4,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
className="bg-white p-6 rounded-lg relative md:max-w-2xl mx-auto w-full h-50 md:h-5/6 flex flex-col justify-between md:justify-around"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="absolute top-2 right-2"
|
||||||
|
onClick={handleClose}
|
||||||
|
>
|
||||||
<X className="w-6 h-6 text-gray-700" />
|
<X className="w-6 h-6 text-gray-700" />
|
||||||
</button>
|
</button>
|
||||||
<h2 className="md:text-xl text-md font-bold text-center mb-4">
|
<h2 className="md:text-md text-base font-bold text-center mb-4">
|
||||||
{slides[currentSlide].title}
|
{slides[currentSlide].title}
|
||||||
</h2>
|
</h2>
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
|
@ -62,19 +107,31 @@ const PopupModal = ({ onClose }: { onClose: () => void }) => {
|
||||||
<ArrowRight className="w-6 h-6 text-gray-700" />
|
<ArrowRight className="w-6 h-6 text-gray-700" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
{/* <h2 className="text-xl font-bold text-center mb-4">
|
<div className="w-full mt-4">
|
||||||
{slides[currentSlide].title}
|
<Progress
|
||||||
</h2> */}
|
value={progressValue}
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
<div className="text-sm text-gray-500 text-center mt-2">
|
||||||
|
{currentSlide + 1} / {slides.length}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<p className="text-sm text-gray-600 text-center mb-4 overflow-hidden text-ellipsis whitespace-nowrap">
|
<p className="text-sm text-gray-600 text-center mb-4 overflow-hidden text-ellipsis whitespace-nowrap">
|
||||||
{slides[currentSlide].description}
|
{slides[currentSlide].description}
|
||||||
</p>
|
</p>
|
||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
|
<Link
|
||||||
|
href={`/sekilasinfo/${slides[currentSlide].slug}`}
|
||||||
|
>
|
||||||
<Button className="bg-green-800 text-white px-6 py-2 rounded-lg">
|
<Button className="bg-green-800 text-white px-6 py-2 rounded-lg">
|
||||||
Selengkapnya
|
Selengkapnya
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</motion.div>
|
||||||
|
)}
|
||||||
|
</AnimatePresence>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,43 +1,106 @@
|
||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Megaphone } from "lucide-react";
|
import { Megaphone } from "lucide-react";
|
||||||
|
import { Link } from "@inertiajs/react";
|
||||||
|
import { Post } from "@/components/DetailArtikel/types";
|
||||||
|
|
||||||
const messages = [
|
interface RunningTextProps {
|
||||||
"Peraturan Menteri Lingkungan Hidup dan Kehutanan No. 14 Tahun 2024 Tentang Penyelenggaraan Pengawasan dan Sanksi Administratif Lingkungan Hidup",
|
posts: Post[];
|
||||||
"Workshop Peningkatan Kapasitas Pengendalian Pencemaran Udara akan diselenggarakan pada bulan depan.",
|
}
|
||||||
"DLH DKI Jakarta mengajak masyarakat untuk berpartisipasi dalam program lingkungan ramah sampah.",
|
|
||||||
];
|
|
||||||
|
|
||||||
export default function RunningText() {
|
export default function RunningText({ posts }: RunningTextProps) {
|
||||||
const [currentMessageIndex, setCurrentMessageIndex] = useState(0);
|
const [currentMessageIndex, setCurrentMessageIndex] = useState(0);
|
||||||
const [isVisible, setIsVisible] = useState(true);
|
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<string, Post[]>);
|
||||||
|
|
||||||
|
// 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(() => {
|
useEffect(() => {
|
||||||
|
if (isPaused || messages.length === 0) return;
|
||||||
|
|
||||||
const interval = setInterval(() => {
|
const interval = setInterval(() => {
|
||||||
setIsVisible(false); // Hide current message
|
setIsVisible(false);
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setCurrentMessageIndex(
|
setCurrentMessageIndex(
|
||||||
(prevIndex) => (prevIndex + 1) % messages.length
|
(prevIndex) => (prevIndex + 1) % messages.length
|
||||||
);
|
);
|
||||||
setIsVisible(true); // Show new message
|
setIsVisible(true);
|
||||||
}, 500); // Wait for fade out animation
|
}, 500);
|
||||||
}, 3000); // Change message every 3 seconds
|
}, 3000);
|
||||||
|
|
||||||
return () => clearInterval(interval);
|
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 (
|
return (
|
||||||
<div className="w-full bg-green-100 p-2 flex overflow-hidden">
|
<div
|
||||||
|
className="w-full bg-green-100 p-2 flex overflow-hidden"
|
||||||
|
onMouseEnter={() => setIsPaused(true)}
|
||||||
|
onMouseLeave={() => setIsPaused(false)}
|
||||||
|
>
|
||||||
<div className="flex container max-w-7xl px-8 relative">
|
<div className="flex container max-w-7xl px-8 relative">
|
||||||
<Megaphone className="w-5 h-5 mr-2 text-black flex-shrink-0" />
|
<Megaphone className="w-5 h-5 mr-2 text-black flex-shrink-0" />
|
||||||
<div className="overflow-hidden relative h-[1.5rem]">
|
<div className="overflow-hidden relative h-[1.5rem]">
|
||||||
|
<Link
|
||||||
|
href={getRouteByCategory(
|
||||||
|
messages[currentMessageIndex].kategori
|
||||||
|
?.NamaKategori || "",
|
||||||
|
messages[currentMessageIndex].SlugPost
|
||||||
|
)}
|
||||||
|
className="hover:text-green-700 transition-colors"
|
||||||
|
>
|
||||||
<p
|
<p
|
||||||
className={`text-sm text-black transition-opacity duration-500 ease-in-out ${
|
className={`text-sm text-black transition-opacity duration-500 ease-in-out ${
|
||||||
isVisible ? "opacity-100" : "opacity-0"
|
isVisible ? "opacity-100" : "opacity-0"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{messages[currentMessageIndex]}
|
<span className="font-semibold mr-2 bg-green-600 p-1 text-white text-xs rounded">
|
||||||
|
{
|
||||||
|
messages[currentMessageIndex].kategori
|
||||||
|
?.NamaKategori
|
||||||
|
}
|
||||||
|
</span>
|
||||||
|
<span>
|
||||||
|
{" "}
|
||||||
|
{messages[currentMessageIndex].JudulPost}
|
||||||
|
</span>
|
||||||
</p>
|
</p>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -17,7 +17,10 @@ const SearchDialog: React.FC = () => {
|
||||||
const handleSearch = (e: React.FormEvent<HTMLFormElement>) => {
|
const handleSearch = (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (searchQuery.trim()) {
|
if (searchQuery.trim()) {
|
||||||
router.visit(`/search/${encodeURIComponent(searchQuery)}`);
|
router.visit(`/search?q=${encodeURIComponent(searchQuery)}`, {
|
||||||
|
preserveState: true,
|
||||||
|
preserveScroll: true,
|
||||||
|
});
|
||||||
setSearchQuery(""); // Clear input setelah search
|
setSearchQuery(""); // Clear input setelah search
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -56,14 +59,14 @@ const SearchDialog: React.FC = () => {
|
||||||
<Search className="h-5 w-5" />
|
<Search className="h-5 w-5" />
|
||||||
</Button>
|
</Button>
|
||||||
</form>
|
</form>
|
||||||
<DialogClose asChild>
|
{/* <DialogClose asChild>
|
||||||
<Button
|
<Button
|
||||||
variant="ghost"
|
variant="ghost"
|
||||||
className="absolute top-4 right-4 text-gray-500 hover:text-gray-700"
|
className="absolute top-4 right-4 text-gray-500 hover:text-gray-700"
|
||||||
>
|
>
|
||||||
✕
|
✕
|
||||||
</Button>
|
</Button>
|
||||||
</DialogClose>
|
</DialogClose> */}
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
);
|
);
|
||||||
|
|
|
@ -21,25 +21,19 @@ export default function Tentang() {
|
||||||
</Badge>
|
</Badge>
|
||||||
|
|
||||||
<h2 className="text-3xl font-bold text-green-600">
|
<h2 className="text-3xl font-bold text-green-600">
|
||||||
Status Ketaatan Lingkungan
|
Sistem Ketaatan Lingkungan
|
||||||
<br />
|
<br />
|
||||||
(SKL)
|
(SKL)
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<p className="text-gray-600 leading-relaxed max-w-2xl">
|
<p className="text-gray-600 leading-relaxed max-w-2xl">
|
||||||
Lorem Ipsum Is Simply Dummy Text Of The Printing
|
SKL adalah Sistem Ketaatan Lingkungan yang
|
||||||
And Typesetting Industry. Lorem Ipsum Has Been
|
dinaungi oleh Dinas Lingkungan Hidup Provinsi
|
||||||
The Industry's Standard Dummy Text Ever Since
|
DKI Jakarta dengan menyediakan sistem yang dapat
|
||||||
The 1500s, When An Unknown Printer Took A Galley
|
memberikan pelayanan dalam pelaporan, analisa
|
||||||
Of Type And Scrambled It To Make A Type Specimen
|
dan evaluasi pengelolaan lingkungan dilakukan
|
||||||
Book. It Has Survived Not Only Five Centuries,
|
oleh perusahaan atau kegiatan usaha yang berada
|
||||||
But Also The Leap Into Electronic Typesetting,
|
di Provinsi DKI Jakarta.
|
||||||
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.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -3,41 +3,35 @@ import { Button } from "@/components/ui/button";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Card, CardContent } from "@/components/ui/card";
|
import { Card, CardContent } from "@/components/ui/card";
|
||||||
import { ArrowRight } from "lucide-react";
|
import { ArrowRight } from "lucide-react";
|
||||||
|
import { Link } from "@inertiajs/react";
|
||||||
|
|
||||||
const undangans = [
|
interface SubKategori {
|
||||||
{
|
SubKategoriId: number;
|
||||||
id: 1,
|
NamaSubKategori: string;
|
||||||
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",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
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 (
|
return (
|
||||||
<section className="container max-w-7xl py-8 px-6">
|
<section className="container max-w-7xl py-8 px-6">
|
||||||
<div className="flex items-center mb-6 justify-between">
|
<div className="flex items-center mb-6 justify-between">
|
||||||
|
@ -49,33 +43,50 @@ const UndanganSection = () => {
|
||||||
Undangan
|
Undangan
|
||||||
</h2>
|
</h2>
|
||||||
</div>
|
</div>
|
||||||
<Button variant="link" className="text-green-700 mt-auto pl-0">
|
<Link href="/undangan">
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
className="text-green-700 mt-auto pl-0"
|
||||||
|
>
|
||||||
Selengkapnya
|
Selengkapnya
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* List of Announcements */}
|
{/* List of Announcements */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||||
{undangans.map((item) => (
|
{undangan.map((item) => (
|
||||||
<Card key={item.id} className="md:p-4">
|
<Card key={item.PostId} className="md:p-4">
|
||||||
<img
|
<img
|
||||||
src={item.image}
|
src={`/storage/${item.ImagePost}`}
|
||||||
alt={item.alt_image}
|
alt={item.JudulPost}
|
||||||
className="rounded-md"
|
className="rounded-md"
|
||||||
/>
|
/>
|
||||||
<CardContent className="p-4">
|
<CardContent className="p-4">
|
||||||
<Badge className="bg-red-600 text-white">
|
<Badge className="bg-red-600 text-white">
|
||||||
{item.title_page}
|
{item.kategori?.NamaKategori}
|
||||||
</Badge>
|
</Badge>
|
||||||
<p className="text-gray-500 text-sm mt-2">
|
<p className="text-gray-500 text-sm mt-2">
|
||||||
{item.date}
|
{new Date(item.created_at).toLocaleDateString(
|
||||||
|
"id-ID",
|
||||||
|
{
|
||||||
|
day: "numeric",
|
||||||
|
month: "long",
|
||||||
|
year: "numeric",
|
||||||
|
}
|
||||||
|
)}
|
||||||
</p>
|
</p>
|
||||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||||
{item.title}
|
{item.JudulPost}
|
||||||
</h3>
|
</h3>
|
||||||
<p className="text-sm text-gray-600 mt-2">
|
<p className="text-sm text-gray-600 mt-2">
|
||||||
{item.description}
|
{item.DescPost.replace(/<[^>]*>/g, "").slice(
|
||||||
|
0,
|
||||||
|
160
|
||||||
|
)}
|
||||||
|
...
|
||||||
</p>
|
</p>
|
||||||
|
<Link href={`/undangan/${item.SlugPost}`}>
|
||||||
<Button
|
<Button
|
||||||
variant="link"
|
variant="link"
|
||||||
className="text-red-600 mt-2 pl-0"
|
className="text-red-600 mt-2 pl-0"
|
||||||
|
@ -83,6 +94,7 @@ const UndanganSection = () => {
|
||||||
Baca Selengkapnya{" "}
|
Baca Selengkapnya{" "}
|
||||||
<ArrowRight className="ml-2 w-4 h-4" />
|
<ArrowRight className="ml-2 w-4 h-4" />
|
||||||
</Button>
|
</Button>
|
||||||
|
</Link>
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -2,11 +2,22 @@
|
||||||
|
|
||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import {
|
import {
|
||||||
|
Archive,
|
||||||
|
Bold,
|
||||||
|
Bolt,
|
||||||
|
BookCheck,
|
||||||
|
BookMarked,
|
||||||
Command,
|
Command,
|
||||||
|
DatabaseBackup,
|
||||||
|
FileKey2,
|
||||||
|
Flag,
|
||||||
FolderArchive,
|
FolderArchive,
|
||||||
Frame,
|
Frame,
|
||||||
|
GalleryVertical,
|
||||||
Home,
|
Home,
|
||||||
LifeBuoy,
|
LifeBuoy,
|
||||||
|
NotepadText,
|
||||||
|
Scale,
|
||||||
Send,
|
Send,
|
||||||
SquareTerminal,
|
SquareTerminal,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
@ -38,6 +49,64 @@ const data = {
|
||||||
url: "/dashboard",
|
url: "/dashboard",
|
||||||
icon: Home,
|
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",
|
title: "Data Master",
|
||||||
url: "#",
|
url: "#",
|
||||||
|
@ -52,28 +121,69 @@ const data = {
|
||||||
title: "Sub Kategori Post",
|
title: "Sub Kategori Post",
|
||||||
url: "/admin/subkategori",
|
url: "/admin/subkategori",
|
||||||
},
|
},
|
||||||
// {
|
{
|
||||||
// title: "Menu",
|
title: "Dinas LH",
|
||||||
// url: "/menus",
|
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",
|
title: "Pengaturan",
|
||||||
url: "/admin/post",
|
url: "#",
|
||||||
icon: Frame,
|
icon: Bolt,
|
||||||
|
isActive: true,
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
title: "Pengguna",
|
||||||
|
url: "/admin/pengguna",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Role",
|
||||||
|
url: "/admin/role",
|
||||||
|
},
|
||||||
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
navSecondary: [
|
navSecondary: [
|
||||||
{
|
{
|
||||||
title: "Support",
|
title: "Tentang",
|
||||||
url: "#",
|
url: "/admin/tentang",
|
||||||
icon: LifeBuoy,
|
icon: LifeBuoy,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Feedback",
|
title: "Catatan Sistem",
|
||||||
url: "#",
|
url: "#",
|
||||||
icon: Send,
|
icon: NotepadText,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Backup",
|
||||||
|
url: "#",
|
||||||
|
icon: Archive,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Restore",
|
||||||
|
url: "#",
|
||||||
|
icon: DatabaseBackup,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|
|
@ -44,11 +44,11 @@ const AppearanceDropdown = () => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className="flex flex-row items-center justify-center">
|
<div className="flex flex-row items-center justify-center">
|
||||||
<span className="text-sm border-r px-5 items-center text-right">
|
<span className="text-sm px-5 items-center text-right">
|
||||||
{formattedDate} <br />
|
{formattedDate} <br />
|
||||||
{formattedTime}
|
{formattedTime}
|
||||||
</span>
|
</span>
|
||||||
<div className="px-5">
|
{/* <div className="px-5">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button
|
<Button
|
||||||
|
@ -74,7 +74,7 @@ const AppearanceDropdown = () => {
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
</div>
|
</div> */}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -46,10 +46,10 @@ export function NavMain({
|
||||||
>
|
>
|
||||||
<SidebarMenuItem>
|
<SidebarMenuItem>
|
||||||
<SidebarMenuButton asChild tooltip={item.title}>
|
<SidebarMenuButton asChild tooltip={item.title}>
|
||||||
<a href={item.url}>
|
<Link href={item.url}>
|
||||||
<item.icon />
|
<item.icon />
|
||||||
<div>{item.title}</div>
|
<div>{item.title}</div>
|
||||||
</a>
|
</Link>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
{item.items?.length ? (
|
{item.items?.length ? (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {
|
||||||
SidebarMenuButton,
|
SidebarMenuButton,
|
||||||
SidebarMenuItem,
|
SidebarMenuItem,
|
||||||
} from "@/components/ui/sidebar";
|
} from "@/components/ui/sidebar";
|
||||||
|
import { Link } from "@inertiajs/react";
|
||||||
|
|
||||||
export function NavSecondary({
|
export function NavSecondary({
|
||||||
items,
|
items,
|
||||||
|
@ -26,10 +27,10 @@ export function NavSecondary({
|
||||||
{items.map((item) => (
|
{items.map((item) => (
|
||||||
<SidebarMenuItem key={item.title}>
|
<SidebarMenuItem key={item.title}>
|
||||||
<SidebarMenuButton asChild size="sm">
|
<SidebarMenuButton asChild size="sm">
|
||||||
<a href={item.url}>
|
<Link href={item.url}>
|
||||||
<item.icon />
|
<item.icon />
|
||||||
<span>{item.title}</span>
|
<span>{item.title}</span>
|
||||||
</a>
|
</Link>
|
||||||
</SidebarMenuButton>
|
</SidebarMenuButton>
|
||||||
</SidebarMenuItem>
|
</SidebarMenuItem>
|
||||||
))}
|
))}
|
||||||
|
|
|
@ -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<HTMLDivElement> & VariantProps<typeof alertVariants>
|
||||||
|
>(({ className, variant, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
role="alert"
|
||||||
|
className={cn(alertVariants({ variant }), className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
Alert.displayName = "Alert"
|
||||||
|
|
||||||
|
const AlertTitle = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLHeadingElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<h5
|
||||||
|
ref={ref}
|
||||||
|
className={cn("mb-1 font-medium leading-none tracking-tight", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertTitle.displayName = "AlertTitle"
|
||||||
|
|
||||||
|
const AlertDescription = React.forwardRef<
|
||||||
|
HTMLParagraphElement,
|
||||||
|
React.HTMLAttributes<HTMLParagraphElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm [&_p]:leading-relaxed", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
AlertDescription.displayName = "AlertDescription"
|
||||||
|
|
||||||
|
export { Alert, AlertTitle, AlertDescription }
|
|
@ -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<TData, TValue> {
|
||||||
|
columns: ColumnDef<TData, TValue>[];
|
||||||
|
data: TData[];
|
||||||
|
searchKey: string;
|
||||||
|
searchPlaceholder?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DataTable<TData, TValue>({
|
||||||
|
columns,
|
||||||
|
data,
|
||||||
|
searchKey,
|
||||||
|
searchPlaceholder = "Filter records...",
|
||||||
|
}: DataTableProps<TData, TValue>) {
|
||||||
|
const [sorting, setSorting] = React.useState<SortingState>([]);
|
||||||
|
const [columnFilters, setColumnFilters] =
|
||||||
|
React.useState<ColumnFiltersState>([]);
|
||||||
|
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 (
|
||||||
|
<div>
|
||||||
|
<div className="flex flex-col sm:flex-row items-center justify-between gap-4 py-4">
|
||||||
|
<Input
|
||||||
|
placeholder={searchPlaceholder}
|
||||||
|
value={
|
||||||
|
(table
|
||||||
|
.getColumn(searchKey)
|
||||||
|
?.getFilterValue() as string) ?? ""
|
||||||
|
}
|
||||||
|
onChange={(event) =>
|
||||||
|
table
|
||||||
|
.getColumn(searchKey)
|
||||||
|
?.setFilterValue(event.target.value)
|
||||||
|
}
|
||||||
|
className="max-w-sm"
|
||||||
|
/>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Select
|
||||||
|
value={`${table.getState().pagination.pageSize}`}
|
||||||
|
onValueChange={(value) => {
|
||||||
|
table.setPageSize(Number(value));
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="h-8 w-[70px]">
|
||||||
|
<SelectValue
|
||||||
|
placeholder={
|
||||||
|
table.getState().pagination.pageSize
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent side="top">
|
||||||
|
{[5, 10, 20, 30, 40, 50].map((pageSize) => (
|
||||||
|
<SelectItem
|
||||||
|
key={pageSize}
|
||||||
|
value={`${pageSize}`}
|
||||||
|
>
|
||||||
|
{pageSize}
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Wrap table in a responsive container */}
|
||||||
|
<div className="relative w-full overflow-auto">
|
||||||
|
<div className="rounded-md border">
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
{table.getHeaderGroups().map((headerGroup) => (
|
||||||
|
<TableRow key={headerGroup.id}>
|
||||||
|
{headerGroup.headers.map((header) => (
|
||||||
|
<TableHead
|
||||||
|
key={header.id}
|
||||||
|
className="whitespace-nowrap"
|
||||||
|
>
|
||||||
|
{header.isPlaceholder
|
||||||
|
? null
|
||||||
|
: flexRender(
|
||||||
|
header.column
|
||||||
|
.columnDef.header,
|
||||||
|
header.getContext()
|
||||||
|
)}
|
||||||
|
</TableHead>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{table.getRowModel().rows?.length ? (
|
||||||
|
table.getRowModel().rows.map((row) => (
|
||||||
|
<TableRow
|
||||||
|
key={row.id}
|
||||||
|
data-state={
|
||||||
|
row.getIsSelected() &&
|
||||||
|
"selected"
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{row
|
||||||
|
.getVisibleCells()
|
||||||
|
.map((cell) => (
|
||||||
|
<TableCell
|
||||||
|
key={cell.id}
|
||||||
|
className="whitespace-nowrap"
|
||||||
|
>
|
||||||
|
{flexRender(
|
||||||
|
cell.column
|
||||||
|
.columnDef.cell,
|
||||||
|
cell.getContext()
|
||||||
|
)}
|
||||||
|
</TableCell>
|
||||||
|
))}
|
||||||
|
</TableRow>
|
||||||
|
))
|
||||||
|
) : (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={columns.length}
|
||||||
|
className="h-24 text-center"
|
||||||
|
>
|
||||||
|
No results.
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex flex-col sm:flex-row items-center justify-between gap-4 py-4">
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
{table.getFilteredSelectedRowModel().rows.length} of{" "}
|
||||||
|
{table.getFilteredRowModel().rows.length} row(s) selected.
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center space-x-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => table.previousPage()}
|
||||||
|
disabled={!table.getCanPreviousPage()}
|
||||||
|
>
|
||||||
|
Previous
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => table.nextPage()}
|
||||||
|
disabled={!table.getCanNextPage()}
|
||||||
|
>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
|
@ -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<typeof ProgressPrimitive.Root>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof ProgressPrimitive.Root>
|
||||||
|
>(({ className, value, ...props }, ref) => (
|
||||||
|
<ProgressPrimitive.Root
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"relative h-2 w-full overflow-hidden rounded-full bg-primary/20",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ProgressPrimitive.Indicator
|
||||||
|
className="h-full w-full flex-1 bg-primary transition-all"
|
||||||
|
style={{ transform: `translateX(-${100 - (value || 0)}%)` }}
|
||||||
|
/>
|
||||||
|
</ProgressPrimitive.Root>
|
||||||
|
))
|
||||||
|
Progress.displayName = ProgressPrimitive.Root.displayName
|
||||||
|
|
||||||
|
export { Progress }
|
|
@ -58,7 +58,7 @@ const TableRow = React.forwardRef<
|
||||||
<tr
|
<tr
|
||||||
ref={ref}
|
ref={ref}
|
||||||
className={cn(
|
className={cn(
|
||||||
"border-b transition-colors hover:bg-muted/50 data-[state=selected]:bg-muted",
|
"border-b transition-colors data-[state=selected]:bg-muted",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
@ -0,0 +1,53 @@
|
||||||
|
import * as React from "react"
|
||||||
|
import * as TabsPrimitive from "@radix-ui/react-tabs"
|
||||||
|
|
||||||
|
import { cn } from "@/lib/utils"
|
||||||
|
|
||||||
|
const Tabs = TabsPrimitive.Root
|
||||||
|
|
||||||
|
const TabsList = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.List>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.List
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"inline-flex h-9 items-center justify-center rounded-lg bg-muted p-1 text-muted-foreground",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TabsList.displayName = TabsPrimitive.List.displayName
|
||||||
|
|
||||||
|
const TabsTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
|
||||||
|
|
||||||
|
const TabsContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
))
|
||||||
|
TabsContent.displayName = TabsPrimitive.Content.displayName
|
||||||
|
|
||||||
|
export { Tabs, TabsList, TabsTrigger, TabsContent }
|
|
@ -27,7 +27,7 @@ const toastVariants = cva(
|
||||||
{
|
{
|
||||||
variants: {
|
variants: {
|
||||||
variant: {
|
variant: {
|
||||||
default: "border bg-background text-foreground",
|
default: "border bg-green-100 text-green-800",
|
||||||
destructive:
|
destructive:
|
||||||
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
||||||
},
|
},
|
||||||
|
|
|
@ -12,7 +12,7 @@ export function Toaster() {
|
||||||
const { toasts } = useToast();
|
const { toasts } = useToast();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ToastProvider duration={2000} swipeDirection="right">
|
<ToastProvider duration={3000} swipeDirection="right">
|
||||||
{toasts.map(function ({
|
{toasts.map(function ({
|
||||||
id,
|
id,
|
||||||
title,
|
title,
|
||||||
|
|
|
@ -25,7 +25,7 @@ export default function AuthenticatedLayout({
|
||||||
<SidebarProvider>
|
<SidebarProvider>
|
||||||
<AppSidebar />
|
<AppSidebar />
|
||||||
|
|
||||||
<SidebarInset>
|
<SidebarInset className="overflow-x-hidden w-full">
|
||||||
<header className="sticky top-0 bg-background flex h-16 shrink-0 items-center gap-2 justify-between p-4 border-b md:border-none md:rounded-xl">
|
<header className="sticky top-0 bg-background flex h-16 shrink-0 items-center gap-2 justify-between p-4 border-b md:border-none md:rounded-xl">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<SidebarTrigger className="-ml-1" />
|
<SidebarTrigger className="-ml-1" />
|
||||||
|
@ -46,8 +46,10 @@ export default function AuthenticatedLayout({
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<main className="p-4 md:pt-0 h-full">{children}</main>
|
<main className="p-4 md:pt-0 h-full w-full max-w-full overflow-x-hidden">
|
||||||
<footer className="text-sm p-4">
|
{children}
|
||||||
|
</main>
|
||||||
|
<footer className="text-sm p-4 text-center bg-background border-t">
|
||||||
© Copyright {new Date().getFullYear()} Bidang Tata
|
© Copyright {new Date().getFullYear()} Bidang Tata
|
||||||
Lingkungan dan Kebersihan - Dinas Lingkungan Hidup Provinsi
|
Lingkungan dan Kebersihan - Dinas Lingkungan Hidup Provinsi
|
||||||
DKI Jakarta
|
DKI Jakarta
|
||||||
|
|
|
@ -4,15 +4,21 @@ import Footer from "@/components/Partials/Footer";
|
||||||
import HeroSecond from "@/components/Card/HeroSecond";
|
import HeroSecond from "@/components/Card/HeroSecond";
|
||||||
import CardPengumuman from "@/components/Card/CardPengumuman";
|
import CardPengumuman from "@/components/Card/CardPengumuman";
|
||||||
import { Head } from "@inertiajs/react";
|
import { Head } from "@inertiajs/react";
|
||||||
|
import { usePage } from "@inertiajs/react";
|
||||||
|
import Guest from "@/layouts/guest-layout";
|
||||||
|
|
||||||
export default function Pengumuman() {
|
export default function Pengumuman() {
|
||||||
|
const { posts } = usePage().props as any;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Guest>
|
||||||
<Head title="Pengumuman" />
|
<Head title="Pengumuman" />
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<HeroSecond title="Pengumuman" />
|
<HeroSecond title="Pengumuman" />
|
||||||
<CardPengumuman posts={[]} />
|
<CardPengumuman posts={posts} />
|
||||||
<Footer />
|
<Footer />
|
||||||
|
</Guest>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,15 +3,16 @@ import Navbar from "@/components/Partials/Navbar";
|
||||||
import Footer from "@/components/Partials/Footer";
|
import Footer from "@/components/Partials/Footer";
|
||||||
import HeroSecond from "@/components/Card/HeroSecond";
|
import HeroSecond from "@/components/Card/HeroSecond";
|
||||||
import CardPeraturan from "@/components/Card/CardPeraturan";
|
import CardPeraturan from "@/components/Card/CardPeraturan";
|
||||||
import { Head } from "@inertiajs/react";
|
import { Head, usePage } from "@inertiajs/react";
|
||||||
|
|
||||||
export default function Peraturan() {
|
export default function Peraturan() {
|
||||||
|
const { posts } = usePage().props as any;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head title="Peraturan" />
|
<Head title="Peraturan" />
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<HeroSecond title="Peraturan" />
|
<HeroSecond title="Peraturan" />
|
||||||
<CardPeraturan />
|
<CardPeraturan posts={posts} />
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
import React from "react";
|
||||||
|
import { Head } from "@inertiajs/react";
|
||||||
|
import DetailSearch from "@/components/DetailSearch/DetailSearch";
|
||||||
|
import Navbar from "@/components/Partials/Navbar";
|
||||||
|
import Footer from "@/components/Partials/Footer";
|
||||||
|
import { Post } from "@/components/DetailArtikel/types";
|
||||||
|
import Guest from "@/layouts/guest-layout";
|
||||||
|
|
||||||
|
interface SearchProps {
|
||||||
|
searchResults: Post[];
|
||||||
|
searchQuery: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Search({ searchResults, searchQuery }: SearchProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head title={`Pencarian: ${searchQuery}`} />
|
||||||
|
<Navbar />
|
||||||
|
<DetailSearch
|
||||||
|
searchResults={searchResults}
|
||||||
|
searchQuery={searchQuery}
|
||||||
|
/>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
|
@ -3,15 +3,16 @@ import Navbar from "@/components/Partials/Navbar";
|
||||||
import Footer from "@/components/Partials/Footer";
|
import Footer from "@/components/Partials/Footer";
|
||||||
import HeroSecond from "@/components/Card/HeroSecond";
|
import HeroSecond from "@/components/Card/HeroSecond";
|
||||||
import CardUndangan from "@/components/Card/CardUndangan";
|
import CardUndangan from "@/components/Card/CardUndangan";
|
||||||
import { Head } from "@inertiajs/react";
|
import { Head, usePage } from "@inertiajs/react";
|
||||||
|
|
||||||
export default function Undangan() {
|
export default function Undangan() {
|
||||||
|
const { posts } = usePage().props as any;
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Head title="Undangan" />
|
<Head title="Undangan" />
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<HeroSecond title="Undangan" />
|
<HeroSecond title="Undangan" />
|
||||||
<CardUndangan />
|
<CardUndangan posts={posts} />
|
||||||
<Footer />
|
<Footer />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
|
@ -0,0 +1,296 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useForm } from "@inertiajs/react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
TableHead,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogFooter,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Search,
|
||||||
|
Plus,
|
||||||
|
Pencil,
|
||||||
|
Trash2,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
} from "lucide-react";
|
||||||
|
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||||
|
import { Head } from "@inertiajs/react";
|
||||||
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
|
import axios from "axios";
|
||||||
|
|
||||||
|
interface Hukum {
|
||||||
|
HukumId: number;
|
||||||
|
PerusahaanId: number | null;
|
||||||
|
JenisSanksiId: number | null;
|
||||||
|
SanksiNumber: string;
|
||||||
|
SanksiDate: string;
|
||||||
|
SanksiFile: string;
|
||||||
|
StatusPenaatan: number;
|
||||||
|
PenaatanNumber: string;
|
||||||
|
PenaatanDate: string;
|
||||||
|
PenaatanFile: string;
|
||||||
|
IsDeleted: number;
|
||||||
|
perusahaan: { nama: string } | null;
|
||||||
|
jenisSanksi: { nama: string } | null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ITEMS_PER_PAGE = 5;
|
||||||
|
|
||||||
|
export default function HukumIndex() {
|
||||||
|
const { toast } = useToast();
|
||||||
|
const [dataHukum, setDataHukum] = useState<Hukum[]>([]);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
const [filteredHukum, setFilteredHukum] = useState<Hukum[]>([]);
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const [deleteConfirm, setDeleteConfirm] = useState<Hukum | null>(null);
|
||||||
|
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
setData,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
delete: destroy,
|
||||||
|
reset,
|
||||||
|
} = useForm<Hukum>({
|
||||||
|
HukumId: 0,
|
||||||
|
PerusahaanId: null,
|
||||||
|
JenisSanksiId: null,
|
||||||
|
SanksiNumber: "",
|
||||||
|
SanksiDate: "",
|
||||||
|
SanksiFile: "",
|
||||||
|
StatusPenaatan: 1,
|
||||||
|
PenaatanNumber: "",
|
||||||
|
PenaatanDate: "",
|
||||||
|
PenaatanFile: "",
|
||||||
|
IsDeleted: 0,
|
||||||
|
perusahaan: null,
|
||||||
|
jenisSanksi: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
axios
|
||||||
|
.get("/admin/hukum")
|
||||||
|
.then((response) => {
|
||||||
|
if (response.data && response.data.data) {
|
||||||
|
setDataHukum(response.data.data);
|
||||||
|
setFilteredHukum(response.data.data);
|
||||||
|
} else {
|
||||||
|
setDataHukum([]);
|
||||||
|
setFilteredHukum([]);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch((error) => {
|
||||||
|
console.error("Error fetching data:", error);
|
||||||
|
setDataHukum([]);
|
||||||
|
setFilteredHukum([]);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
// Filter Data
|
||||||
|
useEffect(() => {
|
||||||
|
let filtered = dataHukum;
|
||||||
|
if (search) {
|
||||||
|
filtered = filtered.filter((hukum) =>
|
||||||
|
hukum.SanksiNumber.toLowerCase().includes(search.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
setFilteredHukum(filtered);
|
||||||
|
setCurrentPage(1);
|
||||||
|
}, [dataHukum, search]);
|
||||||
|
|
||||||
|
// Pagination
|
||||||
|
const totalPages = filteredHukum?.length
|
||||||
|
? Math.ceil(filteredHukum.length / ITEMS_PER_PAGE)
|
||||||
|
: 1;
|
||||||
|
|
||||||
|
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
||||||
|
const endIndex = startIndex + ITEMS_PER_PAGE;
|
||||||
|
const currentItems = filteredHukum.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
// Handle Search
|
||||||
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearch(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle Page Change
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle Form Submission
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
const url = editing ? `/admin/hukum/${data.HukumId}` : "/admin/hukum";
|
||||||
|
|
||||||
|
const requestMethod = editing ? put : post;
|
||||||
|
requestMethod(url, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: editing
|
||||||
|
? "Data berhasil diperbarui"
|
||||||
|
: "Data berhasil ditambahkan",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setIsModalOpen(false);
|
||||||
|
reset();
|
||||||
|
setEditing(false);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description: "Terjadi kesalahan saat menyimpan data",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle Edit
|
||||||
|
const handleEdit = (hukum: Hukum) => {
|
||||||
|
setData({ ...hukum });
|
||||||
|
setEditing(true);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Handle Delete
|
||||||
|
const handleDelete = () => {
|
||||||
|
if (deleteConfirm) {
|
||||||
|
destroy(`/admin/hukum/${deleteConfirm.HukumId}`, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Data berhasil dihapus",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setDeleteConfirm(null);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description: "Terjadi kesalahan saat menghapus data",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthenticatedLayout header="Penegakan Hukum">
|
||||||
|
<Head title="Penegakan Hukum" />
|
||||||
|
<div className="container mx-auto p-4">
|
||||||
|
<Card className="shadow-lg">
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Cari Sanksi..."
|
||||||
|
value={search}
|
||||||
|
onChange={handleSearch}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setEditing(false);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Tambah
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>No</TableHead>
|
||||||
|
<TableHead>Nama Perusahaan</TableHead>
|
||||||
|
<TableHead>Jenis Sanksi</TableHead>
|
||||||
|
<TableHead>Nomor SK Sanksi</TableHead>
|
||||||
|
<TableHead>Tanggal Sanksi</TableHead>
|
||||||
|
<TableHead>SK Sanksi</TableHead>
|
||||||
|
<TableHead>Status Penaatan</TableHead>
|
||||||
|
<TableHead>Nomor Penaatan</TableHead>
|
||||||
|
<TableHead>Tanggal Penaatan</TableHead>
|
||||||
|
<TableHead>SK Penaatan</TableHead>
|
||||||
|
<TableHead>Aksi</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{currentItems.map((hukum, index) => (
|
||||||
|
<TableRow key={hukum.HukumId}>
|
||||||
|
<TableCell>
|
||||||
|
{startIndex + index + 1}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{hukum.perusahaan?.nama}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{hukum.jenisSanksi?.nama}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{hukum.SanksiNumber}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{hukum.SanksiDate}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{hukum.SanksiFile}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{hukum.StatusPenaatan}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{hukum.PenaatanNumber}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{hukum.PenaatanDate}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{hukum.PenaatanFile}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
handleEdit(hukum)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Pencil className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
setDeleteConfirm(hukum)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
<Toaster />
|
||||||
|
</AuthenticatedLayout>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,398 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useForm } from "@inertiajs/react";
|
||||||
|
import { PageProps } from "@/types";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
TableHead,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
// import { toast } from "react-toastify";
|
||||||
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogFooter,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Search,
|
||||||
|
Plus,
|
||||||
|
Pencil,
|
||||||
|
Trash2,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
} from "lucide-react";
|
||||||
|
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||||
|
import { Head } from "@inertiajs/react";
|
||||||
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
|
interface JenisDokIL {
|
||||||
|
JenisDokILId: number | null;
|
||||||
|
KodeJenisDokIL: string;
|
||||||
|
NamaJenisDokIL: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ITEMS_PER_PAGE = 5;
|
||||||
|
|
||||||
|
export default function JenisDokILIndex({
|
||||||
|
jenisdokil,
|
||||||
|
}: PageProps<{ jenisdokil: JenisDokIL[] }>) {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
setData,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
delete: destroy,
|
||||||
|
reset,
|
||||||
|
} = useForm<JenisDokIL>({
|
||||||
|
JenisDokILId: null,
|
||||||
|
KodeJenisDokIL: "",
|
||||||
|
NamaJenisDokIL: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [deleteConfirm, setDeleteConfirm] = useState<JenisDokIL | null>(null);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const [filteredJenisDokIL, setFilteredJenisDokIL] = useState(jenisdokil);
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let filtered = jenisdokil;
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
filtered = filtered.filter(
|
||||||
|
(item) =>
|
||||||
|
item.KodeJenisDokIL.toLowerCase().includes(
|
||||||
|
search.toLowerCase()
|
||||||
|
) ||
|
||||||
|
item.NamaJenisDokIL.toLowerCase().includes(
|
||||||
|
search.toLowerCase()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilteredJenisDokIL(filtered);
|
||||||
|
setCurrentPage(1);
|
||||||
|
}, [jenisdokil, search]);
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(filteredJenisDokIL.length / ITEMS_PER_PAGE);
|
||||||
|
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
||||||
|
const endIndex = startIndex + ITEMS_PER_PAGE;
|
||||||
|
const currentItems = filteredJenisDokIL.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearch(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (editing) {
|
||||||
|
put(`/admin/jenisdokil/${data.JenisDokILId}`, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Data jenis dokumen berhasil diperbarui",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setIsModalOpen(false);
|
||||||
|
reset();
|
||||||
|
setEditing(false);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description: "Terjadi kesalahan saat memperbarui data",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
post("/admin/jenisdokil", {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Data jenis dokumen berhasil ditambahkan",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setIsModalOpen(false);
|
||||||
|
reset();
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description: "Terjadi kesalahan saat menambah data",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = (jenisdokil: JenisDokIL) => {
|
||||||
|
setData({ ...jenisdokil });
|
||||||
|
setEditing(true);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
if (deleteConfirm) {
|
||||||
|
destroy(`/admin/jenisdokil/${deleteConfirm.JenisDokILId}`, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Data jenis dokumen berhasil dihapus",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setDeleteConfirm(null);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description: "Terjadi kesalahan saat menghapus data",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
setDeleteConfirm(null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AuthenticatedLayout header={"Data Jenis Dokumen IL"}>
|
||||||
|
<Head title="Data Jenis Dokumen IL" />
|
||||||
|
<div className="container mx-auto p-4">
|
||||||
|
<Card className="shadow-lg">
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex flex-col space-y-4">
|
||||||
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
||||||
|
<div className="relative w-full md:w-96">
|
||||||
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 h-4 w-4" />
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Cari Jenis Dokumen IL..."
|
||||||
|
value={search}
|
||||||
|
onChange={handleSearch}
|
||||||
|
className="pl-10 pr-4 w-full"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Dialog
|
||||||
|
open={isModalOpen}
|
||||||
|
onOpenChange={setIsModalOpen}
|
||||||
|
>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setEditing(false);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4 mr-2" />
|
||||||
|
Tambah Jenis Dokumen
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>
|
||||||
|
{editing
|
||||||
|
? "Edit Jenis Dokumen"
|
||||||
|
: "Tambah Jenis Dokumen"}
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className="space-y-4"
|
||||||
|
>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label>
|
||||||
|
Kode Jenis Dokumen
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
value={data.KodeJenisDokIL}
|
||||||
|
onChange={(e) =>
|
||||||
|
setData(
|
||||||
|
"KodeJenisDokIL",
|
||||||
|
e.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
placeholder="Masukkan kode jenis dokumen"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label>
|
||||||
|
Nama Jenis Dokumen
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
value={data.NamaJenisDokIL}
|
||||||
|
onChange={(e) =>
|
||||||
|
setData(
|
||||||
|
"NamaJenisDokIL",
|
||||||
|
e.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
placeholder="Masukkan nama jenis dokumen"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button type="submit">
|
||||||
|
{editing
|
||||||
|
? "Simpan"
|
||||||
|
: "Tambah"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow>
|
||||||
|
<TableHead>No</TableHead>
|
||||||
|
<TableHead>Kode</TableHead>
|
||||||
|
<TableHead>Nama Jenis Dokumen</TableHead>
|
||||||
|
<TableHead>Aksi</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{currentItems.map((item, index) => (
|
||||||
|
<TableRow key={item.JenisDokILId}>
|
||||||
|
<TableCell>
|
||||||
|
{startIndex + index + 1}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{item.KodeJenisDokIL}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{item.NamaJenisDokIL}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => handleEdit(item)}
|
||||||
|
>
|
||||||
|
<Pencil className="h-4 w-4 mr-2" />
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={() =>
|
||||||
|
setDeleteConfirm(item)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4 mr-2" />
|
||||||
|
Hapus
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
|
||||||
|
<div className="flex items-center justify-between mt-4">
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
Showing {startIndex + 1} to{" "}
|
||||||
|
{Math.min(endIndex, filteredJenisDokIL.length)}{" "}
|
||||||
|
of {filteredJenisDokIL.length} entries
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
handlePageChange(currentPage - 1)
|
||||||
|
}
|
||||||
|
disabled={currentPage === 1}
|
||||||
|
>
|
||||||
|
<ChevronLeft className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
{Array.from(
|
||||||
|
{ length: totalPages },
|
||||||
|
(_, i) => i + 1
|
||||||
|
).map((page) => (
|
||||||
|
<Button
|
||||||
|
key={page}
|
||||||
|
variant={
|
||||||
|
currentPage === page
|
||||||
|
? "default"
|
||||||
|
: "outline"
|
||||||
|
}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handlePageChange(page)}
|
||||||
|
>
|
||||||
|
{page}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
handlePageChange(currentPage + 1)
|
||||||
|
}
|
||||||
|
disabled={currentPage === totalPages}
|
||||||
|
>
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{deleteConfirm && (
|
||||||
|
<Dialog open={true} onOpenChange={() => setDeleteConfirm(null)}>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle>Konfirmasi Hapus</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<p>
|
||||||
|
Apakah anda yakin ingin menghapus jenis dokumen "
|
||||||
|
{deleteConfirm.NamaJenisDokIL}"?
|
||||||
|
</p>
|
||||||
|
<DialogFooter>
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => setDeleteConfirm(null)}
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="destructive"
|
||||||
|
onClick={handleDelete}
|
||||||
|
>
|
||||||
|
Hapus
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
|
<Toaster />
|
||||||
|
</AuthenticatedLayout>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,467 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useForm } from "@inertiajs/react";
|
||||||
|
import { PageProps } from "@/types";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
TableHead,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
// import { toast } from "react-toastify";
|
||||||
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogFooter,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Search,
|
||||||
|
Plus,
|
||||||
|
Pencil,
|
||||||
|
Trash2,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
} from "lucide-react";
|
||||||
|
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||||
|
import { Head } from "@inertiajs/react";
|
||||||
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
|
interface JenisSanksi {
|
||||||
|
JenisSanksiId: number | null;
|
||||||
|
NamaJenisSanksi: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ITEMS_PER_PAGE = 5;
|
||||||
|
|
||||||
|
export default function JenisSanksiIndex({
|
||||||
|
jenissanksi,
|
||||||
|
}: PageProps<{ jenissanksi: JenisSanksi[] }>) {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
setData,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
delete: destroy,
|
||||||
|
reset,
|
||||||
|
} = useForm<JenisSanksi>({
|
||||||
|
JenisSanksiId: null,
|
||||||
|
NamaJenisSanksi: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [deleteConfirm, setDeleteConfirm] = useState<JenisSanksi | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const [filteredJenisSanksi, setFilteredJenisSanksi] = useState(jenissanksi);
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let filtered = jenissanksi;
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
filtered = filtered.filter((jenissanksi) =>
|
||||||
|
jenissanksi.NamaJenisSanksi.toLowerCase().includes(
|
||||||
|
search.toLowerCase()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilteredJenisSanksi(filtered);
|
||||||
|
setCurrentPage(1);
|
||||||
|
}, [jenissanksi, search]);
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(filteredJenisSanksi.length / ITEMS_PER_PAGE);
|
||||||
|
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
||||||
|
const endIndex = startIndex + ITEMS_PER_PAGE;
|
||||||
|
const currentItems = filteredJenisSanksi.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearch(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (editing) {
|
||||||
|
put(`/admin/jenissanksi/${data.JenisSanksiId}`, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Jenis Sanksi berhasil diperbarui",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setIsModalOpen(false);
|
||||||
|
reset();
|
||||||
|
setEditing(false);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description:
|
||||||
|
"Terjadi kesalahan saat memperbarui Jenis Sanksi",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
post("/admin/jenissanksi", {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Jenis Sanksi berhasil dibuat",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setIsModalOpen(false);
|
||||||
|
reset();
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description:
|
||||||
|
"Terjadi kesalahan saat membuat Jenis Sanksi",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = (jenissanksi: JenisSanksi) => {
|
||||||
|
setData({ ...jenissanksi });
|
||||||
|
setEditing(true);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
if (deleteConfirm) {
|
||||||
|
destroy(`/admin/jenissanksi/${deleteConfirm.JenisSanksiId}`, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Jenis Sanksi berhasil dihapus",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setDeleteConfirm(null);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description:
|
||||||
|
"Terjadi kesalahan saat menghapus Jenis Sanksi",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
setDeleteConfirm(null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<AuthenticatedLayout header={"Jenis Sanksi"}>
|
||||||
|
<Head title="Jenis Sanksi" />
|
||||||
|
<div className="container mx-auto p-4">
|
||||||
|
<Card className="shadow-lg">
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex flex-col space-y-4">
|
||||||
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
||||||
|
<div className="relative w-full md:w-96">
|
||||||
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 h-4 w-4" />
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Cari Jenis Sanksi..."
|
||||||
|
value={search}
|
||||||
|
onChange={handleSearch}
|
||||||
|
className="pl-10 pr-4 w-full border-gray-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 rounded-lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Dialog
|
||||||
|
open={isModalOpen}
|
||||||
|
onOpenChange={setIsModalOpen}
|
||||||
|
>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setEditing(false);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
}}
|
||||||
|
className="bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded-lg flex items-center gap-2 transition-colors duration-200"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Buat Jenis Sanksi
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-xl font-semibold">
|
||||||
|
{editing
|
||||||
|
? "Ubah JenisSanksi"
|
||||||
|
: "Buat JenisSanksi"}
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className="flex flex-col gap-4 mt-4"
|
||||||
|
>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium text-gray-700">
|
||||||
|
Name
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Masukkan nama JenisSanksi"
|
||||||
|
value={data.NamaJenisSanksi}
|
||||||
|
onChange={(e) =>
|
||||||
|
setData(
|
||||||
|
"NamaJenisSanksi",
|
||||||
|
e.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
{/* <div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium text-gray-700">
|
||||||
|
Publish
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
value={
|
||||||
|
data.is_publish
|
||||||
|
? "true"
|
||||||
|
: "false"
|
||||||
|
}
|
||||||
|
onValueChange={(
|
||||||
|
value
|
||||||
|
) =>
|
||||||
|
setData(
|
||||||
|
"is_publish",
|
||||||
|
value === "true"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
<SelectValue placeholder="Select status" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="true">
|
||||||
|
Published
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="false">
|
||||||
|
Unpublished
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
<DialogFooter className="mt-6">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
|
||||||
|
>
|
||||||
|
{editing ? "Ubah" : "Buat"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="rounded-lg border border-gray-200 overflow-hidden">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow className="bg-gray-50 dark:bg-black/50">
|
||||||
|
{/* <TableHead className="font-semibold">
|
||||||
|
ID
|
||||||
|
</TableHead> */}
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
No
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
Name
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
Actions
|
||||||
|
</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{currentItems.map((jenissanksi, index) => (
|
||||||
|
<TableRow
|
||||||
|
key={
|
||||||
|
jenissanksi.JenisSanksiId ||
|
||||||
|
"new"
|
||||||
|
}
|
||||||
|
className="hover:bg-gray-50 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
{/* <TableCell>{category.id}</TableCell> */}
|
||||||
|
<TableCell>
|
||||||
|
{startIndex + index + 1}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{jenissanksi.NamaJenisSanksi}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
handleEdit(jenissanksi)
|
||||||
|
}
|
||||||
|
variant="outline"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Pencil className="h-4 w-4" />
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
setDeleteConfirm(
|
||||||
|
jenissanksi
|
||||||
|
)
|
||||||
|
}
|
||||||
|
variant="destructive"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
{currentItems.length === 0 && (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={4}
|
||||||
|
className="text-center py-4 text-gray-500"
|
||||||
|
>
|
||||||
|
Tidak ada data
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
{/* <div className="flex flex-row mt-3">
|
||||||
|
<div className="text-sm w-1/2">
|
||||||
|
1 : Pengumuman <br /> 2 : Undangan
|
||||||
|
</div>
|
||||||
|
<div className="text-sm w-1/2">
|
||||||
|
3 : Peraturan <br /> 4 : Popup Home
|
||||||
|
</div>
|
||||||
|
</div> */}
|
||||||
|
<div className="flex items-center justify-between mt-4">
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
Showing {startIndex + 1} to{" "}
|
||||||
|
{Math.min(endIndex, filteredJenisSanksi.length)}{" "}
|
||||||
|
of {filteredJenisSanksi.length} entries
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
handlePageChange(currentPage - 1)
|
||||||
|
}
|
||||||
|
disabled={currentPage === 1}
|
||||||
|
>
|
||||||
|
<ChevronLeft className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
{Array.from(
|
||||||
|
{ length: totalPages },
|
||||||
|
(_, i) => i + 1
|
||||||
|
).map((page) => (
|
||||||
|
<Button
|
||||||
|
key={page}
|
||||||
|
variant={
|
||||||
|
currentPage === page
|
||||||
|
? "default"
|
||||||
|
: "outline"
|
||||||
|
}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handlePageChange(page)}
|
||||||
|
>
|
||||||
|
{page}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
handlePageChange(currentPage + 1)
|
||||||
|
}
|
||||||
|
disabled={currentPage === totalPages}
|
||||||
|
>
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{deleteConfirm && (
|
||||||
|
<Dialog open={true} onOpenChange={() => setDeleteConfirm(null)}>
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-xl font-semibold">
|
||||||
|
Konfirmasi Penghapusan
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="py-4">
|
||||||
|
<p className="text-gray-600">
|
||||||
|
Apakah Anda yakin ingin menghapus "
|
||||||
|
<span className="font-medium">
|
||||||
|
{deleteConfirm.NamaJenisSanksi}
|
||||||
|
</span>
|
||||||
|
"?
|
||||||
|
<br />
|
||||||
|
<span className="text-red-500">
|
||||||
|
Tindakan ini tidak dapat dibatalkan.
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<DialogFooter className="gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() => setDeleteConfirm(null)}
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleDelete}
|
||||||
|
variant="destructive"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
Ya, hapus
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
|
<Toaster />
|
||||||
|
</AuthenticatedLayout>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,461 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useForm } from "@inertiajs/react";
|
||||||
|
import { PageProps } from "@/types";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
TableHead,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
// import { toast } from "react-toastify";
|
||||||
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogFooter,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Search,
|
||||||
|
Plus,
|
||||||
|
Pencil,
|
||||||
|
Trash2,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
} from "lucide-react";
|
||||||
|
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||||
|
import { Head } from "@inertiajs/react";
|
||||||
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
|
interface Kategori {
|
||||||
|
KategoriId: number | null;
|
||||||
|
NamaKategori: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ITEMS_PER_PAGE = 5;
|
||||||
|
|
||||||
|
export default function KategoriIndex({
|
||||||
|
kategori,
|
||||||
|
}: PageProps<{ kategori: Kategori[] }>) {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
setData,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
delete: destroy,
|
||||||
|
reset,
|
||||||
|
} = useForm<Kategori>({
|
||||||
|
KategoriId: null,
|
||||||
|
NamaKategori: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [deleteConfirm, setDeleteConfirm] = useState<Kategori | null>(null);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const [filteredKategori, setFilteredKategori] = useState(kategori);
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let filtered = kategori;
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
filtered = filtered.filter((kategori) =>
|
||||||
|
kategori.NamaKategori.toLowerCase().includes(
|
||||||
|
search.toLowerCase()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilteredKategori(filtered);
|
||||||
|
setCurrentPage(1);
|
||||||
|
}, [kategori, search]);
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(filteredKategori.length / ITEMS_PER_PAGE);
|
||||||
|
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
||||||
|
const endIndex = startIndex + ITEMS_PER_PAGE;
|
||||||
|
const currentItems = filteredKategori.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearch(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (editing) {
|
||||||
|
put(`/admin/kategori/${data.KategoriId}`, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Kategori berhasil diperbarui",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setIsModalOpen(false);
|
||||||
|
reset();
|
||||||
|
setEditing(false);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description:
|
||||||
|
"Terjadi kesalahan saat memperbarui kategori",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
post("/admin/kategori", {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Kategori berhasil dibuat",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setIsModalOpen(false);
|
||||||
|
reset();
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description: "Terjadi kesalahan saat membuat kategori",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = (kategori: Kategori) => {
|
||||||
|
setData({ ...kategori });
|
||||||
|
setEditing(true);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
if (deleteConfirm) {
|
||||||
|
destroy(`/admin/kategori/${deleteConfirm.KategoriId}`, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Kategori berhasil dihapus",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setDeleteConfirm(null);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description:
|
||||||
|
"Terjadi kesalahan saat menghapus kategori",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
setDeleteConfirm(null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<AuthenticatedLayout header={"Kategori Post"}>
|
||||||
|
<Head title="Kategori Post" />
|
||||||
|
<div className="container mx-auto p-4">
|
||||||
|
<Card className="shadow-lg">
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex flex-col space-y-4">
|
||||||
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
||||||
|
<div className="relative w-full md:w-96">
|
||||||
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 h-4 w-4" />
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Cari Kategori..."
|
||||||
|
value={search}
|
||||||
|
onChange={handleSearch}
|
||||||
|
className="pl-10 pr-4 w-full border-gray-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 rounded-lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Dialog
|
||||||
|
open={isModalOpen}
|
||||||
|
onOpenChange={setIsModalOpen}
|
||||||
|
>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setEditing(false);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
}}
|
||||||
|
className="bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded-lg flex items-center gap-2 transition-colors duration-200"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Buat Kategori
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-xl font-semibold">
|
||||||
|
{editing
|
||||||
|
? "Ubah Kategori"
|
||||||
|
: "Buat Kategori"}
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className="flex flex-col gap-4 mt-4"
|
||||||
|
>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium text-gray-700">
|
||||||
|
Name
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Masukkan nama kategori"
|
||||||
|
value={data.NamaKategori}
|
||||||
|
onChange={(e) =>
|
||||||
|
setData(
|
||||||
|
"NamaKategori",
|
||||||
|
e.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
{/* <div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium text-gray-700">
|
||||||
|
Publish
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
value={
|
||||||
|
data.is_publish
|
||||||
|
? "true"
|
||||||
|
: "false"
|
||||||
|
}
|
||||||
|
onValueChange={(
|
||||||
|
value
|
||||||
|
) =>
|
||||||
|
setData(
|
||||||
|
"is_publish",
|
||||||
|
value === "true"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
<SelectValue placeholder="Select status" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="true">
|
||||||
|
Published
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="false">
|
||||||
|
Unpublished
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
<DialogFooter className="mt-6">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
|
||||||
|
>
|
||||||
|
{editing ? "Ubah" : "Buat"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="rounded-lg border border-gray-200 overflow-hidden">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow className="bg-gray-50 dark:bg-black/50">
|
||||||
|
{/* <TableHead className="font-semibold">
|
||||||
|
ID
|
||||||
|
</TableHead> */}
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
No
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
Name
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
Actions
|
||||||
|
</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{currentItems.map((kategori, index) => (
|
||||||
|
<TableRow
|
||||||
|
key={kategori.KategoriId || "new"}
|
||||||
|
className="hover:bg-gray-50 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
{/* <TableCell>{category.id}</TableCell> */}
|
||||||
|
<TableCell>
|
||||||
|
{startIndex + index + 1}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{kategori.NamaKategori}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
handleEdit(kategori)
|
||||||
|
}
|
||||||
|
variant="outline"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Pencil className="h-4 w-4" />
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
setDeleteConfirm(
|
||||||
|
kategori
|
||||||
|
)
|
||||||
|
}
|
||||||
|
variant="destructive"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
{currentItems.length === 0 && (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={4}
|
||||||
|
className="text-center py-4 text-gray-500"
|
||||||
|
>
|
||||||
|
Tidak ada data
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
{/* <div className="flex flex-row mt-3">
|
||||||
|
<div className="text-sm w-1/2">
|
||||||
|
1 : Pengumuman <br /> 2 : Undangan
|
||||||
|
</div>
|
||||||
|
<div className="text-sm w-1/2">
|
||||||
|
3 : Peraturan <br /> 4 : Popup Home
|
||||||
|
</div>
|
||||||
|
</div> */}
|
||||||
|
<div className="flex items-center justify-between mt-4">
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
Showing {startIndex + 1} to{" "}
|
||||||
|
{Math.min(endIndex, filteredKategori.length)} of{" "}
|
||||||
|
{filteredKategori.length} entries
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
handlePageChange(currentPage - 1)
|
||||||
|
}
|
||||||
|
disabled={currentPage === 1}
|
||||||
|
>
|
||||||
|
<ChevronLeft className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
{Array.from(
|
||||||
|
{ length: totalPages },
|
||||||
|
(_, i) => i + 1
|
||||||
|
).map((page) => (
|
||||||
|
<Button
|
||||||
|
key={page}
|
||||||
|
variant={
|
||||||
|
currentPage === page
|
||||||
|
? "default"
|
||||||
|
: "outline"
|
||||||
|
}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handlePageChange(page)}
|
||||||
|
>
|
||||||
|
{page}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
handlePageChange(currentPage + 1)
|
||||||
|
}
|
||||||
|
disabled={currentPage === totalPages}
|
||||||
|
>
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{deleteConfirm && (
|
||||||
|
<Dialog open={true} onOpenChange={() => setDeleteConfirm(null)}>
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-xl font-semibold">
|
||||||
|
Konfirmasi Penghapusan
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="py-4">
|
||||||
|
<p className="text-gray-600">
|
||||||
|
Apakah Anda yakin ingin menghapus "
|
||||||
|
<span className="font-medium">
|
||||||
|
{deleteConfirm.NamaKategori}
|
||||||
|
</span>
|
||||||
|
"?
|
||||||
|
<br />
|
||||||
|
<span className="text-red-500">
|
||||||
|
Tindakan ini tidak dapat dibatalkan.
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<DialogFooter className="gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() => setDeleteConfirm(null)}
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleDelete}
|
||||||
|
variant="destructive"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
Ya, hapus
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
|
<Toaster />
|
||||||
|
</AuthenticatedLayout>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,484 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useForm } from "@inertiajs/react";
|
||||||
|
import { PageProps } from "@/types";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
TableHead,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
// import { toast } from "react-toastify";
|
||||||
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogFooter,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Search,
|
||||||
|
Plus,
|
||||||
|
Pencil,
|
||||||
|
Trash2,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
} from "lucide-react";
|
||||||
|
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||||
|
import { Head } from "@inertiajs/react";
|
||||||
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
|
interface HistoryKegiatan {
|
||||||
|
HistoryKegiatanId: number | null;
|
||||||
|
NamaHistoryKegiatan: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ITEMS_PER_PAGE = 5;
|
||||||
|
|
||||||
|
export default function HistoryKegiatanIndex({
|
||||||
|
historykegiatan,
|
||||||
|
}: PageProps<{ historykegiatan: HistoryKegiatan[] }>) {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
setData,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
delete: destroy,
|
||||||
|
reset,
|
||||||
|
} = useForm<HistoryKegiatan>({
|
||||||
|
HistoryKegiatanId: null,
|
||||||
|
NamaHistoryKegiatan: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [deleteConfirm, setDeleteConfirm] = useState<HistoryKegiatan | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const [filteredHistoryKegiatan, setFilteredHistoryKegiatan] =
|
||||||
|
useState(historykegiatan);
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let filtered = historykegiatan;
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
filtered = filtered.filter((historykegiatan) =>
|
||||||
|
historykegiatan.NamaHistoryKegiatan.toLowerCase().includes(
|
||||||
|
search.toLowerCase()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilteredHistoryKegiatan(filtered);
|
||||||
|
setCurrentPage(1);
|
||||||
|
}, [historykegiatan, search]);
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(
|
||||||
|
filteredHistoryKegiatan.length / ITEMS_PER_PAGE
|
||||||
|
);
|
||||||
|
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
||||||
|
const endIndex = startIndex + ITEMS_PER_PAGE;
|
||||||
|
const currentItems = filteredHistoryKegiatan.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearch(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (editing) {
|
||||||
|
put(`/admin/historykegiatan/${data.HistoryKegiatanId}`, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "History Kegiatan berhasil diperbarui",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setIsModalOpen(false);
|
||||||
|
reset();
|
||||||
|
setEditing(false);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description:
|
||||||
|
"Terjadi kesalahan saat memperbarui History Kegiatan",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
post("/admin/historykegiatan", {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "History Kegiatan berhasil dibuat",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setIsModalOpen(false);
|
||||||
|
reset();
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description:
|
||||||
|
"Terjadi kesalahan saat membuat History Kegiatan",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = (historykegiatan: HistoryKegiatan) => {
|
||||||
|
setData({ ...historykegiatan });
|
||||||
|
setEditing(true);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
if (deleteConfirm) {
|
||||||
|
destroy(
|
||||||
|
`/admin/historykegiatan/${deleteConfirm.HistoryKegiatanId}`,
|
||||||
|
{
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "History Kegiatan berhasil dihapus",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setDeleteConfirm(null);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description:
|
||||||
|
"Terjadi kesalahan saat menghapus History Kegiatan",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
setDeleteConfirm(null);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<AuthenticatedLayout header={"History Kegiatan"}>
|
||||||
|
<Head title="History Kegiatan" />
|
||||||
|
<div className="container mx-auto p-4">
|
||||||
|
<Card className="shadow-lg">
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex flex-col space-y-4">
|
||||||
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
||||||
|
<div className="relative w-full md:w-96">
|
||||||
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 h-4 w-4" />
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Cari History Kegiatan..."
|
||||||
|
value={search}
|
||||||
|
onChange={handleSearch}
|
||||||
|
className="pl-10 pr-4 w-full border-gray-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 rounded-lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Dialog
|
||||||
|
open={isModalOpen}
|
||||||
|
onOpenChange={setIsModalOpen}
|
||||||
|
>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setEditing(false);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
}}
|
||||||
|
className="bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded-lg flex items-center gap-2 transition-colors duration-200"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Buat History Kegiatan
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-xl font-semibold">
|
||||||
|
{editing
|
||||||
|
? "Ubah History Kegiatan"
|
||||||
|
: "Buat History Kegiatan"}
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className="flex flex-col gap-4 mt-4"
|
||||||
|
>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium text-gray-700">
|
||||||
|
Name
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Masukkan History Kegiatan"
|
||||||
|
value={
|
||||||
|
data.NamaHistoryKegiatan
|
||||||
|
}
|
||||||
|
onChange={(e) =>
|
||||||
|
setData(
|
||||||
|
"NamaHistoryKegiatan",
|
||||||
|
e.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
{/* <div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium text-gray-700">
|
||||||
|
Publish
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
value={
|
||||||
|
data.is_publish
|
||||||
|
? "true"
|
||||||
|
: "false"
|
||||||
|
}
|
||||||
|
onValueChange={(
|
||||||
|
value
|
||||||
|
) =>
|
||||||
|
setData(
|
||||||
|
"is_publish",
|
||||||
|
value === "true"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
<SelectValue placeholder="Select status" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="true">
|
||||||
|
Published
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="false">
|
||||||
|
Unpublished
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
<DialogFooter className="mt-6">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
|
||||||
|
>
|
||||||
|
{editing ? "Ubah" : "Buat"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="rounded-lg border border-gray-200 overflow-hidden">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow className="bg-gray-50 dark:bg-black/50">
|
||||||
|
{/* <TableHead className="font-semibold">
|
||||||
|
ID
|
||||||
|
</TableHead> */}
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
No
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
Name
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
Actions
|
||||||
|
</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{currentItems.map(
|
||||||
|
(historykegiatan, index) => (
|
||||||
|
<TableRow
|
||||||
|
key={
|
||||||
|
historykegiatan.HistoryKegiatanId ||
|
||||||
|
"new"
|
||||||
|
}
|
||||||
|
className="hover:bg-gray-50 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
{/* <TableCell>{category.id}</TableCell> */}
|
||||||
|
<TableCell>
|
||||||
|
{startIndex + index + 1}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{
|
||||||
|
historykegiatan.NamaHistoryKegiatan
|
||||||
|
}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
handleEdit(
|
||||||
|
historykegiatan
|
||||||
|
)
|
||||||
|
}
|
||||||
|
variant="outline"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Pencil className="h-4 w-4" />
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
setDeleteConfirm(
|
||||||
|
historykegiatan
|
||||||
|
)
|
||||||
|
}
|
||||||
|
variant="destructive"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
{currentItems.length === 0 && (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={4}
|
||||||
|
className="text-center py-4 text-gray-500"
|
||||||
|
>
|
||||||
|
Tidak ada data
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
{/* <div className="flex flex-row mt-3">
|
||||||
|
<div className="text-sm w-1/2">
|
||||||
|
1 : Pengumuman <br /> 2 : Undangan
|
||||||
|
</div>
|
||||||
|
<div className="text-sm w-1/2">
|
||||||
|
3 : Peraturan <br /> 4 : Popup Home
|
||||||
|
</div>
|
||||||
|
</div> */}
|
||||||
|
<div className="flex items-center justify-between mt-4">
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
Showing {startIndex + 1} to{" "}
|
||||||
|
{Math.min(
|
||||||
|
endIndex,
|
||||||
|
filteredHistoryKegiatan.length
|
||||||
|
)}{" "}
|
||||||
|
of {filteredHistoryKegiatan.length} entries
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
handlePageChange(currentPage - 1)
|
||||||
|
}
|
||||||
|
disabled={currentPage === 1}
|
||||||
|
>
|
||||||
|
<ChevronLeft className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
{Array.from(
|
||||||
|
{ length: totalPages },
|
||||||
|
(_, i) => i + 1
|
||||||
|
).map((page) => (
|
||||||
|
<Button
|
||||||
|
key={page}
|
||||||
|
variant={
|
||||||
|
currentPage === page
|
||||||
|
? "default"
|
||||||
|
: "outline"
|
||||||
|
}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handlePageChange(page)}
|
||||||
|
>
|
||||||
|
{page}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
handlePageChange(currentPage + 1)
|
||||||
|
}
|
||||||
|
disabled={currentPage === totalPages}
|
||||||
|
>
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{deleteConfirm && (
|
||||||
|
<Dialog open={true} onOpenChange={() => setDeleteConfirm(null)}>
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-xl font-semibold">
|
||||||
|
Konfirmasi Penghapusan
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="py-4">
|
||||||
|
<p className="text-gray-600">
|
||||||
|
Apakah Anda yakin ingin menghapus "
|
||||||
|
<span className="font-medium">
|
||||||
|
{deleteConfirm.NamaHistoryKegiatan}
|
||||||
|
</span>
|
||||||
|
"?
|
||||||
|
<br />
|
||||||
|
<span className="text-red-500">
|
||||||
|
Tindakan ini tidak dapat dibatalkan.
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<DialogFooter className="gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() => setDeleteConfirm(null)}
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleDelete}
|
||||||
|
variant="destructive"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
Ya, hapus
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
|
<Toaster />
|
||||||
|
</AuthenticatedLayout>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,479 @@
|
||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useForm } from "@inertiajs/react";
|
||||||
|
import { PageProps } from "@/types";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
TableHead,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
// import { toast } from "react-toastify";
|
||||||
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card";
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogTrigger,
|
||||||
|
DialogContent,
|
||||||
|
DialogHeader,
|
||||||
|
DialogTitle,
|
||||||
|
DialogFooter,
|
||||||
|
} from "@/components/ui/dialog";
|
||||||
|
import {
|
||||||
|
Search,
|
||||||
|
Plus,
|
||||||
|
Pencil,
|
||||||
|
Trash2,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
} from "lucide-react";
|
||||||
|
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||||
|
import { Head } from "@inertiajs/react";
|
||||||
|
import { Toaster } from "@/components/ui/toaster";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectItem,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "@/components/ui/select";
|
||||||
|
|
||||||
|
interface JenisKegiatan {
|
||||||
|
JenisKegiatanId: number | null;
|
||||||
|
NamaJenisKegiatan: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ITEMS_PER_PAGE = 5;
|
||||||
|
|
||||||
|
export default function JenisKegiatanIndex({
|
||||||
|
jeniskegiatan,
|
||||||
|
}: PageProps<{ jeniskegiatan: JenisKegiatan[] }>) {
|
||||||
|
const {
|
||||||
|
data,
|
||||||
|
setData,
|
||||||
|
post,
|
||||||
|
put,
|
||||||
|
delete: destroy,
|
||||||
|
reset,
|
||||||
|
} = useForm<JenisKegiatan>({
|
||||||
|
JenisKegiatanId: null,
|
||||||
|
NamaJenisKegiatan: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
|
const [editing, setEditing] = useState(false);
|
||||||
|
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||||
|
const [deleteConfirm, setDeleteConfirm] = useState<JenisKegiatan | null>(
|
||||||
|
null
|
||||||
|
);
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
|
||||||
|
const [filteredJenisKegiatan, setFilteredJenisKegiatan] =
|
||||||
|
useState(jeniskegiatan);
|
||||||
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
let filtered = jeniskegiatan;
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
filtered = filtered.filter((jeniskegiatan) =>
|
||||||
|
jeniskegiatan.NamaJenisKegiatan.toLowerCase().includes(
|
||||||
|
search.toLowerCase()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
setFilteredJenisKegiatan(filtered);
|
||||||
|
setCurrentPage(1);
|
||||||
|
}, [jeniskegiatan, search]);
|
||||||
|
|
||||||
|
const totalPages = Math.ceil(filteredJenisKegiatan.length / ITEMS_PER_PAGE);
|
||||||
|
const startIndex = (currentPage - 1) * ITEMS_PER_PAGE;
|
||||||
|
const endIndex = startIndex + ITEMS_PER_PAGE;
|
||||||
|
const currentItems = filteredJenisKegiatan.slice(startIndex, endIndex);
|
||||||
|
|
||||||
|
const handleSearch = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
|
setSearch(e.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
setCurrentPage(page);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = (e: React.FormEvent) => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
if (editing) {
|
||||||
|
put(`/admin/jeniskegiatan/${data.JenisKegiatanId}`, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Jenis Kegiatan berhasil diperbarui",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setIsModalOpen(false);
|
||||||
|
reset();
|
||||||
|
setEditing(false);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description:
|
||||||
|
"Terjadi kesalahan saat memperbarui Jenis Kegiatan",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
post("/admin/jeniskegiatan", {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Jenis Kegiatan berhasil dibuat",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setIsModalOpen(false);
|
||||||
|
reset();
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description:
|
||||||
|
"Terjadi kesalahan saat membuat Jenis Kegiatan",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEdit = (jeniskegiatan: JenisKegiatan) => {
|
||||||
|
setData({ ...jeniskegiatan });
|
||||||
|
setEditing(true);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDelete = () => {
|
||||||
|
if (deleteConfirm) {
|
||||||
|
destroy(`/admin/jeniskegiatan/${deleteConfirm.JenisKegiatanId}`, {
|
||||||
|
onSuccess: () => {
|
||||||
|
toast({
|
||||||
|
title: "Berhasil",
|
||||||
|
description: "Jenis Kegiatan berhasil dihapus",
|
||||||
|
variant: "default",
|
||||||
|
});
|
||||||
|
setDeleteConfirm(null);
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast({
|
||||||
|
title: "Gagal",
|
||||||
|
description:
|
||||||
|
"Terjadi kesalahan saat menghapus Jenis Kegiatan",
|
||||||
|
variant: "destructive",
|
||||||
|
});
|
||||||
|
setDeleteConfirm(null);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<AuthenticatedLayout header={"Jenis Kegiatan"}>
|
||||||
|
<Head title="Jenis Kegiatan" />
|
||||||
|
<div className="container mx-auto p-4">
|
||||||
|
<Card className="shadow-lg">
|
||||||
|
<CardHeader>
|
||||||
|
<div className="flex flex-col space-y-4">
|
||||||
|
<div className="flex flex-col md:flex-row justify-between items-start md:items-center gap-4">
|
||||||
|
<div className="relative w-full md:w-96">
|
||||||
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-500 h-4 w-4" />
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Cari Jenis Kegiatan..."
|
||||||
|
value={search}
|
||||||
|
onChange={handleSearch}
|
||||||
|
className="pl-10 pr-4 w-full border-gray-200 focus:border-blue-500 focus:ring-2 focus:ring-blue-200 rounded-lg"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<Dialog
|
||||||
|
open={isModalOpen}
|
||||||
|
onOpenChange={setIsModalOpen}
|
||||||
|
>
|
||||||
|
<DialogTrigger asChild>
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
setEditing(false);
|
||||||
|
setIsModalOpen(true);
|
||||||
|
}}
|
||||||
|
className="bg-blue-600 hover:bg-blue-700 text-white font-medium px-4 py-2 rounded-lg flex items-center gap-2 transition-colors duration-200"
|
||||||
|
>
|
||||||
|
<Plus className="h-4 w-4" />
|
||||||
|
Buat Jenis Kegiatan
|
||||||
|
</Button>
|
||||||
|
</DialogTrigger>
|
||||||
|
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-xl font-semibold">
|
||||||
|
{editing
|
||||||
|
? "Ubah Jenis Kegiatan"
|
||||||
|
: "Buat Jenis Kegiatan"}
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<form
|
||||||
|
onSubmit={handleSubmit}
|
||||||
|
className="flex flex-col gap-4 mt-4"
|
||||||
|
>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium text-gray-700">
|
||||||
|
Name
|
||||||
|
</label>
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
placeholder="Masukkan History Kegiatan"
|
||||||
|
value={
|
||||||
|
data.NamaJenisKegiatan
|
||||||
|
}
|
||||||
|
onChange={(e) =>
|
||||||
|
setData(
|
||||||
|
"NamaJenisKegiatan",
|
||||||
|
e.target.value
|
||||||
|
)
|
||||||
|
}
|
||||||
|
className="w-full"
|
||||||
|
/>
|
||||||
|
{/* <div className="space-y-2">
|
||||||
|
<label className="text-sm font-medium text-gray-700">
|
||||||
|
Publish
|
||||||
|
</label>
|
||||||
|
<Select
|
||||||
|
value={
|
||||||
|
data.is_publish
|
||||||
|
? "true"
|
||||||
|
: "false"
|
||||||
|
}
|
||||||
|
onValueChange={(
|
||||||
|
value
|
||||||
|
) =>
|
||||||
|
setData(
|
||||||
|
"is_publish",
|
||||||
|
value === "true"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-full">
|
||||||
|
<SelectValue placeholder="Select status" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
<SelectItem value="true">
|
||||||
|
Published
|
||||||
|
</SelectItem>
|
||||||
|
<SelectItem value="false">
|
||||||
|
Unpublished
|
||||||
|
</SelectItem>
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
<DialogFooter className="mt-6">
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
className="w-full bg-blue-600 hover:bg-blue-700 text-white"
|
||||||
|
>
|
||||||
|
{editing ? "Ubah" : "Buat"}
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</form>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardHeader>
|
||||||
|
<CardContent>
|
||||||
|
<div className="rounded-lg border border-gray-200 overflow-hidden">
|
||||||
|
<Table>
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow className="bg-gray-50 dark:bg-black/50">
|
||||||
|
{/* <TableHead className="font-semibold">
|
||||||
|
ID
|
||||||
|
</TableHead> */}
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
No
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
Name
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="font-semibold">
|
||||||
|
Actions
|
||||||
|
</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody>
|
||||||
|
{currentItems.map(
|
||||||
|
(jeniskegiatan, index) => (
|
||||||
|
<TableRow
|
||||||
|
key={
|
||||||
|
jeniskegiatan.JenisKegiatanId ||
|
||||||
|
"new"
|
||||||
|
}
|
||||||
|
className="hover:bg-gray-50 transition-colors duration-150"
|
||||||
|
>
|
||||||
|
{/* <TableCell>{category.id}</TableCell> */}
|
||||||
|
<TableCell>
|
||||||
|
{startIndex + index + 1}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
{
|
||||||
|
jeniskegiatan.NamaJenisKegiatan
|
||||||
|
}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
handleEdit(
|
||||||
|
jeniskegiatan
|
||||||
|
)
|
||||||
|
}
|
||||||
|
variant="outline"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Pencil className="h-4 w-4" />
|
||||||
|
Edit
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={() =>
|
||||||
|
setDeleteConfirm(
|
||||||
|
jeniskegiatan
|
||||||
|
)
|
||||||
|
}
|
||||||
|
variant="destructive"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
Delete
|
||||||
|
</Button>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)
|
||||||
|
)}
|
||||||
|
{currentItems.length === 0 && (
|
||||||
|
<TableRow>
|
||||||
|
<TableCell
|
||||||
|
colSpan={4}
|
||||||
|
className="text-center py-4 text-gray-500"
|
||||||
|
>
|
||||||
|
Tidak ada data
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
{/* <div className="flex flex-row mt-3">
|
||||||
|
<div className="text-sm w-1/2">
|
||||||
|
1 : Pengumuman <br /> 2 : Undangan
|
||||||
|
</div>
|
||||||
|
<div className="text-sm w-1/2">
|
||||||
|
3 : Peraturan <br /> 4 : Popup Home
|
||||||
|
</div>
|
||||||
|
</div> */}
|
||||||
|
<div className="flex items-center justify-between mt-4">
|
||||||
|
<div className="text-sm text-gray-500">
|
||||||
|
Showing {startIndex + 1} to{" "}
|
||||||
|
{Math.min(
|
||||||
|
endIndex,
|
||||||
|
filteredJenisKegiatan.length
|
||||||
|
)}{" "}
|
||||||
|
of {filteredJenisKegiatan.length} entries
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
handlePageChange(currentPage - 1)
|
||||||
|
}
|
||||||
|
disabled={currentPage === 1}
|
||||||
|
>
|
||||||
|
<ChevronLeft className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
{Array.from(
|
||||||
|
{ length: totalPages },
|
||||||
|
(_, i) => i + 1
|
||||||
|
).map((page) => (
|
||||||
|
<Button
|
||||||
|
key={page}
|
||||||
|
variant={
|
||||||
|
currentPage === page
|
||||||
|
? "default"
|
||||||
|
: "outline"
|
||||||
|
}
|
||||||
|
size="sm"
|
||||||
|
onClick={() => handlePageChange(page)}
|
||||||
|
>
|
||||||
|
{page}
|
||||||
|
</Button>
|
||||||
|
))}
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() =>
|
||||||
|
handlePageChange(currentPage + 1)
|
||||||
|
}
|
||||||
|
disabled={currentPage === totalPages}
|
||||||
|
>
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{deleteConfirm && (
|
||||||
|
<Dialog open={true} onOpenChange={() => setDeleteConfirm(null)}>
|
||||||
|
<DialogContent className="sm:max-w-md">
|
||||||
|
<DialogHeader>
|
||||||
|
<DialogTitle className="text-xl font-semibold">
|
||||||
|
Konfirmasi Penghapusan
|
||||||
|
</DialogTitle>
|
||||||
|
</DialogHeader>
|
||||||
|
<div className="py-4">
|
||||||
|
<p className="text-gray-600">
|
||||||
|
Apakah Anda yakin ingin menghapus "
|
||||||
|
<span className="font-medium">
|
||||||
|
{deleteConfirm.NamaJenisKegiatan}
|
||||||
|
</span>
|
||||||
|
"?
|
||||||
|
<br />
|
||||||
|
<span className="text-red-500">
|
||||||
|
Tindakan ini tidak dapat dibatalkan.
|
||||||
|
</span>
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<DialogFooter className="gap-2">
|
||||||
|
<Button
|
||||||
|
onClick={() => setDeleteConfirm(null)}
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
Batal
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleDelete}
|
||||||
|
variant="destructive"
|
||||||
|
className="flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Trash2 className="h-4 w-4" />
|
||||||
|
Ya, hapus
|
||||||
|
</Button>
|
||||||
|
</DialogFooter>
|
||||||
|
</DialogContent>
|
||||||
|
</Dialog>
|
||||||
|
)}
|
||||||
|
<Toaster />
|
||||||
|
</AuthenticatedLayout>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,161 @@
|
||||||
|
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||||
|
import { Head } from "@inertiajs/react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
export default function tentang() {
|
||||||
|
return (
|
||||||
|
<AuthenticatedLayout header={"Tentang SKL"}>
|
||||||
|
<Head title="Tentang SKL" />
|
||||||
|
<div className="container mx-auto p-6">
|
||||||
|
<div className="bg-white rounded-xl shadow-lg overflow-hidden">
|
||||||
|
<div className="flex flex-col md:flex-row">
|
||||||
|
{/* Left side - Image */}
|
||||||
|
<div className="md:w-1/3 p-6 bg-gradient-to-br from-green-50 to-blue-50">
|
||||||
|
<a
|
||||||
|
href="https://heyzine.com/flip-book/3a966a8c8d.html"
|
||||||
|
target="_blank"
|
||||||
|
>
|
||||||
|
<img
|
||||||
|
className="rounded-lg shadow-md hover:shadow-xl transition-transform duration-300 w-full h-auto object-cover transform hover:scale-105"
|
||||||
|
src="/assets/cover-panduan-skl.png"
|
||||||
|
alt="SKL Cover"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
<div className="mt-6 text-center">
|
||||||
|
<a
|
||||||
|
href="#"
|
||||||
|
className="inline-flex items-center justify-center px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition-colors duration-200"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
className="w-4 h-4 mr-2"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
d="M12 10v6m0 0l-3-3m3 3l3-3m2 8H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
Unduh PDF
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right side - Content */}
|
||||||
|
<div className="md:w-2/3 p-8">
|
||||||
|
<h1 className="text-3xl font-bold text-gray-800 mb-6">
|
||||||
|
STATUS KETAATAN LINGKUNGAN
|
||||||
|
</h1>
|
||||||
|
|
||||||
|
<div className="space-y-6 text-gray-600">
|
||||||
|
<div className="flex items-start">
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
<svg
|
||||||
|
className="w-6 h-6 text-green-500 mr-4"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
d="M13 10V3L4 14h7v7l9-11h-7z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm leading-relaxed">
|
||||||
|
Merupakan bagian dari perangkat lunak
|
||||||
|
CReASINDO yang dikembangkan oleh PT
|
||||||
|
MANDIRI CReASINDO.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-start">
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
<svg
|
||||||
|
className="w-6 h-6 text-blue-500 mr-4"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm leading-relaxed text-justify">
|
||||||
|
Kegunaan Produk ini adalah menyediakan
|
||||||
|
sistem yang dapat memberikan pelayanan
|
||||||
|
dalam pelaporan, analisa dan evaluasi
|
||||||
|
pengelolaan lingkungan dilakukan oleh
|
||||||
|
perusahaan atau kegiatan usaha yang
|
||||||
|
berada di Propinsi DKI Jakarta.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="flex items-start">
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
<svg
|
||||||
|
className="w-6 h-6 text-yellow-500 mr-4"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
d="M12 15v2m-6 4h12a2 2 0 002-2v-6a2 2 0 00-2-2H6a2 2 0 00-2 2v6a2 2 0 002 2zm10-10V7a4 4 0 00-8 0v4h8z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<p className="text-sm leading-relaxed text-justify">
|
||||||
|
Produk ini diberikan kepada BPLHD DKI
|
||||||
|
JAKARTA melalui Proyek "SISTEM
|
||||||
|
PENGAWASAN DAN PENGENDALIAN PENCEMARAN
|
||||||
|
LINGKUNGAN BPLHD DKI JAKARTA 2015"
|
||||||
|
Perangkat Lunak ini dilindungi oleh
|
||||||
|
undang-undang negara Republik Indonesia
|
||||||
|
dan Perjanjian Internasional yang
|
||||||
|
berkaitan dengan Hak-Hak Kekayaan
|
||||||
|
Intelektual.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-8 p-4 bg-blue-50 rounded-lg border border-blue-100">
|
||||||
|
<div className="flex items-center text-blue-600">
|
||||||
|
<svg
|
||||||
|
className="w-5 h-5 mr-2"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
strokeWidth="2"
|
||||||
|
d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
<p className="text-sm font-medium">
|
||||||
|
Silahkan klik gambar cover di samping
|
||||||
|
untuk membuka buku panduan versi
|
||||||
|
flipbook
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AuthenticatedLayout>
|
||||||
|
);
|
||||||
|
}
|
|
@ -0,0 +1,487 @@
|
||||||
|
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||||
|
import { Head } from "@inertiajs/react";
|
||||||
|
import React from "react";
|
||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableHeader,
|
||||||
|
TableRow,
|
||||||
|
} from "@/components/ui/table";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { Input } from "@/components/ui/input";
|
||||||
|
import {
|
||||||
|
BadgeCheck,
|
||||||
|
ChevronLeft,
|
||||||
|
ChevronRight,
|
||||||
|
ChevronUp,
|
||||||
|
Download,
|
||||||
|
FileText,
|
||||||
|
Search,
|
||||||
|
Upload,
|
||||||
|
} from "lucide-react";
|
||||||
|
|
||||||
|
import Select from "react-select";
|
||||||
|
|
||||||
|
interface SKLData {
|
||||||
|
no: number;
|
||||||
|
namaPerusahaan: string;
|
||||||
|
tahun: number;
|
||||||
|
periodePelaporan: string;
|
||||||
|
skl: number;
|
||||||
|
spbm: number;
|
||||||
|
hasil: {
|
||||||
|
il: string;
|
||||||
|
al: string;
|
||||||
|
lb3: string;
|
||||||
|
sb: string;
|
||||||
|
bs: string;
|
||||||
|
stb: string;
|
||||||
|
lp: string;
|
||||||
|
kdm: string;
|
||||||
|
};
|
||||||
|
stt: string;
|
||||||
|
se: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PelaporanIndex() {
|
||||||
|
const [year, setYear] = useState<string>("2025");
|
||||||
|
const [quarter, setQuarter] = useState<string>("Triwulan 1");
|
||||||
|
// const [company, setCompany] = useState<string>("PT Ajinomoto Indonesia");
|
||||||
|
|
||||||
|
const companyOptions = [
|
||||||
|
{ value: "PT Ajinomoto Indonesia", label: "PT Ajinomoto Indonesia" },
|
||||||
|
{ value: "PT Unilever Indonesia", label: "PT Unilever Indonesia" },
|
||||||
|
{
|
||||||
|
value: "PT Indofood Sukses Makmur",
|
||||||
|
label: "PT Indofood Sukses Makmur",
|
||||||
|
},
|
||||||
|
{ value: "PT Mayora Indah", label: "PT Mayora Indah" },
|
||||||
|
];
|
||||||
|
|
||||||
|
const [company, setCompany] = useState<{
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
} | null>(companyOptions[0]);
|
||||||
|
// Color coding helper
|
||||||
|
const getStatusColor = (status: string) => {
|
||||||
|
switch (status) {
|
||||||
|
case "belum":
|
||||||
|
return "bg-red-100 text-red-800";
|
||||||
|
case "pending":
|
||||||
|
return "bg-gray-100 text-gray-800";
|
||||||
|
case "siap":
|
||||||
|
return "bg-orange-100 text-orange-800";
|
||||||
|
case "selesai":
|
||||||
|
return "bg-green-100 text-green-800";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<AuthenticatedLayout header={"Pelaporan SKL"}>
|
||||||
|
<Head title="Pelaporan SKL" />
|
||||||
|
|
||||||
|
<div className="p-8">
|
||||||
|
{/* Filter Section */}
|
||||||
|
<div className="mb-6 space-y-4">
|
||||||
|
<div className="flex justify-between items-center">
|
||||||
|
<div className="flex gap-4 items-center">
|
||||||
|
<Select
|
||||||
|
options={companyOptions}
|
||||||
|
value={company}
|
||||||
|
onChange={setCompany}
|
||||||
|
placeholder="Pilih Perusahaan"
|
||||||
|
isSearchable
|
||||||
|
className="w-[300px]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
value={year}
|
||||||
|
onChange={(e) => setYear(e.target.value)}
|
||||||
|
className="w-24"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
options={[
|
||||||
|
{
|
||||||
|
value: "Triwulan 1",
|
||||||
|
label: "Triwulan 1",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Triwulan 2",
|
||||||
|
label: "Triwulan 2",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Triwulan 3",
|
||||||
|
label: "Triwulan 3",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: "Triwulan 4",
|
||||||
|
label: "Triwulan 4",
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
value={{ value: quarter, label: quarter }}
|
||||||
|
onChange={(newValue) =>
|
||||||
|
setQuarter(newValue?.value || "Triwulan 1")
|
||||||
|
}
|
||||||
|
placeholder="Pilih Periode"
|
||||||
|
className="w-[150px]"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Button>
|
||||||
|
<Search className="w-4 h-4 mr-2" />
|
||||||
|
Cari
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* <div className="flex gap-2">
|
||||||
|
<Button variant="outline">
|
||||||
|
<Upload className="w-4 h-4 mr-2" />
|
||||||
|
Import
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline">
|
||||||
|
<Download className="w-4 h-4 mr-2" />
|
||||||
|
Export
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline">
|
||||||
|
<FileText className="w-4 h-4 mr-2" />
|
||||||
|
Template
|
||||||
|
</Button>
|
||||||
|
</div> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Table Section */}
|
||||||
|
<div className="w-full overflow-x-auto">
|
||||||
|
<Table className="min-w-[1000px] border-collapse">
|
||||||
|
<TableHeader>
|
||||||
|
<TableRow className="border-b border-t bg-green-800">
|
||||||
|
<TableHead
|
||||||
|
rowSpan={2}
|
||||||
|
className="w-[50px] border-r border-l text-white"
|
||||||
|
>
|
||||||
|
No.
|
||||||
|
</TableHead>
|
||||||
|
<TableHead
|
||||||
|
rowSpan={2}
|
||||||
|
className="border-r text-center text-white"
|
||||||
|
>
|
||||||
|
Nama Perusahaan
|
||||||
|
</TableHead>
|
||||||
|
<TableHead
|
||||||
|
rowSpan={2}
|
||||||
|
className="border-r text-center text-white"
|
||||||
|
>
|
||||||
|
Tahun
|
||||||
|
</TableHead>
|
||||||
|
<TableHead
|
||||||
|
rowSpan={2}
|
||||||
|
className="border-r text-center text-white"
|
||||||
|
>
|
||||||
|
Periode Pelaporan
|
||||||
|
</TableHead>
|
||||||
|
<TableHead
|
||||||
|
rowSpan={2}
|
||||||
|
className="border-r text-center text-white"
|
||||||
|
>
|
||||||
|
SKL (%)
|
||||||
|
</TableHead>
|
||||||
|
<TableHead
|
||||||
|
rowSpan={2}
|
||||||
|
className="border-r text-center text-white"
|
||||||
|
>
|
||||||
|
SPBM (%)
|
||||||
|
</TableHead>
|
||||||
|
<TableHead
|
||||||
|
className="border-r text-center text-white"
|
||||||
|
colSpan={8}
|
||||||
|
>
|
||||||
|
Hasil (Nilai SKL)
|
||||||
|
</TableHead>
|
||||||
|
<TableHead
|
||||||
|
rowSpan={2}
|
||||||
|
className="border-r text-center text-white"
|
||||||
|
>
|
||||||
|
STT
|
||||||
|
</TableHead>
|
||||||
|
<TableHead
|
||||||
|
rowSpan={2}
|
||||||
|
className="border-r text-center text-white"
|
||||||
|
>
|
||||||
|
SE
|
||||||
|
</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
<TableRow className="border-b bg-green-600">
|
||||||
|
<TableHead className="text-center border-r text-white">
|
||||||
|
IL
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="text-center border-r text-white">
|
||||||
|
AL
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="text-center border-r text-white">
|
||||||
|
LB3
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="text-center border-r text-white">
|
||||||
|
SB
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="text-center border-r text-white">
|
||||||
|
BS
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="text-center border-r text-white">
|
||||||
|
STB
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="text-center border-r text-white">
|
||||||
|
LP
|
||||||
|
</TableHead>
|
||||||
|
<TableHead className="text-center border-r text-white">
|
||||||
|
KDM
|
||||||
|
</TableHead>
|
||||||
|
</TableRow>
|
||||||
|
</TableHeader>
|
||||||
|
<TableBody className="border-b">
|
||||||
|
<TableRow className="border-b">
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l"
|
||||||
|
>
|
||||||
|
1
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l"
|
||||||
|
>
|
||||||
|
PT Ajinomoto Indonesia
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l"
|
||||||
|
>
|
||||||
|
2023
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l"
|
||||||
|
>
|
||||||
|
Triwulan 2 / Semester 1
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l "
|
||||||
|
>
|
||||||
|
76.70
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l"
|
||||||
|
>
|
||||||
|
76.70
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l bg-red-100 text-red-700"
|
||||||
|
>
|
||||||
|
00.00
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l bg-red-100 text-red-700"
|
||||||
|
>
|
||||||
|
00.00
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l bg-red-100 text-red-700"
|
||||||
|
>
|
||||||
|
00.00
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l bg-gray-100 text-gray-700"
|
||||||
|
>
|
||||||
|
76.70
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l bg-gray-100 text-gray-700"
|
||||||
|
>
|
||||||
|
76.70
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l bg-orange-100 text-orange-700"
|
||||||
|
>
|
||||||
|
76.70
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l bg-green-100 text-green-700"
|
||||||
|
>
|
||||||
|
76.70
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l bg-green-100 text-green-700"
|
||||||
|
>
|
||||||
|
76.70
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l"
|
||||||
|
>
|
||||||
|
<FileText className="w-4 h-4" />
|
||||||
|
</TableCell>
|
||||||
|
<TableCell
|
||||||
|
colSpan={1}
|
||||||
|
className="text-center text-muted-foreground border-r border-l"
|
||||||
|
>
|
||||||
|
<FileText className="w-4 h-4" />
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</div>
|
||||||
|
{/* Pagination */}
|
||||||
|
{/* <div className="mt-4 flex items-center gap-2 justify-end">
|
||||||
|
<Button variant="outline" size="icon">
|
||||||
|
<ChevronLeft className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button variant="outline" size="icon">
|
||||||
|
<ChevronRight className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div> */}
|
||||||
|
|
||||||
|
{/* Legend Section */}
|
||||||
|
<div className="mt-6 grid grid-cols-2 gap-6">
|
||||||
|
{/* Keterangan Section */}
|
||||||
|
<div className="bg-white rounded-lg shadow-sm border p-6">
|
||||||
|
<div className="flex items-center gap-2 mb-4">
|
||||||
|
<FileText className="w-5 h-5 text-green-600" />
|
||||||
|
<h3 className="font-semibold text-lg">
|
||||||
|
Keterangan
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-4">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">
|
||||||
|
SKL : Status Ketaatan Lingkungan
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">
|
||||||
|
SPBM : Status Pemenuhan Baku Mutu
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">
|
||||||
|
IL : Ijin Lingkungan
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">AL : Air Limbah</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">LB3 : Limbah B3</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">
|
||||||
|
SB : Sumber Bergerak
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">
|
||||||
|
BS : Kebisingan & Udara Ambien
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">
|
||||||
|
STB : Sumber Tidak Bergerak
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">LP : Limbah Padat</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">
|
||||||
|
KDM : Kawasan Dilarang Merokok
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2 pt-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">
|
||||||
|
STT : Surat Tanda Terima
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-2 pt-3">
|
||||||
|
<div className="flex items-center gap-2">
|
||||||
|
<div className="w-2 h-2 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm">
|
||||||
|
SE : Surat Evaluasi
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Status Data Section */}
|
||||||
|
<div className="bg-white rounded-lg shadow-sm border p-6">
|
||||||
|
<div className="flex items-center gap-2 mb-4">
|
||||||
|
<BadgeCheck className="w-5 h-5 text-green-600" />
|
||||||
|
<h3 className="font-semibold text-lg">
|
||||||
|
Status Data
|
||||||
|
</h3>
|
||||||
|
</div>
|
||||||
|
<div className="space-y-4">
|
||||||
|
<div className="flex items-center gap-3 p-2 rounded-md bg-red-100">
|
||||||
|
<div className="w-3 h-3 rounded-full bg-red-500"></div>
|
||||||
|
<p className="text-sm text-red-700">
|
||||||
|
Data belum diisi
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 p-2 rounded-md bg-gray-100">
|
||||||
|
<div className="w-3 h-3 rounded-full bg-gray-500"></div>
|
||||||
|
<p className="text-sm text-gray-700">
|
||||||
|
Data telah diisi dan belum diverifikasi
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 p-2 rounded-md bg-orange-100">
|
||||||
|
<div className="w-3 h-3 rounded-full bg-orange-500"></div>
|
||||||
|
<p className="text-sm text-orange-700">
|
||||||
|
Data telah siap untuk diverifikasi
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3 p-2 rounded-md bg-green-100">
|
||||||
|
<div className="w-3 h-3 rounded-full bg-green-500"></div>
|
||||||
|
<p className="text-sm text-green-700">
|
||||||
|
Data telah diisi dan diverifikasi
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</AuthenticatedLayout>
|
||||||
|
);
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue