update: roles permissions
parent
e0fb72af83
commit
722115674f
|
@ -14,21 +14,21 @@ class CheckPerizinanData extends Command
|
|||
{
|
||||
$this->info('📊 Checking synced perizinan data...');
|
||||
|
||||
$data = PerizinanStatus::orderBy('kategori')->orderBy('status_id')->get();
|
||||
$data = PerizinanStatus::orderBy('Kategori_ID')->orderBy('StatusID')->get();
|
||||
|
||||
if ($data->isEmpty()) {
|
||||
$this->warn('No data found in database');
|
||||
return;
|
||||
}
|
||||
|
||||
$groupedData = $data->groupBy('kategori');
|
||||
$groupedData = $data->groupBy('Kategori_ID');
|
||||
|
||||
foreach ($groupedData as $kategori => $items) {
|
||||
$this->newLine();
|
||||
$this->info("=== {$kategori} ===");
|
||||
|
||||
foreach ($items as $item) {
|
||||
$this->line(" {$item->status_id}: {$item->label} = {$item->value}");
|
||||
$this->line(" {$item->StatusID}: {$item->Label} = {$item->Value}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -123,7 +123,7 @@ class SyncPerizinanData extends Command
|
|||
}
|
||||
|
||||
$endTime = Carbon::now();
|
||||
$duration = $endTime->diffInSeconds($startTime);
|
||||
$duration = $endTime->diffInSeconds($startTime, true);
|
||||
|
||||
$this->info("🎉 Data synchronization completed in {$duration} seconds");
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ class TestProductionSetup extends Command
|
|||
$this->info('2. Testing Database Table...');
|
||||
try {
|
||||
$count = PerizinanStatus::count();
|
||||
$this->line(" ✅ perizinan_status table exists with {$count} records");
|
||||
$this->line(" ✅ PerizinanStatus table exists with {$count} records");
|
||||
} catch (\Exception $e) {
|
||||
$this->error(' ❌ Database table issue: ' . $e->getMessage());
|
||||
$allPassed = false;
|
||||
|
|
|
@ -64,10 +64,10 @@ class DashboardHelper
|
|||
|
||||
foreach ($dbData as $item) {
|
||||
$data[] = [
|
||||
'id' => $item->status_id,
|
||||
'label' => $item->label,
|
||||
'value' => $item->value,
|
||||
'color' => $colorMap[$item->status_id] ?? 'secondary'
|
||||
'id' => $item->StatusID,
|
||||
'label' => $item->Label,
|
||||
'value' => $item->Value,
|
||||
'color' => $colorMap[$item->StatusID] ?? 'secondary'
|
||||
];
|
||||
}
|
||||
|
||||
|
@ -281,10 +281,10 @@ class DashboardHelper
|
|||
$index = 1;
|
||||
foreach ($dbData as $item) {
|
||||
$data[] = [
|
||||
'nama' => $item->nama,
|
||||
'total' => $item->total,
|
||||
'durasi_pemohon' => $item->durasi_pemohon,
|
||||
'durasi_petugas' => $item->durasi_petugas,
|
||||
'Nama' => $item->Nama,
|
||||
'Total' => $item->Total,
|
||||
'DurasiPemohon' => $item->DurasiPemohon,
|
||||
'DurasiPetugas' => $item->DurasiPetugas,
|
||||
'rank_order' => $index++ // Generate rank_order dinamis untuk display
|
||||
];
|
||||
}
|
||||
|
@ -302,18 +302,18 @@ class DashboardHelper
|
|||
{
|
||||
$fallbackData = [
|
||||
'pertek' => [
|
||||
['nama' => 'Izin Pengelolaan Limbah B3', 'total' => 45, 'durasi_pemohon' => '2 Hari 3 Jam', 'durasi_petugas' => '5 Hari 2 Jam', 'rank_order' => 1],
|
||||
['nama' => 'Izin Emisi Kendaraan', 'total' => 67, 'durasi_pemohon' => '3 Hari 1 Jam', 'durasi_petugas' => '7 Hari 4 Jam', 'rank_order' => 2],
|
||||
['nama' => 'SPPL Industri', 'total' => 23, 'durasi_pemohon' => '4 Hari 2 Jam', 'durasi_petugas' => '8 Hari 1 Jam', 'rank_order' => 3],
|
||||
['nama' => 'Izin Penyimpanan B3', 'total' => 34, 'durasi_pemohon' => '5 Hari', 'durasi_petugas' => '9 Hari 3 Jam', 'rank_order' => 4],
|
||||
['nama' => 'Izin Pengolahan Limbah', 'total' => 19, 'durasi_pemohon' => '6 Hari 4 Jam', 'durasi_petugas' => '10 Hari 2 Jam', 'rank_order' => 5],
|
||||
['Nama' => 'Izin Pengelolaan Limbah B3', 'Total' => 45, 'DurasiPemohon' => '2 Hari 3 Jam', 'DurasiPetugas' => '5 Hari 2 Jam', 'rank_order' => 1],
|
||||
['Nama' => 'Izin Emisi Kendaraan', 'Total' => 67, 'DurasiPemohon' => '3 Hari 1 Jam', 'DurasiPetugas' => '7 Hari 4 Jam', 'rank_order' => 2],
|
||||
['Nama' => 'SPPL Industri', 'Total' => 23, 'DurasiPemohon' => '4 Hari 2 Jam', 'DurasiPetugas' => '8 Hari 1 Jam', 'rank_order' => 3],
|
||||
['Nama' => 'Izin Penyimpanan B3', 'Total' => 34, 'DurasiPemohon' => '5 Hari', 'DurasiPetugas' => '9 Hari 3 Jam', 'rank_order' => 4],
|
||||
['Nama' => 'Izin Pengolahan Limbah', 'Total' => 19, 'DurasiPemohon' => '6 Hari 4 Jam', 'DurasiPetugas' => '10 Hari 2 Jam', 'rank_order' => 5],
|
||||
],
|
||||
'amdal' => [
|
||||
['nama' => 'AMDAL Bangunan Tinggi', 'total' => 12, 'durasi_pemohon' => '15 Hari', 'durasi_petugas' => '30 Hari', 'rank_order' => 1],
|
||||
['nama' => 'AMDAL Infrastruktur', 'total' => 8, 'durasi_pemohon' => '18 Hari', 'durasi_petugas' => '35 Hari', 'rank_order' => 2],
|
||||
['nama' => 'AMDAL Industri', 'total' => 15, 'durasi_pemohon' => '20 Hari', 'durasi_petugas' => '40 Hari', 'rank_order' => 3],
|
||||
['nama' => 'AMDAL Perumahan', 'total' => 25, 'durasi_pemohon' => '22 Hari', 'durasi_petugas' => '42 Hari', 'rank_order' => 4],
|
||||
['nama' => 'AMDAL Komersial', 'total' => 18, 'durasi_pemohon' => '25 Hari', 'durasi_petugas' => '45 Hari', 'rank_order' => 5],
|
||||
['Nama' => 'AMDAL Bangunan Tinggi', 'Total' => 12, 'DurasiPemohon' => '15 Hari', 'DurasiPetugas' => '30 Hari', 'rank_order' => 1],
|
||||
['Nama' => 'AMDAL Infrastruktur', 'Total' => 8, 'DurasiPemohon' => '18 Hari', 'DurasiPetugas' => '35 Hari', 'rank_order' => 2],
|
||||
['Nama' => 'AMDAL Industri', 'Total' => 15, 'DurasiPemohon' => '20 Hari', 'DurasiPetugas' => '40 Hari', 'rank_order' => 3],
|
||||
['Nama' => 'AMDAL Perumahan', 'Total' => 25, 'DurasiPemohon' => '22 Hari', 'DurasiPetugas' => '42 Hari', 'rank_order' => 4],
|
||||
['Nama' => 'AMDAL Komersial', 'Total' => 18, 'DurasiPemohon' => '25 Hari', 'DurasiPetugas' => '45 Hari', 'rank_order' => 5],
|
||||
]
|
||||
];
|
||||
|
||||
|
@ -377,9 +377,9 @@ class DashboardHelper
|
|||
$index = 1;
|
||||
foreach ($dbData as $item) {
|
||||
$data[] = [
|
||||
'nama_izin' => $item->nama_izin,
|
||||
'pemohon' => $item->pemohon,
|
||||
'tanggal_terbit' => $item->tanggal_terbit,
|
||||
'NamaIzin' => $item->NamaIzin,
|
||||
'Pemohon' => $item->Pemohon,
|
||||
'TanggalTerbit' => $item->TanggalTerbit,
|
||||
'rank_order' => $index++ // Generate rank_order dinamis untuk display
|
||||
];
|
||||
}
|
||||
|
@ -397,18 +397,18 @@ class DashboardHelper
|
|||
{
|
||||
$fallbackData = [
|
||||
'pertek' => [
|
||||
['nama_izin' => 'SERTIFIKAT LAIK OPERASI - PEMENUHAN BAKU MUTU EMISI', 'pemohon' => 'PT. MAJU BERSAMA', 'tanggal_terbit' => Carbon::parse('2025-07-15'), 'rank_order' => 1],
|
||||
['nama_izin' => 'PERSETUJUAN TEKNIS - PEMENUHAN BAKU MUTU AIR LIMBAH', 'pemohon' => 'CV. KARYA MANDIRI', 'tanggal_terbit' => Carbon::parse('2025-07-14'), 'rank_order' => 2],
|
||||
['nama_izin' => 'SERTIFIKAT LAIK OPERASI - PEMENUHAN BAKU MUTU AIR LIMBAH', 'pemohon' => 'PT. INDUSTRI SEJAHTERA', 'tanggal_terbit' => Carbon::parse('2025-07-13'), 'rank_order' => 3],
|
||||
['nama_izin' => 'PERSETUJUAN TEKNIS - PEMENUHAN BAKU MUTU AIR LIMBAH', 'pemohon' => 'PT. TEKNOLOGI MODERN', 'tanggal_terbit' => Carbon::parse('2025-07-12'), 'rank_order' => 4],
|
||||
['nama_izin' => 'SERTIFIKAT LAIK OPERASI - PEMENUHAN BAKU MUTU EMISI', 'pemohon' => 'CV. BERKAH JAYA', 'tanggal_terbit' => Carbon::parse('2025-07-11'), 'rank_order' => 5],
|
||||
['NamaIzin' => 'SERTIFIKAT LAIK OPERASI - PEMENUHAN BAKU MUTU EMISI', 'Pemohon' => 'PT. MAJU BERSAMA', 'TanggalTerbit' => Carbon::parse('2025-07-15'), 'rank_order' => 1],
|
||||
['NamaIzin' => 'PERSETUJUAN TEKNIS - PEMENUHAN BAKU MUTU AIR LIMBAH', 'Pemohon' => 'CV. KARYA MANDIRI', 'TanggalTerbit' => Carbon::parse('2025-07-14'), 'rank_order' => 2],
|
||||
['NamaIzin' => 'SERTIFIKAT LAIK OPERASI - PEMENUHAN BAKU MUTU AIR LIMBAH', 'Pemohon' => 'PT. INDUSTRI SEJAHTERA', 'TanggalTerbit' => Carbon::parse('2025-07-13'), 'rank_order' => 3],
|
||||
['NamaIzin' => 'PERSETUJUAN TEKNIS - PEMENUHAN BAKU MUTU AIR LIMBAH', 'Pemohon' => 'PT. TEKNOLOGI MODERN', 'TanggalTerbit' => Carbon::parse('2025-07-12'), 'rank_order' => 4],
|
||||
['NamaIzin' => 'SERTIFIKAT LAIK OPERASI - PEMENUHAN BAKU MUTU EMISI', 'Pemohon' => 'CV. BERKAH JAYA', 'TanggalTerbit' => Carbon::parse('2025-07-11'), 'rank_order' => 5],
|
||||
],
|
||||
'amdal' => [
|
||||
['nama_izin' => 'AMDAL BANGUNAN TINGGI', 'pemohon' => 'PT. KONSTRUKSI PRIMA', 'tanggal_terbit' => Carbon::parse('2025-07-10'), 'rank_order' => 1],
|
||||
['nama_izin' => 'AMDAL INFRASTRUKTUR', 'pemohon' => 'CV. INFRATEK', 'tanggal_terbit' => Carbon::parse('2025-07-09'), 'rank_order' => 2],
|
||||
['nama_izin' => 'AMDAL INDUSTRI', 'pemohon' => 'PT. INDUSTRI MODERN', 'tanggal_terbit' => Carbon::parse('2025-07-08'), 'rank_order' => 3],
|
||||
['nama_izin' => 'AMDAL PERUMAHAN', 'pemohon' => 'PT. PROPERTI SEJAHTERA', 'tanggal_terbit' => Carbon::parse('2025-07-07'), 'rank_order' => 4],
|
||||
['nama_izin' => 'AMDAL KOMERSIAL', 'pemohon' => 'CV. KOMERSIAL JAYA', 'tanggal_terbit' => Carbon::parse('2025-07-06'), 'rank_order' => 5],
|
||||
['NamaIzin' => 'AMDAL BANGUNAN TINGGI', 'Pemohon' => 'PT. KONSTRUKSI PRIMA', 'TanggalTerbit' => Carbon::parse('2025-07-10'), 'rank_order' => 1],
|
||||
['NamaIzin' => 'AMDAL INFRASTRUKTUR', 'Pemohon' => 'CV. INFRATEK', 'TanggalTerbit' => Carbon::parse('2025-07-09'), 'rank_order' => 2],
|
||||
['NamaIzin' => 'AMDAL INDUSTRI', 'Pemohon' => 'PT. INDUSTRI MODERN', 'TanggalTerbit' => Carbon::parse('2025-07-08'), 'rank_order' => 3],
|
||||
['NamaIzin' => 'AMDAL PERUMAHAN', 'Pemohon' => 'PT. PROPERTI SEJAHTERA', 'TanggalTerbit' => Carbon::parse('2025-07-07'), 'rank_order' => 4],
|
||||
['NamaIzin' => 'AMDAL KOMERSIAL', 'Pemohon' => 'CV. KOMERSIAL JAYA', 'TanggalTerbit' => Carbon::parse('2025-07-06'), 'rank_order' => 5],
|
||||
]
|
||||
];
|
||||
|
||||
|
|
|
@ -0,0 +1,55 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
|
||||
class PermissionController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$permissions = Permission::orderBy('name')->paginate(20);
|
||||
return view('admin.permissions.index', compact('permissions'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
return view('admin.permissions.create');
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => ['required','string','max:150','unique:permissions,name'],
|
||||
]);
|
||||
|
||||
Permission::create(['name' => $validated['name']]);
|
||||
|
||||
return redirect()->route('admin.permissions.index')->with('success', 'Permission berhasil dibuat.');
|
||||
}
|
||||
|
||||
public function edit(Permission $permission)
|
||||
{
|
||||
return view('admin.permissions.edit', compact('permission'));
|
||||
}
|
||||
|
||||
public function update(Request $request, Permission $permission)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => ['required','string','max:150','unique:permissions,name,'.$permission->id],
|
||||
]);
|
||||
|
||||
$permission->update(['name' => $validated['name']]);
|
||||
|
||||
return redirect()->route('admin.permissions.index')->with('success', 'Permission berhasil diperbarui.');
|
||||
}
|
||||
|
||||
public function destroy(Permission $permission)
|
||||
{
|
||||
$permission->delete();
|
||||
return redirect()->route('admin.permissions.index')->with('success', 'Permission berhasil dihapus.');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Admin;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
|
||||
class RoleController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
$roles = Role::withCount('permissions')->orderBy('name')->paginate(15);
|
||||
return view('admin.roles.index', compact('roles'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$permissions = Permission::orderBy('name')->get();
|
||||
return view('admin.roles.create', compact('permissions'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => ['required','string','max:100','unique:roles,name'],
|
||||
'permissions' => ['array'],
|
||||
'permissions.*' => ['string','exists:permissions,name'],
|
||||
]);
|
||||
|
||||
$role = Role::create(['name' => $validated['name']]);
|
||||
if (!empty($validated['permissions'])) {
|
||||
$role->syncPermissions($validated['permissions']);
|
||||
}
|
||||
|
||||
return redirect()->route('admin.roles.index')->with('success', 'Role berhasil dibuat.');
|
||||
}
|
||||
|
||||
public function edit(Role $role)
|
||||
{
|
||||
$permissions = Permission::orderBy('name')->get();
|
||||
$rolePermissions = $role->permissions->pluck('name')->toArray();
|
||||
return view('admin.roles.edit', compact('role','permissions','rolePermissions'));
|
||||
}
|
||||
|
||||
public function update(Request $request, Role $role)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'name' => ['required','string','max:100','unique:roles,name,'.$role->id],
|
||||
'permissions' => ['array'],
|
||||
'permissions.*' => ['string','exists:permissions,name'],
|
||||
]);
|
||||
|
||||
$role->update(['name' => $validated['name']]);
|
||||
$role->syncPermissions($validated['permissions'] ?? []);
|
||||
|
||||
return redirect()->route('admin.roles.index')->with('success', 'Role berhasil diperbarui.');
|
||||
}
|
||||
|
||||
public function destroy(Role $role)
|
||||
{
|
||||
$role->delete();
|
||||
return redirect()->route('admin.roles.index')->with('success', 'Role berhasil dihapus.');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,106 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Auth\LoginRequest;
|
||||
use App\Models\User;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
class AuthController extends Controller
|
||||
{
|
||||
// =============== API (Sanctum Token) ===============
|
||||
public function login(LoginRequest $request)
|
||||
{
|
||||
$data = $request->validated();
|
||||
$identifier = $data['identifier'];
|
||||
$password = $data['password'];
|
||||
$device = $data['device_name'] ?? $request->header('User-Agent') ?? 'api';
|
||||
|
||||
$user = User::where('email', $identifier)->first();
|
||||
if (!$user) {
|
||||
$user = User::where('username', $identifier)->first();
|
||||
}
|
||||
if (!$user || !Hash::check($password, $user->password)) {
|
||||
throw ValidationException::withMessages([
|
||||
'identifier' => ['email/username/password yang diberikan salah.'],
|
||||
]);
|
||||
}
|
||||
|
||||
$user->tokens()->where('name', $device)->delete();
|
||||
$token = $user->createToken($device)->plainTextToken;
|
||||
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'token' => $token,
|
||||
'token_type' => 'Bearer',
|
||||
'user' => [
|
||||
'id' => $user->id,
|
||||
'name' => $user->name,
|
||||
'email' => $user->email,
|
||||
'username' => $user->username,
|
||||
],
|
||||
]);
|
||||
}
|
||||
|
||||
public function me(Request $request)
|
||||
{
|
||||
return response()->json([
|
||||
'success' => true,
|
||||
'user' => $request->user(),
|
||||
]);
|
||||
}
|
||||
|
||||
public function logout(Request $request)
|
||||
{
|
||||
$request->user()->currentAccessToken()->delete();
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
public function logoutAll(Request $request)
|
||||
{
|
||||
$request->user()->tokens()->delete();
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
// =============== Web (Session Guard) ===============
|
||||
public function sessionLogin(LoginRequest $request)
|
||||
{
|
||||
$data = $request->validated();
|
||||
$identifier = $data['identifier'];
|
||||
$password = $data['password'];
|
||||
|
||||
$user = User::where('email', $identifier)->first();
|
||||
if (!$user) {
|
||||
$user = User::where('username', $identifier)->first();
|
||||
}
|
||||
|
||||
if (!$user || !Hash::check($password, $user->password)) {
|
||||
throw ValidationException::withMessages([
|
||||
'identifier' => ['email/username/password yang diberikan salah.'],
|
||||
]);
|
||||
}
|
||||
|
||||
Auth::login($user, true);
|
||||
$request->session()->regenerate();
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
public function sessionLogout(Request $request)
|
||||
{
|
||||
Auth::logout();
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
return redirect()->route('login.index');
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Http\Requests\Auth\LoginRequest;
|
||||
use App\Models\User;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class WebAuthController extends Controller
|
||||
{
|
||||
public function sessionLogin(LoginRequest $request)
|
||||
{
|
||||
$data = $request->validated();
|
||||
$identifier = $data['identifier'];
|
||||
$password = $data['password'];
|
||||
|
||||
$user = User::where('email', $identifier)->first();
|
||||
if (!$user) {
|
||||
$user = User::where('username', $identifier)->first();
|
||||
}
|
||||
|
||||
if (!$user || !Hash::check($password, $user->password)) {
|
||||
throw ValidationException::withMessages([
|
||||
'identifier' => ['email/username/password yang diberikan salah.'],
|
||||
]);
|
||||
}
|
||||
|
||||
Auth::login($user, true);
|
||||
request()->session()->regenerate();
|
||||
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
public function logout(Request $request)
|
||||
{
|
||||
Auth::logout();
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json(['success' => true]);
|
||||
}
|
||||
|
||||
return redirect()->route('login.index');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests\Auth;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class LoginRequest extends FormRequest
|
||||
{
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'identifier' => ['required', 'string'],
|
||||
'password' => [
|
||||
'required',
|
||||
'string',
|
||||
'min:8',
|
||||
'regex:/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/',
|
||||
],
|
||||
'device_name' => ['nullable', 'string'],
|
||||
];
|
||||
}
|
||||
|
||||
public function messages(): array
|
||||
{
|
||||
return [
|
||||
'password.min' => 'Password minimal 8 karakter.',
|
||||
'password.regex' => 'Password harus mengandung huruf besar, huruf kecil, angka, dan simbol khusus.',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
@ -7,22 +7,23 @@ use Carbon\Carbon;
|
|||
|
||||
class FastestPermohonan extends Model
|
||||
{
|
||||
protected $table = 'fastest_permohonan';
|
||||
protected $table = 'FastestPermohonan';
|
||||
protected $primaryKey = 'FastestPermohonanID';
|
||||
|
||||
protected $fillable = [
|
||||
'kategori',
|
||||
'nama',
|
||||
'total',
|
||||
'durasi_pemohon',
|
||||
'durasi_petugas',
|
||||
'api_last_updated',
|
||||
'sync_date'
|
||||
'Kategori_ID',
|
||||
'Nama',
|
||||
'Total',
|
||||
'DurasiPemohon',
|
||||
'DurasiPetugas',
|
||||
'ApiLastUpdated',
|
||||
'SyncDate'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'api_last_updated' => 'datetime',
|
||||
'sync_date' => 'date',
|
||||
'total' => 'integer'
|
||||
'ApiLastUpdated' => 'datetime',
|
||||
'SyncDate' => 'date',
|
||||
'Total' => 'integer'
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -32,15 +33,15 @@ class FastestPermohonan extends Model
|
|||
*/
|
||||
public static function getLatestByKategori($kategori)
|
||||
{
|
||||
$latestSyncDate = self::where('kategori', $kategori)->max('sync_date');
|
||||
$latestSyncDate = self::where('Kategori_ID', $kategori)->max('SyncDate');
|
||||
|
||||
if (!$latestSyncDate) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
return self::where('kategori', $kategori)
|
||||
->whereDate('sync_date', $latestSyncDate)
|
||||
->orderBy('id') // Keep API order by using insertion order
|
||||
return self::where('Kategori_ID', $kategori)
|
||||
->whereDate('SyncDate', $latestSyncDate)
|
||||
->orderBy('FastestPermohonanID') // Keep API order by using insertion order
|
||||
->limit(5)
|
||||
->get();
|
||||
}
|
||||
|
@ -52,9 +53,9 @@ class FastestPermohonan extends Model
|
|||
{
|
||||
$date = $date ?: Carbon::today();
|
||||
|
||||
return self::where('kategori', $kategori)
|
||||
->whereDate('sync_date', $date)
|
||||
->orderBy('id') // Keep API order by using insertion order
|
||||
return self::where('Kategori_ID', $kategori)
|
||||
->whereDate('SyncDate', $date)
|
||||
->orderBy('FastestPermohonanID') // Keep API order by using insertion order
|
||||
->limit(5)
|
||||
->get();
|
||||
}
|
||||
|
|
|
@ -7,21 +7,22 @@ use Carbon\Carbon;
|
|||
|
||||
class PerizinanStatus extends Model
|
||||
{
|
||||
protected $table = 'perizinan_status';
|
||||
protected $table = 'PerizinanStatus';
|
||||
protected $primaryKey = 'PerizinanStatusID';
|
||||
|
||||
protected $fillable = [
|
||||
'kategori',
|
||||
'status_id',
|
||||
'label',
|
||||
'value',
|
||||
'api_last_updated',
|
||||
'sync_date'
|
||||
'Kategori_ID',
|
||||
'StatusID',
|
||||
'Label',
|
||||
'Value',
|
||||
'ApiLastUpdated',
|
||||
'SyncDate'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'api_last_updated' => 'datetime',
|
||||
'sync_date' => 'date',
|
||||
'value' => 'integer'
|
||||
'ApiLastUpdated' => 'datetime',
|
||||
'SyncDate' => 'date',
|
||||
'Value' => 'integer'
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -30,18 +31,18 @@ class PerizinanStatus extends Model
|
|||
*/
|
||||
public static function getLatestByKategori($kategori)
|
||||
{
|
||||
// Find the most recent sync_date for the given kategori
|
||||
$latestSyncDate = self::where('kategori', $kategori)->max('sync_date');
|
||||
// Find the most recent SyncDate for the given kategori
|
||||
$latestSyncDate = self::where('Kategori_ID', $kategori)->max('SyncDate');
|
||||
|
||||
if (!$latestSyncDate) {
|
||||
// No data at all for this kategori
|
||||
return collect();
|
||||
}
|
||||
|
||||
return self::where('kategori', $kategori)
|
||||
->whereDate('sync_date', $latestSyncDate)
|
||||
return self::where('Kategori_ID', $kategori)
|
||||
->whereDate('SyncDate', $latestSyncDate)
|
||||
->get()
|
||||
->keyBy('status_id');
|
||||
->keyBy('StatusID');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -51,9 +52,9 @@ class PerizinanStatus extends Model
|
|||
{
|
||||
$date = $date ?: Carbon::today();
|
||||
|
||||
return self::where('kategori', $kategori)
|
||||
->whereDate('sync_date', $date)
|
||||
return self::where('Kategori_ID', $kategori)
|
||||
->whereDate('SyncDate', $date)
|
||||
->get()
|
||||
->keyBy('status_id');
|
||||
->keyBy('StatusID');
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,21 +7,22 @@ use Carbon\Carbon;
|
|||
|
||||
class TerakhirTerbit extends Model
|
||||
{
|
||||
protected $table = 'terakhir_terbit';
|
||||
protected $table = 'TerakhirTerbit';
|
||||
protected $primaryKey = 'TerakhirTerbitID';
|
||||
|
||||
protected $fillable = [
|
||||
'kategori',
|
||||
'nama_izin',
|
||||
'pemohon',
|
||||
'tanggal_terbit',
|
||||
'api_last_updated',
|
||||
'sync_date'
|
||||
'Kategori_ID',
|
||||
'NamaIzin',
|
||||
'Pemohon',
|
||||
'TanggalTerbit',
|
||||
'ApiLastUpdated',
|
||||
'SyncDate'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'tanggal_terbit' => 'date',
|
||||
'api_last_updated' => 'datetime',
|
||||
'sync_date' => 'date'
|
||||
'TanggalTerbit' => 'date',
|
||||
'ApiLastUpdated' => 'datetime',
|
||||
'SyncDate' => 'date'
|
||||
];
|
||||
|
||||
/**
|
||||
|
@ -30,15 +31,15 @@ class TerakhirTerbit extends Model
|
|||
*/
|
||||
public static function getLatestByKategori($kategori)
|
||||
{
|
||||
$latestSyncDate = self::where('kategori', $kategori)->max('sync_date');
|
||||
$latestSyncDate = self::where('Kategori_ID', $kategori)->max('SyncDate');
|
||||
|
||||
if (!$latestSyncDate) {
|
||||
return collect();
|
||||
}
|
||||
|
||||
return self::where('kategori', $kategori)
|
||||
->whereDate('sync_date', $latestSyncDate)
|
||||
->orderBy('tanggal_terbit', 'desc')
|
||||
return self::where('Kategori_ID', $kategori)
|
||||
->whereDate('SyncDate', $latestSyncDate)
|
||||
->orderBy('TanggalTerbit', 'desc')
|
||||
->limit(5)
|
||||
->get();
|
||||
}
|
||||
|
@ -48,9 +49,9 @@ class TerakhirTerbit extends Model
|
|||
*/
|
||||
public static function getByKategoriAndDate($kategori, $date)
|
||||
{
|
||||
return self::where('kategori', $kategori)
|
||||
->whereDate('sync_date', $date)
|
||||
->orderBy('tanggal_terbit', 'desc')
|
||||
return self::where('Kategori_ID', $kategori)
|
||||
->whereDate('SyncDate', $date)
|
||||
->orderBy('TanggalTerbit', 'desc')
|
||||
->limit(5)
|
||||
->get();
|
||||
}
|
||||
|
|
|
@ -4,13 +4,15 @@ namespace App\Models;
|
|||
|
||||
// use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Laravel\Sanctum\HasApiTokens;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Spatie\Permission\Traits\HasRoles;
|
||||
|
||||
class User extends Authenticatable
|
||||
{
|
||||
/** @use HasFactory<\Database\Factories\UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
use HasApiTokens, HasFactory, Notifiable, HasRoles;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
|
@ -20,6 +22,7 @@ class User extends Authenticatable
|
|||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'username',
|
||||
'password',
|
||||
];
|
||||
|
||||
|
|
|
@ -94,19 +94,19 @@ class PerizinanApiService
|
|||
|
||||
foreach ($apiData['data'] as $item) {
|
||||
// Check if data already exists for today
|
||||
$exists = PerizinanStatus::where('kategori', $kategori)
|
||||
->where('status_id', $item['id'])
|
||||
->whereDate('sync_date', $syncDate)
|
||||
$exists = PerizinanStatus::where('Kategori_ID', $kategori)
|
||||
->where('StatusID', $item['id'])
|
||||
->whereDate('SyncDate', $syncDate)
|
||||
->exists();
|
||||
|
||||
if (!$exists) {
|
||||
PerizinanStatus::create([
|
||||
'kategori' => $kategori,
|
||||
'status_id' => $item['id'],
|
||||
'label' => $item['label'],
|
||||
'value' => $item['value'],
|
||||
'api_last_updated' => $apiLastUpdated,
|
||||
'sync_date' => $syncDate
|
||||
'Kategori_ID' => $kategori,
|
||||
'StatusID' => $item['id'],
|
||||
'Label' => $item['label'],
|
||||
'Value' => $item['value'],
|
||||
'ApiLastUpdated' => $apiLastUpdated,
|
||||
'SyncDate' => $syncDate
|
||||
]);
|
||||
|
||||
$syncedCount++;
|
||||
|
@ -199,19 +199,19 @@ class PerizinanApiService
|
|||
$syncedCount = 0;
|
||||
|
||||
// Delete existing data for today to ensure we have fresh data
|
||||
FastestPermohonan::where('kategori', $kategori)
|
||||
->whereDate('sync_date', $syncDate)
|
||||
FastestPermohonan::where('Kategori_ID', $kategori)
|
||||
->whereDate('SyncDate', $syncDate)
|
||||
->delete();
|
||||
|
||||
foreach ($apiData['data'] as $index => $item) {
|
||||
FastestPermohonan::create([
|
||||
'kategori' => $kategori,
|
||||
'nama' => $item['nama'],
|
||||
'total' => $item['total'],
|
||||
'durasi_pemohon' => $item['durasi_pemohon'],
|
||||
'durasi_petugas' => $item['durasi_petugas'],
|
||||
'api_last_updated' => $apiLastUpdated,
|
||||
'sync_date' => $syncDate
|
||||
'Kategori_ID' => $kategori,
|
||||
'Nama' => $item['nama'],
|
||||
'Total' => $item['total'],
|
||||
'DurasiPemohon' => $item['durasi_pemohon'],
|
||||
'DurasiPetugas' => $item['durasi_petugas'],
|
||||
'ApiLastUpdated' => $apiLastUpdated,
|
||||
'SyncDate' => $syncDate
|
||||
]);
|
||||
|
||||
$syncedCount++;
|
||||
|
@ -303,18 +303,18 @@ class PerizinanApiService
|
|||
$syncedCount = 0;
|
||||
|
||||
// Delete existing data for today to ensure we have fresh ranking
|
||||
TerakhirTerbit::where('kategori', $kategori)
|
||||
->whereDate('sync_date', $syncDate)
|
||||
TerakhirTerbit::where('Kategori_ID', $kategori)
|
||||
->whereDate('SyncDate', $syncDate)
|
||||
->delete();
|
||||
|
||||
foreach ($apiData['data'] as $index => $item) {
|
||||
TerakhirTerbit::create([
|
||||
'kategori' => $kategori,
|
||||
'nama_izin' => $item['nama_izin'],
|
||||
'pemohon' => $item['pemohon'],
|
||||
'tanggal_terbit' => Carbon::parse($item['tanggal_terbit']),
|
||||
'api_last_updated' => $apiLastUpdated,
|
||||
'sync_date' => $syncDate
|
||||
'Kategori_ID' => $kategori,
|
||||
'NamaIzin' => $item['nama_izin'],
|
||||
'Pemohon' => $item['pemohon'],
|
||||
'TanggalTerbit' => Carbon::parse($item['tanggal_terbit']),
|
||||
'ApiLastUpdated' => $apiLastUpdated,
|
||||
'SyncDate' => $syncDate
|
||||
]);
|
||||
|
||||
$syncedCount++;
|
||||
|
|
|
@ -3,15 +3,24 @@
|
|||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
use Spatie\Permission\Middleware\PermissionMiddleware;
|
||||
use Spatie\Permission\Middleware\RoleMiddleware;
|
||||
use Spatie\Permission\Middleware\RoleOrPermissionMiddleware;
|
||||
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withRouting(
|
||||
web: __DIR__.'/../routes/web.php',
|
||||
api: __DIR__.'/../routes/api.php',
|
||||
commands: __DIR__.'/../routes/console.php',
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
//
|
||||
// Spatie Permission middleware aliases
|
||||
$middleware->alias([
|
||||
'role' => RoleMiddleware::class,
|
||||
'permission' => PermissionMiddleware::class,
|
||||
'role_or_permission' => RoleOrPermissionMiddleware::class,
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions) {
|
||||
//
|
||||
|
|
|
@ -12,9 +12,11 @@
|
|||
"php": "^8.2",
|
||||
"barryvdh/laravel-dompdf": "^3.1",
|
||||
"laravel/framework": "^12.0",
|
||||
"laravel/sanctum": "^4.2",
|
||||
"laravel/tinker": "^2.10.1",
|
||||
"mallardduck/blade-lucide-icons": "^1.23",
|
||||
"spatie/laravel-html": "^3.12"
|
||||
"spatie/laravel-html": "^3.12",
|
||||
"spatie/laravel-permission": "^6.21"
|
||||
},
|
||||
"require-dev": {
|
||||
"fakerphp/faker": "^1.23",
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "a630bee8cc849f41bc0261821020fefa",
|
||||
"content-hash": "324973c896fb611b576d446434dcb170",
|
||||
"packages": [
|
||||
{
|
||||
"name": "barryvdh/laravel-dompdf",
|
||||
|
@ -1641,6 +1641,70 @@
|
|||
},
|
||||
"time": "2025-02-11T13:34:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/sanctum",
|
||||
"version": "v4.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laravel/sanctum.git",
|
||||
"reference": "fd6df4f79f48a72992e8d29a9c0ee25422a0d677"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laravel/sanctum/zipball/fd6df4f79f48a72992e8d29a9c0ee25422a0d677",
|
||||
"reference": "fd6df4f79f48a72992e8d29a9c0ee25422a0d677",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
"illuminate/console": "^11.0|^12.0",
|
||||
"illuminate/contracts": "^11.0|^12.0",
|
||||
"illuminate/database": "^11.0|^12.0",
|
||||
"illuminate/support": "^11.0|^12.0",
|
||||
"php": "^8.2",
|
||||
"symfony/console": "^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"mockery/mockery": "^1.6",
|
||||
"orchestra/testbench": "^9.0|^10.0",
|
||||
"phpstan/phpstan": "^1.10",
|
||||
"phpunit/phpunit": "^11.3"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Laravel\\Sanctum\\SanctumServiceProvider"
|
||||
]
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laravel\\Sanctum\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Taylor Otwell",
|
||||
"email": "taylor@laravel.com"
|
||||
}
|
||||
],
|
||||
"description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
|
||||
"keywords": [
|
||||
"auth",
|
||||
"laravel",
|
||||
"sanctum"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/laravel/sanctum/issues",
|
||||
"source": "https://github.com/laravel/sanctum"
|
||||
},
|
||||
"time": "2025-07-09T19:45:24+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/serializable-closure",
|
||||
"version": "v2.0.3",
|
||||
|
@ -3869,6 +3933,89 @@
|
|||
],
|
||||
"time": "2025-03-21T08:58:06+00:00"
|
||||
},
|
||||
{
|
||||
"name": "spatie/laravel-permission",
|
||||
"version": "6.21.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/spatie/laravel-permission.git",
|
||||
"reference": "6a118e8855dfffcd90403aab77bbf35a03db51b3"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/spatie/laravel-permission/zipball/6a118e8855dfffcd90403aab77bbf35a03db51b3",
|
||||
"reference": "6a118e8855dfffcd90403aab77bbf35a03db51b3",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"illuminate/auth": "^8.12|^9.0|^10.0|^11.0|^12.0",
|
||||
"illuminate/container": "^8.12|^9.0|^10.0|^11.0|^12.0",
|
||||
"illuminate/contracts": "^8.12|^9.0|^10.0|^11.0|^12.0",
|
||||
"illuminate/database": "^8.12|^9.0|^10.0|^11.0|^12.0",
|
||||
"php": "^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"laravel/passport": "^11.0|^12.0",
|
||||
"laravel/pint": "^1.0",
|
||||
"orchestra/testbench": "^6.23|^7.0|^8.0|^9.0|^10.0",
|
||||
"phpunit/phpunit": "^9.4|^10.1|^11.5"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"providers": [
|
||||
"Spatie\\Permission\\PermissionServiceProvider"
|
||||
]
|
||||
},
|
||||
"branch-alias": {
|
||||
"dev-main": "6.x-dev",
|
||||
"dev-master": "6.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/helpers.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Spatie\\Permission\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Freek Van der Herten",
|
||||
"email": "freek@spatie.be",
|
||||
"homepage": "https://spatie.be",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Permission handling for Laravel 8.0 and up",
|
||||
"homepage": "https://github.com/spatie/laravel-permission",
|
||||
"keywords": [
|
||||
"acl",
|
||||
"laravel",
|
||||
"permission",
|
||||
"permissions",
|
||||
"rbac",
|
||||
"roles",
|
||||
"security",
|
||||
"spatie"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/spatie/laravel-permission/issues",
|
||||
"source": "https://github.com/spatie/laravel-permission/tree/6.21.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/spatie",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2025-07-23T16:08:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/clock",
|
||||
"version": "v7.2.0",
|
||||
|
|
|
@ -0,0 +1,202 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
|
||||
'models' => [
|
||||
|
||||
/*
|
||||
* When using the "HasPermissions" trait from this package, we need to know which
|
||||
* Eloquent model should be used to retrieve your permissions. Of course, it
|
||||
* is often just the "Permission" model but you may use whatever you like.
|
||||
*
|
||||
* The model you want to use as a Permission model needs to implement the
|
||||
* `Spatie\Permission\Contracts\Permission` contract.
|
||||
*/
|
||||
|
||||
'permission' => Spatie\Permission\Models\Permission::class,
|
||||
|
||||
/*
|
||||
* When using the "HasRoles" trait from this package, we need to know which
|
||||
* Eloquent model should be used to retrieve your roles. Of course, it
|
||||
* is often just the "Role" model but you may use whatever you like.
|
||||
*
|
||||
* The model you want to use as a Role model needs to implement the
|
||||
* `Spatie\Permission\Contracts\Role` contract.
|
||||
*/
|
||||
|
||||
'role' => Spatie\Permission\Models\Role::class,
|
||||
|
||||
],
|
||||
|
||||
'table_names' => [
|
||||
|
||||
/*
|
||||
* When using the "HasRoles" trait from this package, we need to know which
|
||||
* table should be used to retrieve your roles. We have chosen a basic
|
||||
* default value but you may easily change it to any table you like.
|
||||
*/
|
||||
|
||||
'roles' => 'roles',
|
||||
|
||||
/*
|
||||
* When using the "HasPermissions" trait from this package, we need to know which
|
||||
* table should be used to retrieve your permissions. We have chosen a basic
|
||||
* default value but you may easily change it to any table you like.
|
||||
*/
|
||||
|
||||
'permissions' => 'permissions',
|
||||
|
||||
/*
|
||||
* When using the "HasPermissions" trait from this package, we need to know which
|
||||
* table should be used to retrieve your models permissions. We have chosen a
|
||||
* basic default value but you may easily change it to any table you like.
|
||||
*/
|
||||
|
||||
'model_has_permissions' => 'model_has_permissions',
|
||||
|
||||
/*
|
||||
* When using the "HasRoles" trait from this package, we need to know which
|
||||
* table should be used to retrieve your models roles. We have chosen a
|
||||
* basic default value but you may easily change it to any table you like.
|
||||
*/
|
||||
|
||||
'model_has_roles' => 'model_has_roles',
|
||||
|
||||
/*
|
||||
* When using the "HasRoles" trait from this package, we need to know which
|
||||
* table should be used to retrieve your roles permissions. We have chosen a
|
||||
* basic default value but you may easily change it to any table you like.
|
||||
*/
|
||||
|
||||
'role_has_permissions' => 'role_has_permissions',
|
||||
],
|
||||
|
||||
'column_names' => [
|
||||
/*
|
||||
* Change this if you want to name the related pivots other than defaults
|
||||
*/
|
||||
'role_pivot_key' => null, // default 'role_id',
|
||||
'permission_pivot_key' => null, // default 'permission_id',
|
||||
|
||||
/*
|
||||
* Change this if you want to name the related model primary key other than
|
||||
* `model_id`.
|
||||
*
|
||||
* For example, this would be nice if your primary keys are all UUIDs. In
|
||||
* that case, name this `model_uuid`.
|
||||
*/
|
||||
|
||||
'model_morph_key' => 'model_id',
|
||||
|
||||
/*
|
||||
* Change this if you want to use the teams feature and your related model's
|
||||
* foreign key is other than `team_id`.
|
||||
*/
|
||||
|
||||
'team_foreign_key' => 'team_id',
|
||||
],
|
||||
|
||||
/*
|
||||
* When set to true, the method for checking permissions will be registered on the gate.
|
||||
* Set this to false if you want to implement custom logic for checking permissions.
|
||||
*/
|
||||
|
||||
'register_permission_check_method' => true,
|
||||
|
||||
/*
|
||||
* When set to true, Laravel\Octane\Events\OperationTerminated event listener will be registered
|
||||
* this will refresh permissions on every TickTerminated, TaskTerminated and RequestTerminated
|
||||
* NOTE: This should not be needed in most cases, but an Octane/Vapor combination benefited from it.
|
||||
*/
|
||||
'register_octane_reset_listener' => false,
|
||||
|
||||
/*
|
||||
* Events will fire when a role or permission is assigned/unassigned:
|
||||
* \Spatie\Permission\Events\RoleAttached
|
||||
* \Spatie\Permission\Events\RoleDetached
|
||||
* \Spatie\Permission\Events\PermissionAttached
|
||||
* \Spatie\Permission\Events\PermissionDetached
|
||||
*
|
||||
* To enable, set to true, and then create listeners to watch these events.
|
||||
*/
|
||||
'events_enabled' => false,
|
||||
|
||||
/*
|
||||
* Teams Feature.
|
||||
* When set to true the package implements teams using the 'team_foreign_key'.
|
||||
* If you want the migrations to register the 'team_foreign_key', you must
|
||||
* set this to true before doing the migration.
|
||||
* If you already did the migration then you must make a new migration to also
|
||||
* add 'team_foreign_key' to 'roles', 'model_has_roles', and 'model_has_permissions'
|
||||
* (view the latest version of this package's migration file)
|
||||
*/
|
||||
|
||||
'teams' => false,
|
||||
|
||||
/*
|
||||
* The class to use to resolve the permissions team id
|
||||
*/
|
||||
'team_resolver' => \Spatie\Permission\DefaultTeamResolver::class,
|
||||
|
||||
/*
|
||||
* Passport Client Credentials Grant
|
||||
* When set to true the package will use Passports Client to check permissions
|
||||
*/
|
||||
|
||||
'use_passport_client_credentials' => false,
|
||||
|
||||
/*
|
||||
* When set to true, the required permission names are added to exception messages.
|
||||
* This could be considered an information leak in some contexts, so the default
|
||||
* setting is false here for optimum safety.
|
||||
*/
|
||||
|
||||
'display_permission_in_exception' => false,
|
||||
|
||||
/*
|
||||
* When set to true, the required role names are added to exception messages.
|
||||
* This could be considered an information leak in some contexts, so the default
|
||||
* setting is false here for optimum safety.
|
||||
*/
|
||||
|
||||
'display_role_in_exception' => false,
|
||||
|
||||
/*
|
||||
* By default wildcard permission lookups are disabled.
|
||||
* See documentation to understand supported syntax.
|
||||
*/
|
||||
|
||||
'enable_wildcard_permission' => false,
|
||||
|
||||
/*
|
||||
* The class to use for interpreting wildcard permissions.
|
||||
* If you need to modify delimiters, override the class and specify its name here.
|
||||
*/
|
||||
// 'wildcard_permission' => Spatie\Permission\WildcardPermission::class,
|
||||
|
||||
/* Cache-specific settings */
|
||||
|
||||
'cache' => [
|
||||
|
||||
/*
|
||||
* By default all permissions are cached for 24 hours to speed up performance.
|
||||
* When permissions or roles are updated the cache is flushed automatically.
|
||||
*/
|
||||
|
||||
'expiration_time' => \DateInterval::createFromDateString('24 hours'),
|
||||
|
||||
/*
|
||||
* The cache key used to store all permissions.
|
||||
*/
|
||||
|
||||
'key' => 'spatie.permission.cache',
|
||||
|
||||
/*
|
||||
* You may optionally indicate a specific cache driver to use for permission and
|
||||
* role caching using any of the `store` drivers listed in the cache.php config
|
||||
* file. Using 'default' here means to use the `default` set in cache.php.
|
||||
*/
|
||||
|
||||
'store' => 'default',
|
||||
],
|
||||
];
|
|
@ -0,0 +1,33 @@
|
|||
<?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('personal_access_tokens', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->morphs('tokenable');
|
||||
$table->text('name');
|
||||
$table->string('token', 64)->unique();
|
||||
$table->text('abilities')->nullable();
|
||||
$table->timestamp('last_used_at')->nullable();
|
||||
$table->timestamp('expires_at')->nullable()->index();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('personal_access_tokens');
|
||||
}
|
||||
};
|
|
@ -11,19 +11,19 @@ return new class extends Migration
|
|||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('perizinan_status', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('kategori'); // pertek, amdal, etc
|
||||
$table->string('status_id'); // ditolak, selesai, proses, total
|
||||
$table->string('label'); // Izin Ditolak, Izin Selesai, etc
|
||||
$table->integer('value'); // count value
|
||||
$table->datetime('api_last_updated'); // from API last_updated field
|
||||
$table->date('sync_date'); // date when this data was synced
|
||||
Schema::create('PerizinanStatus', function (Blueprint $table) {
|
||||
$table->id('PerizinanStatusID');
|
||||
$table->string('Kategori_ID'); // pertek, amdal, etc (identifier string)
|
||||
$table->string('StatusID'); // ditolak, selesai, proses, total
|
||||
$table->string('Label'); // Izin Ditolak, Izin Selesai, etc
|
||||
$table->integer('Value'); // count value
|
||||
$table->dateTime('ApiLastUpdated'); // from API last_updated field
|
||||
$table->date('SyncDate'); // date when this data was synced
|
||||
$table->timestamps();
|
||||
|
||||
// Index untuk performa
|
||||
$table->index(['kategori', 'status_id', 'sync_date']);
|
||||
$table->unique(['kategori', 'status_id', 'sync_date']); // prevent duplicate data per day
|
||||
$table->index(['Kategori_ID', 'StatusID', 'SyncDate']);
|
||||
$table->unique(['Kategori_ID', 'StatusID', 'SyncDate']); // prevent duplicate data per day
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,6 @@ return new class extends Migration
|
|||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('perizinan_status');
|
||||
Schema::dropIfExists('PerizinanStatus');
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11,20 +11,20 @@ return new class extends Migration
|
|||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('fastest_permohonan', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('kategori'); // pertek, amdal, etc
|
||||
$table->string('nama'); // nama izin from API
|
||||
$table->integer('total'); // jumlah total izin from API
|
||||
$table->string('durasi_pemohon')->nullable(); // durasi rata-rata pemohon from API
|
||||
$table->string('durasi_petugas')->nullable(); // durasi rata-rata petugas from API
|
||||
$table->datetime('api_last_updated'); // from API last_updated field
|
||||
$table->date('sync_date'); // date when this data was synced
|
||||
Schema::create('FastestPermohonan', function (Blueprint $table) {
|
||||
$table->id('FastestPermohonanID');
|
||||
$table->string('Kategori_ID'); // pertek, amdal, etc (identifier string)
|
||||
$table->string('Nama'); // nama izin from API
|
||||
$table->integer('Total'); // jumlah total izin from API
|
||||
$table->string('DurasiPemohon')->nullable(); // durasi rata-rata pemohon from API
|
||||
$table->string('DurasiPetugas')->nullable(); // durasi rata-rata petugas from API
|
||||
$table->dateTime('ApiLastUpdated'); // from API last_updated field
|
||||
$table->date('SyncDate'); // date when this data was synced
|
||||
$table->timestamps();
|
||||
|
||||
// Index untuk performa - urutkan berdasarkan durasi dan total
|
||||
$table->index(['kategori', 'sync_date']);
|
||||
$table->index(['kategori', 'total']); // untuk sorting fastest
|
||||
$table->index(['Kategori_ID', 'SyncDate']);
|
||||
$table->index(['Kategori_ID', 'Total']); // untuk sorting fastest
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -33,6 +33,6 @@ return new class extends Migration
|
|||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('fastest_permohonan');
|
||||
Schema::dropIfExists('FastestPermohonan');
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11,19 +11,19 @@ return new class extends Migration
|
|||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('terakhir_terbit', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('kategori'); // pertek, amdal, etc
|
||||
$table->string('nama_izin'); // nama izin dari API
|
||||
$table->string('pemohon'); // nama pemohon dari API
|
||||
$table->date('tanggal_terbit'); // tanggal terbit dari API
|
||||
$table->datetime('api_last_updated'); // from API last_updated field
|
||||
$table->date('sync_date'); // date when this data was synced
|
||||
Schema::create('TerakhirTerbit', function (Blueprint $table) {
|
||||
$table->id('TerakhirTerbitID');
|
||||
$table->string('Kategori_ID'); // pertek, amdal, dll (identifier string)
|
||||
$table->string('NamaIzin'); // nama izin dari API
|
||||
$table->string('Pemohon'); // nama pemohon dari API
|
||||
$table->date('TanggalTerbit'); // tanggal terbit dari API
|
||||
$table->dateTime('ApiLastUpdated'); // from API last_updated field
|
||||
$table->date('SyncDate'); // date when this data was synced
|
||||
$table->timestamps();
|
||||
|
||||
// Index untuk performa
|
||||
$table->index(['kategori', 'tanggal_terbit']);
|
||||
$table->index(['kategori', 'sync_date']);
|
||||
$table->index(['Kategori_ID', 'TanggalTerbit']);
|
||||
$table->index(['Kategori_ID', 'SyncDate']);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,6 @@ return new class extends Migration
|
|||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('terakhir_terbit');
|
||||
Schema::dropIfExists('TerakhirTerbit');
|
||||
}
|
||||
};
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('username')->nullable()->unique()->after('email');
|
||||
});
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropUnique(['username']);
|
||||
$table->dropColumn('username');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,136 @@
|
|||
<?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
|
||||
{
|
||||
$teams = config('permission.teams');
|
||||
$tableNames = config('permission.table_names');
|
||||
$columnNames = config('permission.column_names');
|
||||
$pivotRole = $columnNames['role_pivot_key'] ?? 'role_id';
|
||||
$pivotPermission = $columnNames['permission_pivot_key'] ?? 'permission_id';
|
||||
|
||||
throw_if(empty($tableNames), new Exception('Error: config/permission.php not loaded. Run [php artisan config:clear] and try again.'));
|
||||
throw_if($teams && empty($columnNames['team_foreign_key'] ?? null), new Exception('Error: team_foreign_key on config/permission.php not loaded. Run [php artisan config:clear] and try again.'));
|
||||
|
||||
Schema::create($tableNames['permissions'], static function (Blueprint $table) {
|
||||
// $table->engine('InnoDB');
|
||||
$table->bigIncrements('id'); // permission id
|
||||
$table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
|
||||
$table->string('guard_name'); // For MyISAM use string('guard_name', 25);
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['name', 'guard_name']);
|
||||
});
|
||||
|
||||
Schema::create($tableNames['roles'], static function (Blueprint $table) use ($teams, $columnNames) {
|
||||
// $table->engine('InnoDB');
|
||||
$table->bigIncrements('id'); // role id
|
||||
if ($teams || config('permission.testing')) { // permission.testing is a fix for sqlite testing
|
||||
$table->unsignedBigInteger($columnNames['team_foreign_key'])->nullable();
|
||||
$table->index($columnNames['team_foreign_key'], 'roles_team_foreign_key_index');
|
||||
}
|
||||
$table->string('name'); // For MyISAM use string('name', 225); // (or 166 for InnoDB with Redundant/Compact row format)
|
||||
$table->string('guard_name'); // For MyISAM use string('guard_name', 25);
|
||||
$table->timestamps();
|
||||
if ($teams || config('permission.testing')) {
|
||||
$table->unique([$columnNames['team_foreign_key'], 'name', 'guard_name']);
|
||||
} else {
|
||||
$table->unique(['name', 'guard_name']);
|
||||
}
|
||||
});
|
||||
|
||||
Schema::create($tableNames['model_has_permissions'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotPermission, $teams) {
|
||||
$table->unsignedBigInteger($pivotPermission);
|
||||
|
||||
$table->string('model_type');
|
||||
$table->unsignedBigInteger($columnNames['model_morph_key']);
|
||||
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_permissions_model_id_model_type_index');
|
||||
|
||||
$table->foreign($pivotPermission)
|
||||
->references('id') // permission id
|
||||
->on($tableNames['permissions'])
|
||||
->onDelete('cascade');
|
||||
if ($teams) {
|
||||
$table->unsignedBigInteger($columnNames['team_foreign_key']);
|
||||
$table->index($columnNames['team_foreign_key'], 'model_has_permissions_team_foreign_key_index');
|
||||
|
||||
$table->primary([$columnNames['team_foreign_key'], $pivotPermission, $columnNames['model_morph_key'], 'model_type'],
|
||||
'model_has_permissions_permission_model_type_primary');
|
||||
} else {
|
||||
$table->primary([$pivotPermission, $columnNames['model_morph_key'], 'model_type'],
|
||||
'model_has_permissions_permission_model_type_primary');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
Schema::create($tableNames['model_has_roles'], static function (Blueprint $table) use ($tableNames, $columnNames, $pivotRole, $teams) {
|
||||
$table->unsignedBigInteger($pivotRole);
|
||||
|
||||
$table->string('model_type');
|
||||
$table->unsignedBigInteger($columnNames['model_morph_key']);
|
||||
$table->index([$columnNames['model_morph_key'], 'model_type'], 'model_has_roles_model_id_model_type_index');
|
||||
|
||||
$table->foreign($pivotRole)
|
||||
->references('id') // role id
|
||||
->on($tableNames['roles'])
|
||||
->onDelete('cascade');
|
||||
if ($teams) {
|
||||
$table->unsignedBigInteger($columnNames['team_foreign_key']);
|
||||
$table->index($columnNames['team_foreign_key'], 'model_has_roles_team_foreign_key_index');
|
||||
|
||||
$table->primary([$columnNames['team_foreign_key'], $pivotRole, $columnNames['model_morph_key'], 'model_type'],
|
||||
'model_has_roles_role_model_type_primary');
|
||||
} else {
|
||||
$table->primary([$pivotRole, $columnNames['model_morph_key'], 'model_type'],
|
||||
'model_has_roles_role_model_type_primary');
|
||||
}
|
||||
});
|
||||
|
||||
Schema::create($tableNames['role_has_permissions'], static function (Blueprint $table) use ($tableNames, $pivotRole, $pivotPermission) {
|
||||
$table->unsignedBigInteger($pivotPermission);
|
||||
$table->unsignedBigInteger($pivotRole);
|
||||
|
||||
$table->foreign($pivotPermission)
|
||||
->references('id') // permission id
|
||||
->on($tableNames['permissions'])
|
||||
->onDelete('cascade');
|
||||
|
||||
$table->foreign($pivotRole)
|
||||
->references('id') // role id
|
||||
->on($tableNames['roles'])
|
||||
->onDelete('cascade');
|
||||
|
||||
$table->primary([$pivotPermission, $pivotRole], 'role_has_permissions_permission_id_role_id_primary');
|
||||
});
|
||||
|
||||
app('cache')
|
||||
->store(config('permission.cache.store') != 'default' ? config('permission.cache.store') : null)
|
||||
->forget(config('permission.cache.key'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
$tableNames = config('permission.table_names');
|
||||
|
||||
if (empty($tableNames)) {
|
||||
throw new \Exception('Error: config/permission.php not found and defaults could not be merged. Please publish the package configuration before proceeding, or drop the tables manually.');
|
||||
}
|
||||
|
||||
Schema::drop($tableNames['role_has_permissions']);
|
||||
Schema::drop($tableNames['model_has_roles']);
|
||||
Schema::drop($tableNames['model_has_permissions']);
|
||||
Schema::drop($tableNames['roles']);
|
||||
Schema::drop($tableNames['permissions']);
|
||||
}
|
||||
};
|
|
@ -3,6 +3,8 @@
|
|||
namespace Database\Seeders;
|
||||
|
||||
use App\Models\User;
|
||||
use Database\Seeders\UserSeeder;
|
||||
use Database\Seeders\RolesAndPermissionsSeeder;
|
||||
// use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||
use Illuminate\Database\Seeder;
|
||||
|
||||
|
@ -13,11 +15,10 @@ class DatabaseSeeder extends Seeder
|
|||
*/
|
||||
public function run(): void
|
||||
{
|
||||
// User::factory(10)->create();
|
||||
|
||||
User::factory()->create([
|
||||
'name' => 'Test User',
|
||||
'email' => 'test@example.com',
|
||||
// panggil seeder khusus
|
||||
$this->call([
|
||||
RolesAndPermissionsSeeder::class,
|
||||
UserSeeder::class,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Spatie\Permission\Models\Permission;
|
||||
|
||||
class RolesAndPermissionsSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
|
||||
|
||||
$permissions = [
|
||||
// Dashboard tabs
|
||||
'dashboard.view.pertek',
|
||||
'dashboard.view.rintek',
|
||||
'dashboard.view.amdal',
|
||||
'dashboard.view.izin_angkut',
|
||||
'dashboard.view.uji_emisi',
|
||||
|
||||
// Menus
|
||||
'penjadwalan.access',
|
||||
'persetujuan_teknis.access',
|
||||
'rincian_teknis.access',
|
||||
'persetujuan_lingkungan.access',
|
||||
'izin_angkut_olah.access',
|
||||
'izin_tempat_uji_emisi.access',
|
||||
|
||||
// Management
|
||||
'settings.manage',
|
||||
'content.manage',
|
||||
'master_data.manage',
|
||||
];
|
||||
|
||||
foreach ($permissions as $perm) {
|
||||
Permission::firstOrCreate(['name' => $perm]);
|
||||
}
|
||||
|
||||
// Roles
|
||||
$kadis = Role::firstOrCreate(['name' => 'Kadis']);
|
||||
$ppkl = Role::firstOrCreate(['name' => 'PPKL']);
|
||||
$dlh = Role::firstOrCreate(['name' => 'DLH']);
|
||||
|
||||
// Kadis permissions (semua dashboard tabs + modules, tanpa pengaturan/konten/master data)
|
||||
$kadisPerms = [
|
||||
'dashboard.view.pertek',
|
||||
'dashboard.view.rintek',
|
||||
'dashboard.view.amdal',
|
||||
'dashboard.view.izin_angkut',
|
||||
'dashboard.view.uji_emisi',
|
||||
'penjadwalan.access',
|
||||
'persetujuan_teknis.access',
|
||||
'rincian_teknis.access',
|
||||
'persetujuan_lingkungan.access',
|
||||
'izin_angkut_olah.access',
|
||||
'izin_tempat_uji_emisi.access',
|
||||
];
|
||||
$kadis->syncPermissions($kadisPerms);
|
||||
|
||||
// PPKL permissions (dashboard pertek + penjadwalan + persetujuan teknis)
|
||||
$ppklPerms = [
|
||||
'dashboard.view.pertek',
|
||||
'penjadwalan.access',
|
||||
'persetujuan_teknis.access',
|
||||
];
|
||||
$ppkl->syncPermissions($ppklPerms);
|
||||
|
||||
// DLH (super admin) -> assign all permissions
|
||||
$dlh->syncPermissions(Permission::all());
|
||||
|
||||
app()[\Spatie\Permission\PermissionRegistrar::class]->forgetCachedPermissions();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace Database\Seeders;
|
||||
|
||||
use Illuminate\Database\Seeder;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use App\Models\User;
|
||||
|
||||
class UserSeeder extends Seeder
|
||||
{
|
||||
public function run(): void
|
||||
{
|
||||
$user = User::updateOrCreate(
|
||||
['email' => 'ammar@example.com'],
|
||||
[
|
||||
'name' => 'Ammar',
|
||||
'username' => 'ammar',
|
||||
'password' => Hash::make('Muammar123$'),
|
||||
]
|
||||
);
|
||||
|
||||
// Assign DLH (super admin) role
|
||||
$user->syncRoles(['DLH']);
|
||||
|
||||
// Kadis user
|
||||
$kadis = User::updateOrCreate(
|
||||
['email' => 'kadis@dinaslhdki.id'],
|
||||
[
|
||||
'name' => 'Kadis',
|
||||
'username' => 'kadis',
|
||||
'password' => Hash::make('Perling2025$'),
|
||||
]
|
||||
);
|
||||
$kadis->syncRoles(['Kadis']);
|
||||
|
||||
// PPKL user (note: domain as provided)
|
||||
$ppkl = User::updateOrCreate(
|
||||
['email' => 'ppkl@dinaslhkdki.id'],
|
||||
[
|
||||
'name' => 'PPKL',
|
||||
'username' => 'ppkl',
|
||||
'password' => Hash::make('Perling2025$'),
|
||||
]
|
||||
);
|
||||
$ppkl->syncRoles(['PPKL']);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
@extends('layout.layout')
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow-none border">
|
||||
<div class="card-body p-24">
|
||||
<h5 class="mb-3">Tambah Permission</h5>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger"><ul class="mb-0">@foreach ($errors->all() as $error)<li>{{ $error }}</li>@endforeach</ul></div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('admin.permissions.store') }}">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nama Permission</label>
|
||||
<input type="text" name="name" class="form-control" required value="{{ old('name') }}">
|
||||
</div>
|
||||
<button class="btn btn-primary">Simpan</button>
|
||||
<a href="{{ route('admin.permissions.index') }}" class="btn btn-secondary">Batal</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
@extends('layout.layout')
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow-none border">
|
||||
<div class="card-body p-24">
|
||||
<h5 class="mb-3">Edit Permission</h5>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger"><ul class="mb-0">@foreach ($errors->all() as $error)<li>{{ $error }}</li>@endforeach</ul></div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('admin.permissions.update', $permission) }}">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Nama Permission</label>
|
||||
<input type="text" name="name" class="form-control" required value="{{ old('name', $permission->name) }}">
|
||||
</div>
|
||||
<button class="btn btn-primary">Simpan</button>
|
||||
<a href="{{ route('admin.permissions.index') }}" class="btn btn-secondary">Batal</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
@extends('layout.layout')
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow-none border">
|
||||
<div class="card-body p-24">
|
||||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||||
<h5 class="mb-0">Permissions</h5>
|
||||
<a href="{{ route('admin.permissions.create') }}" class="btn btn-primary btn-sm">Tambah Permission</a>
|
||||
</div>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">{{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nama</th>
|
||||
<th>Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($permissions as $permission)
|
||||
<tr>
|
||||
<td>{{ $permission->name }}</td>
|
||||
<td>
|
||||
<a href="{{ route('admin.permissions.edit', $permission) }}" class="btn btn-sm btn-outline-secondary">Edit</a>
|
||||
<form action="{{ route('admin.permissions.destroy', $permission) }}" method="POST" class="d-inline" onsubmit="return confirm('Hapus permission ini?')">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button class="btn btn-sm btn-outline-danger">Hapus</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{{ $permissions->links() }}
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
|
@ -0,0 +1,62 @@
|
|||
@extends('layout.layout')
|
||||
|
||||
@php
|
||||
$title = 'Tambah Role';
|
||||
$subTitle = 'Manajemen Roles Akses';
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="card basic-data-table">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center mb-3 gap-3">
|
||||
<div>
|
||||
<h5 class="mb-0">Tambah Role</h5>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{{ route('admin.roles.index') }}" class="btn btn-secondary btn-sm d-flex align-items-center gap-2">
|
||||
<iconify-icon icon="iconoir:arrow-left"></iconify-icon>
|
||||
<span>Kembali</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger"><ul class="mb-0">@foreach ($errors->all() as $error)<li>{{ $error }}</li>@endforeach</ul></div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('admin.roles.store') }}">
|
||||
@csrf
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label">Nama Role</label>
|
||||
<input type="text" name="name" class="form-control" required value="{{ old('name') }}" placeholder="Contoh: Kadis, PPKL, DLH">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<label class="form-label">Permissions</label>
|
||||
<div class="row g-2">
|
||||
@foreach($permissions as $perm)
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input border border-neutral-300 me-8" type="checkbox" name="permissions[]" value="{{ $perm->name }}" id="perm_{{ $perm->id }}">
|
||||
<label class="form-check-label" for="perm_{{ $perm->id }}">{{ $perm->name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 d-flex gap-2">
|
||||
<button class="btn btn-primary d-flex align-items-center gap-2">
|
||||
<iconify-icon icon="material-symbols:save"></iconify-icon>
|
||||
<span>Simpan</span>
|
||||
</button>
|
||||
<a href="{{ route('admin.roles.index') }}" class="btn btn-light">Batal</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
|
@ -0,0 +1,63 @@
|
|||
@extends('layout.layout')
|
||||
|
||||
@php
|
||||
$title = 'Edit Role';
|
||||
$subTitle = 'Manajemen Roles Akses';
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="card basic-data-table">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center mb-3 gap-3">
|
||||
<div>
|
||||
<h5 class="mb-0">Edit Role: {{ $role->name }}</h5>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<a href="{{ route('admin.roles.index') }}" class="btn btn-secondary btn-sm d-flex align-items-center gap-2">
|
||||
<iconify-icon icon="iconoir:arrow-left"></iconify-icon>
|
||||
<span>Kembali</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if ($errors->any())
|
||||
<div class="alert alert-danger"><ul class="mb-0">@foreach ($errors->all() as $error)<li>{{ $error }}</li>@endforeach</ul></div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('admin.roles.update', $role) }}">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="row g-3">
|
||||
<div class="col-12 col-md-6">
|
||||
<label class="form-label">Nama Role</label>
|
||||
<input type="text" name="name" class="form-control" required value="{{ old('name', $role->name) }}" placeholder="Contoh: Kadis, PPKL, DLH">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<label class="form-label">Permissions</label>
|
||||
<div class="row g-2">
|
||||
@foreach($permissions as $perm)
|
||||
<div class="col-12 col-md-4">
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input border border-neutral-300 me-8" type="checkbox" name="permissions[]" value="{{ $perm->name }}" id="perm_{{ $perm->id }}" {{ in_array($perm->name, $rolePermissions) ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="perm_{{ $perm->id }}">{{ $perm->name }}</label>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 d-flex gap-2">
|
||||
<button class="btn btn-primary d-flex align-items-center gap-2">
|
||||
<iconify-icon icon="material-symbols:save"></iconify-icon>
|
||||
<span>Simpan</span>
|
||||
</button>
|
||||
<a href="{{ route('admin.roles.index') }}" class="btn btn-light">Batal</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
|
@ -0,0 +1,112 @@
|
|||
@extends('layout.layout')
|
||||
|
||||
@php
|
||||
$title = 'Daftar Roles';
|
||||
$subTitle = 'Manajemen Roles Akses';
|
||||
$script = '
|
||||
<script>
|
||||
let table = new DataTable("#dataTable");
|
||||
|
||||
// Handle delete modal
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const deleteModal = document.getElementById("deleteModal");
|
||||
const deleteForm = document.getElementById("deleteForm");
|
||||
const roleNameText = document.getElementById("roleNameText");
|
||||
|
||||
deleteModal.addEventListener("show.bs.modal", function(event) {
|
||||
const button = event.relatedTarget;
|
||||
const roleName = button.getAttribute("data-role-name");
|
||||
const roleId = button.getAttribute("data-role-id");
|
||||
|
||||
roleNameText.textContent = roleName;
|
||||
deleteForm.action = "' . route('admin.roles.index') . '/" + roleId;
|
||||
});
|
||||
});
|
||||
</script>';
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
|
||||
<div class="card basic-data-table">
|
||||
<div class="card-body">
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-start align-items-md-center mb-3 gap-3">
|
||||
<div>
|
||||
<h5 class="mb-0">Daftar Roles</h5>
|
||||
</div>
|
||||
<div class="d-flex flex-column flex-sm-row align-items-stretch align-items-sm-center justify-content-end gap-2 w-md-auto">
|
||||
<div class="flex-fill flex-sm-fill-0">
|
||||
<a href="{{ route('admin.roles.create') }}" class="btn btn-primary btn-sm d-flex align-items-center justify-content-center gap-2 w-100">
|
||||
<iconify-icon icon="material-symbols:add" class="text-lg"></iconify-icon>
|
||||
<span>Tambah Role</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">{{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table bordered-table mb-0" id="dataTable" data-page-length='10'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" class="text-center">No</th>
|
||||
<th scope="col" class="text-center">Nama Role</th>
|
||||
<th scope="col" class="text-center">Jumlah Permissions</th>
|
||||
<th scope="col" class="text-center">Aksi</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($roles as $role)
|
||||
<tr>
|
||||
<td class="text-center">{{ isset($roles->firstItem) ? ($roles->firstItem() + $loop->index) : $loop->iteration }}</td>
|
||||
<td class="text-center">{{ $role->name }}</td>
|
||||
<td class="text-center">{{ $role->permissions_count }}</td>
|
||||
<td class="text-center">
|
||||
<div class="d-flex align-items-center gap-10 justify-content-center">
|
||||
<a href="{{ route('admin.roles.edit', $role) }}" class="bg-success-focus text-success-600 bg-hover-success-200 fw-medium w-40-px h-40-px d-flex justify-content-center align-items-center rounded-circle text-decoration-none" title="Edit Role">
|
||||
<iconify-icon icon="lucide:edit" class="menu-icon"></iconify-icon>
|
||||
</a>
|
||||
<button type="button" class="remove-item-btn bg-danger-focus bg-hover-danger-200 text-danger-600 fw-medium w-40-px h-40-px d-flex justify-content-center align-items-center rounded-circle" title="Hapus Role" data-bs-toggle="modal" data-bs-target="#deleteModal" data-role-name="{{ $role->name }}" data-role-id="{{ $role->id }}">
|
||||
<iconify-icon icon="fluent:delete-24-regular" class="menu-icon"></iconify-icon>
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Delete Modal -->
|
||||
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header border-0 pb-0">
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body text-center px-4 pb-4">
|
||||
<div class="mb-3">
|
||||
<div class="bg-danger-focus w-80-px h-80-px d-flex justify-content-center align-items-center rounded-circle mx-auto mb-3">
|
||||
<iconify-icon icon="fluent:delete-24-regular" class="text-danger-600 text-4xl"></iconify-icon>
|
||||
</div>
|
||||
<h4 class="mb-2 text-dark">Hapus Role</h4>
|
||||
<p class="text-secondary mb-0">Apakah Anda yakin ingin menghapus role <strong id="roleNameText"></strong>? Tindakan ini tidak dapat dibatalkan.</p>
|
||||
</div>
|
||||
<div class="d-flex gap-2 justify-content-center">
|
||||
<button type="button" class="btn btn-light-secondary px-4" data-bs-dismiss="modal">Batal</button>
|
||||
<form id="deleteForm" method="POST" class="d-inline">
|
||||
@csrf
|
||||
@method('DELETE')
|
||||
<button type="submit" class="btn btn-danger px-4">Ya, Hapus</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@endsection
|
|
@ -4,8 +4,6 @@
|
|||
|
||||
<x-head/>
|
||||
|
||||
|
||||
|
||||
<link rel="stylesheet" href="{{ asset('assets/css/login/login.css') }}">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons/font/bootstrap-icons.css">
|
||||
|
||||
|
@ -22,11 +20,11 @@
|
|||
<img src="{{ asset('assets/images/auth/logo-login.svg') }}" alt="Balai Sertifikasi Elektronik" class="logo">
|
||||
|
||||
<h1 class="text-login">Login</h1>
|
||||
<p class="subtext">Silakan masuk menggunakan akun DLH</p>
|
||||
<form action="#" method="POST" style="width: 100%;">
|
||||
<p class="subtext">Silakan masuk menggunakan email/username</p>
|
||||
<form id="login-form" action="#" method="POST" style="width: 100%;">
|
||||
@csrf
|
||||
<div class="form-group">
|
||||
<input type="text" id="nik" name="nik" placeholder="NIK (No. KTP) / Email" required>
|
||||
<input type="text" id="identifier" name="identifier" placeholder="Email atau Username" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
|
@ -38,19 +36,19 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="">
|
||||
<div class="mb-2">
|
||||
<small id="login-error" class="text-danger" style="display:none;"></small>
|
||||
</div>
|
||||
|
||||
{{-- <div class="">
|
||||
<div class="d-flex justify-content-between gap-2">
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input border border-neutral-300" type="checkbox" value="" id="remember">
|
||||
<label class="form-check-label" for="remember">Ingat saya</label>
|
||||
</div>
|
||||
{{-- <a href="javascript:void(0)" class="text-primary-600 fw-medium">Forgot Password?</a> --}}
|
||||
</div>
|
||||
</div>
|
||||
<a class="login-button" href="{{ route('dashboard.index') }}">
|
||||
Login
|
||||
</a>
|
||||
{{-- <button type="submit" class="login-button">Log in</button> --}}
|
||||
</div> --}}
|
||||
<button id="btn-login" type="submit" class="login-button">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -70,6 +68,81 @@
|
|||
icon.className = 'bi bi-eye';
|
||||
}
|
||||
});
|
||||
|
||||
const form = document.getElementById('login-form');
|
||||
const btn = document.getElementById('btn-login');
|
||||
const errorBox = document.getElementById('login-error');
|
||||
|
||||
form.addEventListener('submit', async function (e) {
|
||||
e.preventDefault();
|
||||
errorBox.style.display = 'none';
|
||||
errorBox.textContent = '';
|
||||
|
||||
const identifier = document.getElementById('identifier').value.trim();
|
||||
const password = document.getElementById('password').value;
|
||||
if (!identifier || !password) return;
|
||||
|
||||
// Validasi kompleksitas password (min 8, upper, lower, digit, special)
|
||||
const strongPwd = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[^A-Za-z0-9]).{8,}$/;
|
||||
if (!strongPwd.test(password)) {
|
||||
errorBox.textContent = 'Password minimal 8 karakter dan harus mengandung huruf besar, huruf kecil, angka, dan simbol khusus.';
|
||||
errorBox.style.display = 'inline';
|
||||
return;
|
||||
}
|
||||
|
||||
const originalText = btn.textContent;
|
||||
btn.disabled = true;
|
||||
btn.textContent = 'Memproses...';
|
||||
|
||||
try {
|
||||
const resp = await fetch('{{ url('/api/auth/login') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({
|
||||
identifier: identifier,
|
||||
password: password,
|
||||
device_name: 'web-portal'
|
||||
})
|
||||
});
|
||||
|
||||
const data = await resp.json().catch(() => ({}));
|
||||
|
||||
if (!resp.ok || !data.success) {
|
||||
throw new Error((data && (data.message || (data.errors && (data.errors.identifier?.[0] || data.errors.password?.[0])))) || 'Login gagal');
|
||||
}
|
||||
|
||||
// Simpan token (Catatan: localStorage memiliki risiko XSS; gunakan hati-hati)
|
||||
localStorage.setItem('auth_token', data.token);
|
||||
localStorage.setItem('auth_user', JSON.stringify(data.user || {}));
|
||||
|
||||
// Start web session agar middleware permission berfungsi
|
||||
const sessionResp = await fetch('{{ route('login.session') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'Accept': 'application/json',
|
||||
'X-CSRF-TOKEN': document.querySelector('input[name=_token]').value,
|
||||
},
|
||||
body: JSON.stringify({ identifier, password })
|
||||
});
|
||||
|
||||
if (!sessionResp.ok) {
|
||||
throw new Error('Gagal membuat sesi login web');
|
||||
}
|
||||
|
||||
// Redirect ke dashboard
|
||||
window.location.href = '{{ route('dashboard.index') }}';
|
||||
} catch (err) {
|
||||
errorBox.textContent = err.message || 'Login gagal';
|
||||
errorBox.style.display = 'inline';
|
||||
} finally {
|
||||
btn.disabled = false;
|
||||
btn.textContent = originalText;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</html>
|
||||
|
|
|
@ -117,12 +117,18 @@
|
|||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Login Button with enhanced style -->
|
||||
<a href="/auth/login" class=" bg-green-500 ml-4 relative overflow-hidden group text-white px-6 py-2.5 rounded-full
|
||||
hover:shadow-lg">
|
||||
<span class="relative z-10">MASUK</span>
|
||||
<span class="absolute left-0 bottom-0 w-0 h-full bg-blue-600 group-hover:w-full transition-all duration-300"></span>
|
||||
</a>
|
||||
<!-- Login/Dashboard Button -->
|
||||
@auth
|
||||
<a href="{{ route('dashboard.index') }}" class=" bg-green-500 ml-4 relative overflow-hidden group text-white px-6 py-2.5 rounded-full hover:shadow-lg">
|
||||
<span class="relative z-10">DASHBOARD</span>
|
||||
<span class="absolute left-0 bottom-0 w-0 h-full bg-blue-600 group-hover:w-full transition-all duration-300"></span>
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ route('login.index') }}" class=" bg-green-500 ml-4 relative overflow-hidden group text-white px-6 py-2.5 rounded-full hover:shadow-lg">
|
||||
<span class="relative z-10">MASUK</span>
|
||||
<span class="absolute left-0 bottom-0 w-0 h-full bg-blue-600 group-hover:w-full transition-all duration-300"></span>
|
||||
</a>
|
||||
@endauth
|
||||
</div>
|
||||
|
||||
<!-- Mobile Menu Button -->
|
||||
|
@ -180,9 +186,15 @@
|
|||
</div>
|
||||
|
||||
<div class="mt-8">
|
||||
<a href="#login" class="block text-center bg-blue-500 text-white px-6 py-3 rounded-full hover:bg-blue-600">
|
||||
MASUK
|
||||
</a>
|
||||
@auth
|
||||
<a href="{{ route('dashboard.index') }}" class="block text-center bg-blue-500 text-white px-6 py-3 rounded-full hover:bg-blue-600">
|
||||
DASHBOARD
|
||||
</a>
|
||||
@else
|
||||
<a href="{{ route('login.index') }}" class="block text-center bg-blue-500 text-white px-6 py-3 rounded-full hover:bg-blue-600">
|
||||
MASUK
|
||||
</a>
|
||||
@endauth
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{{ $title ?? 'Sistem Perizinan Lingkungan' }}</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<!-- SEO Meta Tags -->
|
||||
<meta name="description" content="{{ $description ?? 'Sistem Perizinan Lingkungan Dinas Lingkungan Hidup Provinsi DKI Jakarta' }}" />
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="d-flex flex-wrap align-items-center gap-3">
|
||||
|
||||
{{--
|
||||
<div class="dropdown">
|
||||
<button class="has-indicator w-40-px h-40-px bg-neutral-200 rounded-circle d-flex justify-content-center align-items-center" type="button" data-bs-toggle="dropdown">
|
||||
<iconify-icon icon="mage:email" class="text-primary-light text-xl"></iconify-icon>
|
||||
|
@ -118,9 +118,9 @@
|
|||
<a href="javascript:void(0)" class="text-primary-600 fw-semibold text-md">See All Message</a>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- Message dropdown end -->
|
||||
</div><!-- Message dropdown end --> --}}
|
||||
|
||||
<div class="dropdown">
|
||||
{{-- <div class="dropdown">
|
||||
<button class="has-indicator w-40-px h-40-px bg-neutral-200 rounded-circle d-flex justify-content-center align-items-center" type="button" data-bs-toggle="dropdown">
|
||||
<iconify-icon icon="iconoir:bell" class="text-primary-light text-xl"></iconify-icon>
|
||||
</button>
|
||||
|
@ -204,17 +204,17 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
</div><!-- Notification dropdown end -->
|
||||
</div><!-- Notification dropdown end --> --}}
|
||||
|
||||
<div class="dropdown">
|
||||
<button class="d-flex justify-content-center align-items-center rounded-circle" type="button" data-bs-toggle="dropdown">
|
||||
<img src="{{ asset('assets/images/user.png') }}" alt="image" class="w-40-px h-40-px object-fit-cover rounded-circle">
|
||||
<iconify-icon icon="solar:user-linear" class="text-primary-light text-xl w-40-px h-40-px d-flex justify-content-center align-items-center bg-neutral-200 rounded-circle"></iconify-icon>
|
||||
</button>
|
||||
<div class="dropdown-menu to-top dropdown-menu-sm">
|
||||
<div class="py-12 px-16 radius-8 bg-primary-50 mb-16 d-flex align-items-center justify-content-between gap-2">
|
||||
<div>
|
||||
<h6 class="text-lg text-primary-light fw-semibold mb-2">Muammar</h6>
|
||||
<span class="text-secondary-light fw-medium text-sm">Admin</span>
|
||||
<h6 class="text-lg text-primary-light fw-semibold mb-2">{{ auth()->user()->name ?? 'User' }}</h6>
|
||||
<span class="text-secondary-light fw-medium text-sm">{{ optional(auth()->user())->getRoleNames()->implode(', ') ?? '-' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<ul class="to-top-list">
|
||||
|
@ -224,7 +224,7 @@
|
|||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="dropdown-item text-black px-0 py-8 hover-bg-transparent hover-text-danger d-flex align-items-center gap-3" href="javascript:void(0)">
|
||||
<a id="btn-logout" class="dropdown-item text-black px-0 py-8 hover-bg-transparent hover-text-danger d-flex align-items-center gap-3" href="#">
|
||||
<iconify-icon icon="lucide:power" class="icon text-xl"></iconify-icon> Log Out
|
||||
</a>
|
||||
</li>
|
||||
|
@ -236,3 +236,35 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
(function(){
|
||||
const btn = document.getElementById('btn-logout');
|
||||
if(!btn) return;
|
||||
btn.addEventListener('click', async function(e){
|
||||
e.preventDefault();
|
||||
const token = localStorage.getItem('auth_token');
|
||||
try {
|
||||
if (token) {
|
||||
await fetch('{{ url('/api/auth/logout') }}', {
|
||||
method: 'POST',
|
||||
headers: { 'Authorization': 'Bearer ' + token, 'Accept': 'application/json' }
|
||||
}).catch(()=>{});
|
||||
}
|
||||
} catch (err) { /* ignore */ }
|
||||
|
||||
try {
|
||||
await fetch('{{ route('logout.session') }}', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'),
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
}).catch(()=>{});
|
||||
} catch (err) { /* ignore */ }
|
||||
|
||||
localStorage.removeItem('auth_token');
|
||||
localStorage.removeItem('auth_user');
|
||||
window.location.href = '{{ route('login.index') }}';
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
</button>
|
||||
<div class="sidebar-logo-area">
|
||||
<a href="{{ route('dashboard.index') }}" class="sidebar-logo">
|
||||
<img src="{{ asset('assets/images/asset/LOGO_PL.svg') }}" alt="site logo" class="light-logo">
|
||||
<img src="{{ asset('assets/images/LOGO_PL.svg') }}" alt="site logo" class="light-logo">
|
||||
<img src="{{ asset('assets/images/logo-light.png') }}" alt="site logo" class="dark-logo">
|
||||
<img src="{{ asset('assets/images/logo_only.svg') }}" alt="site logo" class="logo-icon">
|
||||
</a>
|
||||
|
@ -13,19 +13,22 @@
|
|||
<ul class="sidebar-menu" id="sidebar-menu">
|
||||
<li>
|
||||
<a href="{{ route('dashboard.index') }}">
|
||||
<i class="w-5 h-5" data-lucide="home"></i>
|
||||
<iconify-icon icon="solar:home-smile-angle-outline" class="menu-icon"></iconify-icon>
|
||||
<span>DASHBOARD</span>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@can('penjadwalan.access')
|
||||
<li>
|
||||
<a href="{{ route('jadwal.index') }}">
|
||||
<iconify-icon icon="bi:calendar-date" class="menu-icon"></iconify-icon>
|
||||
<span>PENJADWALAN</span>
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
{{-- <li class="sidebar-menu-group-title">Persetujuan Teknis</li> --}}
|
||||
@can('persetujuan_teknis.access')
|
||||
<li class="dropdown">
|
||||
<a href="javascript:void(0)">
|
||||
<iconify-icon icon="bi:file-earmark-medical" class="menu-icon"></iconify-icon>
|
||||
|
@ -65,9 +68,11 @@
|
|||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
{{-- <li class="sidebar-menu-group-title">Rincian Teknis</li> --}}
|
||||
|
||||
@can('rincian_teknis.access')
|
||||
<li class="dropdown">
|
||||
<a href="javascript:void(0)">
|
||||
<iconify-icon icon="bi:card-text" class="menu-icon"></iconify-icon>
|
||||
|
@ -83,9 +88,11 @@
|
|||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
{{-- <li class="sidebar-menu-group-title">Persetujuan Lingkungan</li> --}}
|
||||
|
||||
@can('persetujuan_lingkungan.access')
|
||||
<li class="dropdown">
|
||||
<a href="javascript:void(0)">
|
||||
<iconify-icon icon="bi:card-text" class="menu-icon"></iconify-icon>
|
||||
|
@ -143,7 +150,9 @@
|
|||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@can('izin_angkut_olah.access')
|
||||
<li class="dropdown">
|
||||
<a href="javascript:void(0)">
|
||||
<iconify-icon icon="bi:card-text" class="menu-icon"></iconify-icon>
|
||||
|
@ -159,7 +168,9 @@
|
|||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@can('izin_tempat_uji_emisi.access')
|
||||
<li class="dropdown">
|
||||
<a href="javascript:void(0)">
|
||||
<iconify-icon icon="bi:card-text" class="menu-icon"></iconify-icon>
|
||||
|
@ -175,8 +186,10 @@
|
|||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
|
||||
@can('settings.manage')
|
||||
<li class="sidebar-menu-group-title">Pengaturan</li>
|
||||
<li class="dropdown">
|
||||
<a href="javascript:void(0)">
|
||||
|
@ -192,15 +205,17 @@
|
|||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#"><i class="text-primary-600 w-auto"></i>
|
||||
<a href="{{ route('admin.roles.index') }}"><i class="text-primary-600 w-auto"></i>
|
||||
<iconify-icon icon="bi:person-gear" class="menu-icon"></iconify-icon>
|
||||
<span>Roles & Akses</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
|
||||
@can('content.manage')
|
||||
<li class="sidebar-menu-group-title">Konten</li>
|
||||
<li>
|
||||
<a href="{{ route('news.index_newsvideo') }}">
|
||||
|
@ -208,7 +223,9 @@
|
|||
<span>News & Video</span>
|
||||
</a>
|
||||
</li>
|
||||
@endcan
|
||||
|
||||
@can('master_data.manage')
|
||||
<li class="sidebar-menu-group-title">Data Master</li>
|
||||
|
||||
<li class="dropdown">
|
||||
|
@ -250,6 +267,7 @@
|
|||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@endcan
|
||||
</ul>
|
||||
</div>
|
||||
</aside>
|
||||
|
|
|
@ -226,10 +226,10 @@
|
|||
@forelse($pertekFastestData as $fastest)
|
||||
<tr>
|
||||
<td>{{ $fastest['rank_order'] }}</td>
|
||||
<td class="text-sm">{{ $fastest['nama'] }}</td>
|
||||
<td class="text-sm d-none d-md-table-cell">{{ number_format($fastest['total']) }}</td>
|
||||
<td class="text-sm d-none d-xl-table-cell">{{ $fastest['durasi_pemohon'] }}</td>
|
||||
<td class="text-sm d-none d-xl-table-cell">{{ $fastest['durasi_petugas'] }}</td>
|
||||
<td class="text-sm">{{ $fastest['Nama'] }}</td>
|
||||
<td class="text-sm d-none d-md-table-cell">{{ number_format($fastest['Total']) }}</td>
|
||||
<td class="text-sm d-none d-xl-table-cell">{{ $fastest['DurasiPemohon'] }}</td>
|
||||
<td class="text-sm d-none d-xl-table-cell">{{ $fastest['DurasiPetugas'] }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
|
@ -297,9 +297,9 @@
|
|||
@forelse($pertekTerakhirTerbitData as $terakhir)
|
||||
<tr>
|
||||
<td>{{ $terakhir['rank_order'] }}</td>
|
||||
<td class="text-sm">{{ $terakhir['nama_izin'] }}</td>
|
||||
<td class="text-sm d-none d-md-table-cell">{{ $terakhir['pemohon'] }}</td>
|
||||
<td class="text-sm d-none d-xl-table-cell">{{ \App\Helpers\DashboardHelper::formatTanggalTerbit($terakhir['tanggal_terbit']) }}</td>
|
||||
<td class="text-sm">{{ $terakhir['NamaIzin'] }}</td>
|
||||
<td class="text-sm d-none d-md-table-cell">{{ $terakhir['Pemohon'] }}</td>
|
||||
<td class="text-sm d-none d-xl-table-cell">{{ \App\Helpers\DashboardHelper::formatTanggalTerbit($terakhir['TanggalTerbit']) }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
|
@ -821,10 +821,10 @@
|
|||
@forelse($amdalFastestData as $fastest)
|
||||
<tr>
|
||||
<td>{{ $fastest['rank_order'] }}</td>
|
||||
<td class="text-sm">{{ $fastest['nama'] }}</td>
|
||||
<td class="text-sm">{{ number_format($fastest['total']) }}</td>
|
||||
<td class="text-sm d-none d-lg-table-cell">{{ $fastest['durasi_pemohon'] }}</td>
|
||||
<td class="text-sm d-none d-lg-table-cell">{{ $fastest['durasi_petugas'] }}</td>
|
||||
<td class="text-sm">{{ $fastest['Nama'] }}</td>
|
||||
<td class="text-sm">{{ number_format($fastest['Total']) }}</td>
|
||||
<td class="text-sm d-none d-lg-table-cell">{{ $fastest['DurasiPemohon'] }}</td>
|
||||
<td class="text-sm d-none d-lg-table-cell">{{ $fastest['DurasiPetugas'] }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
|
@ -892,9 +892,9 @@
|
|||
@forelse($amdalTerakhirTerbitData as $terakhir)
|
||||
<tr>
|
||||
<td>{{ $terakhir['rank_order'] }}</td>
|
||||
<td class="text-sm">{{ $terakhir['nama_izin'] }}</td>
|
||||
<td class="text-sm">{{ $terakhir['pemohon'] }}</td>
|
||||
<td class="text-sm d-none d-lg-table-cell">{{ \App\Helpers\DashboardHelper::formatTanggalTerbit($terakhir['tanggal_terbit']) }}</td>
|
||||
<td class="text-sm">{{ $terakhir['NamaIzin'] }}</td>
|
||||
<td class="text-sm">{{ $terakhir['Pemohon'] }}</td>
|
||||
<td class="text-sm d-none d-lg-table-cell">{{ \App\Helpers\DashboardHelper::formatTanggalTerbit($terakhir['TanggalTerbit']) }}</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
|
|
|
@ -0,0 +1,120 @@
|
|||
@extends('layout.layout')
|
||||
|
||||
@php
|
||||
$title = '403 Forbidden';
|
||||
$subTitle = 'Akses Ditolak';
|
||||
@endphp
|
||||
|
||||
@section('content')
|
||||
<div class="row justify-content-center min-vh-100 align-items-center">
|
||||
<div class="col-lg-6 col-md-8 col-12">
|
||||
<div class="card border-0 shadow-sm">
|
||||
<div class="card-body p-5 text-center">
|
||||
<!-- Error Icon -->
|
||||
<div class="mb-4">
|
||||
<div class="error-icon-wrapper d-inline-block position-relative">
|
||||
<iconify-icon icon="lucide:shield-x" class="error-icon text-danger"></iconify-icon>
|
||||
<div class="pulse-ring"></div>
|
||||
<div class="pulse-ring-2"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="mb-4">
|
||||
<h1 class="display-1 fw-bold text-danger mb-3">403</h1>
|
||||
<h3 class="fw-semibold mb-3 text-dark">Akses Ditolak</h3>
|
||||
<p class="text-muted mb-2">Anda tidak memiliki izin untuk mengakses halaman ini.</p>
|
||||
<p class="text-muted small">Silakan hubungi administrator jika diperlukan.</p>
|
||||
</div>
|
||||
|
||||
<!-- Action Button -->
|
||||
<div class="d-flex flex-row justify-content-center">
|
||||
<a href="{{ route('dashboard.index') }}" class="btn btn-primary px-4 py-2 d-flex align-items-center justify-content-center">
|
||||
<iconify-icon icon="lucide:home" class="me-2"></iconify-icon>
|
||||
<span>Kembali ke Dashboard</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@push('css')
|
||||
<style>
|
||||
.error-icon-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.error-icon {
|
||||
font-size: 64px;
|
||||
animation: shake 2s ease-in-out infinite;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.pulse-ring, .pulse-ring-2 {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 2px solid #dc3545;
|
||||
border-radius: 50%;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
animation: pulsate 2s ease-out infinite;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.pulse-ring-2 {
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
0%, 100% { transform: translateX(0); }
|
||||
10%, 30%, 50%, 70%, 90% { transform: translateX(-3px); }
|
||||
20%, 40%, 60%, 80% { transform: translateX(3px); }
|
||||
}
|
||||
|
||||
@keyframes pulsate {
|
||||
0% {
|
||||
transform: translate(-50%, -50%) scale(1);
|
||||
opacity: 0.7;
|
||||
}
|
||||
100% {
|
||||
transform: translate(-50%, -50%) scale(1.4);
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.btn {
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.btn:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.error-icon {
|
||||
font-size: 48px;
|
||||
}
|
||||
|
||||
.pulse-ring, .pulse-ring-2 {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
}
|
||||
|
||||
.display-1 {
|
||||
font-size: 4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@endpush
|
||||
@endsection
|
|
@ -1,7 +1,7 @@
|
|||
@extends('layout.layout')
|
||||
@php
|
||||
$title='Data Pengguna';
|
||||
$subTitle = 'Data Pengguna';
|
||||
$title='Users Grid';
|
||||
$subTitle = 'Users Grid';
|
||||
$script ='<script>
|
||||
$(".remove-item-btn").on("click", function() {
|
||||
$(this).closest("tr").addClass("d-none")
|
||||
|
@ -49,15 +49,17 @@
|
|||
<tr>
|
||||
<th scope="col">
|
||||
<div class="d-flex align-items-center gap-10">
|
||||
{{-- <div class="form-check style-check d-flex align-items-center">
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input radius-4 border input-form-dark" type="checkbox" name="checkbox" id="selectAll">
|
||||
</div> --}}
|
||||
No.
|
||||
</div>
|
||||
S.L
|
||||
</div>
|
||||
</th>
|
||||
<th scope="col">Join Date</th>
|
||||
<th scope="col">Name</th>
|
||||
<th scope="col">Email</th>
|
||||
<th scope="col">Roles</th>
|
||||
<th scope="col">Department</th>
|
||||
<th scope="col">Designation</th>
|
||||
<th scope="col" class="text-center">Status</th>
|
||||
<th scope="col" class="text-center">Action</th>
|
||||
</tr>
|
||||
|
@ -66,20 +68,24 @@
|
|||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-10">
|
||||
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input radius-4 border border-neutral-400" type="checkbox" name="checkbox">
|
||||
</div>
|
||||
01
|
||||
</div>
|
||||
</td>
|
||||
<td>25 Jan 2024</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://img.freepik.com/premium-vector/user-profile-icon-flat-style-member-avatar-vector-illustration-isolated-background-human-permission-sign-business-concept_157943-15752.jpg?semt=ais_hybrid&w=740" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<img src="{{ asset('assets/images/user-list/user-list1.png') }}" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<div class="flex-grow-1">
|
||||
<span class="text-md mb-0 fw-normal text-secondary-light">Kathryn Murphy</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="text-md mb-0 fw-normal text-secondary-light">osgoodwy@gmail.com</span></td>
|
||||
<td>Sekretariat</td>
|
||||
<td>HR</td>
|
||||
<td>Manager</td>
|
||||
<td class="text-center">
|
||||
<span class="bg-success-focus text-success-600 border border-success-main px-24 py-4 radius-4 fw-medium text-sm">Active</span>
|
||||
</td>
|
||||
|
@ -100,21 +106,24 @@
|
|||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-10">
|
||||
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input radius-4 border border-neutral-400" type="checkbox" name="checkbox">
|
||||
</div>
|
||||
02
|
||||
</div>
|
||||
</td>
|
||||
<td>25 Jan 2024</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://img.freepik.com/premium-vector/user-profile-icon-flat-style-member-avatar-vector-illustration-isolated-background-human-permission-sign-business-concept_157943-15752.jpg?semt=ais_hybrid&w=740" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<img src="{{ asset('assets/images/user-list/user-list2.png') }}" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<div class="flex-grow-1">
|
||||
<span class="text-md mb-0 fw-normal text-secondary-light">Annette Black</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="text-md mb-0 fw-normal text-secondary-light">redaniel@gmail.com</span></td>
|
||||
<td>Tim Teknis</td>
|
||||
|
||||
<td>Design</td>
|
||||
<td>UI UX Designer</td>
|
||||
<td class="text-center">
|
||||
<span class="bg-neutral-200 text-neutral-600 border border-neutral-400 px-24 py-4 radius-4 fw-medium text-sm">Inactive</span>
|
||||
</td>
|
||||
|
@ -135,21 +144,24 @@
|
|||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-10">
|
||||
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input radius-4 border border-neutral-400" type="checkbox" name="checkbox">
|
||||
</div>
|
||||
03
|
||||
</div>
|
||||
</td>
|
||||
<td>10 Feb 2024</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://img.freepik.com/premium-vector/user-profile-icon-flat-style-member-avatar-vector-illustration-isolated-background-human-permission-sign-business-concept_157943-15752.jpg?semt=ais_hybrid&w=740" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<img src="{{ asset('assets/images/user-list/user-list3.png') }}" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<div class="flex-grow-1">
|
||||
<span class="text-md mb-0 fw-normal text-secondary-light">Ronald Richards</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="text-md mb-0 fw-normal text-secondary-light">seannand@mail.ru</span></td>
|
||||
<td>Tim Teknis</td>
|
||||
|
||||
<td>Design</td>
|
||||
<td>UI UX Designer</td>
|
||||
<td class="text-center">
|
||||
<span class="bg-success-focus text-success-600 border border-success-main px-24 py-4 radius-4 fw-medium text-sm">Active</span>
|
||||
</td>
|
||||
|
@ -171,21 +183,24 @@
|
|||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-10">
|
||||
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input radius-4 border border-neutral-400" type="checkbox" name="checkbox">
|
||||
</div>
|
||||
04
|
||||
</div>
|
||||
</td>
|
||||
<td>10 Feb 2024</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://img.freepik.com/premium-vector/user-profile-icon-flat-style-member-avatar-vector-illustration-isolated-background-human-permission-sign-business-concept_157943-15752.jpg?semt=ais_hybrid&w=740" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<img src="{{ asset('assets/images/user-list/user-list4.png') }}" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<div class="flex-grow-1">
|
||||
<span class="text-md mb-0 fw-normal text-secondary-light">Eleanor Pena</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="text-md mb-0 fw-normal text-secondary-light">miyokoto@mail.ru</span></td>
|
||||
<td>Tim Teknis</td>
|
||||
|
||||
<td>Design</td>
|
||||
<td>UI UX Designer</td>
|
||||
<td class="text-center">
|
||||
<span class="bg-success-focus text-success-600 border border-success-main px-24 py-4 radius-4 fw-medium text-sm">Active</span>
|
||||
</td>
|
||||
|
@ -207,21 +222,24 @@
|
|||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-10">
|
||||
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input radius-4 border border-neutral-400" type="checkbox" name="checkbox">
|
||||
</div>
|
||||
05
|
||||
</div>
|
||||
</td>
|
||||
<td>15 March 2024</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://img.freepik.com/premium-vector/user-profile-icon-flat-style-member-avatar-vector-illustration-isolated-background-human-permission-sign-business-concept_157943-15752.jpg?semt=ais_hybrid&w=740" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<img src="{{ asset('assets/images/user-list/user-list5.png') }}" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<div class="flex-grow-1">
|
||||
<span class="text-md mb-0 fw-normal text-secondary-light">Leslie Alexander</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="text-md mb-0 fw-normal text-secondary-light">icadahli@gmail.com</span></td>
|
||||
<td>Tim Teknis</td>
|
||||
|
||||
<td>Design</td>
|
||||
<td>UI UX Designer</td>
|
||||
<td class="text-center">
|
||||
<span class="bg-neutral-200 text-neutral-600 border border-neutral-400 px-24 py-4 radius-4 fw-medium text-sm">Inactive</span>
|
||||
</td>
|
||||
|
@ -243,21 +261,24 @@
|
|||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-10">
|
||||
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input radius-4 border border-neutral-400" type="checkbox" name="checkbox">
|
||||
</div>
|
||||
06
|
||||
</div>
|
||||
</td>
|
||||
<td>15 March 2024</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://img.freepik.com/premium-vector/user-profile-icon-flat-style-member-avatar-vector-illustration-isolated-background-human-permission-sign-business-concept_157943-15752.jpg?semt=ais_hybrid&w=740" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<img src="{{ asset('assets/images/user-list/user-list6.png') }}" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<div class="flex-grow-1">
|
||||
<span class="text-md mb-0 fw-normal text-secondary-light">Albert Flores</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="text-md mb-0 fw-normal text-secondary-light">warn@mail.ru</span></td>
|
||||
<td>Tim Teknis</td>
|
||||
|
||||
<td>Design</td>
|
||||
<td>UI UX Designer</td>
|
||||
<td class="text-center">
|
||||
<span class="bg-success-focus text-success-600 border border-success-main px-24 py-4 radius-4 fw-medium text-sm">Active</span>
|
||||
</td>
|
||||
|
@ -279,21 +300,24 @@
|
|||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-10">
|
||||
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input radius-4 border border-neutral-400" type="checkbox" name="checkbox">
|
||||
</div>
|
||||
07
|
||||
</div>
|
||||
</td>
|
||||
<td>27 April 2024</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://img.freepik.com/premium-vector/user-profile-icon-flat-style-member-avatar-vector-illustration-isolated-background-human-permission-sign-business-concept_157943-15752.jpg?semt=ais_hybrid&w=740" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<img src="{{ asset('assets/images/user-list/user-list7.png') }}" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<div class="flex-grow-1">
|
||||
<span class="text-md mb-0 fw-normal text-secondary-light">Jacob Jones</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="text-md mb-0 fw-normal text-secondary-light">zitka@mail.ru</span></td>
|
||||
<td>Kepala Dinas</td>
|
||||
|
||||
<td>Development</td>
|
||||
<td>Frontend developer</td>
|
||||
<td class="text-center">
|
||||
<span class="bg-success-focus text-success-600 border border-success-main px-24 py-4 radius-4 fw-medium text-sm">Active</span>
|
||||
</td>
|
||||
|
@ -315,21 +339,24 @@
|
|||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-10">
|
||||
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input radius-4 border border-neutral-400" type="checkbox" name="checkbox">
|
||||
</div>
|
||||
08
|
||||
</div>
|
||||
</td>
|
||||
<td>25 Jan 2024</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://img.freepik.com/premium-vector/user-profile-icon-flat-style-member-avatar-vector-illustration-isolated-background-human-permission-sign-business-concept_157943-15752.jpg?semt=ais_hybrid&w=740" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<img src="{{ asset('assets/images/user-list/user-list8.png') }}" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<div class="flex-grow-1">
|
||||
<span class="text-md mb-0 fw-normal text-secondary-light">Jerome Bell</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="text-md mb-0 fw-normal text-secondary-light">igerrin@gmail.com</span></td>
|
||||
<td>Kepala Dinas</td>
|
||||
|
||||
<td>Development</td>
|
||||
<td>Frontend developer</td>
|
||||
<td class="text-center">
|
||||
<span class="bg-neutral-200 text-neutral-600 border border-neutral-400 px-24 py-4 radius-4 fw-medium text-sm">Inactive</span>
|
||||
</td>
|
||||
|
@ -351,21 +378,24 @@
|
|||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-10">
|
||||
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input radius-4 border border-neutral-400" type="checkbox" name="checkbox">
|
||||
</div>
|
||||
09
|
||||
</div>
|
||||
</td>
|
||||
<td>30 April 2024</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://img.freepik.com/premium-vector/user-profile-icon-flat-style-member-avatar-vector-illustration-isolated-background-human-permission-sign-business-concept_157943-15752.jpg?semt=ais_hybrid&w=740" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<img src="{{ asset('assets/images/user-list/user-list2.png') }}" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<div class="flex-grow-1">
|
||||
<span class="text-md mb-0 fw-normal text-secondary-light">Marvin McKinney</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="text-md mb-0 fw-normal text-secondary-light">maka@yandex.ru</span></td>
|
||||
<td>Kepala Dinas</td>
|
||||
|
||||
<td>Development</td>
|
||||
<td>Frontend developer</td>
|
||||
<td class="text-center">
|
||||
<span class="bg-success-focus text-success-600 border border-success-main px-24 py-4 radius-4 fw-medium text-sm">Active</span>
|
||||
</td>
|
||||
|
@ -387,21 +417,24 @@
|
|||
<tr>
|
||||
<td>
|
||||
<div class="d-flex align-items-center gap-10">
|
||||
|
||||
<div class="form-check style-check d-flex align-items-center">
|
||||
<input class="form-check-input radius-4 border border-neutral-400" type="checkbox" name="checkbox">
|
||||
</div>
|
||||
10
|
||||
</div>
|
||||
</td>
|
||||
<td>30 April 2024</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<img src="https://img.freepik.com/premium-vector/user-profile-icon-flat-style-member-avatar-vector-illustration-isolated-background-human-permission-sign-business-concept_157943-15752.jpg?semt=ais_hybrid&w=740" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<img src="{{ asset('assets/images/user-list/user-list10.png') }}" alt="" class="w-40-px h-40-px rounded-circle flex-shrink-0 me-12 overflow-hidden">
|
||||
<div class="flex-grow-1">
|
||||
<span class="text-md mb-0 fw-normal text-secondary-light">Cameron Williamson</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="text-md mb-0 fw-normal text-secondary-light">danten@mail.ru</span></td>
|
||||
<td>Kepala Dinas</td>
|
||||
|
||||
<td>Development</td>
|
||||
<td>Frontend developer</td>
|
||||
<td class="text-center">
|
||||
<span class="bg-success-focus text-success-600 border border-success-main px-24 py-4 radius-4 fw-medium text-sm">Active</span>
|
||||
</td>
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use App\Http\Controllers\AuthController;
|
||||
|
||||
Route::prefix('auth')->group(function () {
|
||||
Route::post('/login', [AuthController::class, 'login']);
|
||||
Route::middleware('auth:sanctum')->group(function () {
|
||||
Route::post('/logout', [AuthController::class, 'logout']);
|
||||
Route::post('/logout-all', [AuthController::class, 'logoutAll']);
|
||||
Route::get('/me', [AuthController::class, 'me']);
|
||||
});
|
||||
});
|
|
@ -6,9 +6,12 @@ use App\Http\Controllers\Izin\IzinAngkutController;
|
|||
use App\Http\Controllers\Izin\IzinEmisiController;
|
||||
use App\Http\Controllers\JadwalSidangController;
|
||||
use App\Http\Controllers\LoginController;
|
||||
use App\Http\Controllers\AuthController as WebAuthController;
|
||||
use App\Http\Controllers\NewsController;
|
||||
use App\Http\Controllers\PenugasanController;
|
||||
use App\Http\Controllers\PerizinanLingkunganController;
|
||||
use App\Http\Controllers\Admin\RoleController;
|
||||
use App\Http\Controllers\Admin\PermissionController;
|
||||
use App\Http\Controllers\Persetujuan\AddendumController;
|
||||
use App\Http\Controllers\Persetujuan\AmdalController;
|
||||
use App\Http\Controllers\Persetujuan\DelhController;
|
||||
|
@ -60,45 +63,64 @@ Route::match(['get', 'post'], '/surat/exportPDF', [SuratArahanController::class,
|
|||
|
||||
// Dashboard
|
||||
Route::get('/dashboard', [DashboardController::class, 'index'])->name('dashboard.index');
|
||||
Route::get('/dashboard-pertek', [DashboardController::class, 'pertek'])->name('dashboard.pertek');
|
||||
Route::get('/dashboard-rintek', [DashboardController::class, 'rintek'])->name('dashboard.rintek');
|
||||
Route::get('/dashboard-amdal', [DashboardController::class, 'amdal'])->name('dashboard.amdal');
|
||||
Route::get('/dashboard-bengkel', [DashboardController::class, 'bengkel'])->name('dashboard.bengkel');
|
||||
Route::get('/dashboard-pertek', [DashboardController::class, 'pertek'])->middleware('permission:dashboard.view.pertek')->name('dashboard.pertek');
|
||||
Route::get('/dashboard-rintek', [DashboardController::class, 'rintek'])->middleware('permission:dashboard.view.rintek')->name('dashboard.rintek');
|
||||
Route::get('/dashboard-amdal', [DashboardController::class, 'amdal'])->middleware('permission:dashboard.view.amdal')->name('dashboard.amdal');
|
||||
Route::get('/dashboard-bengkel', [DashboardController::class, 'bengkel'])->middleware('permission:dashboard.view.uji_emisi')->name('dashboard.bengkel');
|
||||
|
||||
|
||||
|
||||
// login
|
||||
Route::get('/auth/login', [LoginController::class, 'index'])->name('login.index');
|
||||
Route::post('/auth/session-login', [WebAuthController::class, 'sessionLogin'])->name('login.session');
|
||||
Route::post('/auth/logout', [WebAuthController::class, 'logout'])->name('logout.session');
|
||||
|
||||
// Roles & Permissions Management (restricted to settings.manage)
|
||||
Route::prefix('admin')->middleware('permission:settings.manage')->group(function () {
|
||||
Route::get('/roles', [RoleController::class, 'index'])->name('admin.roles.index');
|
||||
Route::get('/roles/create', [RoleController::class, 'create'])->name('admin.roles.create');
|
||||
Route::post('/roles', [RoleController::class, 'store'])->name('admin.roles.store');
|
||||
Route::get('/roles/{role}/edit', [RoleController::class, 'edit'])->name('admin.roles.edit');
|
||||
Route::put('/roles/{role}', [RoleController::class, 'update'])->name('admin.roles.update');
|
||||
Route::delete('/roles/{role}', [RoleController::class, 'destroy'])->name('admin.roles.destroy');
|
||||
|
||||
Route::get('/permissions', [PermissionController::class, 'index'])->name('admin.permissions.index');
|
||||
Route::get('/permissions/create', [PermissionController::class, 'create'])->name('admin.permissions.create');
|
||||
Route::post('/permissions', [PermissionController::class, 'store'])->name('admin.permissions.store');
|
||||
Route::get('/permissions/{permission}/edit', [PermissionController::class, 'edit'])->name('admin.permissions.edit');
|
||||
Route::put('/permissions/{permission}', [PermissionController::class, 'update'])->name('admin.permissions.update');
|
||||
Route::delete('/permissions/{permission}', [PermissionController::class, 'destroy'])->name('admin.permissions.destroy');
|
||||
});
|
||||
|
||||
// Pertek
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/pertek/arahan', [PersetujuanTeknisController::class, 'index_arahan'])->name('pertek.index_arahan');
|
||||
Route::get('/pertek/slo', [PersetujuanTeknisController::class, 'index_slo'])->name('pertek.index_slo');
|
||||
Route::get('/pertek/detail-slo', [PersetujuanTeknisController::class, 'detail_slo'])->name('pertek.detail_slo');
|
||||
Route::get('/pertek/create-arahan', [PersetujuanTeknisController::class, 'create_arahan'])->name('pertek.create_arahan');
|
||||
Route::get('/pertek/verifikator/arahan', [PersetujuanTeknisController::class, 'verifikator_arahan'])->name('pertek.verifikator_arahan');
|
||||
Route::get('/pertek/user/arahan', [PersetujuanTeknisController::class, 'user_arahan'])->name('pertek.user_arahan');
|
||||
Route::get('/pertek/emisi', [PersetujuanTeknisController::class, 'index_emisi'])->name('pertek.index_emisi');
|
||||
Route::get('/pertek/emisi/detail', [PersetujuanTeknisController::class, 'detail_emisi'])->name('pertek.detail_emisi');
|
||||
Route::get('/pertek/airlimbah', [PersetujuanTeknisController::class, 'index_airlimbah'])->name('pertek.index_airlimbah');
|
||||
Route::get('/pertek/limbahb3', [PersetujuanTeknisController::class, 'index_limbahb3'])->name('pertek.index_limbahb3');
|
||||
Route::get('/pertek/arahan', [PersetujuanTeknisController::class, 'index_arahan'])->middleware('permission:persetujuan_teknis.access')->name('pertek.index_arahan');
|
||||
Route::get('/pertek/slo', [PersetujuanTeknisController::class, 'index_slo'])->middleware('permission:persetujuan_teknis.access')->name('pertek.index_slo');
|
||||
Route::get('/pertek/detail-slo', [PersetujuanTeknisController::class, 'detail_slo'])->middleware('permission:persetujuan_teknis.access')->name('pertek.detail_slo');
|
||||
Route::get('/pertek/create-arahan', [PersetujuanTeknisController::class, 'create_arahan'])->middleware('permission:persetujuan_teknis.access')->name('pertek.create_arahan');
|
||||
Route::get('/pertek/verifikator/arahan', [PersetujuanTeknisController::class, 'verifikator_arahan'])->middleware('permission:persetujuan_teknis.access')->name('pertek.verifikator_arahan');
|
||||
Route::get('/pertek/user/arahan', [PersetujuanTeknisController::class, 'user_arahan'])->middleware('permission:persetujuan_teknis.access')->name('pertek.user_arahan');
|
||||
Route::get('/pertek/emisi', [PersetujuanTeknisController::class, 'index_emisi'])->middleware('permission:persetujuan_teknis.access')->name('pertek.index_emisi');
|
||||
Route::get('/pertek/emisi/detail', [PersetujuanTeknisController::class, 'detail_emisi'])->middleware('permission:persetujuan_teknis.access')->name('pertek.detail_emisi');
|
||||
Route::get('/pertek/airlimbah', [PersetujuanTeknisController::class, 'index_airlimbah'])->middleware('permission:persetujuan_teknis.access')->name('pertek.index_airlimbah');
|
||||
Route::get('/pertek/limbahb3', [PersetujuanTeknisController::class, 'index_limbahb3'])->middleware('permission:persetujuan_teknis.access')->name('pertek.index_limbahb3');
|
||||
});
|
||||
|
||||
//rintek
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/rintek/arahan', [RincianTeknisController::class, 'index_arahan'])->name('rintek.index_arahan');
|
||||
Route::get('/rintek/create-arahan', [RincianTeknisController::class, 'create_arahan'])->name('rintek.create_arahan');
|
||||
Route::get('/rintek/verifikator/arahan', [RincianTeknisController::class, 'verifikator_arahan'])->name('rintek.verifikator_arahan');
|
||||
Route::get('/rintek/arahan', [RincianTeknisController::class, 'index_arahan'])->middleware('permission:rincian_teknis.access')->name('rintek.index_arahan');
|
||||
Route::get('/rintek/create-arahan', [RincianTeknisController::class, 'create_arahan'])->middleware('permission:rincian_teknis.access')->name('rintek.create_arahan');
|
||||
Route::get('/rintek/verifikator/arahan', [RincianTeknisController::class, 'verifikator_arahan'])->middleware('permission:rincian_teknis.access')->name('rintek.verifikator_arahan');
|
||||
});
|
||||
|
||||
//izin angkut
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/izinangkut', [IzinAngkutController::class, 'index_angkut'])->name('izinangkut.index_permohonan');
|
||||
Route::get('/izinangkut', [IzinAngkutController::class, 'index_angkut'])->middleware('permission:izin_angkut_olah.access')->name('izinangkut.index_permohonan');
|
||||
});
|
||||
|
||||
//izin angkut
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/izinemisi', [IzinEmisiController::class, 'index_emisi'])->name('izinemisi.index_permohonan');
|
||||
Route::get('/izinemisi', [IzinEmisiController::class, 'index_emisi'])->middleware('permission:izin_tempat_uji_emisi.access')->name('izinemisi.index_permohonan');
|
||||
});
|
||||
|
||||
//rintek
|
||||
|
@ -108,8 +130,8 @@ Route::prefix('admin')->group(function () {
|
|||
// });
|
||||
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/jadwal', [JadwalSidangController::class, 'index'])->name('jadwal.index');
|
||||
Route::match(['get', 'post'], '/jadwal/create', [JadwalSidangController::class, 'create'])->name('jadwal.create');
|
||||
Route::get('/jadwal', [JadwalSidangController::class, 'index'])->middleware('permission:penjadwalan.access')->name('jadwal.index');
|
||||
Route::match(['get', 'post'], '/jadwal/create', [JadwalSidangController::class, 'create'])->middleware('permission:penjadwalan.access')->name('jadwal.create');
|
||||
});
|
||||
|
||||
// Penugasan
|
||||
|
@ -127,44 +149,42 @@ Route::prefix('admin')->group(function () {
|
|||
});
|
||||
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/kerangka', [KerangkaController::class, 'index'])->name('persetujuan.kerangka.index');
|
||||
Route::get('/kerangka/create', [KerangkaController::class, 'create'])->name('persetujuan.kerangka.create');
|
||||
Route::get('/kerangka', [KerangkaController::class, 'index'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.kerangka.index');
|
||||
Route::get('/kerangka/create', [KerangkaController::class, 'create'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.kerangka.create');
|
||||
});
|
||||
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/amdal', [AmdalController::class, 'index'])->name('persetujuan.amdal.index');
|
||||
Route::get('/amdal/detail', [AmdalController::class, 'detail'])->name('persetujuan.amdal.detail');
|
||||
Route::get('/amdal', [AmdalController::class, 'index'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.amdal.index');
|
||||
Route::get('/amdal/detail', [AmdalController::class, 'detail'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.amdal.detail');
|
||||
});
|
||||
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/ukl', [UklController::class, 'index'])->name('persetujuan.ukl.index');
|
||||
Route::get('/ukl/detail', [UklController::class, 'detail'])->name('persetujuan.ukl.detail');
|
||||
Route::get('/ukl', [UklController::class, 'index'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.ukl.index');
|
||||
Route::get('/ukl/detail', [UklController::class, 'detail'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.ukl.detail');
|
||||
});
|
||||
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/rkl', [RklController::class, 'index'])->name('persetujuan.rkl.index');
|
||||
Route::get('/rkl/detail', [RklController::class, 'detail'])->name('persetujuan.rkl.detail');
|
||||
Route::get('/rkl', [RklController::class, 'index'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.rkl.index');
|
||||
Route::get('/rkl/detail', [RklController::class, 'detail'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.rkl.detail');
|
||||
});
|
||||
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/sppl', [SpplController::class, 'index'])->name('persetujuan.sppl.index');
|
||||
Route::get('/sppl/detail', [SpplController::class, 'detail'])->name('persetujuan.sppl.detail');
|
||||
Route::get('/sppl', [SpplController::class, 'index'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.sppl.index');
|
||||
Route::get('/sppl/detail', [SpplController::class, 'detail'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.sppl.detail');
|
||||
});
|
||||
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/addendum', [AddendumController::class, 'index'])->name('persetujuan.addendum.index');
|
||||
Route::get('/addendum/detail', [AddendumController::class, 'detail'])->name('persetujuan.addendum.detail');
|
||||
Route::get('/addendum', [AddendumController::class, 'index'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.addendum.index');
|
||||
Route::get('/addendum/detail', [AddendumController::class, 'detail'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.addendum.detail');
|
||||
});
|
||||
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/delh', [DelhController::class, 'index'])->name('persetujuan.delh.index');
|
||||
Route::get('/delh/detail', [DelhController::class, 'detail'])->name('persetujuan.delh.detail');
|
||||
Route::get('/delh', [DelhController::class, 'index'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.delh.index');
|
||||
Route::get('/delh/detail', [DelhController::class, 'detail'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.delh.detail');
|
||||
});
|
||||
|
||||
|
||||
Route::prefix('admin')->group(function () {
|
||||
Route::get('/dplh', [DplhController::class, 'index'])->name('persetujuan.dplh.index');
|
||||
Route::get('/dplh/detail', [DplhController::class, 'detail'])->name('persetujuan.dplh.detail');
|
||||
Route::get('/dplh', [DplhController::class, 'index'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.dplh.index');
|
||||
Route::get('/dplh/detail', [DplhController::class, 'detail'])->middleware('permission:persetujuan_lingkungan.access')->name('persetujuan.dplh.detail');
|
||||
});
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue