versi presentasi
parent
f00612339c
commit
0562099ca0
|
@ -5,6 +5,7 @@ namespace App\Http\Controllers\Admin;
|
|||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Http\Requests\RoleRequest;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
use Spatie\Permission\Models\Role;
|
||||
|
||||
class RoleController extends Controller
|
||||
|
@ -18,4 +19,84 @@ class RoleController extends Controller
|
|||
// Kembalikan data ke komponen Inertia 'Admin/Roles/Index'
|
||||
return inertia('Admin/Roles/Index', compact('roles'));
|
||||
}
|
||||
public function create()
|
||||
{
|
||||
// Ambil semua permission
|
||||
$permissions = Permission::all();
|
||||
|
||||
// Kembalikan ke Inertia 'Admin/Roles/Create' dengan data permissions
|
||||
return inertia('Admin/Roles/Create', [
|
||||
'permissions' => $permissions,
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(RoleRequest $request)
|
||||
{
|
||||
// Validasi data input melalui RoleRequest
|
||||
$validatedData = $request->validated();
|
||||
|
||||
// Buat role baru dengan data yang telah divalidasi
|
||||
$role = Role::create($validatedData);
|
||||
|
||||
// Tetapkan permissions pada role yang baru dibuat
|
||||
$role->syncPermissions($request->input('permissions'));
|
||||
|
||||
// Jika berhasil simpan, arahkan dengan pesan sukses, jika gagal dengan pesan error
|
||||
if ($role) {
|
||||
return redirect()->route('admin.roles.index')->with(['success' => 'Data Berhasil Disimpan!']);
|
||||
} else {
|
||||
return redirect()->route('admin.roles.index')->with(['error' => 'Data Gagal Disimpan!']);
|
||||
}
|
||||
}
|
||||
|
||||
public function edit(Role $role)
|
||||
{
|
||||
// Ambil semua permission, diurutkan dari yang terbaru
|
||||
$permissions = Permission::latest()->get();
|
||||
|
||||
// Ambil ID dari permissions yang dimiliki oleh role saat ini
|
||||
$rolePermissions = $role->permissions->pluck('id')->toArray();
|
||||
|
||||
// Map permissions agar hanya menyertakan 'id' dan 'name'
|
||||
$permissions = $permissions->map(function ($permission) {
|
||||
return [
|
||||
'id' => $permission->id,
|
||||
'name' => $permission->name,
|
||||
];
|
||||
});
|
||||
|
||||
// Kembalikan data ke Inertia 'Admin/Roles/Edit' dengan data role, permissions, dan rolePermissions
|
||||
return inertia('Admin/Roles/Edit', [
|
||||
'permissions' => $permissions,
|
||||
'role' => $role,
|
||||
'rolePermissions' => $rolePermissions,
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(RoleRequest $request, Role $role)
|
||||
{
|
||||
// Update data role dengan data yang telah divalidasi
|
||||
$role->name = $request->name;
|
||||
|
||||
// Sync permissions
|
||||
$role->permissions()->sync($request->permissions);
|
||||
|
||||
// Simpan perubahan
|
||||
$role->save();
|
||||
|
||||
// Arahkan kembali ke daftar role
|
||||
return redirect()->route('admin.roles.index')->with('success', 'Role updated successfully.');
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
// Cari role berdasarkan ID, error jika tidak ditemukan
|
||||
$role = Role::findOrFail($id);
|
||||
|
||||
// Hapus role dari database
|
||||
$role->delete();
|
||||
|
||||
// Arahkan kembali dengan pesan bahwa role berhasil dihapus
|
||||
return redirect()->route('admin.roles.index')->with('message', 'Role deleted successfully.');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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\Storage;
|
||||
use Inertia\Inertia;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
|
||||
class PostController extends Controller
|
||||
{
|
||||
|
@ -32,34 +33,68 @@ class PostController extends Controller
|
|||
$subkategori = SubKategori::all();
|
||||
|
||||
return Inertia::render('admin/post/add_post', [
|
||||
'kategori' => $kategori,
|
||||
'subkategori' => $subkategori
|
||||
'kategori' => Kategori::all(),
|
||||
'subkategori' => SubKategori::all(),
|
||||
'existingPosts' => Post::select('JudulPost', 'KategoriId')->get()
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(PostRequest $request)
|
||||
{
|
||||
|
||||
try {
|
||||
$data = $request->validated();
|
||||
$data['IsPublish'] = $request->has('IsPublish');
|
||||
|
||||
DB::beginTransaction();
|
||||
|
||||
try {
|
||||
$data['IsPublish'] = $request->boolean('IsPublish');
|
||||
|
||||
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) {
|
||||
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)
|
||||
{
|
||||
|
||||
return Inertia::render('admin/post/edit_post', [
|
||||
'post' => $post,
|
||||
'kategori' => Kategori::all(),
|
||||
|
@ -70,19 +105,30 @@ class PostController extends Controller
|
|||
public function update(PostRequest $request, Post $post)
|
||||
{
|
||||
try {
|
||||
$data = $request->validated();
|
||||
$data = array_filter($request->validated(), function($value) {
|
||||
return $value !== null;
|
||||
});
|
||||
|
||||
if ($request->hasFile('ImagePost')) {
|
||||
if ($post->ImagePost && Storage::disk('public')->exists($post->ImagePost)) {
|
||||
Storage::disk('public')->delete($post->ImagePost);
|
||||
}
|
||||
$data['ImagePost'] = $request->file('ImagePost')->store('images/posts', 'public');
|
||||
}
|
||||
|
||||
if (isset($data['IsPublish'])) {
|
||||
$data['IsPublish'] = (bool) $data['IsPublish'];
|
||||
}
|
||||
|
||||
$post->update($data);
|
||||
|
||||
return redirect()->route('admin.post.index')->with('success', 'Post berhasil diperbarui.');
|
||||
return redirect()
|
||||
->route('admin.post.index')
|
||||
->with('success', 'Post berhasil diperbarui.');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error updating Post: ' . $e->getMessage());
|
||||
return back()->with('error', 'Something went wrong.');
|
||||
return back()->with('error', 'Terjadi kesalahan saat memperbarui post.');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,6 +144,4 @@ class PostController extends Controller
|
|||
return back()->with('error', 'Something went wrong.');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
use Illuminate\Validation\Rule;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class PostRequest extends FormRequest
|
||||
{
|
||||
|
@ -22,15 +22,53 @@ class PostRequest extends FormRequest
|
|||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
if ($this->isMethod('POST')) {
|
||||
return [
|
||||
'KategoriId' => 'required|exists:Kategori,KategoriId',
|
||||
'SubKategoriId' => [
|
||||
'required',
|
||||
Rule::exists('SubKategori', 'SubKategoriId')->where('KategoriId', $this->KategoriId),
|
||||
],
|
||||
'KategoriId' => 'required|integer|exists:Kategori,KategoriId',
|
||||
'SubKategoriId' => 'required|integer|exists:SubKategori,SubKategoriId',
|
||||
'JudulPost' => ['required', 'string', 'max:255'],
|
||||
'SlugPost' => ['required', 'string', 'max:255'],
|
||||
'DescPost' => ['required', 'string'],
|
||||
'ImagePost' => ['nullable', 'image', 'mimes:jpg,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');
|
||||
}
|
||||
|
||||
public static function boot()
|
||||
{
|
||||
parent::boot();
|
||||
// public static function boot()
|
||||
// {
|
||||
// parent::boot();
|
||||
|
||||
static::creating(function ($post) {
|
||||
$post->Slug = Str::slug($post->JudulPost);
|
||||
});
|
||||
}
|
||||
// static::creating(function ($post) {
|
||||
// $post->SlugPost = Str::slug($post->JudulPost);
|
||||
// });
|
||||
// }
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
use App\Models\Post;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
|
||||
use Inertia\Inertia;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
|
@ -20,6 +21,11 @@ class AppServiceProvider extends ServiceProvider
|
|||
*/
|
||||
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-navigation-menu": "^1.2.4",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-progress": "^1.1.2",
|
||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||
"@radix-ui/react-select": "^2.1.6",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.1",
|
||||
"@radix-ui/react-switch": "^1.1.2",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
"@radix-ui/react-toast": "^1.2.6",
|
||||
"@radix-ui/react-tooltip": "^1.1.3",
|
||||
"@reduxjs/toolkit": "^2.5.1",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"@tinymce/tinymce-react": "^5.1.1",
|
||||
"@types/react-redux": "^7.1.34",
|
||||
"apexcharts": "^4.4.0",
|
||||
"ckeditor4-react": "^4.1.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"framer-motion": "^12.4.3",
|
||||
"highlight.js": "^11.11.1",
|
||||
"i18next-browser-languagedetector": "^8.0.2",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
|
@ -43,10 +48,12 @@
|
|||
"react-redux": "^9.2.0",
|
||||
"react-resizable-panels": "^2.0.19",
|
||||
"react-router-dom": "^7.1.4",
|
||||
"react-select": "^5.10.0",
|
||||
"react-type-animation": "^3.2.0",
|
||||
"recharts": "^2.15.1",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tinymce": "^7.6.1",
|
||||
"vaul": "^1.1.2",
|
||||
"ziggy-js": "^2.5.0",
|
||||
"zod": "^3.23.8"
|
||||
|
@ -101,7 +108,6 @@
|
|||
"version": "7.26.2",
|
||||
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz",
|
||||
"integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-validator-identifier": "^7.25.9",
|
||||
|
@ -157,7 +163,6 @@
|
|||
"version": "7.26.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz",
|
||||
"integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/parser": "^7.26.5",
|
||||
|
@ -191,7 +196,6 @@
|
|||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz",
|
||||
"integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/traverse": "^7.25.9",
|
||||
|
@ -233,7 +237,6 @@
|
|||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz",
|
||||
"integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
@ -243,7 +246,6 @@
|
|||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
|
||||
"integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
|
@ -277,7 +279,6 @@
|
|||
"version": "7.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz",
|
||||
"integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.26.7"
|
||||
|
@ -337,7 +338,6 @@
|
|||
"version": "7.25.9",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
|
||||
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.25.9",
|
||||
|
@ -352,7 +352,6 @@
|
|||
"version": "7.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz",
|
||||
"integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.26.2",
|
||||
|
@ -371,7 +370,6 @@
|
|||
"version": "7.26.7",
|
||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz",
|
||||
"integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-string-parser": "^7.25.9",
|
||||
|
@ -381,6 +379,126 @@
|
|||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin": {
|
||||
"version": "11.13.5",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz",
|
||||
"integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.16.7",
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/hash": "^0.9.2",
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"babel-plugin-macros": "^3.1.0",
|
||||
"convert-source-map": "^1.5.0",
|
||||
"escape-string-regexp": "^4.0.0",
|
||||
"find-root": "^1.1.0",
|
||||
"source-map": "^0.5.7",
|
||||
"stylis": "4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/babel-plugin/node_modules/convert-source-map": {
|
||||
"version": "1.9.0",
|
||||
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
|
||||
"integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/cache": {
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz",
|
||||
"integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/sheet": "^1.4.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"@emotion/weak-memoize": "^0.4.0",
|
||||
"stylis": "4.2.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/hash": {
|
||||
"version": "0.9.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz",
|
||||
"integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/memoize": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz",
|
||||
"integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/react": {
|
||||
"version": "11.14.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz",
|
||||
"integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.18.3",
|
||||
"@emotion/babel-plugin": "^11.13.5",
|
||||
"@emotion/cache": "^11.14.0",
|
||||
"@emotion/serialize": "^1.3.3",
|
||||
"@emotion/use-insertion-effect-with-fallbacks": "^1.2.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"@emotion/weak-memoize": "^0.4.0",
|
||||
"hoist-non-react-statics": "^3.3.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/serialize": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz",
|
||||
"integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/hash": "^0.9.2",
|
||||
"@emotion/memoize": "^0.9.0",
|
||||
"@emotion/unitless": "^0.10.0",
|
||||
"@emotion/utils": "^1.4.2",
|
||||
"csstype": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/sheet": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz",
|
||||
"integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/unitless": {
|
||||
"version": "0.10.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz",
|
||||
"integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/use-insertion-effect-with-fallbacks": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz",
|
||||
"integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@emotion/utils": {
|
||||
"version": "1.4.2",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz",
|
||||
"integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@emotion/weak-memoize": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz",
|
||||
"integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.21.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz",
|
||||
|
@ -1729,6 +1847,71 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-progress": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.2.tgz",
|
||||
"integrity": "sha512-u1IgJFQ4zNAUTjGdDL5dcl/U8ntOR6jsnhxKb5RKp5Ozwl88xKR9EqRZOe/Mk8tnx0x5tNUe2F+MzsyjqMg0MA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-primitive": "2.0.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-roving-focus": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.1.tgz",
|
||||
|
@ -2125,6 +2308,134 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.3.tgz",
|
||||
"integrity": "sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.1",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-direction": "1.1.0",
|
||||
"@radix-ui/react-id": "1.1.0",
|
||||
"@radix-ui/react-presence": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.0.2",
|
||||
"@radix-ui/react-roving-focus": "1.1.2",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-collection": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz",
|
||||
"integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-primitive": "2.0.2",
|
||||
"@radix-ui/react-slot": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-roving-focus": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz",
|
||||
"integrity": "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.1",
|
||||
"@radix-ui/react-collection": "1.1.2",
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-direction": "1.1.0",
|
||||
"@radix-ui/react-id": "1.1.0",
|
||||
"@radix-ui/react-primitive": "2.0.2",
|
||||
"@radix-ui/react-use-callback-ref": "1.1.0",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-tabs/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-toast": {
|
||||
"version": "1.2.6",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.6.tgz",
|
||||
|
@ -2839,6 +3150,26 @@
|
|||
"tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-table": {
|
||||
"version": "8.21.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-table/-/react-table-8.21.2.tgz",
|
||||
"integrity": "sha512-11tNlEDTdIhMJba2RBH+ecJ9l1zgS2kjmexDPAraulc8jeNA4xocSNeyzextT0XJyASil4XsCYlJmf5jEWAtYg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@tanstack/table-core": "8.21.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": ">=16.8",
|
||||
"react-dom": ">=16.8"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/react-virtual": {
|
||||
"version": "3.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.11.3.tgz",
|
||||
|
@ -2857,6 +3188,19 @@
|
|||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/table-core": {
|
||||
"version": "8.21.2",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/table-core/-/table-core-8.21.2.tgz",
|
||||
"integrity": "sha512-uvXk/U4cBiFMxt+p9/G7yUWI/UbHYbyghLCjlpWZ3mLeIZiUBSKcUnw9UnKkdRz7Z/N4UBuFLWQdJCjUe7HjvA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tanstack/virtual-core": {
|
||||
"version": "3.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.3.tgz",
|
||||
|
@ -2868,6 +3212,20 @@
|
|||
"url": "https://github.com/sponsors/tannerlinsley"
|
||||
}
|
||||
},
|
||||
"node_modules/@tinymce/tinymce-react": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@tinymce/tinymce-react/-/tinymce-react-5.1.1.tgz",
|
||||
"integrity": "sha512-DQ0wpvnf/9z8RsOEAmrWZ1DN1PKqcQHfU+DpM3llLze7FHmxVtzuN8O+FYh0oAAF4stzAXwiCIVacfqjMwRieQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"prop-types": "^15.6.2",
|
||||
"tinymce": "^7.0.0 || ^6.0.0 || ^5.5.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^18.0.0 || ^17.0.1 || ^16.7.0",
|
||||
"react-dom": "^18.0.0 || ^17.0.1 || ^16.7.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/babel__core": {
|
||||
"version": "7.20.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
|
||||
|
@ -3016,6 +3374,12 @@
|
|||
"undici-types": "~5.26.4"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/parse-json": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz",
|
||||
"integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@types/prop-types": {
|
||||
"version": "15.7.14",
|
||||
"resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz",
|
||||
|
@ -3078,6 +3442,15 @@
|
|||
"@babel/runtime": "^7.9.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/react-transition-group": {
|
||||
"version": "4.4.12",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz",
|
||||
"integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"@types/react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/@types/use-sync-external-store": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
|
||||
|
@ -3242,6 +3615,21 @@
|
|||
"proxy-from-env": "^1.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/babel-plugin-macros": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
|
||||
"integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.5",
|
||||
"cosmiconfig": "^7.0.0",
|
||||
"resolve": "^1.19.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10",
|
||||
"npm": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/balanced-match": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
|
||||
|
@ -3361,6 +3749,15 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/callsites": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/camelcase-css": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
|
||||
|
@ -3547,6 +3944,31 @@
|
|||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig": {
|
||||
"version": "7.1.0",
|
||||
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
|
||||
"integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@types/parse-json": "^4.0.0",
|
||||
"import-fresh": "^3.2.1",
|
||||
"parse-json": "^5.0.0",
|
||||
"path-type": "^4.0.0",
|
||||
"yaml": "^1.10.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
}
|
||||
},
|
||||
"node_modules/cosmiconfig/node_modules/yaml": {
|
||||
"version": "1.10.2",
|
||||
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz",
|
||||
"integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": ">= 6"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-fetch": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||
|
@ -3723,7 +4145,6 @@
|
|||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
|
||||
"integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"ms": "^2.1.3"
|
||||
|
@ -3878,6 +4299,15 @@
|
|||
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/error-ex": {
|
||||
"version": "1.3.2",
|
||||
"resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
|
||||
"integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"is-arrayish": "^0.2.1"
|
||||
}
|
||||
},
|
||||
"node_modules/es-define-property": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
|
||||
|
@ -3957,6 +4387,18 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/escape-string-regexp": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
|
||||
"integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/eventemitter3": {
|
||||
"version": "4.0.7",
|
||||
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
|
||||
|
@ -4033,6 +4475,12 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/find-root": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
|
||||
"integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/follow-redirects": {
|
||||
"version": "1.15.9",
|
||||
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
|
||||
|
@ -4099,6 +4547,33 @@
|
|||
"url": "https://github.com/sponsors/rawify"
|
||||
}
|
||||
},
|
||||
"node_modules/framer-motion": {
|
||||
"version": "12.4.3",
|
||||
"resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.4.3.tgz",
|
||||
"integrity": "sha512-rsMeO7w3dKyNG09o3cGwSH49iHU+VgDmfSSfsX+wfkO3zDA6WWkh4sUsMXd155YROjZP+7FTIhDrBYfgZeHjKQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-dom": "^12.0.0",
|
||||
"motion-utils": "^12.0.0",
|
||||
"tslib": "^2.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@emotion/is-prop-valid": "*",
|
||||
"react": "^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@emotion/is-prop-valid": {
|
||||
"optional": true
|
||||
},
|
||||
"react": {
|
||||
"optional": true
|
||||
},
|
||||
"react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
|
||||
|
@ -4223,7 +4698,6 @@
|
|||
"version": "11.12.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
|
||||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
|
@ -4379,6 +4853,22 @@
|
|||
"url": "https://opencollective.com/immer"
|
||||
}
|
||||
},
|
||||
"node_modules/import-fresh": {
|
||||
"version": "3.3.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz",
|
||||
"integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"parent-module": "^1.0.0",
|
||||
"resolve-from": "^4.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/internmap": {
|
||||
"version": "2.0.3",
|
||||
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
|
||||
|
@ -4404,6 +4894,12 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/is-arrayish": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
|
||||
"integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/is-binary-path": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
|
||||
|
@ -4544,7 +5040,6 @@
|
|||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
|
||||
"integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
|
||||
"dev": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"jsesc": "bin/jsesc"
|
||||
|
@ -4553,6 +5048,12 @@
|
|||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/json-parse-even-better-errors": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz",
|
||||
"integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/json5": {
|
||||
"version": "2.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
|
||||
|
@ -4658,6 +5159,12 @@
|
|||
"node": ">= 0.4"
|
||||
}
|
||||
},
|
||||
"node_modules/memoize-one": {
|
||||
"version": "6.0.0",
|
||||
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
|
||||
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/merge2": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
|
||||
|
@ -4737,11 +5244,25 @@
|
|||
"node": ">=16 || 14 >=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-dom": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.0.0.tgz",
|
||||
"integrity": "sha512-CvYd15OeIR6kHgMdonCc1ihsaUG4MYh/wrkz8gZ3hBX/uamyZCXN9S9qJoYF03GqfTt7thTV/dxnHYX4+55vDg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"motion-utils": "^12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/motion-utils": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.0.0.tgz",
|
||||
"integrity": "sha512-MNFiBKbbqnmvOjkPyOKgHUp3Q6oiokLkI1bEwm5QA28cxMZrv0CbbBGDNmhF6DIXsi1pCQBSs0dX8xjeER1tmA==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/ms": {
|
||||
"version": "2.1.3",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
|
||||
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/mz": {
|
||||
|
@ -4904,6 +5425,36 @@
|
|||
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/parent-module": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
|
||||
"integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"callsites": "^3.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6"
|
||||
}
|
||||
},
|
||||
"node_modules/parse-json": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz",
|
||||
"integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"error-ex": "^1.3.1",
|
||||
"json-parse-even-better-errors": "^2.3.0",
|
||||
"lines-and-columns": "^1.1.6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/path-key": {
|
||||
"version": "3.1.1",
|
||||
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
|
||||
|
@ -4941,6 +5492,15 @@
|
|||
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/path-type": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
|
||||
"integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/perfect-scrollbar": {
|
||||
"version": "1.5.6",
|
||||
"resolved": "https://registry.npmjs.org/perfect-scrollbar/-/perfect-scrollbar-1.5.6.tgz",
|
||||
|
@ -5487,6 +6047,27 @@
|
|||
"react-dom": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/react-select": {
|
||||
"version": "5.10.0",
|
||||
"resolved": "https://registry.npmjs.org/react-select/-/react-select-5.10.0.tgz",
|
||||
"integrity": "sha512-k96gw+i6N3ExgDwPIg0lUPmexl1ygPe6u5BdQFNBhkpbwroIgCNXdubtIzHfThYXYYTubwOBafoMnn7ruEP1xA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@babel/runtime": "^7.12.0",
|
||||
"@emotion/cache": "^11.4.0",
|
||||
"@emotion/react": "^11.8.1",
|
||||
"@floating-ui/dom": "^1.0.1",
|
||||
"@types/react-transition-group": "^4.4.0",
|
||||
"memoize-one": "^6.0.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"react-transition-group": "^4.3.0",
|
||||
"use-isomorphic-layout-effect": "^1.2.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
||||
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/react-smooth": {
|
||||
"version": "4.0.4",
|
||||
"resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz",
|
||||
|
@ -5677,6 +6258,15 @@
|
|||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/resolve-from": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
|
||||
"integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
}
|
||||
},
|
||||
"node_modules/reusify": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
|
||||
|
@ -5915,6 +6505,15 @@
|
|||
"url": "https://github.com/sponsors/isaacs"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map": {
|
||||
"version": "0.5.7",
|
||||
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
|
||||
"integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==",
|
||||
"license": "BSD-3-Clause",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/source-map-js": {
|
||||
"version": "1.2.1",
|
||||
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
|
||||
|
@ -6020,6 +6619,12 @@
|
|||
"node": ">=8"
|
||||
}
|
||||
},
|
||||
"node_modules/stylis": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz",
|
||||
"integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/sucrase": {
|
||||
"version": "3.35.0",
|
||||
"resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
|
||||
|
@ -6137,6 +6742,12 @@
|
|||
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/tinymce": {
|
||||
"version": "7.6.1",
|
||||
"resolved": "https://registry.npmjs.org/tinymce/-/tinymce-7.6.1.tgz",
|
||||
"integrity": "sha512-5cHhaAoyyTHfAVTInNfoSp0KkUHmeVUbGSu37QKQbOFIPqxYPhqBiaLm1WVLgoNBYOHRProVc3xzxnNTeWHyoQ==",
|
||||
"license": "GPL-2.0-or-later"
|
||||
},
|
||||
"node_modules/to-regex-range": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
|
||||
|
@ -6246,6 +6857,20 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-isomorphic-layout-effect": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz",
|
||||
"integrity": "sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/use-sidecar": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
|
||||
|
|
|
@ -34,20 +34,25 @@
|
|||
"@radix-ui/react-label": "^2.0.2",
|
||||
"@radix-ui/react-navigation-menu": "^1.2.4",
|
||||
"@radix-ui/react-popover": "^1.0.7",
|
||||
"@radix-ui/react-progress": "^1.1.2",
|
||||
"@radix-ui/react-scroll-area": "^1.0.5",
|
||||
"@radix-ui/react-select": "^2.1.6",
|
||||
"@radix-ui/react-separator": "^1.1.0",
|
||||
"@radix-ui/react-slot": "^1.1.1",
|
||||
"@radix-ui/react-switch": "^1.1.2",
|
||||
"@radix-ui/react-tabs": "^1.1.3",
|
||||
"@radix-ui/react-toast": "^1.2.6",
|
||||
"@radix-ui/react-tooltip": "^1.1.3",
|
||||
"@reduxjs/toolkit": "^2.5.1",
|
||||
"@tanstack/react-table": "^8.21.2",
|
||||
"@tinymce/tinymce-react": "^5.1.1",
|
||||
"@types/react-redux": "^7.1.34",
|
||||
"apexcharts": "^4.4.0",
|
||||
"ckeditor4-react": "^4.1.2",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"date-fns": "^3.6.0",
|
||||
"framer-motion": "^12.4.3",
|
||||
"highlight.js": "^11.11.1",
|
||||
"i18next-browser-languagedetector": "^8.0.2",
|
||||
"i18next-http-backend": "^3.0.2",
|
||||
|
@ -63,10 +68,12 @@
|
|||
"react-redux": "^9.2.0",
|
||||
"react-resizable-panels": "^2.0.19",
|
||||
"react-router-dom": "^7.1.4",
|
||||
"react-select": "^5.10.0",
|
||||
"react-type-animation": "^3.2.0",
|
||||
"recharts": "^2.15.1",
|
||||
"tailwind-merge": "^2.6.0",
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tinymce": "^7.6.1",
|
||||
"vaul": "^1.1.2",
|
||||
"ziggy-js": "^2.5.0",
|
||||
"zod": "^3.23.8"
|
||||
|
|
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 { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { Link } from "@inertiajs/react";
|
||||
|
||||
// const pengumumans = [
|
||||
// {
|
||||
// id: 1,
|
||||
// title_page: "Pengumuman",
|
||||
// alt_image: "Pengumuman",
|
||||
// date: "16 Januari 2025",
|
||||
// title: "Pelatihan & Sertifikasi Online Bidang Pengendalian Pencemaran Air Dan Udara",
|
||||
// description:
|
||||
// "Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan Tersebut Bekerjasama Dengan Lembaga P...",
|
||||
// image: "/assets/img1.jpg",
|
||||
// },
|
||||
// {
|
||||
// id: 2,
|
||||
// title_page: "Pengumuman",
|
||||
// alt_image: "Pengumuman",
|
||||
// date: "12 Desember 2024",
|
||||
// title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
// description:
|
||||
// "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
// image: "/assets/img1.jpg",
|
||||
// },
|
||||
// {
|
||||
// id: 3,
|
||||
// title_page: "Pengumuman",
|
||||
// alt_image: "Pengumuman",
|
||||
// date: "12 Desember 2024",
|
||||
// title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
// description:
|
||||
// "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
// image: "/assets/img1.jpg",
|
||||
// },
|
||||
// {
|
||||
// id: 4,
|
||||
// title_page: "Pengumuman",
|
||||
// alt_image: "Pengumuman",
|
||||
// date: "12 Desember 2024",
|
||||
// title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
// description:
|
||||
// "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
// image: "/assets/img1.jpg",
|
||||
// },
|
||||
// {
|
||||
// id: 5,
|
||||
// title_page: "Pengumuman",
|
||||
// alt_image: "Pengumuman",
|
||||
// date: "12 Desember 2024",
|
||||
// title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
// description:
|
||||
// "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
// image: "/assets/img1.jpg",
|
||||
// },
|
||||
// {
|
||||
// id: 6,
|
||||
// title_page: "Pengumuman",
|
||||
// alt_image: "Pengumuman",
|
||||
// date: "12 Desember 2024",
|
||||
// title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
// description:
|
||||
// "Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
// image: "/assets/img1.jpg",
|
||||
// },
|
||||
// ];
|
||||
|
||||
interface SubKategori {
|
||||
SubKategoriId: number;
|
||||
NamaSubKategori: string;
|
||||
|
@ -95,24 +33,39 @@ interface CardPengumumanProps {
|
|||
}
|
||||
|
||||
const CardPengumuman = ({ posts }: CardPengumumanProps) => {
|
||||
return (
|
||||
<section className="container max-w-7xl py-8 px-6">
|
||||
{/* List of Announcements */}
|
||||
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">
|
||||
{posts.map((post) => (
|
||||
<Card key={post.PostId} className="md:p-4">
|
||||
{items.slice(0, visibleItems).map((item) => (
|
||||
<Card key={item.PostId} className="md:p-4">
|
||||
<img
|
||||
src={`/storage/${post.ImagePost}`}
|
||||
alt={post.JudulPost}
|
||||
src={`/storage/${item.ImagePost}`}
|
||||
alt={item.JudulPost}
|
||||
className="rounded-md"
|
||||
/>
|
||||
<CardContent className="p-4">
|
||||
<Badge className="bg-red-600 text-white">
|
||||
{post.kategori?.NamaKategori} |{" "}
|
||||
{post.subkategori?.NamaSubKategori}
|
||||
{item.subkategori?.NamaSubKategori}
|
||||
</Badge>
|
||||
<p className="text-gray-500 text-sm mt-2">
|
||||
{new Date(post.created_at).toLocaleDateString(
|
||||
{new Date(item.created_at).toLocaleDateString(
|
||||
"id-ID",
|
||||
{
|
||||
day: "numeric",
|
||||
|
@ -122,12 +75,16 @@ const CardPengumuman = ({ posts }: CardPengumumanProps) => {
|
|||
)}
|
||||
</p>
|
||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||
{post.JudulPost}
|
||||
{item.JudulPost}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mt-2">
|
||||
{post.DescPost.replace(/<[^>]*>/g, "")}
|
||||
{item.DescPost.replace(/<[^>]*>/g, "").slice(
|
||||
0,
|
||||
160
|
||||
)}
|
||||
...
|
||||
</p>
|
||||
<Link href={`/post/${post.SlugPost}`}>
|
||||
<Link href={route("pengumuman.show", item.SlugPost)}>
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-red-600 mt-2 pl-0"
|
||||
|
@ -140,11 +97,69 @@ const CardPengumuman = ({ posts }: CardPengumumanProps) => {
|
|||
</Card>
|
||||
))}
|
||||
</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">
|
||||
<Button variant="link" className="text-green-700">
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-green-700"
|
||||
onClick={loadMore}
|
||||
>
|
||||
Lihat Lebih Banyak
|
||||
</Button>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -2,96 +2,89 @@ import React from "react";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { Link } from "@inertiajs/react";
|
||||
|
||||
const peraturans = [
|
||||
{
|
||||
id: 1,
|
||||
title_page: "Peraturan",
|
||||
alt_image: "Peraturan",
|
||||
date: "16 Januari 2025",
|
||||
title: "Pelatihan & Sertifikasi Online Bidang Pengendalian Pencemaran Air Dan Udara",
|
||||
description:
|
||||
"Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan Tersebut Bekerjasama Dengan Lembaga P...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title_page: "Peraturan",
|
||||
alt_image: "Peraturan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title_page: "Peraturan",
|
||||
alt_image: "Peraturan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title_page: "Peraturan",
|
||||
alt_image: "Peraturan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title_page: "Peraturan",
|
||||
alt_image: "Peraturan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title_page: "Peraturan",
|
||||
alt_image: "Peraturan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
];
|
||||
interface SubKategori {
|
||||
SubKategoriId: number;
|
||||
NamaSubKategori: string;
|
||||
}
|
||||
|
||||
const CardPeraturan = () => {
|
||||
return (
|
||||
<section className="container max-w-7xl py-8 px-6">
|
||||
{/* List of Announcements */}
|
||||
interface Kategori {
|
||||
KategoriId: number;
|
||||
NamaKategori: string;
|
||||
}
|
||||
|
||||
interface Post {
|
||||
PostId: number;
|
||||
JudulPost: string;
|
||||
DescPost: string;
|
||||
SlugPost: string;
|
||||
ImagePost: string;
|
||||
IsPublish: boolean;
|
||||
created_at: string;
|
||||
kategori?: Kategori;
|
||||
subkategori?: SubKategori;
|
||||
}
|
||||
|
||||
interface CardPeraturanProps {
|
||||
posts: Post[];
|
||||
}
|
||||
|
||||
const CardPeraturan = ({ posts }: CardPeraturanProps) => {
|
||||
const subcategories = Array.from(
|
||||
new Set(posts.map((item) => item.subkategori?.NamaSubKategori))
|
||||
).filter(Boolean);
|
||||
|
||||
const filterPosts = (subkategori: string | null) => {
|
||||
if (!subkategori) return posts;
|
||||
return posts.filter(
|
||||
(item) => item.subkategori?.NamaSubKategori === subkategori
|
||||
);
|
||||
};
|
||||
|
||||
const [visibleItems, setVisibleItems] = React.useState(3);
|
||||
const [activeTab, setActiveTab] = React.useState("all");
|
||||
|
||||
const loadMore = () => {
|
||||
setVisibleItems((prev) => prev + 3);
|
||||
};
|
||||
|
||||
const PostGrid = ({ items }: { items: Post[] }) => (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{peraturans.map((item) => (
|
||||
<Card key={item.id} className="md:p-4">
|
||||
{items.slice(0, visibleItems).map((item) => (
|
||||
<Card key={item.PostId} className="md:p-4">
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.alt_image}
|
||||
src={`/storage/${item.ImagePost}`}
|
||||
alt={item.JudulPost}
|
||||
className="rounded-md"
|
||||
/>
|
||||
<CardContent className="p-4">
|
||||
<Badge className="bg-red-600 text-white">
|
||||
{item.title_page}
|
||||
{item.subkategori?.NamaSubKategori}
|
||||
</Badge>
|
||||
<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>
|
||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||
{item.title}
|
||||
{item.JudulPost}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mt-2">
|
||||
{item.description}
|
||||
{item.DescPost.replace(/<[^>]*>/g, "").slice(
|
||||
0,
|
||||
160
|
||||
)}
|
||||
...
|
||||
</p>
|
||||
<Link href={route("peraturan.show", item.SlugPost)}>
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-red-600 mt-2 pl-0"
|
||||
|
@ -99,15 +92,74 @@ const CardPeraturan = () => {
|
|||
Baca Selengkapnya{" "}
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</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">
|
||||
<Button variant="link" className="text-green-700">
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-green-700"
|
||||
onClick={loadMore}
|
||||
>
|
||||
Lihat Lebih Banyak
|
||||
</Button>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -2,96 +2,89 @@ import React from "react";
|
|||
import { Button } from "@/components/ui/button";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { Link } from "@inertiajs/react";
|
||||
|
||||
const undangans = [
|
||||
{
|
||||
id: 1,
|
||||
title_page: "Undangan",
|
||||
alt_image: "Undangan",
|
||||
date: "16 Januari 2025",
|
||||
title: "Pelatihan & Sertifikasi Online Bidang Pengendalian Pencemaran Air Dan Udara",
|
||||
description:
|
||||
"Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan Tersebut Bekerjasama Dengan Lembaga P...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title_page: "Undangan",
|
||||
alt_image: "Undangan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title_page: "Undangan",
|
||||
alt_image: "Undangan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title_page: "Undangan",
|
||||
alt_image: "Undangan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title_page: "Undangan",
|
||||
alt_image: "Undangan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title_page: "Undangan",
|
||||
alt_image: "Undangan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
];
|
||||
interface SubKategori {
|
||||
SubKategoriId: number;
|
||||
NamaSubKategori: string;
|
||||
}
|
||||
|
||||
const CardUndangan = () => {
|
||||
return (
|
||||
<section className="container max-w-7xl py-8 px-6">
|
||||
{/* List of Announcements */}
|
||||
interface Kategori {
|
||||
KategoriId: number;
|
||||
NamaKategori: string;
|
||||
}
|
||||
|
||||
interface Post {
|
||||
PostId: number;
|
||||
JudulPost: string;
|
||||
DescPost: string;
|
||||
SlugPost: string;
|
||||
ImagePost: string;
|
||||
IsPublish: boolean;
|
||||
created_at: string;
|
||||
kategori?: Kategori;
|
||||
subkategori?: SubKategori;
|
||||
}
|
||||
|
||||
interface CardUndanganProps {
|
||||
posts: Post[];
|
||||
}
|
||||
|
||||
const CardUndangan = ({ posts }: CardUndanganProps) => {
|
||||
const subcategories = Array.from(
|
||||
new Set(posts.map((item) => item.subkategori?.NamaSubKategori))
|
||||
).filter(Boolean);
|
||||
|
||||
const filterPosts = (subkategori: string | null) => {
|
||||
if (!subkategori) return posts;
|
||||
return posts.filter(
|
||||
(item) => item.subkategori?.NamaSubKategori === subkategori
|
||||
);
|
||||
};
|
||||
|
||||
const [visibleItems, setVisibleItems] = React.useState(3);
|
||||
const [activeTab, setActiveTab] = React.useState("all");
|
||||
|
||||
const loadMore = () => {
|
||||
setVisibleItems((prev) => prev + 3);
|
||||
};
|
||||
|
||||
const PostGrid = ({ items }: { items: Post[] }) => (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{undangans.map((item) => (
|
||||
<Card key={item.id} className="md:p-4">
|
||||
{items.slice(0, visibleItems).map((item) => (
|
||||
<Card key={item.PostId} className="md:p-4">
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.alt_image}
|
||||
src={`/storage/${item.ImagePost}`}
|
||||
alt={item.JudulPost}
|
||||
className="rounded-md"
|
||||
/>
|
||||
<CardContent className="p-4">
|
||||
<Badge className="bg-red-600 text-white">
|
||||
{item.title_page}
|
||||
{item.subkategori?.NamaSubKategori}
|
||||
</Badge>
|
||||
<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>
|
||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||
{item.title}
|
||||
{item.JudulPost}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mt-2">
|
||||
{item.description}
|
||||
{item.DescPost.replace(/<[^>]*>/g, "").slice(
|
||||
0,
|
||||
160
|
||||
)}
|
||||
...
|
||||
</p>
|
||||
<Link href={route("undangan.show", item.SlugPost)}>
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-red-600 mt-2 pl-0"
|
||||
|
@ -99,15 +92,74 @@ const CardUndangan = () => {
|
|||
Baca Selengkapnya{" "}
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</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">
|
||||
<Button variant="link" className="text-green-700">
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-green-700"
|
||||
onClick={loadMore}
|
||||
>
|
||||
Lihat Lebih Banyak
|
||||
</Button>
|
||||
</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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -17,13 +17,22 @@ export default function HeroSecond({ title }: HeroSecondProps) {
|
|||
/>
|
||||
|
||||
{/* Overlay Content */}
|
||||
<div className="absolute inset-0 flex flex-col items-center justify-center">
|
||||
<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="absolute inset-0 flex flex-col items-center justify-center md:flex-row md:items-center md:justify-between md:p-8">
|
||||
<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}
|
||||
</h1>
|
||||
</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">
|
||||
<TypeAnimation
|
||||
sequence={[
|
||||
"Status Ketaatan Lingkungan",
|
||||
"Sistem Ketaatan Lingkungan",
|
||||
1000,
|
||||
"",
|
||||
500,
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
NavigationMenuItem,
|
||||
} from "@/components/ui/navigation-menu";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Link, router } from "@inertiajs/react";
|
||||
import { Link, router, usePage } from "@inertiajs/react";
|
||||
import {
|
||||
Drawer,
|
||||
DrawerClose,
|
||||
|
@ -18,13 +18,22 @@ import {
|
|||
import RunningText from "./RunningText";
|
||||
import { useTheme } from "next-themes";
|
||||
import SearchDialog from "./SearchDialog";
|
||||
import { Post } from "../DetailArtikel/types";
|
||||
import { PageProps as InertiaPageProps } from "@inertiajs/core";
|
||||
|
||||
interface NavItemsProps {
|
||||
mobile?: boolean;
|
||||
onClose?: () => void;
|
||||
}
|
||||
|
||||
interface CustomPageProps extends InertiaPageProps {
|
||||
runPosts?: Post[];
|
||||
errors: Record<string, string>;
|
||||
}
|
||||
|
||||
const Navbar = () => {
|
||||
const { runPosts = [] } = usePage<CustomPageProps>().props;
|
||||
// const runPosts = props.runPosts || [];
|
||||
const [isScrolled, setIsScrolled] = useState(false);
|
||||
const [isDrawerOpen, setIsDrawerOpen] = useState(false);
|
||||
// const [searchQuery, setSearchQuery] = useState("");
|
||||
|
@ -103,7 +112,7 @@ const Navbar = () => {
|
|||
|
||||
return (
|
||||
<div className="w-full">
|
||||
<RunningText />
|
||||
<RunningText posts={runPosts} />
|
||||
|
||||
<div
|
||||
className={`w-full rounded-br-2xl rounded-bl-2xl text-white transition-all duration-300 ${
|
||||
|
@ -184,7 +193,7 @@ const Navbar = () => {
|
|||
</Drawer>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
{/* <Button
|
||||
variant="ghost"
|
||||
onClick={toggleTheme}
|
||||
className="p-3 bg-[#2d7448] hover:bg-[#16a34a] rounded-full text-white "
|
||||
|
@ -194,7 +203,7 @@ const Navbar = () => {
|
|||
) : (
|
||||
<Sun className="h-6 w-6" />
|
||||
)}
|
||||
</Button>
|
||||
</Button> */}
|
||||
|
||||
<div className="hidden md:block">
|
||||
<Link
|
||||
|
|
|
@ -3,73 +3,84 @@ import { Button } from "@/components/ui/button";
|
|||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { Link } from "@inertiajs/react";
|
||||
|
||||
const announcements = [
|
||||
{
|
||||
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",
|
||||
},
|
||||
];
|
||||
interface SubKategori {
|
||||
SubKategoriId: number;
|
||||
NamaSubKategori: string;
|
||||
}
|
||||
|
||||
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 (
|
||||
<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">
|
||||
<h2 className="text-2xl font-bold text-green-800">
|
||||
Pengumuman
|
||||
</h2>
|
||||
<Link href="/pengumuman">
|
||||
<Button variant="link" className="text-green-700">
|
||||
Selengkapnya
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* Highlight Announcement */}
|
||||
{posts.length > 1 && (
|
||||
<div className="grid grid-cols-1 gap-6 mb-8">
|
||||
<Card className="p-4 flex flex-col md:flex-row items-start">
|
||||
<img
|
||||
src={announcements[0].image}
|
||||
alt={announcements[0].alt_image}
|
||||
src={`/storage/${posts[0].ImagePost}`}
|
||||
alt={posts[0].JudulPost}
|
||||
className="rounded-md w-full md:w-1/2"
|
||||
/>
|
||||
<CardContent className="p-4 w-full md:w-1/2">
|
||||
<Badge className="bg-red-600 text-white">
|
||||
{announcements[0].title_page}
|
||||
{posts[0].kategori?.NamaKategori}
|
||||
</Badge>
|
||||
<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>
|
||||
<h3 className="text-lg font-semibold mt-2 text-gray-900">
|
||||
{announcements[0].title}
|
||||
{posts[0].JudulPost}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mt-2">
|
||||
{announcements[0].description}
|
||||
{posts[0].DescPost.replace(
|
||||
/<[^>]*>/g,
|
||||
""
|
||||
).slice(0, 160)}
|
||||
...
|
||||
</p>
|
||||
<Link href={`/pengumuman/${posts[0].SlugPost}`}>
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-red-600 mt-2 pl-0"
|
||||
|
@ -77,32 +88,46 @@ const AnnouncementSection = () => {
|
|||
Baca Selengkapnya{" "}
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* List of Announcements */}
|
||||
{posts.length > 1 && (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{announcements.map((item) => (
|
||||
<Card key={item.id} className="p-4">
|
||||
{posts.slice(1).map((post) => (
|
||||
<Card key={post.PostId} className="p-4">
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.alt_image}
|
||||
src={`/storage/${post.ImagePost}`}
|
||||
alt={post.JudulPost}
|
||||
className="rounded-md"
|
||||
/>
|
||||
<CardContent className="p-4">
|
||||
<Badge className="bg-red-600 text-white">
|
||||
{item.title_page}
|
||||
{post.kategori?.NamaKategori}
|
||||
</Badge>
|
||||
<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>
|
||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||
{item.title}
|
||||
{post.JudulPost}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mt-2">
|
||||
{item.description}
|
||||
{post.DescPost.replace(
|
||||
/<[^>]*>/g,
|
||||
""
|
||||
).slice(0, 160)}
|
||||
...
|
||||
</p>
|
||||
<Link href={`/pengumuman/${post.SlugPost}`}>
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-red-600 mt-2 pl-0"
|
||||
|
@ -110,10 +135,12 @@ const AnnouncementSection = () => {
|
|||
Baca Selengkapnya{" "}
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -3,31 +3,35 @@ import { Button } from "@/components/ui/button";
|
|||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { ArrowRight, FileText } from "lucide-react";
|
||||
import { Link } from "@inertiajs/react";
|
||||
|
||||
const regulations = [
|
||||
{
|
||||
id: 1,
|
||||
title_page: "Peraturan",
|
||||
alt_image: "Peraturan",
|
||||
date: "16 Januari 2025",
|
||||
title: "Pelatihan & Sertifikasi Online Bidang Pengendalian Pencemaran Air Dan Udara",
|
||||
description:
|
||||
"Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan Tersebut Bekerjasama Dengan Lembaga P...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title_page: "Peraturan",
|
||||
alt_image: "Peraturan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
];
|
||||
interface SubKategori {
|
||||
SubKategoriId: number;
|
||||
NamaSubKategori: string;
|
||||
}
|
||||
|
||||
const RegulationSection = () => {
|
||||
interface Kategori {
|
||||
KategoriId: number;
|
||||
NamaKategori: string;
|
||||
}
|
||||
|
||||
interface Post {
|
||||
PostId: number;
|
||||
JudulPost: string;
|
||||
DescPost: string;
|
||||
SlugPost: string;
|
||||
ImagePost: string;
|
||||
IsPublish: boolean;
|
||||
created_at: string;
|
||||
kategori?: Kategori;
|
||||
subkategori?: SubKategori;
|
||||
}
|
||||
|
||||
interface CardPeraturanProps {
|
||||
peraturan: Post[];
|
||||
}
|
||||
|
||||
const RegulationSection = ({ peraturan }: CardPeraturanProps) => {
|
||||
return (
|
||||
<section className="relative w-full py-8 px-6">
|
||||
{/* Background Green Box */}
|
||||
|
@ -43,38 +47,51 @@ const RegulationSection = () => {
|
|||
Informasi
|
||||
</Badge>
|
||||
<h2 className="text-2xl font-bold">Peraturan</h2>
|
||||
<p className="text-sm text-[#94bb98] mt-2 text-center md:text-left">
|
||||
Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan
|
||||
Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan
|
||||
Tersebut Bekerjasama Dengan Lembaga P...
|
||||
<p className="text-sm text-[#94bb98] mt-2 text-center md:text-left pr-3">
|
||||
Aplikasi Sistem Ketaatan Lingkungan memastikan kepatuhan
|
||||
terhadap perizinan, pengelolaan limbah, emisi, serta
|
||||
pengawasan lingkungan.
|
||||
</p>
|
||||
<Link href="/peraturan">
|
||||
<Button variant="link" className="text-white mt-4 pl-0">
|
||||
Selengkapnya
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* List of Regulations */}
|
||||
<div className="col-span-2 grid grid-cols-1 md:grid-cols-2 gap-6 relative z-10">
|
||||
{regulations.map((item) => (
|
||||
<Card key={item.id} className="md:p-4">
|
||||
{peraturan.map((item) => (
|
||||
<Card key={item.PostId} className="md:p-4">
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.alt_image}
|
||||
src={`/storage/${item.ImagePost}`}
|
||||
alt={item.JudulPost}
|
||||
className="rounded-md"
|
||||
/>
|
||||
<CardContent className="p-4">
|
||||
<Badge className="bg-red-600 text-white">
|
||||
{item.title_page}
|
||||
{item.kategori?.NamaKategori}
|
||||
</Badge>
|
||||
<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>
|
||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||
{item.title}
|
||||
{item.JudulPost}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mt-2">
|
||||
{item.description}
|
||||
{item.DescPost.replace(
|
||||
/<[^>]*>/g,
|
||||
""
|
||||
).slice(0, 160)}
|
||||
...
|
||||
</p>
|
||||
<Link href={`/peraturan/${item.SlugPost}`}>
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-red-600 mt-2 pl-0"
|
||||
|
@ -82,6 +99,7 @@ const RegulationSection = () => {
|
|||
Baca Selengkapnya{" "}
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
|
|
|
@ -1,31 +1,26 @@
|
|||
import React, { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Progress } from "@/components/ui/progress";
|
||||
import { X, ArrowLeft, ArrowRight } from "lucide-react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { usePage } from "@inertiajs/react";
|
||||
import { Link } from "@inertiajs/react";
|
||||
|
||||
const PopupModal = ({ onClose }: { onClose: () => void }) => {
|
||||
const slides = [
|
||||
{
|
||||
title: "Pelatihan dan Sertifikasi Online Bidang Pengendalian Pencemaran Udara",
|
||||
image: "/assets/popup-1.jpeg",
|
||||
description:
|
||||
"Kegiatan ini diselenggarakan oleh DLH Provinsi DKI Jakarta bekerja sama dengan LSP Tersertifikasi BNSP.",
|
||||
},
|
||||
{
|
||||
title: "Workshop Peningkatan Kapasitas Pengendalian Pencemaran Udara",
|
||||
image: "/assets/popup-2.jpeg",
|
||||
description:
|
||||
"Workshop ini bertujuan untuk meningkatkan pengetahuan dan keterampilan di bidang lingkungan hidup. Workshop ini bertujuan untuk meningkatkan pengetahuan dan keterampilan di bidang lingkungan hidup.",
|
||||
},
|
||||
{
|
||||
title: "Sosialisasi Program Ramah Lingkungan",
|
||||
image: "/assets/popup-2.jpeg",
|
||||
description:
|
||||
"DLH DKI Jakarta mengajak masyarakat untuk lebih peduli terhadap lingkungan sekitar.",
|
||||
},
|
||||
];
|
||||
|
||||
const { sekilasInfo } = usePage().props as any;
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
const [currentSlide, setCurrentSlide] = useState(0);
|
||||
|
||||
const progressValue = ((currentSlide + 1) / sekilasInfo.length) * 100;
|
||||
|
||||
const slides = sekilasInfo.map((info: any) => ({
|
||||
title: info.JudulPost,
|
||||
image: `/storage/${info.ImagePost}`,
|
||||
description:
|
||||
info.DescPost.replace(/<[^>]*>/g, "").slice(0, 160) + "...",
|
||||
slug: info.SlugPost,
|
||||
}));
|
||||
|
||||
const nextSlide = () => {
|
||||
setCurrentSlide((prev) => (prev + 1) % slides.length);
|
||||
};
|
||||
|
@ -34,13 +29,63 @@ const PopupModal = ({ onClose }: { onClose: () => void }) => {
|
|||
setCurrentSlide((prev) => (prev - 1 + slides.length) % slides.length);
|
||||
};
|
||||
|
||||
const handleClose = () => {
|
||||
setIsVisible(false);
|
||||
setTimeout(onClose, 500);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 flex items-center justify-center bg-black bg-opacity-50 z-50 container md:max-w-none">
|
||||
<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">
|
||||
<button className="absolute top-2 right-2" onClick={onClose}>
|
||||
<AnimatePresence mode="wait">
|
||||
{isVisible && sekilasInfo.length > 0 && (
|
||||
<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" />
|
||||
</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}
|
||||
</h2>
|
||||
<div className="relative">
|
||||
|
@ -62,19 +107,31 @@ const PopupModal = ({ onClose }: { onClose: () => void }) => {
|
|||
<ArrowRight className="w-6 h-6 text-gray-700" />
|
||||
</button>
|
||||
</div>
|
||||
{/* <h2 className="text-xl font-bold text-center mb-4">
|
||||
{slides[currentSlide].title}
|
||||
</h2> */}
|
||||
<div className="w-full mt-4">
|
||||
<Progress
|
||||
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">
|
||||
{slides[currentSlide].description}
|
||||
</p>
|
||||
<div className="flex justify-center">
|
||||
<Link
|
||||
href={`/sekilasinfo/${slides[currentSlide].slug}`}
|
||||
>
|
||||
<Button className="bg-green-800 text-white px-6 py-2 rounded-lg">
|
||||
Selengkapnya
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -1,43 +1,106 @@
|
|||
import React, { useState, useEffect } from "react";
|
||||
import { Megaphone } from "lucide-react";
|
||||
import { Link } from "@inertiajs/react";
|
||||
import { Post } from "@/components/DetailArtikel/types";
|
||||
|
||||
const messages = [
|
||||
"Peraturan Menteri Lingkungan Hidup dan Kehutanan No. 14 Tahun 2024 Tentang Penyelenggaraan Pengawasan dan Sanksi Administratif Lingkungan Hidup",
|
||||
"Workshop Peningkatan Kapasitas Pengendalian Pencemaran Udara akan diselenggarakan pada bulan depan.",
|
||||
"DLH DKI Jakarta mengajak masyarakat untuk berpartisipasi dalam program lingkungan ramah sampah.",
|
||||
];
|
||||
interface RunningTextProps {
|
||||
posts: Post[];
|
||||
}
|
||||
|
||||
export default function RunningText() {
|
||||
export default function RunningText({ posts }: RunningTextProps) {
|
||||
const [currentMessageIndex, setCurrentMessageIndex] = useState(0);
|
||||
const [isVisible, setIsVisible] = useState(true);
|
||||
const [isPaused, setIsPaused] = useState(false);
|
||||
|
||||
const getLatestPostsByCategory = () => {
|
||||
const groupedPosts = posts.reduce((acc, post) => {
|
||||
const category = post.kategori?.NamaKategori || "Uncategorized";
|
||||
if (!acc[category]) {
|
||||
acc[category] = [];
|
||||
}
|
||||
acc[category].push(post);
|
||||
return acc;
|
||||
}, {} as Record<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(() => {
|
||||
if (isPaused || messages.length === 0) return;
|
||||
|
||||
const interval = setInterval(() => {
|
||||
setIsVisible(false); // Hide current message
|
||||
setIsVisible(false);
|
||||
|
||||
setTimeout(() => {
|
||||
setCurrentMessageIndex(
|
||||
(prevIndex) => (prevIndex + 1) % messages.length
|
||||
);
|
||||
setIsVisible(true); // Show new message
|
||||
}, 500); // Wait for fade out animation
|
||||
}, 3000); // Change message every 3 seconds
|
||||
setIsVisible(true);
|
||||
}, 500);
|
||||
}, 3000);
|
||||
|
||||
return () => clearInterval(interval);
|
||||
}, []);
|
||||
}, [isPaused, messages.length]);
|
||||
|
||||
const getRouteByCategory = (kategori: string, slug: string) => {
|
||||
const routes: { [key: string]: string } = {
|
||||
Pengumuman: `/pengumuman/${slug}`,
|
||||
Peraturan: `/peraturan/${slug}`,
|
||||
Undangan: `/undangan/${slug}`,
|
||||
};
|
||||
|
||||
return routes[kategori] || `/sekilasinfo/${slug}`;
|
||||
};
|
||||
|
||||
if (messages.length === 0) return null;
|
||||
|
||||
return (
|
||||
<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">
|
||||
<Megaphone className="w-5 h-5 mr-2 text-black flex-shrink-0" />
|
||||
<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
|
||||
className={`text-sm text-black transition-opacity duration-500 ease-in-out ${
|
||||
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>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -17,7 +17,10 @@ const SearchDialog: React.FC = () => {
|
|||
const handleSearch = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
if (searchQuery.trim()) {
|
||||
router.visit(`/search/${encodeURIComponent(searchQuery)}`);
|
||||
router.visit(`/search?q=${encodeURIComponent(searchQuery)}`, {
|
||||
preserveState: true,
|
||||
preserveScroll: true,
|
||||
});
|
||||
setSearchQuery(""); // Clear input setelah search
|
||||
}
|
||||
};
|
||||
|
@ -56,14 +59,14 @@ const SearchDialog: React.FC = () => {
|
|||
<Search className="h-5 w-5" />
|
||||
</Button>
|
||||
</form>
|
||||
<DialogClose asChild>
|
||||
{/* <DialogClose asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
className="absolute top-4 right-4 text-gray-500 hover:text-gray-700"
|
||||
>
|
||||
✕
|
||||
</Button>
|
||||
</DialogClose>
|
||||
</DialogClose> */}
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
|
|
@ -21,25 +21,19 @@ export default function Tentang() {
|
|||
</Badge>
|
||||
|
||||
<h2 className="text-3xl font-bold text-green-600">
|
||||
Status Ketaatan Lingkungan
|
||||
Sistem Ketaatan Lingkungan
|
||||
<br />
|
||||
(SKL)
|
||||
</h2>
|
||||
|
||||
<p className="text-gray-600 leading-relaxed max-w-2xl">
|
||||
Lorem Ipsum Is Simply Dummy Text Of The Printing
|
||||
And Typesetting Industry. Lorem Ipsum Has Been
|
||||
The Industry's Standard Dummy Text Ever Since
|
||||
The 1500s, When An Unknown Printer Took A Galley
|
||||
Of Type And Scrambled It To Make A Type Specimen
|
||||
Book. It Has Survived Not Only Five Centuries,
|
||||
But Also The Leap Into Electronic Typesetting,
|
||||
Remaining Essentially Unchanged. It Was
|
||||
Popularised In The 1960s With The Release Of
|
||||
Letraset Sheets Containing Lorem Ipsum Passages,
|
||||
And More Recently With Desktop Publishing
|
||||
Software Like Aldus PageMaker Including Versions
|
||||
Of Lorem Ipsum.
|
||||
SKL adalah Sistem Ketaatan Lingkungan yang
|
||||
dinaungi oleh Dinas Lingkungan Hidup Provinsi
|
||||
DKI Jakarta dengan menyediakan sistem yang dapat
|
||||
memberikan pelayanan dalam pelaporan, analisa
|
||||
dan evaluasi pengelolaan lingkungan dilakukan
|
||||
oleh perusahaan atau kegiatan usaha yang berada
|
||||
di Provinsi DKI Jakarta.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,41 +3,35 @@ import { Button } from "@/components/ui/button";
|
|||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { ArrowRight } from "lucide-react";
|
||||
import { Link } from "@inertiajs/react";
|
||||
|
||||
const undangans = [
|
||||
{
|
||||
id: 1,
|
||||
title_page: "Undangan",
|
||||
alt_image: "Undangan",
|
||||
date: "16 Januari 2025",
|
||||
title: "Pelatihan & Sertifikasi Online Bidang Pengendalian Pencemaran Air Dan Udara",
|
||||
description:
|
||||
"Kegiatan Pelatihan Dan Uji Sertifikasi Tersebut Akan Diselenggarakan Sebagaimana Jadwal Terlampir, Kegiatan Tersebut Bekerjasama Dengan Lembaga P...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title_page: "Undangan",
|
||||
alt_image: "Undangan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title_page: "Undangan",
|
||||
alt_image: "Undangan",
|
||||
date: "12 Desember 2024",
|
||||
title: "Pembinaan Pelaporan secara Online Pengelolaan Lingkungan melalui situs Status Ketaatan Lingkungan (SKL)",
|
||||
description:
|
||||
"Sukolompok Pengawasan Lingkungan Bidang Pengawasan dan Penataan Hukum Dinas Lingkungan Hidup Prov. DKI Jakarta...",
|
||||
image: "/assets/img1.jpg",
|
||||
},
|
||||
];
|
||||
interface SubKategori {
|
||||
SubKategoriId: number;
|
||||
NamaSubKategori: string;
|
||||
}
|
||||
|
||||
const UndanganSection = () => {
|
||||
interface Kategori {
|
||||
KategoriId: number;
|
||||
NamaKategori: string;
|
||||
}
|
||||
|
||||
interface Post {
|
||||
PostId: number;
|
||||
JudulPost: string;
|
||||
DescPost: string;
|
||||
SlugPost: string;
|
||||
ImagePost: string;
|
||||
IsPublish: boolean;
|
||||
created_at: string;
|
||||
kategori?: Kategori;
|
||||
subkategori?: SubKategori;
|
||||
}
|
||||
|
||||
interface CardUndanganProps {
|
||||
undangan: Post[];
|
||||
}
|
||||
|
||||
const UndanganSection = ({ undangan }: CardUndanganProps) => {
|
||||
return (
|
||||
<section className="container max-w-7xl py-8 px-6">
|
||||
<div className="flex items-center mb-6 justify-between">
|
||||
|
@ -49,33 +43,50 @@ const UndanganSection = () => {
|
|||
Undangan
|
||||
</h2>
|
||||
</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
|
||||
</Button>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
{/* List of Announcements */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{undangans.map((item) => (
|
||||
<Card key={item.id} className="md:p-4">
|
||||
{undangan.map((item) => (
|
||||
<Card key={item.PostId} className="md:p-4">
|
||||
<img
|
||||
src={item.image}
|
||||
alt={item.alt_image}
|
||||
src={`/storage/${item.ImagePost}`}
|
||||
alt={item.JudulPost}
|
||||
className="rounded-md"
|
||||
/>
|
||||
<CardContent className="p-4">
|
||||
<Badge className="bg-red-600 text-white">
|
||||
{item.title_page}
|
||||
{item.kategori?.NamaKategori}
|
||||
</Badge>
|
||||
<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>
|
||||
<h3 className="text-md font-semibold mt-2 text-gray-900">
|
||||
{item.title}
|
||||
{item.JudulPost}
|
||||
</h3>
|
||||
<p className="text-sm text-gray-600 mt-2">
|
||||
{item.description}
|
||||
{item.DescPost.replace(/<[^>]*>/g, "").slice(
|
||||
0,
|
||||
160
|
||||
)}
|
||||
...
|
||||
</p>
|
||||
<Link href={`/undangan/${item.SlugPost}`}>
|
||||
<Button
|
||||
variant="link"
|
||||
className="text-red-600 mt-2 pl-0"
|
||||
|
@ -83,6 +94,7 @@ const UndanganSection = () => {
|
|||
Baca Selengkapnya{" "}
|
||||
<ArrowRight className="ml-2 w-4 h-4" />
|
||||
</Button>
|
||||
</Link>
|
||||
</CardContent>
|
||||
</Card>
|
||||
))}
|
||||
|
|
|
@ -2,11 +2,22 @@
|
|||
|
||||
import * as React from "react";
|
||||
import {
|
||||
Archive,
|
||||
Bold,
|
||||
Bolt,
|
||||
BookCheck,
|
||||
BookMarked,
|
||||
Command,
|
||||
DatabaseBackup,
|
||||
FileKey2,
|
||||
Flag,
|
||||
FolderArchive,
|
||||
Frame,
|
||||
GalleryVertical,
|
||||
Home,
|
||||
LifeBuoy,
|
||||
NotepadText,
|
||||
Scale,
|
||||
Send,
|
||||
SquareTerminal,
|
||||
} from "lucide-react";
|
||||
|
@ -38,6 +49,64 @@ const data = {
|
|||
url: "/dashboard",
|
||||
icon: Home,
|
||||
},
|
||||
{
|
||||
title: "Pelaporan",
|
||||
url: "/admin/pelaporan",
|
||||
icon: BookMarked,
|
||||
},
|
||||
{
|
||||
title: "Verifikasi Pelaporan",
|
||||
url: "/admin/verifikasi",
|
||||
icon: BookCheck,
|
||||
},
|
||||
{
|
||||
title: "Penegakan Hukum",
|
||||
url: "/admin/hukum",
|
||||
icon: Scale,
|
||||
},
|
||||
{
|
||||
title: "Perizinan Lingkungan",
|
||||
url: "/admin/perizinan-lingkungan",
|
||||
icon: FileKey2,
|
||||
},
|
||||
{
|
||||
title: "History Perusahaan",
|
||||
url: "/admin/history-perusahaan",
|
||||
icon: GalleryVertical,
|
||||
},
|
||||
{
|
||||
title: "Post",
|
||||
url: "/admin/post",
|
||||
icon: Frame,
|
||||
},
|
||||
{
|
||||
title: "Reporting",
|
||||
url: "#",
|
||||
icon: Flag,
|
||||
isActive: true,
|
||||
items: [
|
||||
{
|
||||
title: "Rekapitulasi",
|
||||
url: "/admin/rekap",
|
||||
},
|
||||
{
|
||||
title: "SKL & SPL",
|
||||
url: "/admin/skl-spl",
|
||||
},
|
||||
{
|
||||
title: "Daftar Perusahaan",
|
||||
url: "/admin/daftar-perusahaan",
|
||||
},
|
||||
{
|
||||
title: "SKL",
|
||||
url: "/admin/skl",
|
||||
},
|
||||
{
|
||||
title: "SPL",
|
||||
url: "/admin/spl",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Data Master",
|
||||
url: "#",
|
||||
|
@ -52,28 +121,69 @@ const data = {
|
|||
title: "Sub Kategori Post",
|
||||
url: "/admin/subkategori",
|
||||
},
|
||||
// {
|
||||
// title: "Menu",
|
||||
// url: "/menus",
|
||||
// },
|
||||
{
|
||||
title: "Dinas LH",
|
||||
url: "/admin/verifikator",
|
||||
},
|
||||
{
|
||||
title: "Jenis Kegiatan",
|
||||
url: "/admin/jeniskegiatan",
|
||||
},
|
||||
{
|
||||
title: "Jenis Dokumen Izin",
|
||||
url: "/admin/jenisdokil",
|
||||
},
|
||||
{
|
||||
title: "Perusahaan",
|
||||
url: "/admin/perusahaan",
|
||||
},
|
||||
{
|
||||
title: "History Kegiatan",
|
||||
url: "/admin/historykegiatan",
|
||||
},
|
||||
{
|
||||
title: "Jenis Sanksi",
|
||||
url: "/admin/jenissanksi",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: "Post",
|
||||
url: "/admin/post",
|
||||
icon: Frame,
|
||||
title: "Pengaturan",
|
||||
url: "#",
|
||||
icon: Bolt,
|
||||
isActive: true,
|
||||
items: [
|
||||
{
|
||||
title: "Pengguna",
|
||||
url: "/admin/pengguna",
|
||||
},
|
||||
{
|
||||
title: "Role",
|
||||
url: "/admin/role",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
navSecondary: [
|
||||
{
|
||||
title: "Support",
|
||||
url: "#",
|
||||
title: "Tentang",
|
||||
url: "/admin/tentang",
|
||||
icon: LifeBuoy,
|
||||
},
|
||||
{
|
||||
title: "Feedback",
|
||||
title: "Catatan Sistem",
|
||||
url: "#",
|
||||
icon: Send,
|
||||
icon: NotepadText,
|
||||
},
|
||||
{
|
||||
title: "Backup",
|
||||
url: "#",
|
||||
icon: Archive,
|
||||
},
|
||||
{
|
||||
title: "Restore",
|
||||
url: "#",
|
||||
icon: DatabaseBackup,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
|
|
@ -44,11 +44,11 @@ const AppearanceDropdown = () => {
|
|||
return (
|
||||
<>
|
||||
<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 />
|
||||
{formattedTime}
|
||||
</span>
|
||||
<div className="px-5">
|
||||
{/* <div className="px-5">
|
||||
<DropdownMenu>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button
|
||||
|
@ -74,7 +74,7 @@ const AppearanceDropdown = () => {
|
|||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
</div>
|
||||
</div> */}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -46,10 +46,10 @@ export function NavMain({
|
|||
>
|
||||
<SidebarMenuItem>
|
||||
<SidebarMenuButton asChild tooltip={item.title}>
|
||||
<a href={item.url}>
|
||||
<Link href={item.url}>
|
||||
<item.icon />
|
||||
<div>{item.title}</div>
|
||||
</a>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
{item.items?.length ? (
|
||||
<>
|
||||
|
|
|
@ -8,6 +8,7 @@ import {
|
|||
SidebarMenuButton,
|
||||
SidebarMenuItem,
|
||||
} from "@/components/ui/sidebar";
|
||||
import { Link } from "@inertiajs/react";
|
||||
|
||||
export function NavSecondary({
|
||||
items,
|
||||
|
@ -26,10 +27,10 @@ export function NavSecondary({
|
|||
{items.map((item) => (
|
||||
<SidebarMenuItem key={item.title}>
|
||||
<SidebarMenuButton asChild size="sm">
|
||||
<a href={item.url}>
|
||||
<Link href={item.url}>
|
||||
<item.icon />
|
||||
<span>{item.title}</span>
|
||||
</a>
|
||||
</Link>
|
||||
</SidebarMenuButton>
|
||||
</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
|
||||
ref={ref}
|
||||
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
|
||||
)}
|
||||
{...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: {
|
||||
variant: {
|
||||
default: "border bg-background text-foreground",
|
||||
default: "border bg-green-100 text-green-800",
|
||||
destructive:
|
||||
"destructive group border-destructive bg-destructive text-destructive-foreground",
|
||||
},
|
||||
|
|
|
@ -12,7 +12,7 @@ export function Toaster() {
|
|||
const { toasts } = useToast();
|
||||
|
||||
return (
|
||||
<ToastProvider duration={2000} swipeDirection="right">
|
||||
<ToastProvider duration={3000} swipeDirection="right">
|
||||
{toasts.map(function ({
|
||||
id,
|
||||
title,
|
||||
|
|
|
@ -25,7 +25,7 @@ export default function AuthenticatedLayout({
|
|||
<SidebarProvider>
|
||||
<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">
|
||||
<div className="flex items-center gap-2">
|
||||
<SidebarTrigger className="-ml-1" />
|
||||
|
@ -46,8 +46,10 @@ export default function AuthenticatedLayout({
|
|||
</div>
|
||||
</header>
|
||||
|
||||
<main className="p-4 md:pt-0 h-full">{children}</main>
|
||||
<footer className="text-sm p-4">
|
||||
<main className="p-4 md:pt-0 h-full w-full max-w-full overflow-x-hidden">
|
||||
{children}
|
||||
</main>
|
||||
<footer className="text-sm p-4 text-center bg-background border-t">
|
||||
© Copyright {new Date().getFullYear()} Bidang Tata
|
||||
Lingkungan dan Kebersihan - Dinas Lingkungan Hidup Provinsi
|
||||
DKI Jakarta
|
||||
|
|
|
@ -4,15 +4,21 @@ import Footer from "@/components/Partials/Footer";
|
|||
import HeroSecond from "@/components/Card/HeroSecond";
|
||||
import CardPengumuman from "@/components/Card/CardPengumuman";
|
||||
import { Head } from "@inertiajs/react";
|
||||
import { usePage } from "@inertiajs/react";
|
||||
import Guest from "@/layouts/guest-layout";
|
||||
|
||||
export default function Pengumuman() {
|
||||
const { posts } = usePage().props as any;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Guest>
|
||||
<Head title="Pengumuman" />
|
||||
<Navbar />
|
||||
<HeroSecond title="Pengumuman" />
|
||||
<CardPengumuman posts={[]} />
|
||||
<CardPengumuman posts={posts} />
|
||||
<Footer />
|
||||
</Guest>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,15 +3,16 @@ import Navbar from "@/components/Partials/Navbar";
|
|||
import Footer from "@/components/Partials/Footer";
|
||||
import HeroSecond from "@/components/Card/HeroSecond";
|
||||
import CardPeraturan from "@/components/Card/CardPeraturan";
|
||||
import { Head } from "@inertiajs/react";
|
||||
import { Head, usePage } from "@inertiajs/react";
|
||||
|
||||
export default function Peraturan() {
|
||||
const { posts } = usePage().props as any;
|
||||
return (
|
||||
<>
|
||||
<Head title="Peraturan" />
|
||||
<Navbar />
|
||||
<HeroSecond title="Peraturan" />
|
||||
<CardPeraturan />
|
||||
<CardPeraturan posts={posts} />
|
||||
<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 HeroSecond from "@/components/Card/HeroSecond";
|
||||
import CardUndangan from "@/components/Card/CardUndangan";
|
||||
import { Head } from "@inertiajs/react";
|
||||
import { Head, usePage } from "@inertiajs/react";
|
||||
|
||||
export default function Undangan() {
|
||||
const { posts } = usePage().props as any;
|
||||
return (
|
||||
<>
|
||||
<Head title="Undangan" />
|
||||
<Navbar />
|
||||
<HeroSecond title="Undangan" />
|
||||
<CardUndangan />
|
||||
<CardUndangan posts={posts} />
|
||||
<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