fixing perusahaan
parent
0562099ca0
commit
08c45c61ca
64
.env.example
64
.env.example
|
@ -1,64 +0,0 @@
|
|||
APP_NAME=Laravel
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_TIMEZONE=UTC
|
||||
APP_URL=http://localhost
|
||||
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
APP_MAINTENANCE_STORE=database
|
||||
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
LOG_STACK=single
|
||||
LOG_DEPRECATIONS_CHANNEL=null
|
||||
LOG_LEVEL=debug
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
# DB_HOST=127.0.0.1
|
||||
# DB_PORT=3306
|
||||
# DB_DATABASE=laravel
|
||||
# DB_USERNAME=root
|
||||
# DB_PASSWORD=
|
||||
|
||||
SESSION_DRIVER=database
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
FILESYSTEM_DISK=local
|
||||
QUEUE_CONNECTION=database
|
||||
|
||||
CACHE_STORE=database
|
||||
CACHE_PREFIX=
|
||||
|
||||
MEMCACHED_HOST=127.0.0.1
|
||||
|
||||
REDIS_CLIENT=phpredis
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_MAILER=log
|
||||
MAIL_HOST=127.0.0.1
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS="hello@example.com"
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
AWS_USE_PATH_STYLE_ENDPOINT=false
|
||||
|
||||
VITE_APP_NAME="${APP_NAME}"
|
|
@ -63,4 +63,17 @@ class JenisKegiatanController extends Controller
|
|||
return back()->with('error', 'Something went wrong.');
|
||||
}
|
||||
}
|
||||
|
||||
public function getAll()
|
||||
{
|
||||
try {
|
||||
$jeniskegiatan = JenisKegiatan::where('IsPublish', true)
|
||||
->select('JenisKegiatanId', 'NamaJenisKegiatan')
|
||||
->get();
|
||||
return response()->json($jeniskegiatan);
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error fetching all Jenis Kegiatan: ' . $e->getMessage());
|
||||
return response()->json(['error' => 'Something went wrong'], 500);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Inertia\Inertia;
|
||||
|
||||
class PelaporanALController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
try {
|
||||
return Inertia::render('admin/pelaporan/AL/index_AL');
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error rendering view: ' . $e->getMessage());
|
||||
return back()->with('error', 'Something went wrong.');
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,178 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Models\Perusahaan;
|
||||
use App\Models\JenisKegiatan;
|
||||
use App\Http\Requests\PerusahaanRequest;
|
||||
use App\Models\JenisDokIL;
|
||||
use App\Models\Kabupaten;
|
||||
use App\Models\Kecamatan;
|
||||
use App\Models\Kelurahan;
|
||||
use App\Models\Verifikator;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Inertia\Inertia;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
|
||||
class PerusahaanController extends Controller
|
||||
{
|
||||
public function index()
|
||||
{
|
||||
try {
|
||||
$perusahaan = Perusahaan::with('jenisKegiatan', 'kelurahan.kecamatan.kabupaten', 'verifikator', 'jenisDokIL')->get();
|
||||
return Inertia::render('admin/perusahaan/index_perusahaan', [
|
||||
'perusahaan' => $perusahaan,
|
||||
'jenisKegiatan' => JenisKegiatan::all(),
|
||||
'jenisDokIL' => JenisDokIL::all(),
|
||||
'verifikator' => Verifikator::all(),
|
||||
'kabupaten' => Kabupaten::all(),
|
||||
'kecamatan' => Kecamatan::all(),
|
||||
'kelurahan' => Kelurahan::all(),
|
||||
]);
|
||||
|
||||
} catch (\Exception $e) {
|
||||
Log::error('Error fetching data: ' . $e->getMessage());
|
||||
return back()->with('error', 'Terjadi kesalahan saat memuat data.');
|
||||
}
|
||||
}
|
||||
|
||||
// public function store(Request $request)
|
||||
// {
|
||||
// $request->validate([
|
||||
// 'ILDokumen' => 'required|file|mimes:pdf|max:20480',
|
||||
// ]);
|
||||
|
||||
// try {
|
||||
// return DB::transaction(function () use ($request) {
|
||||
// if ($request->hasFile('ILDokumen')) {
|
||||
// $file = $request->file('ILDokumen');
|
||||
// $fileName = time() . '_' . $file->getClientOriginalName();
|
||||
|
||||
// if (!Storage::exists('public/files/il')) {
|
||||
// Storage::makeDirectory('public/files/il');
|
||||
// }
|
||||
|
||||
// $path = $file->storeAs('files/il', $fileName, 'public');
|
||||
|
||||
// $request->merge(['ILDokumen' => $path]);
|
||||
// }
|
||||
|
||||
// $perusahaan = Perusahaan::create($request->all());
|
||||
|
||||
// return response()->json([
|
||||
// 'message' => 'Perusahaan berhasil ditambahkan',
|
||||
// 'data' => $perusahaan
|
||||
// ]);
|
||||
// });
|
||||
// } catch (\Exception $e) {
|
||||
// if (isset($path) && Storage::exists($path)) {
|
||||
// Storage::delete($path);
|
||||
// }
|
||||
|
||||
// return response()->json([
|
||||
// 'message' => 'Error: ' . $e->getMessage()
|
||||
// ], 500);
|
||||
// }
|
||||
// }
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$request->validate([
|
||||
'NomorInduk' => 'required|string|unique:Perusahaan',
|
||||
'NamaPerusahaan' => 'required|string',
|
||||
'JenisKegiatanId' => 'required|string',
|
||||
'VerifikatorId' => 'required|string',
|
||||
'KelurahanId' => 'required|string',
|
||||
'Email' => 'required|email',
|
||||
]);
|
||||
|
||||
try {
|
||||
DB::beginTransaction();
|
||||
|
||||
$data = $request->all();
|
||||
|
||||
if ($request->hasFile('ILDokumen')) {
|
||||
$file = $request->file('ILDokumen');
|
||||
|
||||
if (!$file->isValid()) {
|
||||
throw new \Exception('Invalid PDF file');
|
||||
}
|
||||
|
||||
$fileName = time() . '_' . $file->getClientOriginalName();
|
||||
$path = $file->storeAs('files/il', $fileName, 'public');
|
||||
|
||||
if ($path === false) {
|
||||
throw new \Exception('Failed to store PDF file');
|
||||
}
|
||||
|
||||
$data['ILDokumen'] = $path;
|
||||
}
|
||||
|
||||
// Convert boolean string to actual boolean
|
||||
$data['IsPublish'] = filter_var($request->input('IsPublish'), FILTER_VALIDATE_BOOLEAN);
|
||||
$data['ReportLocked'] = filter_var($request->input('ReportLocked'), FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
$perusahaan = Perusahaan::create($data);
|
||||
|
||||
DB::commit();
|
||||
|
||||
return redirect()
|
||||
->route('admin.perusahaan.index')
|
||||
->with('success', 'Perusahaan berhasil ditambahkan');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
|
||||
if (isset($path) && Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->delete($path);
|
||||
}
|
||||
|
||||
Log::error('Error creating perusahaan: ' . $e->getMessage());
|
||||
|
||||
return response()->json([
|
||||
'message' => 'Error: ' . $e->getMessage()
|
||||
], 500);
|
||||
}
|
||||
}
|
||||
|
||||
public function show(Perusahaan $perusahaan): JsonResponse
|
||||
{
|
||||
$perusahaan->load([
|
||||
'JenisKegiatan',
|
||||
'Kelurahan',
|
||||
'JenisDokIL',
|
||||
'Verifikator',
|
||||
'Kawasan'
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'data' => $perusahaan
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(PerusahaanRequest $request, Perusahaan $perusahaan): JsonResponse
|
||||
{
|
||||
$perusahaan->update($request->validated());
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Data perusahaan berhasil diperbarui',
|
||||
'data' => $perusahaan
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy(Perusahaan $perusahaan): JsonResponse
|
||||
{
|
||||
$perusahaan->delete();
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Data perusahaan berhasil dihapus'
|
||||
]);
|
||||
}
|
||||
}
|
|
@ -95,41 +95,52 @@ class PostController extends Controller
|
|||
|
||||
public function edit(Post $post)
|
||||
{
|
||||
// Debug the image path
|
||||
Log::info('Image path:', ['path' => $post->ImagePost]);
|
||||
|
||||
return Inertia::render('admin/post/edit_post', [
|
||||
'post' => $post,
|
||||
'posting' => [
|
||||
...$post->toArray(),
|
||||
'ImagePost' => $post->ImagePost ? '/storage/' . $post->ImagePost : null,
|
||||
],
|
||||
'kategori' => Kategori::all(),
|
||||
'subkategori' => SubKategori::where('KategoriId', $post->KategoriId)->get(),
|
||||
'subkategori' => SubKategori::all(),
|
||||
'existingPosts' => Post::where('PostId', '!=', $post->PostId)
|
||||
->select('JudulPost', 'KategoriId')
|
||||
->get()
|
||||
]);
|
||||
}
|
||||
|
||||
public function update(PostRequest $request, Post $post)
|
||||
{
|
||||
try {
|
||||
$data = array_filter($request->validated(), function($value) {
|
||||
return $value !== null;
|
||||
});
|
||||
DB::beginTransaction();
|
||||
$data = $request->validated();
|
||||
|
||||
// Only update image if new one is uploaded
|
||||
if ($request->hasFile('ImagePost')) {
|
||||
// Delete old image if exists
|
||||
if ($post->ImagePost && Storage::disk('public')->exists($post->ImagePost)) {
|
||||
Storage::disk('public')->delete($post->ImagePost);
|
||||
}
|
||||
$data['ImagePost'] = $request->file('ImagePost')->store('images/posts', 'public');
|
||||
} else {
|
||||
// Keep existing image if no new one uploaded
|
||||
unset($data['ImagePost']);
|
||||
}
|
||||
|
||||
if (isset($data['IsPublish'])) {
|
||||
$data['IsPublish'] = (bool) $data['IsPublish'];
|
||||
}
|
||||
|
||||
$data['IsPublish'] = $request->boolean('IsPublish');
|
||||
$post->update($data);
|
||||
|
||||
return redirect()
|
||||
->route('admin.post.index')
|
||||
->with('success', 'Post berhasil diperbarui.');
|
||||
DB::commit();
|
||||
return redirect()->route('admin.post.index')->with('success', 'Post berhasil diperbarui.');
|
||||
|
||||
} catch (\Exception $e) {
|
||||
DB::rollBack();
|
||||
Log::error('Error updating Post: ' . $e->getMessage());
|
||||
return back()->with('error', 'Terjadi kesalahan saat memperbarui post.');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function destroy(Post $post)
|
||||
|
|
|
@ -23,7 +23,6 @@ class JenisDokILRequest extends FormRequest
|
|||
{
|
||||
return [
|
||||
'NamaJenisDokIL' => 'required|string|max:255',
|
||||
'KodeJenisDokIL' => 'required|string|max:20',
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace App\Http\Requests;
|
||||
|
||||
use Illuminate\Foundation\Http\FormRequest;
|
||||
|
||||
class PerusahaanRequest extends FormRequest
|
||||
{
|
||||
/**
|
||||
* Determine if the user is authorized to make this request.
|
||||
*/
|
||||
public function authorize(): bool
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the validation rules that apply to the request.
|
||||
*
|
||||
* @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
|
||||
*/
|
||||
public function rules(): array
|
||||
{
|
||||
return [
|
||||
'NomorInduk' => ['string'],
|
||||
'JenisKegiatanId' => ['required', 'integer'],
|
||||
'NamaPerusahaan' => ['required', 'string'],
|
||||
'Alamat' => ['string'],
|
||||
'KelurahanId' => ['required', 'integer'],
|
||||
'KodePos' => ['string'],
|
||||
'Telepon' => ['string'],
|
||||
'Fax' => ['string'],
|
||||
'Email' => ['required', 'string'],
|
||||
'Lintang' => ['string'],
|
||||
'Bujur' => ['string'],
|
||||
'CPNama' => ['string'],
|
||||
'CPTelepon' => ['string'],
|
||||
'ILNomor' => ['string'],
|
||||
'ILTanggal' => ['string'],
|
||||
'JenisDokILId' => ['integer'],
|
||||
'VerifikatorId' => ['required', 'integer'],
|
||||
'IsPublish' => ['required', 'boolean'],
|
||||
'ILDokumen' => ['string'],
|
||||
'Kawasan' => ['string'],
|
||||
];
|
||||
}
|
||||
}
|
|
@ -9,7 +9,6 @@ class JenisDokIL extends Model
|
|||
protected $table = 'JenisDokIL';
|
||||
protected $primaryKey = 'JenisDokILId';
|
||||
protected $fillable = [
|
||||
'KodeJenisDokIL',
|
||||
'NamaJenisDokIL'
|
||||
];
|
||||
}
|
||||
|
|
|
@ -21,4 +21,9 @@ class JenisKegiatan extends Model
|
|||
'IsPublish',
|
||||
];
|
||||
|
||||
public function Perusahaan()
|
||||
{
|
||||
return $this->hasMany(Perusahaan::class, 'JenisKegiatanId');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,16 +2,20 @@
|
|||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
class Perusahaan extends Model
|
||||
{
|
||||
use HasFactory;
|
||||
protected $table = 'Perusahaan';
|
||||
protected $primaryKey = 'PerusahaanId';
|
||||
|
||||
|
||||
protected $fillable = [
|
||||
'NomorInduk',
|
||||
'NomorInduk',
|
||||
'JenisKegiatanId',
|
||||
'NamaPerusahaan',
|
||||
'Alamat',
|
||||
|
@ -28,57 +32,67 @@ class Perusahaan extends Model
|
|||
'ILTanggal',
|
||||
'JenisDokILId',
|
||||
'VerifikatorId',
|
||||
'ReportLocked',
|
||||
'IsPublish',
|
||||
'ILDokumen',
|
||||
'ILPdlNomor',
|
||||
'ILPdlJenis',
|
||||
'DocPdlOrig',
|
||||
'DocPdlHash',
|
||||
'DocPdlPath',
|
||||
'ILPdlTanggal',
|
||||
'Kawasan'
|
||||
'ReportLocked'
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'ReportLocked' => 'boolean',
|
||||
'IsPublish' => 'boolean',
|
||||
'ILPdlTanggal' => 'date',
|
||||
'JenisKegiatanId' => 'integer',
|
||||
'VerifikatorId' => 'integer',
|
||||
'KelurahanId' => 'integer',
|
||||
];
|
||||
|
||||
// Relationship with JenisKegiatan
|
||||
public function jenisKegiatan(): BelongsTo
|
||||
public function jenisKegiatan()
|
||||
{
|
||||
return $this->belongsTo(JenisKegiatan::class, 'JenisKegiatanId', 'JenisKegiatanId');
|
||||
}
|
||||
|
||||
// Relationship with Kelurahan
|
||||
public function kelurahan(): BelongsTo
|
||||
protected $with = ['JenisKegiatan', 'Kelurahan'];
|
||||
|
||||
public function kelurahan()
|
||||
{
|
||||
return $this->belongsTo(Kelurahan::class, 'KelurahanId', 'KelurahanId');
|
||||
}
|
||||
|
||||
|
||||
public function kecamatan()
|
||||
{
|
||||
return $this->hasOneThrough(
|
||||
Kecamatan::class,
|
||||
Kelurahan::class,
|
||||
'KelurahanId', // Foreign key di tabel Kelurahan
|
||||
'KecamatanId', // Foreign key di tabel Kecamatan
|
||||
'KelurahanId', // Foreign key di tabel Perusahaan
|
||||
'KecamatanId' // Foreign key di tabel Kelurahan
|
||||
);
|
||||
}
|
||||
|
||||
public function kabupaten()
|
||||
{
|
||||
return $this->hasOneThrough(
|
||||
Kabupaten::class,
|
||||
Kecamatan::class,
|
||||
'KecamatanId', // Foreign key di tabel Kecamatan
|
||||
'KabupatenId', // Foreign key di tabel Kabupaten
|
||||
'KelurahanId', // Foreign key di tabel Perusahaan
|
||||
'KabupatenId' // Foreign key di tabel Kecamatan
|
||||
);
|
||||
}
|
||||
|
||||
public function verifikator()
|
||||
{
|
||||
return $this->belongsTo(Verifikator::class, 'VerifikatorId');
|
||||
}
|
||||
// Relationship with JenisDokIL
|
||||
public function jenisDokIL(): BelongsTo
|
||||
public function jenisDokIL()
|
||||
{
|
||||
return $this->belongsTo(JenisDokIL::class, 'JenisDokILId', 'JenisDokILId');
|
||||
}
|
||||
|
||||
// Relationship with JenisDokIL for PDL
|
||||
public function jenisDokILPdl(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(JenisDokIL::class, 'ILPdlJenis', 'JenisDokILId');
|
||||
}
|
||||
|
||||
// Relationship with Verifikator
|
||||
public function verifikator(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Verifikator::class, 'VerifikatorId', 'VerifikatorId');
|
||||
}
|
||||
|
||||
// Self-referential relationship for Kawasan
|
||||
public function kawasan(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(Perusahaan::class, 'Kawasan', 'PerusahaanId');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ return new class extends Migration
|
|||
Schema::create('users', function (Blueprint $table) {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('username')->unique();
|
||||
$table->string('email')->unique();
|
||||
$table->timestamp('email_verified_at')->nullable();
|
||||
$table->string('password');
|
||||
|
|
|
@ -13,7 +13,7 @@ return new class extends Migration
|
|||
{
|
||||
Schema::create('Kategori', function (Blueprint $table) {
|
||||
$table->id('KategoriId');
|
||||
$table->string('NamaKategori')->unique();
|
||||
$table->string('NamaKategori');
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
});
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->string('username')->unique()->after('name');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('users', function (Blueprint $table) {
|
||||
$table->dropColumn('username');
|
||||
});
|
||||
}
|
||||
};
|
|
@ -19,6 +19,7 @@ return new class extends Migration
|
|||
$table->string('SlugPost');
|
||||
$table->text('DescPost');
|
||||
$table->string('ImagePost')->nullable();
|
||||
$table->boolean('IsPublish')->default(1);
|
||||
$table->timestamps();
|
||||
|
||||
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('Post', function (Blueprint $table) {
|
||||
$table->boolean('IsPublish')->default(0);
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('Post', function (Blueprint $table) {
|
||||
$table->dropColumn('IsPublish');
|
||||
});
|
||||
}
|
||||
};
|
|
@ -11,10 +11,11 @@ return new class extends Migration
|
|||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('historykegiatan', function (Blueprint $table) {
|
||||
Schema::create('HistoryKegiatan', function (Blueprint $table) {
|
||||
$table->id('HistoryKegiatanId');
|
||||
$table->string('NamaHistoryKegiatan');
|
||||
$table->boolean('IsPublish')->default(0);
|
||||
$table->boolean('IsPublish')->default(1);
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
@ -24,6 +25,6 @@ return new class extends Migration
|
|||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('historykegiatan');
|
||||
Schema::dropIfExists('HistoryKegiatan');
|
||||
}
|
||||
};
|
||||
|
|
|
@ -11,10 +11,11 @@ return new class extends Migration
|
|||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('jeniskegiatan', function (Blueprint $table) {
|
||||
Schema::create('JenisKegiatan', function (Blueprint $table) {
|
||||
$table->id('JenisKegiatanId');
|
||||
$table->string('NamaJenisKegiatan');
|
||||
$table->boolean('IsPublish')->default(0);
|
||||
$table->boolean('IsPublish')->default(1);
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
@ -24,6 +25,6 @@ return new class extends Migration
|
|||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('jeniskegiatan');
|
||||
Schema::dropIfExists('JenisKegiatan');
|
||||
}
|
||||
};
|
||||
|
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('JenisKegiatan', function (Blueprint $table) {
|
||||
$table->softDeletes()->after('IsPublish');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('JenisKegiatan', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('HistoryKegiatan', function (Blueprint $table) {
|
||||
$table->softDeletes()->after('IsPublish');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('HistoryKegiatan', function (Blueprint $table) {
|
||||
$table->dropSoftDeletes();
|
||||
});
|
||||
}
|
||||
};
|
|
@ -1,28 +0,0 @@
|
|||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::table('Kategori', function (Blueprint $table) {
|
||||
$table->dropUnique(['NamaKategori']);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::table('Kategori', function (Blueprint $table) {
|
||||
$table->unique('NamaKategori');
|
||||
});
|
||||
}
|
||||
};
|
|
@ -30,22 +30,13 @@ return new class extends Migration
|
|||
$table->string('CPTelepon')->nullable();
|
||||
$table->string('ILNomor')->nullable();
|
||||
$table->string('ILTanggal')->nullable();
|
||||
$table->string('ILDokumen')->nullable();
|
||||
$table->unsignedBigInteger('JenisDokILId');
|
||||
$table->foreign('JenisDokILId')->references('JenisDokILId')->on('JenisDokIL')->onDelete('cascade');
|
||||
$table->unsignedBigInteger('VerifikatorId');
|
||||
$table->foreign('VerifikatorId')->references('VerifikatorId')->on('Verifikator')->onDelete('cascade');
|
||||
$table->boolean('ReportLocked')->default(1);
|
||||
$table->boolean('IsPublish')->default(0);
|
||||
$table->string('ILDokumen')->nullable();
|
||||
$table->string('ILPdlNomor')->default('n')->comment('Dok IL versi PDL');
|
||||
$table->unsignedInteger('ILPdlJenis')->nullable()->comment('id dari refjenisdokil');
|
||||
$table->foreign('ILPdlJenis')->references('JenisDokILId')->on('JenisDokIL')->onDelete('set null');
|
||||
$table->string('DocPdlOrig')->nullable()->comment('nama file original');
|
||||
$table->string('DocPdlHash')->nullable()->comment('nama file hash');
|
||||
$table->string('DocPdlPath')->nullable()->comment('upload path');
|
||||
$table->date('ILPdlTanggal')->nullable();
|
||||
$table->unsignedInteger('Kawasan')->nullable()->comment('id dari perusahaan, digunakan utk pengelola kawasan');
|
||||
$table->foreign('Kawasan')->references('PerusahaanId')->on('Perusahaan')->onDelete('set null');
|
||||
$table->boolean('IsPublish')->default(1);
|
||||
|
||||
|
||||
$table->timestamps();
|
||||
|
|
|
@ -13,7 +13,6 @@ return new class extends Migration
|
|||
{
|
||||
Schema::create('JenisDokIL', function (Blueprint $table) {
|
||||
$table->id('JenisDokILId');
|
||||
$table->string('KodeJenisDokIL')->unique();
|
||||
$table->string('NamaJenisDokIL');
|
||||
$table->timestamps();
|
||||
});
|
||||
|
|
|
@ -16,6 +16,7 @@ return new class extends Migration
|
|||
$table->string('NamaUnitKerja');
|
||||
$table->string('NamaKepala');
|
||||
$table->string('NIP');
|
||||
$table->softDeletes();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"name": "DLH-SKL",
|
||||
"name": "skl2025",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
|
@ -8,6 +8,7 @@
|
|||
"@iconify/react": "^5.2.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||
"@radix-ui/react-avatar": "^1.1.1",
|
||||
"@radix-ui/react-checkbox": "^1.1.4",
|
||||
"@radix-ui/react-collapsible": "^1.1.1",
|
||||
"@radix-ui/react-dialog": "^1.1.6",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
||||
|
@ -1240,6 +1241,77 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-checkbox": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz",
|
||||
"integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/primitive": "1.1.1",
|
||||
"@radix-ui/react-compose-refs": "1.1.1",
|
||||
"@radix-ui/react-context": "1.1.1",
|
||||
"@radix-ui/react-presence": "1.1.2",
|
||||
"@radix-ui/react-primitive": "2.0.2",
|
||||
"@radix-ui/react-use-controllable-state": "1.1.0",
|
||||
"@radix-ui/react-use-previous": "1.1.0",
|
||||
"@radix-ui/react-use-size": "1.1.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-primitive": {
|
||||
"version": "2.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz",
|
||||
"integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-slot": "1.1.2"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"@types/react-dom": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
|
||||
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-slot": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz",
|
||||
"integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@radix-ui/react-compose-refs": "1.1.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/react": "*",
|
||||
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"@types/react": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@radix-ui/react-collapsible": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz",
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
"@iconify/react": "^5.2.0",
|
||||
"@radix-ui/react-alert-dialog": "^1.0.5",
|
||||
"@radix-ui/react-avatar": "^1.1.1",
|
||||
"@radix-ui/react-checkbox": "^1.1.4",
|
||||
"@radix-ui/react-collapsible": "^1.1.1",
|
||||
"@radix-ui/react-dialog": "^1.1.6",
|
||||
"@radix-ui/react-dropdown-menu": "^2.1.2",
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
"use client";
|
||||
|
||||
import { TrendingUp } from "lucide-react";
|
||||
import { Bar, BarChart, CartesianGrid, XAxis } from "recharts";
|
||||
|
||||
import { useState } from "react";
|
||||
import { TrendingUp, BarChart2 } from "lucide-react";
|
||||
import {
|
||||
Bar,
|
||||
BarChart,
|
||||
CartesianGrid,
|
||||
XAxis,
|
||||
YAxis,
|
||||
ResponsiveContainer,
|
||||
} from "recharts";
|
||||
import {
|
||||
Card,
|
||||
CardContent,
|
||||
CardDescription,
|
||||
CardFooter,
|
||||
CardHeader,
|
||||
CardTitle,
|
||||
} from "@/components/ui/card";
|
||||
|
@ -17,10 +21,11 @@ import {
|
|||
ChartTooltip,
|
||||
ChartTooltipContent,
|
||||
} from "@/components/ui/chart";
|
||||
|
||||
const chartData = [
|
||||
{ month: "Genset", desktop: 186 },
|
||||
{ month: "Boiler", desktop: 305 },
|
||||
{ month: "Proses", desktop: 237 },
|
||||
{ month: "Genset", desktop: 186, color: "#22c55e" },
|
||||
{ month: "Boiler", desktop: 305, color: "#16a34a" },
|
||||
{ month: "Proses", desktop: 237, color: "#15803d" },
|
||||
];
|
||||
|
||||
const chartConfig = {
|
||||
|
@ -31,40 +36,151 @@ const chartConfig = {
|
|||
} satisfies ChartConfig;
|
||||
|
||||
export function ChartCard() {
|
||||
const [activeIndex, setActiveIndex] = useState<number | null>(null);
|
||||
|
||||
const handleMouseEnter = (index: number) => {
|
||||
setActiveIndex(index);
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setActiveIndex(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle>Sumber Emisi</CardTitle>
|
||||
{/* <CardDescription>January - June 2024</CardDescription> */}
|
||||
<Card className="relative overflow-hidden transition-all duration-300 hover:shadow-lg">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-green-50/30 to-transparent dark:from-green-950/30" />
|
||||
|
||||
<CardHeader className="relative">
|
||||
<div className="flex items-center gap-2">
|
||||
<BarChart2 className="h-6 w-6 text-green-600" />
|
||||
<CardTitle className="text-xl font-bold">
|
||||
Sumber Emisi
|
||||
</CardTitle>
|
||||
</div>
|
||||
<CardDescription className="text-sm text-green-600 font-medium">
|
||||
Total Emisi per Sumber
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<ChartContainer config={chartConfig}>
|
||||
<BarChart accessibilityLayer data={chartData}>
|
||||
<CartesianGrid vertical={false} />
|
||||
<XAxis
|
||||
dataKey="month"
|
||||
tickLine={false}
|
||||
tickMargin={10}
|
||||
axisLine={false}
|
||||
// tickFormatter={(value) => value.slice(0, 3)}
|
||||
/>
|
||||
<ChartTooltip
|
||||
cursor={false}
|
||||
content={<ChartTooltipContent hideLabel />}
|
||||
/>
|
||||
<Bar dataKey="desktop" fill="#53a946" radius={8} />
|
||||
</BarChart>
|
||||
</ChartContainer>
|
||||
|
||||
<CardContent className="relative pb-6">
|
||||
<div className="h-[300px] w-full">
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<BarChart data={chartData}>
|
||||
<defs>
|
||||
{chartData.map((entry, index) => (
|
||||
<linearGradient
|
||||
key={`gradient-${index}`}
|
||||
id={`barGradient-${index}`}
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="0"
|
||||
y2="1"
|
||||
>
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={entry.color}
|
||||
stopOpacity={0.8}
|
||||
/>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={entry.color}
|
||||
stopOpacity={0.3}
|
||||
/>
|
||||
</linearGradient>
|
||||
))}
|
||||
</defs>
|
||||
|
||||
<CartesianGrid
|
||||
vertical={false}
|
||||
stroke="#e5e7eb"
|
||||
strokeDasharray="4 4"
|
||||
/>
|
||||
|
||||
<XAxis
|
||||
dataKey="month"
|
||||
tickLine={false}
|
||||
axisLine={false}
|
||||
tick={{ fill: "#6b7280", fontSize: 12 }}
|
||||
tickMargin={12}
|
||||
/>
|
||||
|
||||
<YAxis
|
||||
axisLine={false}
|
||||
tickLine={false}
|
||||
tick={{ fill: "#6b7280", fontSize: 12 }}
|
||||
tickMargin={8}
|
||||
/>
|
||||
|
||||
<ChartTooltip
|
||||
cursor={false}
|
||||
content={({ active, payload }) => {
|
||||
if (active && payload && payload.length) {
|
||||
return (
|
||||
<div className="rounded-lg bg-white p-3 shadow-lg border border-green-100 backdrop-blur-sm">
|
||||
<p className="font-medium text-green-800">
|
||||
{payload[0].payload.month}
|
||||
</p>
|
||||
<p className="text-sm text-green-600">
|
||||
{payload[0].value} ton CO₂
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
/>
|
||||
|
||||
<Bar
|
||||
dataKey="desktop"
|
||||
radius={[8, 8, 0, 0]}
|
||||
onMouseEnter={(data, index) =>
|
||||
handleMouseEnter(index)
|
||||
}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
{chartData.map((entry, index) => (
|
||||
<rect
|
||||
key={`bar-${index}`}
|
||||
fill={`url(#barGradient-${index})`}
|
||||
className={`transition-all duration-300 ${
|
||||
activeIndex === index
|
||||
? "opacity-100 scale-y-105"
|
||||
: activeIndex === null
|
||||
? "opacity-90"
|
||||
: "opacity-50"
|
||||
}`}
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
<div className="mt-6 flex justify-between items-center px-4">
|
||||
{chartData.map((item, index) => (
|
||||
<div
|
||||
key={item.month}
|
||||
className="flex flex-col items-center gap-2"
|
||||
onMouseEnter={() => handleMouseEnter(index)}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<div
|
||||
className={`h-3 w-3 rounded-full transition-all duration-300 ${
|
||||
activeIndex === index
|
||||
? "scale-150"
|
||||
: activeIndex === null
|
||||
? "scale-100"
|
||||
: "scale-75 opacity-50"
|
||||
}`}
|
||||
style={{ backgroundColor: item.color }}
|
||||
/>
|
||||
<span className="text-sm font-medium text-gray-600">
|
||||
{item.month}
|
||||
</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
{/* <CardFooter className="flex-col items-start gap-2 text-sm">
|
||||
<div className="flex gap-2 font-medium leading-none">
|
||||
Trending up by 5.2% this month{" "}
|
||||
<TrendingUp className="h-4 w-4" />
|
||||
</div>
|
||||
<div className="leading-none text-muted-foreground">
|
||||
Showing total visitors for the last 6 months
|
||||
</div>
|
||||
</CardFooter> */}
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ const AnnouncementSection = ({ posts }: CardPengumumanProps) => {
|
|||
{/* List of Announcements */}
|
||||
{posts.length > 1 && (
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{posts.slice(1).map((post) => (
|
||||
{posts.slice(1, 4).map((post) => (
|
||||
<Card key={post.PostId} className="p-4">
|
||||
<img
|
||||
src={`/storage/${post.ImagePost}`}
|
||||
|
|
|
@ -38,7 +38,7 @@ const SearchDialog: React.FC = () => {
|
|||
<DialogContent className="bg-white text-black p-6 rounded-lg w-[90%] max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-lg font-bold text-center">
|
||||
Search
|
||||
Ketik Pencarianmu di sini...
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<form
|
||||
|
|
|
@ -55,7 +55,7 @@ const UndanganSection = ({ undangan }: CardUndanganProps) => {
|
|||
|
||||
{/* List of Announcements */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-3 gap-6">
|
||||
{undangan.map((item) => (
|
||||
{undangan.slice(0, 3).map((item) => (
|
||||
<Card key={item.PostId} className="md:p-4">
|
||||
<img
|
||||
src={`/storage/${item.ImagePost}`}
|
||||
|
|
|
@ -0,0 +1,812 @@
|
|||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import Select from "react-select";
|
||||
import { useState } from "react";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Calendar } from "@/components/ui/calendar";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { format } from "date-fns";
|
||||
import { cn } from "@/lib/utils";
|
||||
import {
|
||||
Calendar as CalendarIcon,
|
||||
FileText,
|
||||
Hotel,
|
||||
LocateFixed,
|
||||
MapPinned,
|
||||
UsersRound,
|
||||
Verified,
|
||||
VerifiedIcon,
|
||||
} from "lucide-react";
|
||||
import { Separator } from "../ui/separator";
|
||||
import { Textarea } from "../ui/textarea";
|
||||
|
||||
import {
|
||||
JenisDokIL,
|
||||
JenisKegiatan,
|
||||
Verifikator,
|
||||
Kabupaten,
|
||||
Kecamatan,
|
||||
Kelurahan,
|
||||
Perusahaan,
|
||||
Kawasan,
|
||||
} from "@/types/perusahaan";
|
||||
import { useForm, usePage } from "@inertiajs/react";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
|
||||
interface FormDataType {
|
||||
NomorInduk: string;
|
||||
JenisKegiatanId: string;
|
||||
NamaPerusahaan: string;
|
||||
Alamat: string;
|
||||
KelurahanId: string;
|
||||
KodePos: string;
|
||||
Telepon: string;
|
||||
Fax: string;
|
||||
Email: string;
|
||||
Lintang: string;
|
||||
Bujur: string;
|
||||
CPNama: string;
|
||||
CPTelepon: string;
|
||||
JenisDokILId: string;
|
||||
VerifikatorId: string;
|
||||
IsPublish: boolean;
|
||||
ILNomor: string;
|
||||
ILTanggal: string;
|
||||
ILDokumen: File | null;
|
||||
ReportLocked: true;
|
||||
PerusahaanId: string;
|
||||
}
|
||||
|
||||
// interface ExistingPerusahaan {
|
||||
// NamaPerusahaan: string;
|
||||
// PerusahaanId: string;
|
||||
// }
|
||||
|
||||
interface ExistingInduk {
|
||||
NomorInduk: string;
|
||||
}
|
||||
|
||||
interface AddPerusahaanModalProps {
|
||||
open: boolean;
|
||||
onClose: () => void;
|
||||
onSuccess: () => void;
|
||||
jenisKegiatan: JenisKegiatan[];
|
||||
jenisDokIL: JenisDokIL[];
|
||||
verifikator: Verifikator[];
|
||||
kabupaten: Kabupaten[];
|
||||
kecamatan: Kecamatan[];
|
||||
kelurahan: Kelurahan[];
|
||||
perusahaan: Perusahaan[];
|
||||
// kawasan: Kawasan[];
|
||||
// existingPerusahaan: ExistingPerusahaan[];
|
||||
// existingInduk: ExistingInduk[];
|
||||
}
|
||||
|
||||
export function AddPerusahaanModal({
|
||||
open,
|
||||
onClose,
|
||||
onSuccess,
|
||||
jenisKegiatan,
|
||||
jenisDokIL,
|
||||
verifikator,
|
||||
kabupaten,
|
||||
kecamatan,
|
||||
kelurahan,
|
||||
perusahaan,
|
||||
}: // existingPerusahaan,
|
||||
// existingInduk,
|
||||
// kawasan,
|
||||
AddPerusahaanModalProps) {
|
||||
const { toast } = useToast();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [date, setDate] = useState<Date>();
|
||||
const { data, setData, post, reset } = useForm({
|
||||
NomorInduk: "",
|
||||
PerusahaanId: "",
|
||||
JenisKegiatanId: "",
|
||||
NamaPerusahaan: "",
|
||||
Alamat: "",
|
||||
KelurahanId: "",
|
||||
KodePos: "",
|
||||
Telepon: "",
|
||||
Fax: "",
|
||||
Email: "",
|
||||
Lintang: "",
|
||||
Bujur: "",
|
||||
CPNama: "",
|
||||
CPTelepon: "",
|
||||
JenisDokILId: "",
|
||||
VerifikatorId: "",
|
||||
IsPublish: true,
|
||||
ILDokumen: null as File | null,
|
||||
ILNomor: "",
|
||||
ILTanggal: "",
|
||||
ReportLocked: true,
|
||||
});
|
||||
|
||||
const [selectedKabupaten, setSelectedKabupaten] = useState<{
|
||||
value: number;
|
||||
label: string;
|
||||
} | null>(null);
|
||||
|
||||
const [selectedKecamatan, setSelectedKecamatan] = useState<{
|
||||
value: number;
|
||||
label: string;
|
||||
} | null>(null);
|
||||
|
||||
const [selectedKelurahan, setSelectedKelurahan] = useState<{
|
||||
value: number;
|
||||
label: string;
|
||||
} | null>(null);
|
||||
|
||||
const [selectedPerusahaan, setSelectedPerusahaan] = useState<{
|
||||
value: number;
|
||||
label: string;
|
||||
} | null>(null);
|
||||
|
||||
const jenisKegiatanOptions = jenisKegiatan.map((jk) => ({
|
||||
value: jk.JenisKegiatanId,
|
||||
label: jk.NamaJenisKegiatan,
|
||||
}));
|
||||
|
||||
const jenisDokILOptions = jenisDokIL.map((jdi) => ({
|
||||
value: jdi.JenisDokILId,
|
||||
label: jdi.NamaJenisDokIL,
|
||||
}));
|
||||
|
||||
const verifikatorOptions = verifikator.map((v) => ({
|
||||
value: v.VerifikatorId,
|
||||
label: v.NamaUnitKerja,
|
||||
}));
|
||||
|
||||
const kabupatenOptions = kabupaten.map((k) => ({
|
||||
value: k.KabupatenId,
|
||||
label: k.NamaKabupaten,
|
||||
}));
|
||||
|
||||
const perusahaanOptions = perusahaan.map((per) => ({
|
||||
value: per.PerusahaanId,
|
||||
label: per.PerusahaanId,
|
||||
}));
|
||||
|
||||
const kecamatanOptions = kecamatan
|
||||
.filter(
|
||||
(kec) =>
|
||||
selectedKabupaten && kec.KabupatenId === selectedKabupaten.value
|
||||
)
|
||||
.map((kec) => ({
|
||||
value: kec.KecamatanId,
|
||||
label: kec.NamaKecamatan,
|
||||
}));
|
||||
|
||||
const kelurahanOptions = kelurahan
|
||||
.filter(
|
||||
(kel) =>
|
||||
selectedKecamatan && kel.KecamatanId === selectedKecamatan.value
|
||||
)
|
||||
.map((kel) => ({
|
||||
value: kel.KelurahanId,
|
||||
label: kel.NamaKelurahan,
|
||||
}));
|
||||
|
||||
const [showAlert, setShowAlert] = useState(false);
|
||||
const [errors, setErrors] = useState<Record<string, string>>({});
|
||||
|
||||
const validateForm = () => {
|
||||
const newErrors: Record<string, string> = {};
|
||||
|
||||
// const titleExists = existingPerusahaan.some(
|
||||
// (perus) =>
|
||||
// perus.PerusahaanId.toLowerCase() ===
|
||||
// data.PerusahaanId.toLowerCase()
|
||||
// );
|
||||
|
||||
// const indukExists = existingInduk.some(
|
||||
// (induk) =>
|
||||
// induk.NomorInduk.toLowerCase() === data.NomorInduk.toLowerCase()
|
||||
// );
|
||||
|
||||
// if (indukExists) {
|
||||
// setShowAlert(true);
|
||||
// newErrors.NomorInduk = "Nomor Induk sudah ada";
|
||||
// // Auto-hide alert after 5 seconds
|
||||
// setTimeout(() => setShowAlert(false), 5000);
|
||||
// }
|
||||
|
||||
// if (titleExists) {
|
||||
// setShowAlert(true);
|
||||
// newErrors.NamaPerusahaan = "Nama Perusahaan sudah ada";
|
||||
// // Auto-hide alert after 5 seconds
|
||||
// setTimeout(() => setShowAlert(false), 5000);
|
||||
// }
|
||||
|
||||
// if (!data.PerusahaanId) {
|
||||
// newErrors.PerusahaanId = "Perusahaan harus diisi";
|
||||
// }
|
||||
if (!data.JenisKegiatanId) {
|
||||
newErrors.JenisKegiatanId = "Jenis Kegiatan harus dipilih";
|
||||
}
|
||||
if (!data.VerifikatorId) {
|
||||
newErrors.VerifikatorId = "Admin harus dipilih";
|
||||
}
|
||||
if (!data.Email) {
|
||||
newErrors.Email = "Email harus diisi";
|
||||
}
|
||||
|
||||
setErrors(newErrors);
|
||||
return Object.keys(newErrors).length === 0;
|
||||
};
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!validateForm()) {
|
||||
toast({
|
||||
title: "Validasi Gagal",
|
||||
description: "Silakan periksa kembali form anda",
|
||||
variant: "destructive",
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
setLoading(true);
|
||||
|
||||
const formData = new FormData();
|
||||
|
||||
formData.append("NomorInduk", data.NomorInduk);
|
||||
formData.append("JenisKegiatanId", data.JenisKegiatanId);
|
||||
formData.append("VerifikatorId", data.VerifikatorId);
|
||||
formData.append("KelurahanId", data.KelurahanId);
|
||||
formData.append("NamaPerusahaan", data.NamaPerusahaan);
|
||||
formData.append("Alamat", data.Alamat);
|
||||
formData.append("KodePos", data.KodePos);
|
||||
formData.append("Telepon", data.Telepon);
|
||||
formData.append("Fax", data.Fax);
|
||||
formData.append("Email", data.Email);
|
||||
formData.append("Lintang", data.Lintang);
|
||||
formData.append("Bujur", data.Bujur);
|
||||
formData.append("CPNama", data.CPNama);
|
||||
formData.append("CPTelepon", data.CPTelepon);
|
||||
formData.append("JenisDokILId", data.JenisDokILId);
|
||||
formData.append("IsPublish", data.IsPublish.toString());
|
||||
formData.append("ILNomor", data.ILNomor);
|
||||
formData.append("ILTanggal", data.ILTanggal);
|
||||
formData.append("ReportLocked", data.ReportLocked.toString());
|
||||
|
||||
if (data.ILDokumen) {
|
||||
formData.append("ILDokumen", data.ILDokumen);
|
||||
}
|
||||
|
||||
post("/admin/perusahaan", {
|
||||
data: formData,
|
||||
forceFormData: true,
|
||||
onSuccess: (response) => {
|
||||
toast({
|
||||
title: "Berhasil",
|
||||
description: "Perusahaan berhasil ditambahkan",
|
||||
variant: "default",
|
||||
});
|
||||
reset();
|
||||
onSuccess();
|
||||
onClose();
|
||||
},
|
||||
onError: (error) => {
|
||||
console.error("Error:", error);
|
||||
toast({
|
||||
title: "Gagal",
|
||||
description:
|
||||
"Terjadi kesalahan saat menambahkan perusahaan",
|
||||
variant: "destructive",
|
||||
});
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onClose}>
|
||||
<DialogContent className="max-w-4xl max-h-[90vh] overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Tambah Perusahaan Baru</DialogTitle>
|
||||
<DialogDescription>
|
||||
Masukkan informasi perusahaan yang akan ditambahkan ke
|
||||
dalam sistem.
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<form onSubmit={handleSubmit} className="space-y-6">
|
||||
<div className="flex flex-row gap-6">
|
||||
{/* Sisi Kiri */}
|
||||
<div className="w-1/2">
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-row bg-green-200 px-2 py-1 rounded w-fit gap-2 items-center">
|
||||
<Hotel size={16} />
|
||||
<h3 className="text-sm ">
|
||||
Data Perusahaan
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="NomorInduk">
|
||||
Nomor Induk
|
||||
</Label>
|
||||
<Input
|
||||
id="NomorInduk"
|
||||
value={data.NomorInduk}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
NomorInduk: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
{errors.NomorInduk && (
|
||||
<p className="text-red-500 text-sm">
|
||||
{errors.NomorInduk}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="NamaPerusahaan">
|
||||
Nama Perusahaan *
|
||||
</Label>
|
||||
<Input
|
||||
id="NamaPerusahaan"
|
||||
required
|
||||
value={data.NamaPerusahaan}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
NamaPerusahaan: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
{errors.PerusahaanId && (
|
||||
<p className="text-red-500 text-sm">
|
||||
{errors.PerusahaanId}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="NamaPerusahaan">
|
||||
Jenis Kegiatan *
|
||||
</Label>
|
||||
<Select
|
||||
id="JenisKegiatanId"
|
||||
options={jenisKegiatanOptions}
|
||||
required
|
||||
placeholder="Pilih Jenis Kegiatan"
|
||||
onChange={(option) =>
|
||||
setData({
|
||||
...data,
|
||||
JenisKegiatanId:
|
||||
option?.value?.toString() ||
|
||||
"",
|
||||
})
|
||||
}
|
||||
/>
|
||||
{errors.JenisKegiatanId && (
|
||||
<p className="text-red-500 text-sm">
|
||||
{errors.JenisKegiatanId}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="NamaUnitKerja">
|
||||
Admin *
|
||||
</Label>
|
||||
<Select
|
||||
id="VerifikatorId"
|
||||
options={verifikatorOptions}
|
||||
required
|
||||
placeholder="Pilih Admin"
|
||||
onChange={(option) =>
|
||||
setData({
|
||||
...data,
|
||||
VerifikatorId:
|
||||
option?.value?.toString() ||
|
||||
"",
|
||||
})
|
||||
}
|
||||
/>
|
||||
{errors.VerifikatorId && (
|
||||
<p className="text-red-500 text-sm">
|
||||
{errors.VerifikatorId}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="PerusahaanId">
|
||||
Pengelola Kawasan
|
||||
</Label>
|
||||
<Select
|
||||
id="PerusahaanId"
|
||||
options={perusahaanOptions}
|
||||
placeholder="Pilih Pengelola Kawasan"
|
||||
onChange={(option) =>
|
||||
setData({
|
||||
...data,
|
||||
PerusahaanId:
|
||||
option?.value?.toString() ||
|
||||
"",
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Separator />
|
||||
|
||||
{/* Kontak Person */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-row bg-green-200 px-2 py-1 rounded w-fit gap-2 items-center">
|
||||
<UsersRound size={16} />
|
||||
<h3 className="text-sm ">
|
||||
Kontak Person
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="CPNama">Nama</Label>
|
||||
<Input
|
||||
id="CPNama"
|
||||
value={data.CPNama}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
CPNama: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="CPTelepon">
|
||||
Telepon
|
||||
</Label>
|
||||
<Input
|
||||
id="CPTelepon"
|
||||
value={data.CPTelepon}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
CPTelepon: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Separator className="space-y-4" />
|
||||
|
||||
{/* Dokumen Izin */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-row bg-green-200 px-2 py-1 rounded w-fit gap-2 items-center">
|
||||
<FileText size={16} />
|
||||
<h3 className="text-sm ">
|
||||
Dokumen Izin
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="ILNomor">Nomor</Label>
|
||||
<Input
|
||||
id="ILNomor"
|
||||
value={data.ILNomor}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
ILNomor: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="ILTanggal">
|
||||
Tanggal
|
||||
</Label>
|
||||
<Input
|
||||
id="ILTanggal"
|
||||
type="date"
|
||||
value={data.ILTanggal}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
ILTanggal: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="JenisDokILId">
|
||||
Jenis Dokumen
|
||||
</Label>
|
||||
<Select
|
||||
id="JenisDokILId"
|
||||
options={jenisDokILOptions}
|
||||
placeholder="Pilih Jenis Dokumen"
|
||||
onChange={(option) =>
|
||||
setData({
|
||||
...data,
|
||||
JenisDokILId:
|
||||
option?.value?.toString() ||
|
||||
"",
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="ILDokumen">
|
||||
Unggah Dokumen
|
||||
</Label>
|
||||
<Input
|
||||
id="ILDokumen"
|
||||
type="file"
|
||||
accept="pdf"
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
ILDokumen: e.target.files
|
||||
? e.target.files[0]
|
||||
: null,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Sisi Kanan */}
|
||||
<div className="w-1/2">
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-row bg-green-200 px-2 py-1 rounded w-fit gap-2 items-center">
|
||||
<MapPinned size={16} />
|
||||
<h3 className="text-sm ">
|
||||
Alamat Perusahaan
|
||||
</h3>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="KelurahanId">
|
||||
Kabupaten/Kota *
|
||||
</Label>
|
||||
<Select
|
||||
id="KabupatenId"
|
||||
options={kabupatenOptions}
|
||||
value={selectedKabupaten}
|
||||
onChange={(value) => {
|
||||
setSelectedKabupaten(value);
|
||||
setSelectedKecamatan(null);
|
||||
setSelectedKelurahan(null);
|
||||
}}
|
||||
placeholder="Pilih Kabupaten/Kota"
|
||||
isSearchable
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="KelurahanId">
|
||||
Kecamatan *
|
||||
</Label>
|
||||
<Select
|
||||
id="KecamatanId"
|
||||
options={kecamatanOptions}
|
||||
value={selectedKecamatan}
|
||||
onChange={(value) => {
|
||||
setSelectedKecamatan(value);
|
||||
setSelectedKelurahan(null);
|
||||
}}
|
||||
placeholder="Pilih Kecamatan"
|
||||
isSearchable
|
||||
isDisabled={!selectedKabupaten}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="KelurahanId">
|
||||
Kelurahan *
|
||||
</Label>
|
||||
<Select
|
||||
id="KelurahanId"
|
||||
options={kelurahanOptions}
|
||||
value={selectedKelurahan}
|
||||
onChange={(value) => {
|
||||
setSelectedKelurahan(value);
|
||||
setData({
|
||||
...data,
|
||||
KelurahanId:
|
||||
value?.value?.toString() ||
|
||||
"",
|
||||
});
|
||||
}}
|
||||
placeholder="Pilih Kelurahan"
|
||||
isSearchable
|
||||
isDisabled={!selectedKecamatan}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="Alamat">Alamat</Label>
|
||||
<Textarea
|
||||
id="Alamat"
|
||||
value={data.Alamat}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
Alamat: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="KodePos">Kode Pos</Label>
|
||||
<Input
|
||||
id="KodePos"
|
||||
value={data.KodePos}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
KodePos: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="Telepon">Telepon</Label>
|
||||
<Input
|
||||
id="Telepon"
|
||||
value={data.Telepon}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
Telepon: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="Fax">Fax</Label>
|
||||
<Input
|
||||
id="Fax"
|
||||
value={data.Fax}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
Fax: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="Email">Email *</Label>
|
||||
<Input
|
||||
id="Email"
|
||||
type="email"
|
||||
required
|
||||
value={data.Email}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
Email: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Separator className="space-y-4" />
|
||||
|
||||
{/* Kontak dan Koordinat */}
|
||||
<div className="space-y-4">
|
||||
<div className="flex flex-row bg-green-200 px-2 py-1 rounded w-fit gap-2 items-center">
|
||||
<LocateFixed size={16} />
|
||||
<h3 className="text-sm ">Koordinat</h3>
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="Lintang">Lintang</Label>
|
||||
<Input
|
||||
id="Lintang"
|
||||
value={data.Lintang}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
Lintang: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="Bujur">Bujur</Label>
|
||||
<Input
|
||||
id="Bujur"
|
||||
value={data.Bujur}
|
||||
onChange={(e) =>
|
||||
setData({
|
||||
...data,
|
||||
Bujur: e.target.value,
|
||||
})
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Separator className="space-y-4" />
|
||||
|
||||
<div className="flex flex-row bg-green-200 px-2 py-1 rounded w-fit gap-2 items-center">
|
||||
<VerifiedIcon size={16} />
|
||||
<h3 className="text-sm">Status</h3>
|
||||
</div>
|
||||
<div className="flex space-x-4">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="IsPublishActive"
|
||||
checked={data.IsPublish === true}
|
||||
onCheckedChange={() =>
|
||||
setData({
|
||||
...data,
|
||||
IsPublish: true,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Label htmlFor="IsPublishActive">
|
||||
Aktif
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center space-x-2">
|
||||
<Checkbox
|
||||
id="IsPublishInactive"
|
||||
checked={data.IsPublish === false}
|
||||
onCheckedChange={() =>
|
||||
setData({
|
||||
...data,
|
||||
IsPublish: false,
|
||||
})
|
||||
}
|
||||
/>
|
||||
<Label htmlFor="IsPublishInactive">
|
||||
Non Aktif
|
||||
</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Separator className="space-y-4" />
|
||||
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={onClose}
|
||||
disabled={loading}
|
||||
>
|
||||
Batal
|
||||
</Button>
|
||||
<Button type="submit" disabled={loading}>
|
||||
{loading ? "Menyimpan..." : "Simpan"}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
import * as React from "react"
|
||||
import * as CheckboxPrimitive from "@radix-ui/react-checkbox"
|
||||
import { Check } from "lucide-react"
|
||||
|
||||
import { cn } from "@/lib/utils"
|
||||
|
||||
const Checkbox = React.forwardRef<
|
||||
React.ElementRef<typeof CheckboxPrimitive.Root>,
|
||||
React.ComponentPropsWithoutRef<typeof CheckboxPrimitive.Root>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<CheckboxPrimitive.Root
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"peer h-4 w-4 shrink-0 rounded-sm border border-primary shadow focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator
|
||||
className={cn("flex items-center justify-center text-current")}
|
||||
>
|
||||
<Check className="h-4 w-4" />
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</CheckboxPrimitive.Root>
|
||||
))
|
||||
Checkbox.displayName = CheckboxPrimitive.Root.displayName
|
||||
|
||||
export { Checkbox }
|
|
@ -0,0 +1,135 @@
|
|||
import React, { useState } from "react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { FileUp, X } from "lucide-react";
|
||||
|
||||
interface UploadDocProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onUpload: (file: File) => void;
|
||||
}
|
||||
|
||||
const UploadDoc = ({ isOpen, onClose, onUpload }: UploadDocProps) => {
|
||||
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
||||
const [dragActive, setDragActive] = useState<boolean>(false);
|
||||
|
||||
const handleDrag = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
if (e.type === "dragenter" || e.type === "dragover") {
|
||||
setDragActive(true);
|
||||
} else if (e.type === "dragleave") {
|
||||
setDragActive(false);
|
||||
}
|
||||
};
|
||||
|
||||
const handleDrop = (e: React.DragEvent) => {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
setDragActive(false);
|
||||
|
||||
const file = e.dataTransfer.files[0];
|
||||
if (file && file.type === "application/pdf") {
|
||||
setSelectedFile(file);
|
||||
} else {
|
||||
alert("Hanya file PDF yang diperbolehkan");
|
||||
}
|
||||
};
|
||||
|
||||
const handleFileChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const file = e.target.files?.[0];
|
||||
if (file && file.type === "application/pdf") {
|
||||
setSelectedFile(file);
|
||||
} else {
|
||||
alert("Hanya file PDF yang diperbolehkan");
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpload = () => {
|
||||
if (selectedFile) {
|
||||
onUpload(selectedFile);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>Lampiran *</DialogTitle>
|
||||
<div className="flex flex-row items-center">
|
||||
<div className="h-8 w-[3px] bg-gray-300 mr-2" />
|
||||
<small>
|
||||
Silahkan unggah dokumen Anda dengan drag & drop
|
||||
semua berkas atau klik pada kotak ini. Ukuran file
|
||||
tidak boleh melebihi 20 MB.
|
||||
</small>
|
||||
</div>
|
||||
</DialogHeader>
|
||||
<div
|
||||
className={`mt-4 p-6 border-2 border-dashed rounded-lg text-center ${
|
||||
dragActive
|
||||
? "border-teal-500 bg-teal-50"
|
||||
: "border-gray-300"
|
||||
}`}
|
||||
onDragEnter={handleDrag}
|
||||
onDragLeave={handleDrag}
|
||||
onDragOver={handleDrag}
|
||||
onDrop={handleDrop}
|
||||
>
|
||||
<FileUp className="mx-auto h-12 w-12 text-gray-400" />
|
||||
<div className="mt-4">
|
||||
<p className="text-sm text-gray-600">
|
||||
Drag and drop file PDF di sini atau
|
||||
</p>
|
||||
<label className="mt-2 cursor-pointer">
|
||||
<Input
|
||||
type="file"
|
||||
accept=".pdf"
|
||||
className="hidden"
|
||||
onChange={handleFileChange}
|
||||
/>
|
||||
<span className="text-teal-600 hover:text-teal-500">
|
||||
Pilih file
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
{selectedFile && (
|
||||
<div className="mt-4 p-2 bg-gray-50 rounded flex items-center justify-between">
|
||||
<span className="text-sm text-gray-600">
|
||||
{selectedFile.name}
|
||||
</span>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={() => setSelectedFile(null)}
|
||||
>
|
||||
<X className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="mt-4 flex justify-end gap-3">
|
||||
<Button variant="outline" onClick={onClose}>
|
||||
Batal
|
||||
</Button>
|
||||
<Button
|
||||
onClick={handleUpload}
|
||||
disabled={!selectedFile}
|
||||
className="bg-teal-600 hover:bg-teal-700"
|
||||
>
|
||||
Unggah
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default UploadDoc;
|
|
@ -14,6 +14,7 @@ import {
|
|||
BreadcrumbPage,
|
||||
} from "@/components/ui/breadcrumb";
|
||||
import AppearanceDropdown from "@/components/appearance-dropdown";
|
||||
import { Toaster } from "@/components/ui/toaster";
|
||||
|
||||
export default function AuthenticatedLayout({
|
||||
header,
|
||||
|
@ -48,6 +49,7 @@ export default function AuthenticatedLayout({
|
|||
|
||||
<main className="p-4 md:pt-0 h-full w-full max-w-full overflow-x-hidden">
|
||||
{children}
|
||||
<Toaster />
|
||||
</main>
|
||||
<footer className="text-sm p-4 text-center bg-background border-t">
|
||||
© Copyright {new Date().getFullYear()} Bidang Tata
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
import React from "react";
|
||||
import { Head, Link } from "@inertiajs/react";
|
||||
import "../../css/app.css";
|
||||
|
||||
const NotFound = () => {
|
||||
return (
|
||||
<>
|
||||
<Head title="Not Found" />
|
||||
<div className="flex flex-col items-center justify-center h-screen bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-white">
|
||||
<h1 className="text-6xl font-bold">404</h1>
|
||||
<p className="text-lg mt-4">
|
||||
Oops! Halaman yang kamu cari tidak ditemukan.
|
||||
</p>
|
||||
<Link
|
||||
href="/dashboard"
|
||||
className="mt-6 px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition"
|
||||
>
|
||||
Kembali ke Dashboard
|
||||
</Link>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotFound;
|
|
@ -43,7 +43,6 @@ import {
|
|||
|
||||
interface JenisDokIL {
|
||||
JenisDokILId: number | null;
|
||||
KodeJenisDokIL: string;
|
||||
NamaJenisDokIL: string;
|
||||
}
|
||||
|
||||
|
@ -61,7 +60,6 @@ export default function JenisDokILIndex({
|
|||
reset,
|
||||
} = useForm<JenisDokIL>({
|
||||
JenisDokILId: null,
|
||||
KodeJenisDokIL: "",
|
||||
NamaJenisDokIL: "",
|
||||
});
|
||||
|
||||
|
@ -79,14 +77,8 @@ export default function JenisDokILIndex({
|
|||
let filtered = jenisdokil;
|
||||
|
||||
if (search) {
|
||||
filtered = filtered.filter(
|
||||
(item) =>
|
||||
item.KodeJenisDokIL.toLowerCase().includes(
|
||||
search.toLowerCase()
|
||||
) ||
|
||||
item.NamaJenisDokIL.toLowerCase().includes(
|
||||
search.toLowerCase()
|
||||
)
|
||||
filtered = filtered.filter((item) =>
|
||||
item.NamaJenisDokIL.toLowerCase().includes(search.toLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -227,21 +219,6 @@ export default function JenisDokILIndex({
|
|||
onSubmit={handleSubmit}
|
||||
className="space-y-4"
|
||||
>
|
||||
<div className="space-y-2">
|
||||
<label>
|
||||
Kode Jenis Dokumen
|
||||
</label>
|
||||
<Input
|
||||
value={data.KodeJenisDokIL}
|
||||
onChange={(e) =>
|
||||
setData(
|
||||
"KodeJenisDokIL",
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
placeholder="Masukkan kode jenis dokumen"
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<label>
|
||||
Nama Jenis Dokumen
|
||||
|
@ -275,7 +252,6 @@ export default function JenisDokILIndex({
|
|||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead>No</TableHead>
|
||||
<TableHead>Kode</TableHead>
|
||||
<TableHead>Nama Jenis Dokumen</TableHead>
|
||||
<TableHead>Aksi</TableHead>
|
||||
</TableRow>
|
||||
|
@ -286,9 +262,6 @@ export default function JenisDokILIndex({
|
|||
<TableCell>
|
||||
{startIndex + index + 1}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{item.KodeJenisDokIL}
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
{item.NamaJenisDokIL}
|
||||
</TableCell>
|
||||
|
|
|
@ -0,0 +1,367 @@
|
|||
import React, { useState } from "react";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { CircleHelp, FileDown, Printer } from "lucide-react";
|
||||
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||
import { Head } from "@inertiajs/react";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import {
|
||||
Tooltip,
|
||||
TooltipContent,
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip";
|
||||
import UploadDoc from "@/components/upload_doc";
|
||||
|
||||
interface PelaporanALFormProps {
|
||||
onSubmit: (data: any) => void;
|
||||
}
|
||||
|
||||
const PelaporanALForm = ({ onSubmit }: PelaporanALFormProps) => {
|
||||
const [isConnected, setIsConnected] = useState<boolean | null>(null);
|
||||
const [isUploadModalOpen, setIsUploadModalOpen] = useState(false);
|
||||
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
||||
|
||||
return (
|
||||
<AuthenticatedLayout header="Pelaporan Air Limbah">
|
||||
<Head title="Pelaporan Air Limbah" />
|
||||
<div className="p-4 space-y-6">
|
||||
<Card className="shadow-lg">
|
||||
<CardHeader className="bg-[#166534] text-white rounded-t-lg">
|
||||
<div className="flex justify-between items-center">
|
||||
<div>
|
||||
<CardTitle className="text-xl">
|
||||
Laporan Pengelolaan Air Limbah - Ujicoba
|
||||
</CardTitle>
|
||||
<p className="text-sm opacity-90">
|
||||
Tahun 2025 - Periode Triwulan 1
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => window.history.back()}
|
||||
>
|
||||
Kembali
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => window.print()}
|
||||
>
|
||||
<Printer />
|
||||
</Button>
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
// Add PDF download logic here
|
||||
}}
|
||||
>
|
||||
<FileDown />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent className="p-6">
|
||||
<div className="space-y-6">
|
||||
{/* Header Section */}
|
||||
<div className="flex justify-between items-center gap-4 mb-6">
|
||||
<div className="flex items-center gap-2">
|
||||
<h2>Tersambung IPAL Komunal:</h2>
|
||||
<div className="flex flex-row gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="radio"
|
||||
id="tersambung"
|
||||
name="connection"
|
||||
className="w-4 h-4 text-teal-600"
|
||||
onChange={() =>
|
||||
setIsConnected(true)
|
||||
}
|
||||
checked={isConnected === true}
|
||||
/>
|
||||
<Label htmlFor="tersambung">
|
||||
Ya
|
||||
</Label>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<input
|
||||
type="radio"
|
||||
id="tidak"
|
||||
name="connection"
|
||||
className="w-4 h-4 text-teal-600"
|
||||
onChange={() =>
|
||||
setIsConnected(false)
|
||||
}
|
||||
checked={isConnected === false}
|
||||
/>
|
||||
<Label htmlFor="tidak">Tidak</Label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
{isConnected && (
|
||||
<div className="flex items-center gap-2">
|
||||
<h2>
|
||||
Lampiran
|
||||
<span className="text-red-600">
|
||||
*
|
||||
</span>{" "}
|
||||
:
|
||||
</h2>
|
||||
<Button
|
||||
variant="outline"
|
||||
className="w-48"
|
||||
onClick={() =>
|
||||
setIsUploadModalOpen(true)
|
||||
}
|
||||
>
|
||||
{uploadedFile
|
||||
? uploadedFile.name
|
||||
: "Unggah Surat Kerjasama"}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UploadDoc
|
||||
isOpen={isUploadModalOpen}
|
||||
onClose={() => setIsUploadModalOpen(false)}
|
||||
onUpload={(file) => {
|
||||
setUploadedFile(file);
|
||||
// Handle the file upload logic here
|
||||
console.log("File uploaded:", file);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* Main Form Table */}
|
||||
<div className="border rounded-lg overflow-hidden">
|
||||
<table className="w-full">
|
||||
<thead>
|
||||
<tr className="bg-[#166534] text-white">
|
||||
<th className="p-3 text-left w-16">
|
||||
No
|
||||
</th>
|
||||
<th className="p-3 text-left">
|
||||
Komponen
|
||||
</th>
|
||||
<th className="p-3 text-left w-32">
|
||||
Hasil
|
||||
</th>
|
||||
<th className="p-3 text-left w-24">
|
||||
Nilai
|
||||
</th>
|
||||
<th className="p-3 text-left w-32">
|
||||
Lampiran
|
||||
</th>
|
||||
<th className="p-3 text-left w-32">
|
||||
Verifikasi
|
||||
</th>
|
||||
<th className="p-3 text-left w-32">
|
||||
Keterangan
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{/* Section 1 */}
|
||||
<tr className="bg-teal-50">
|
||||
<td className="p-3">I</td>
|
||||
<td
|
||||
className="p-3 font-medium"
|
||||
colSpan={1}
|
||||
>
|
||||
Nilai tingkat ketaatan
|
||||
pengelolaan air limbah (IKLl)
|
||||
(%)
|
||||
</td>
|
||||
<td className="p-3"></td>
|
||||
<td className="p-3">100</td>
|
||||
<td className="p-3"></td>
|
||||
<td className="p-3"></td>
|
||||
<td className="p-3"></td>
|
||||
</tr>
|
||||
|
||||
{/* Technical Requirements */}
|
||||
{[
|
||||
"Instalasi pengolah air limbah",
|
||||
"Flowmeter",
|
||||
"Titik pengambilan sampel",
|
||||
"Saluran air limbah & air hujan terpisah",
|
||||
"Izin pembuangan air limbah",
|
||||
].map((item, index) => (
|
||||
<tr
|
||||
key={index}
|
||||
className="border-t"
|
||||
>
|
||||
<td className="p-3">
|
||||
A{index + 1}
|
||||
</td>
|
||||
<td className="p-3">
|
||||
<div className="flex justify-between items-center w-full">
|
||||
<div>{item}</div>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<CircleHelp className="text-teal-600 w-5 h-5" />
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>
|
||||
Deskripsi
|
||||
untuk {item}
|
||||
</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-3">
|
||||
{" "}
|
||||
<Select>
|
||||
<SelectTrigger className="w-24">
|
||||
<SelectValue placeholder="Ada" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="ada">
|
||||
Ada
|
||||
</SelectItem>
|
||||
<SelectItem value="tidak">
|
||||
Tidak Ada
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</td>
|
||||
<td className="p-3">100</td>
|
||||
<td className="p-3"></td>
|
||||
<td className="p-3"></td>
|
||||
<td className="p-3"></td>
|
||||
</tr>
|
||||
))}
|
||||
|
||||
{/* Implementation Section */}
|
||||
{[
|
||||
{
|
||||
name: "Pengujian air limbah",
|
||||
hasAttachment: true,
|
||||
},
|
||||
{
|
||||
name: "Pemenuhan baku mutu air limbah",
|
||||
hasAttachment: false,
|
||||
},
|
||||
{
|
||||
name: "Pelaksanaan dan pemutusan wewenang",
|
||||
hasSelect: true,
|
||||
},
|
||||
{
|
||||
name: "Pembuatan neraca air",
|
||||
hasSelect: true,
|
||||
},
|
||||
{
|
||||
name: "Sertifikasi kompetensi",
|
||||
hasSelect: true,
|
||||
},
|
||||
].map((item, index) => (
|
||||
<tr
|
||||
key={index}
|
||||
className="border-t"
|
||||
>
|
||||
<td className="p-3">
|
||||
A{index + 6}
|
||||
</td>
|
||||
<td className="p-3">
|
||||
<div className="flex justify-between items-center w-full">
|
||||
<div>{item.name}</div>
|
||||
<CircleHelp className="text-teal-600 w-5 h-5" />
|
||||
</div>
|
||||
</td>
|
||||
<td className="p-3">
|
||||
<Select>
|
||||
<SelectTrigger className="w-24">
|
||||
<SelectValue placeholder="Ada" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="ada">
|
||||
Ada
|
||||
</SelectItem>
|
||||
<SelectItem value="tidak">
|
||||
Tidak Ada
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</td>
|
||||
<td className="p-3">100</td>
|
||||
<td className="p-3">
|
||||
{item.hasAttachment && (
|
||||
<span className="text-teal-600">
|
||||
Data...
|
||||
</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="p-3"></td>
|
||||
<td className="p-3"></td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
{/* Notes Section */}
|
||||
<div className="bg-gray-50 p-6 rounded-lg border border-gray-200 mt-6">
|
||||
<h2 className="text-lg font-semibold mb-3 text-gray-800">
|
||||
Catatan
|
||||
</h2>
|
||||
<Textarea
|
||||
placeholder="Tambahkan catatan atau komentar tambahan di sini..."
|
||||
className="min-h-[100px] bg-white"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Agreement & Submit Section */}
|
||||
<div className="mt-8 space-y-6">
|
||||
<div className="flex items-start gap-3 p-4 bg-green-50 rounded-lg border border-teal-200">
|
||||
<Checkbox id="agreement" className="mt-1" />
|
||||
<Label
|
||||
htmlFor="agreement"
|
||||
className="text-sm leading-relaxed text-gray-700"
|
||||
>
|
||||
Dengan mencentang ini, kami menyatakan
|
||||
bahwa laporan ini telah disusun
|
||||
berdasarkan ketentuan peraturan yang
|
||||
berlaku dan kami bersedia bertanggung
|
||||
jawab atas kebenaran data-data yang kami
|
||||
kirimkan sesuai dengan fakta dilapangan.
|
||||
</Label>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end gap-4">
|
||||
<Button
|
||||
variant="outline"
|
||||
className="hover:bg-gray-100"
|
||||
>
|
||||
Simpan Draft
|
||||
</Button>
|
||||
<Button className="bg-[#166534] hover:bg-green-700 transition-colors px-8">
|
||||
Kirim Laporan
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export default PelaporanALForm;
|
|
@ -1,5 +1,5 @@
|
|||
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||
import { Head } from "@inertiajs/react";
|
||||
import { Head, Link } from "@inertiajs/react";
|
||||
import React from "react";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
|
@ -288,12 +288,16 @@ export default function PelaporanIndex() {
|
|||
>
|
||||
00.00
|
||||
</TableCell>
|
||||
|
||||
<TableCell
|
||||
colSpan={1}
|
||||
className="text-center text-muted-foreground border-r border-l bg-red-100 text-red-700"
|
||||
>
|
||||
00.00
|
||||
<Link href="/admin/pelaporan/al">
|
||||
00.00
|
||||
</Link>
|
||||
</TableCell>
|
||||
|
||||
<TableCell
|
||||
colSpan={1}
|
||||
className="text-center text-muted-foreground border-r border-l bg-red-100 text-red-700"
|
||||
|
|
|
@ -0,0 +1,637 @@
|
|||
import AuthenticatedLayout from "@/layouts/authenticated-layout";
|
||||
import { Head } from "@inertiajs/react";
|
||||
import React from "react";
|
||||
import { useState } from "react";
|
||||
import {
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableHead,
|
||||
TableHeader,
|
||||
TableRow,
|
||||
} from "@/components/ui/table";
|
||||
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
BadgeCheck,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
ChevronUp,
|
||||
Download,
|
||||
FileText,
|
||||
Key,
|
||||
LockKeyhole,
|
||||
Plus,
|
||||
Search,
|
||||
TouchpadOff,
|
||||
Upload,
|
||||
} from "lucide-react";
|
||||
|
||||
import Select from "react-select";
|
||||
import { AddPerusahaanModal } from "@/components/modals/add-perusahaan-modal";
|
||||
import {
|
||||
Perusahaan,
|
||||
JenisDokIL,
|
||||
JenisKegiatan,
|
||||
Verifikator,
|
||||
Kabupaten,
|
||||
Kecamatan,
|
||||
Kelurahan,
|
||||
Kawasan,
|
||||
} from "@/types/perusahaan";
|
||||
|
||||
export default function PerusahaanIndex({
|
||||
perusahaan,
|
||||
jenisKegiatan,
|
||||
jenisDokIL,
|
||||
verifikator,
|
||||
kabupaten,
|
||||
kecamatan,
|
||||
kelurahan,
|
||||
}: {
|
||||
perusahaan: Perusahaan[];
|
||||
jenisKegiatan: JenisKegiatan[];
|
||||
jenisDokIL: JenisDokIL[];
|
||||
verifikator: Verifikator[];
|
||||
kabupaten: Kabupaten[];
|
||||
kecamatan: Kecamatan[];
|
||||
kelurahan: Kelurahan[];
|
||||
}) {
|
||||
console.log("Perusahaan data:", perusahaan);
|
||||
// console.log("Jenis Kegiatan data:", jenisKegiatan);
|
||||
// console.log("Jenis Dok IL data:", jenisDokIL);
|
||||
// console.log("Verifikator data:", verifikator);
|
||||
// console.log("Kabupaten data:", kabupaten);
|
||||
// console.log("Kecamatan data:", kecamatan);
|
||||
// console.log("Kelurahan data:", kelurahan);
|
||||
|
||||
const [year, setYear] = useState<string>("2025");
|
||||
const [quarter, setQuarter] = useState<string>("Triwulan 1");
|
||||
|
||||
const [selectedJenisKegiatan, setSelectedJenisKegiatan] = useState<{
|
||||
value: string;
|
||||
label: string;
|
||||
} | null>(null);
|
||||
|
||||
const [selectedJenisDokIL, setSelectedJenisDokIL] = useState<{
|
||||
value: string;
|
||||
label: string;
|
||||
} | null>(null);
|
||||
|
||||
const [selectedVerifikator, setSelectedVerifikator] = useState<{
|
||||
value: string;
|
||||
label: string;
|
||||
} | null>(null);
|
||||
|
||||
const [selectedKabupaten, setSelectedKabupaten] = useState<{
|
||||
value: number;
|
||||
label: string;
|
||||
} | null>(null);
|
||||
|
||||
const [selectedKecamatan, setSelectedKecamatan] = useState<{
|
||||
value: number;
|
||||
label: string;
|
||||
} | null>(null);
|
||||
|
||||
const [selectedKelurahan, setSelectedKelurahan] = useState<{
|
||||
value: number;
|
||||
label: string;
|
||||
} | null>(null);
|
||||
|
||||
const jenisKegiatanOptions = jenisKegiatan.map((jk) => ({
|
||||
value: jk.JenisKegiatanId.toString(),
|
||||
label: jk.NamaJenisKegiatan,
|
||||
}));
|
||||
|
||||
const jenisDokILOptions = jenisDokIL.map((jdi) => ({
|
||||
value: jdi.JenisDokILId.toString(),
|
||||
label: jdi.NamaJenisDokIL,
|
||||
}));
|
||||
|
||||
const verifikatorOptions = verifikator.map((v) => ({
|
||||
value: v.VerifikatorId.toString(),
|
||||
label: v.NamaUnitKerja,
|
||||
}));
|
||||
|
||||
const kabupatenOptions = kabupaten.map((k) => ({
|
||||
value: k.KabupatenId,
|
||||
label: k.NamaKabupaten,
|
||||
}));
|
||||
|
||||
const kecamatanOptions = kecamatan
|
||||
.filter(
|
||||
(kec) =>
|
||||
selectedKabupaten && kec.KabupatenId === selectedKabupaten.value
|
||||
)
|
||||
.map((kec) => ({
|
||||
value: kec.KecamatanId,
|
||||
label: kec.NamaKecamatan,
|
||||
}));
|
||||
|
||||
const kelurahanOptions = kelurahan
|
||||
.filter(
|
||||
(kel) =>
|
||||
selectedKecamatan && kel.KecamatanId === selectedKecamatan.value
|
||||
)
|
||||
.map((kel) => ({
|
||||
value: kel.KelurahanId,
|
||||
label: kel.NamaKelurahan,
|
||||
}));
|
||||
|
||||
// const companyOptions = [
|
||||
// { value: "PT Ajinomoto Indonesia", label: "PT Ajinomoto Indonesia" },
|
||||
// { value: "PT Unilever Indonesia", label: "PT Unilever Indonesia" },
|
||||
// {
|
||||
// value: "PT Indofood Sukses Makmur",
|
||||
// label: "PT Indofood Sukses Makmur",
|
||||
// },
|
||||
// { value: "PT Mayora Indah", label: "PT Mayora Indah" },
|
||||
// ];
|
||||
|
||||
type CompanyOption = {
|
||||
value: string;
|
||||
label: string;
|
||||
};
|
||||
|
||||
const companyOptions: CompanyOption[] = perusahaan.map((c) => ({
|
||||
value: c.PerusahaanId.toString(),
|
||||
label: c.NamaPerusahaan,
|
||||
}));
|
||||
|
||||
const [company, setCompany] = useState<CompanyOption | null>(
|
||||
companyOptions[0]
|
||||
);
|
||||
const [showAddModal, setShowAddModal] = useState(false);
|
||||
|
||||
const handleSuccess = () => {
|
||||
// Refresh data
|
||||
window.location.reload();
|
||||
};
|
||||
|
||||
// Color coding helper
|
||||
const getStatusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case "belum":
|
||||
return "bg-red-100 text-red-800";
|
||||
case "pending":
|
||||
return "bg-gray-100 text-gray-800";
|
||||
case "siap":
|
||||
return "bg-orange-100 text-orange-800";
|
||||
case "selesai":
|
||||
return "bg-green-100 text-green-800";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
};
|
||||
return (
|
||||
<AuthenticatedLayout header={"Daftar Perusahaan"}>
|
||||
<Head title="Daftar Perusahaan" />
|
||||
|
||||
<AddPerusahaanModal
|
||||
open={showAddModal}
|
||||
onClose={() => setShowAddModal(false)}
|
||||
onSuccess={handleSuccess}
|
||||
jenisKegiatan={jenisKegiatan}
|
||||
jenisDokIL={jenisDokIL}
|
||||
verifikator={verifikator}
|
||||
kabupaten={kabupaten}
|
||||
kecamatan={kecamatan}
|
||||
kelurahan={kelurahan}
|
||||
perusahaan={perusahaan}
|
||||
// existingPerusahaan={perusahaan.map(p => ({...p, PerusahaanId: p.PerusahaanId.toString()}))}
|
||||
// existingInduk={[]}
|
||||
// kawasan={kawasan}
|
||||
/>
|
||||
|
||||
{/* Filter Section */}
|
||||
<div className="bg-white p-6 rounded-lg shadow-sm mb-6">
|
||||
<div className="flex flex-col md:flex-row md:gap-6 py-4">
|
||||
<div className="w-1/2">
|
||||
<h2 className="text-lg font-semibold mb-4 text-gray-800">
|
||||
Filter Pencarian
|
||||
</h2>
|
||||
</div>
|
||||
<div className="w-1/2">
|
||||
<div className="flex md:mt-0 md:justify-end">
|
||||
<Button
|
||||
className="bg-blue-600 hover:bg-blue-950 text-white"
|
||||
onClick={() => setShowAddModal(true)}
|
||||
>
|
||||
<Plus className="w-4 h-4 mr-2" />
|
||||
Tambah Perusahaan
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{/* Left Column */}
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Perusahaan
|
||||
</label>
|
||||
<div className="col-span-2">
|
||||
<Select
|
||||
options={companyOptions}
|
||||
value={company}
|
||||
onChange={setCompany}
|
||||
placeholder="Pilih Perusahaan"
|
||||
isSearchable
|
||||
className="w-full"
|
||||
isClearable
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Kabupaten/Kota
|
||||
</label>
|
||||
<div className="col-span-2">
|
||||
<Select
|
||||
options={kabupatenOptions}
|
||||
value={selectedKabupaten}
|
||||
onChange={(value) => {
|
||||
setSelectedKabupaten(value);
|
||||
setSelectedKecamatan(null);
|
||||
setSelectedKelurahan(null);
|
||||
}}
|
||||
placeholder="Pilih Kabupaten/Kota"
|
||||
isSearchable
|
||||
className="w-full"
|
||||
isClearable
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Kecamatan
|
||||
</label>
|
||||
<div className="col-span-2">
|
||||
<Select
|
||||
options={kecamatanOptions}
|
||||
value={selectedKecamatan}
|
||||
onChange={(value) => {
|
||||
setSelectedKecamatan(value);
|
||||
setSelectedKelurahan(null);
|
||||
}}
|
||||
placeholder="Pilih Kecamatan"
|
||||
isSearchable
|
||||
className="w-full"
|
||||
isClearable
|
||||
isDisabled={!selectedKabupaten}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Kelurahan
|
||||
</label>
|
||||
<div className="col-span-2">
|
||||
<Select
|
||||
options={kelurahanOptions}
|
||||
value={selectedKelurahan}
|
||||
onChange={setSelectedKelurahan}
|
||||
placeholder="Pilih Kelurahan"
|
||||
isSearchable
|
||||
className="w-full"
|
||||
isClearable
|
||||
isDisabled={!selectedKecamatan}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Column */}
|
||||
<div className="space-y-4">
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Jenis Kegiatan
|
||||
</label>
|
||||
<div className="col-span-2">
|
||||
<Select
|
||||
options={jenisKegiatanOptions}
|
||||
value={selectedJenisKegiatan}
|
||||
onChange={setSelectedJenisKegiatan}
|
||||
placeholder="Pilih Jenis Kegiatan"
|
||||
isSearchable
|
||||
className="w-full"
|
||||
isClearable
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Admin
|
||||
</label>
|
||||
<div className="col-span-2">
|
||||
<Select
|
||||
options={verifikatorOptions}
|
||||
value={selectedVerifikator}
|
||||
onChange={setSelectedVerifikator}
|
||||
placeholder="Pilih Admin"
|
||||
isSearchable
|
||||
className="w-full"
|
||||
isClearable
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-3 items-center gap-4">
|
||||
<label className="text-sm font-medium text-gray-700">
|
||||
Jenis Izin
|
||||
</label>
|
||||
<div className="col-span-2">
|
||||
<Select
|
||||
options={jenisDokILOptions}
|
||||
value={selectedJenisDokIL}
|
||||
onChange={setSelectedJenisDokIL}
|
||||
placeholder="Pilih Jenis Izin"
|
||||
isSearchable
|
||||
className="w-full"
|
||||
isClearable
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end mt-6">
|
||||
<Button className="bg-green-600 hover:bg-green-700 text-white">
|
||||
<Search className="w-4 h-4 mr-2" />
|
||||
Cari
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Table Section */}
|
||||
<div className="w-full overflow-x-auto">
|
||||
<Table className="min-w-[1000px] border-collapse">
|
||||
<TableHeader>
|
||||
<TableRow className="border-b border-t bg-green-800">
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="w-[50px] border-r border-l text-white"
|
||||
>
|
||||
No.
|
||||
</TableHead>
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="border-r text-center text-white"
|
||||
>
|
||||
Nama Perusahaan
|
||||
</TableHead>
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="border-r text-center text-white"
|
||||
>
|
||||
Jenis Kegiatan
|
||||
</TableHead>
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="border-r text-center text-white"
|
||||
>
|
||||
Alamat
|
||||
</TableHead>
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="border-r text-center text-white"
|
||||
>
|
||||
Kelurahan
|
||||
</TableHead>
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="border-r text-center text-white"
|
||||
>
|
||||
Kecamatan
|
||||
</TableHead>
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="border-r text-center text-white"
|
||||
>
|
||||
Kota
|
||||
</TableHead>
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="border-r text-center text-white"
|
||||
>
|
||||
Kode Pos
|
||||
</TableHead>
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="border-r text-center text-white"
|
||||
>
|
||||
Telp
|
||||
</TableHead>
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="border-r text-center text-white"
|
||||
>
|
||||
Fax
|
||||
</TableHead>
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="border-r text-center text-white"
|
||||
>
|
||||
Email
|
||||
</TableHead>
|
||||
<TableHead
|
||||
className="border-r text-center text-white"
|
||||
colSpan={2}
|
||||
>
|
||||
Koordinat
|
||||
</TableHead>
|
||||
<TableHead
|
||||
className="border-r text-center text-white"
|
||||
colSpan={2}
|
||||
>
|
||||
Kontak Person
|
||||
</TableHead>
|
||||
<TableHead
|
||||
className="border-r text-center text-white"
|
||||
rowSpan={2}
|
||||
>
|
||||
Admin
|
||||
</TableHead>
|
||||
<TableHead
|
||||
className="border-r text-center text-white"
|
||||
colSpan={4}
|
||||
>
|
||||
Dokumen Izin / Persetujuan Lingkungan Bidang
|
||||
Tata Lingkungan
|
||||
</TableHead>
|
||||
|
||||
<TableHead
|
||||
rowSpan={2}
|
||||
className="border-r text-center text-white"
|
||||
>
|
||||
Status Laporan
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
<TableRow className="border-b bg-green-600">
|
||||
<TableHead className="text-center border-r text-white">
|
||||
Lintang
|
||||
</TableHead>
|
||||
<TableHead className="text-center border-r text-white">
|
||||
Bujur
|
||||
</TableHead>
|
||||
<TableHead className="text-center border-r text-white">
|
||||
Nama
|
||||
</TableHead>
|
||||
<TableHead className="text-center border-r text-white">
|
||||
Telepon
|
||||
</TableHead>
|
||||
<TableHead className="text-center border-r text-white">
|
||||
Nomor
|
||||
</TableHead>
|
||||
<TableHead className="text-center border-r text-white">
|
||||
Tanggal
|
||||
</TableHead>
|
||||
<TableHead className="text-center border-r text-white">
|
||||
Jenis
|
||||
</TableHead>
|
||||
<TableHead className="text-center border-r text-white">
|
||||
Dok
|
||||
</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody className="border-b">
|
||||
{perusahaan.length === 0 ? (
|
||||
<TableRow>
|
||||
<TableCell
|
||||
colSpan={20}
|
||||
className="text-center py-4"
|
||||
>
|
||||
Tidak ada data perusahaan
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
perusahaan.map((item, index) => {
|
||||
// Add console log to debug each perusahaan item
|
||||
// console.log(
|
||||
// "Perusahaan item:",
|
||||
// item.verifikator
|
||||
// );
|
||||
|
||||
return (
|
||||
<TableRow
|
||||
key={item.PerusahaanId}
|
||||
className="border-b"
|
||||
>
|
||||
<TableCell className="text-center border-r border-l">
|
||||
{index + 1}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.NamaPerusahaan || "N/A"}
|
||||
</TableCell>
|
||||
|
||||
<TableCell className="text-center border-r">
|
||||
{item.jenis_kegiatan
|
||||
?.NamaJenisKegiatan || "N/A"}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.Alamat}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.kelurahan?.NamaKelurahan ||
|
||||
"N/A"}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.kelurahan?.kecamatan
|
||||
?.NamaKecamatan || "N/A"}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.kelurahan?.kecamatan
|
||||
?.kabupaten?.NamaKabupaten ||
|
||||
"N/A"}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.KodePos}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.Telepon}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.Fax}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.Email}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.Lintang}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.Bujur}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.CPNama}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.CPTelepon}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.verifikator?.NamaUnitKerja ??
|
||||
"N/A"}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.ILNomor || "N/A"}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.ILTanggal
|
||||
? new Date(
|
||||
item.ILTanggal
|
||||
).toLocaleDateString("en-GB")
|
||||
: "N/A"}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.jenis_dok_i_l
|
||||
?.NamaJenisDokIL || "N/A"}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
{item.ILDokumen ? (
|
||||
<a
|
||||
href={`/storage/${item.ILDokumen}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="flex items-center justify-center"
|
||||
>
|
||||
<FileText className="w-4 h-4 text-green-600 hover:text-green-800" />
|
||||
</a>
|
||||
) : (
|
||||
<div className="flex items-center justify-center">
|
||||
<TouchpadOff className="w-4 h-4" />
|
||||
</div>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="text-center border-r">
|
||||
<div className="flex items-center justify-center">
|
||||
{item.ReportLocked ? (
|
||||
<LockKeyhole className="w-4 h-4 text-red-700" />
|
||||
) : (
|
||||
<Key className="w-4 h-4 text-green-700" />
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
})
|
||||
)}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
{/* Pagination */}
|
||||
<div className="mt-4 flex items-center gap-2 justify-end">
|
||||
<Button variant="outline" size="icon">
|
||||
<ChevronLeft className="h-4 w-4" />
|
||||
</Button>
|
||||
<Button variant="outline" size="icon">
|
||||
<ChevronRight className="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
);
|
||||
}
|
|
@ -214,7 +214,7 @@ export default function VerifikatorIndex({
|
|||
}}
|
||||
>
|
||||
<Plus className="h-4 w-4 mr-2" />
|
||||
Tambah Verifikator
|
||||
Tambah Dinas
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
|
||||
|
|
|
@ -5,120 +5,177 @@ import {
|
|||
Building,
|
||||
School,
|
||||
Skull,
|
||||
TestTubeDiagonalIcon,
|
||||
UsersRound,
|
||||
Waves,
|
||||
TrendingUp,
|
||||
ArrowUpRight,
|
||||
ArrowDownRight,
|
||||
Activity,
|
||||
} from "lucide-react";
|
||||
import StatsCard from "@/components/Dashboard/DashCard";
|
||||
import CerobongCard from "@/components/Dashboard/CerobongCard";
|
||||
import { ChartCard } from "@/components/Dashboard/ChartCard";
|
||||
import { Card } from "@/components/ui/card";
|
||||
|
||||
export default function Dashboard() {
|
||||
return (
|
||||
<AuthenticatedLayout header="Dashboard">
|
||||
<Head title="Dashboard" />
|
||||
|
||||
<div className="flex flex-1 flex-col gap-4 h-full">
|
||||
<DatePickerWithRange />
|
||||
<div className="grid gap-4 md:grid-cols-5 items-center w-full">
|
||||
<StatsCard
|
||||
title="Total Laporan Perusahaan"
|
||||
icon={<School size={24} />}
|
||||
value={1305}
|
||||
fromColor="from-[#E6F9FF]"
|
||||
toColor=""
|
||||
/>
|
||||
<StatsCard
|
||||
title="Jumlah Perusahaan"
|
||||
icon={<Building size={24} />}
|
||||
value={1305}
|
||||
fromColor="from-[#F7E9FF]"
|
||||
toColor=""
|
||||
/>
|
||||
<StatsCard
|
||||
title="Jumlah Verifikator"
|
||||
icon={<UsersRound size={24} />}
|
||||
value={1305}
|
||||
fromColor="from-[#F7E9FF]"
|
||||
toColor=""
|
||||
/>
|
||||
<StatsCard
|
||||
title="Air Limbah"
|
||||
icon={<Waves size={24} />}
|
||||
value={1305}
|
||||
fromColor="from-[#F7E9FF]"
|
||||
toColor="#fffff"
|
||||
/>
|
||||
<StatsCard
|
||||
title="Limbah B3"
|
||||
icon={<Skull size={24} />}
|
||||
value={1305}
|
||||
fromColor="from-[#F7E9FF]"
|
||||
toColor="#fffff"
|
||||
/>
|
||||
<div className="flex flex-1 flex-col gap-6 h-full p-6">
|
||||
{/* Top Section */}
|
||||
<div className="flex justify-between items-center gap-4 flex-wrap">
|
||||
<div className="space-y-1">
|
||||
<h2 className="text-2xl font-bold tracking-tight">
|
||||
Dashboard Overview
|
||||
</h2>
|
||||
{/* <p className="text-muted-foreground">
|
||||
Detailed monitoring and analytics of environmental
|
||||
metrics
|
||||
</p> */}
|
||||
</div>
|
||||
<DatePickerWithRange />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-stretch space-y-0 border-b p-0 sm:flex-row mt-8">
|
||||
<div className="flex flex-1 flex-col justify-center gap-1 px-6 py-5 sm:py-6">
|
||||
<div className="font-semibold leading-none tracking-tight">
|
||||
Total Cerobong per hari ini
|
||||
{/* Stats Cards Grid */}
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-5">
|
||||
<Card className="relative overflow-hidden group transition-all hover:shadow-lg">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-blue-50 to-transparent opacity-50 group-hover:opacity-70 transition-opacity" />
|
||||
<div className="p-6 relative">
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="p-2 bg-blue-100 rounded-lg">
|
||||
<School className="h-5 w-5 text-blue-600" />
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-green-600">
|
||||
<TrendingUp className="h-4 w-4 mr-1" />
|
||||
+12.5%
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<h3 className="text-sm font-medium text-muted-foreground">
|
||||
Total Laporan
|
||||
</h3>
|
||||
<p className="text-2xl font-bold">1,305</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-sm text-muted-foreground">
|
||||
Senin, 03 Feb 2025
|
||||
</Card>
|
||||
|
||||
<Card className="relative overflow-hidden group transition-all hover:shadow-lg">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-purple-50 to-transparent opacity-50 group-hover:opacity-70 transition-opacity" />
|
||||
<div className="p-6 relative">
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="p-2 bg-purple-100 rounded-lg">
|
||||
<Building className="h-5 w-5 text-purple-600" />
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-green-600">
|
||||
<TrendingUp className="h-4 w-4 mr-1" />
|
||||
+8.2%
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<h3 className="text-sm font-medium text-muted-foreground">
|
||||
Jumlah Perusahaan
|
||||
</h3>
|
||||
<p className="text-2xl font-bold">847</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<button
|
||||
data-active="true"
|
||||
className="flex flex-1 flex-col justify-center gap-1 border-t px-6 py-4 text-left even:border-l data-[active=true]:bg-muted/50 sm:border-l sm:border-t-0 sm:px-8 sm:py-6"
|
||||
>
|
||||
<span className="text-xs text-muted-foreground">
|
||||
Seluruh Perusahaan
|
||||
</span>
|
||||
<span className="text-lg font-bold leading-none sm:text-3xl">
|
||||
2336
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<ChartCard />
|
||||
</Card>
|
||||
|
||||
<Card className="relative overflow-hidden group transition-all hover:shadow-lg">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-green-50 to-transparent opacity-50 group-hover:opacity-70 transition-opacity" />
|
||||
<div className="p-6 relative">
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="p-2 bg-green-100 rounded-lg">
|
||||
<UsersRound className="h-5 w-5 text-green-600" />
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-red-600">
|
||||
<ArrowDownRight className="h-4 w-4 mr-1" />
|
||||
-3.1%
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<h3 className="text-sm font-medium text-muted-foreground">
|
||||
Jumlah Verifikator
|
||||
</h3>
|
||||
<p className="text-2xl font-bold">124</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="relative overflow-hidden group transition-all hover:shadow-lg">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-cyan-50 to-transparent opacity-50 group-hover:opacity-70 transition-opacity" />
|
||||
<div className="p-6 relative">
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="p-2 bg-cyan-100 rounded-lg">
|
||||
<Waves className="h-5 w-5 text-cyan-600" />
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-green-600">
|
||||
<ArrowUpRight className="h-4 w-4 mr-1" />
|
||||
+5.7%
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<h3 className="text-sm font-medium text-muted-foreground">
|
||||
Air Limbah
|
||||
</h3>
|
||||
<p className="text-2xl font-bold">2,431</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card className="relative overflow-hidden group transition-all hover:shadow-lg">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-red-50 to-transparent opacity-50 group-hover:opacity-70 transition-opacity" />
|
||||
<div className="p-6 relative">
|
||||
<div className="flex justify-between items-start">
|
||||
<div className="p-2 bg-red-100 rounded-lg">
|
||||
<Skull className="h-5 w-5 text-red-600" />
|
||||
</div>
|
||||
<div className="flex items-center text-sm text-red-600">
|
||||
<ArrowDownRight className="h-4 w-4 mr-1" />
|
||||
-2.3%
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-4">
|
||||
<h3 className="text-sm font-medium text-muted-foreground">
|
||||
Limbah B3
|
||||
</h3>
|
||||
<p className="text-2xl font-bold">567</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* <div className="flex flex-1 flex-col gap-4 h-full mt-4">
|
||||
<div>
|
||||
<h2>Total Cerobong per hari ini</h2>
|
||||
<span>Senin, 03 Feb 2025</span>
|
||||
</div>
|
||||
<div className="grid gap-4 md:grid-cols-4 items-center w-full">
|
||||
<CerobongCard
|
||||
title="Seluruh Perusahaan"
|
||||
value={1305}
|
||||
fromColor="from-[#F7E9FF]"
|
||||
toColor="#fffff"
|
||||
/>
|
||||
<CardChart />
|
||||
</div>
|
||||
</div> */}
|
||||
|
||||
{/* <div className="flex-row mt-5">
|
||||
<div className="w-1/3 gap-4 md:grid-cols-3 items-center">
|
||||
<div className="flex flex-row gap-4 h-full">
|
||||
<div className="flex flex-col">
|
||||
<h2>Total Cerobong per hari ini</h2>
|
||||
<span>Senin, 03 Feb 2025</span>
|
||||
{/* Cerobong Stats Section */}
|
||||
<Card className="relative overflow-hidden">
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-orange-50/50 to-transparent" />
|
||||
<div className="relative p-6">
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="p-2 bg-orange-100 rounded-lg">
|
||||
<Activity className="h-5 w-5 text-orange-600" />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-xl font-semibold">
|
||||
Total Cerobong
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Update terakhir: Senin, 03 Feb 2025
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-right">
|
||||
<p className="text-3xl font-bold">2,336</p>
|
||||
<div className="flex items-center justify-end gap-1 text-sm text-green-600 mt-1">
|
||||
<span>Seluruh Perusahaan</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="w-2/3 gap-4 md:grid-cols-3 items-center mt-3 ">
|
||||
<CerobongCard
|
||||
title="Jumlah Perusahaan"
|
||||
value={1305}
|
||||
fromColor="from-[#F7E9FF]"
|
||||
toColor=""
|
||||
/>
|
||||
</div>
|
||||
</div> */}
|
||||
</Card>
|
||||
|
||||
{/* Chart Section */}
|
||||
<div className="w-full">
|
||||
<ChartCard />
|
||||
</div>
|
||||
</div>
|
||||
</AuthenticatedLayout>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,97 @@
|
|||
export interface Perusahaan {
|
||||
[x: string]: any;
|
||||
PerusahaanId: number;
|
||||
NamaPerusahaan: string;
|
||||
Alamat: string;
|
||||
KodePos: string;
|
||||
Telepon: string;
|
||||
Fax: string;
|
||||
Email: string;
|
||||
Lintang: string;
|
||||
Bujur: string;
|
||||
CPNama: string;
|
||||
CPTelepon: string;
|
||||
ILNomor: string;
|
||||
ILTanggal: string;
|
||||
ILDokumen: string;
|
||||
ReportLocked: boolean;
|
||||
JenisKegiatan: {
|
||||
NamaJenisKegiatan: string;
|
||||
};
|
||||
Kelurahan: {
|
||||
NamaKelurahan: string;
|
||||
Kecamatan: {
|
||||
NamaKecamatan: string;
|
||||
Kabupaten: {
|
||||
NamaKabupaten: string;
|
||||
};
|
||||
};
|
||||
};
|
||||
Verifikator: {
|
||||
NamaUnitKerja: string;
|
||||
};
|
||||
JenisDokIL: {
|
||||
NamaJenisDokIL: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface FormDataType {
|
||||
NomorInduk: string;
|
||||
JenisKegiatanId: string;
|
||||
NamaPerusahaan: string;
|
||||
Alamat: string;
|
||||
KelurahanId: string;
|
||||
KodePos: string;
|
||||
Telepon: string;
|
||||
Fax: string;
|
||||
Email: string;
|
||||
Lintang: string;
|
||||
Bujur: string;
|
||||
CPNama: string;
|
||||
CPTelepon: string;
|
||||
JenisDokILId: string;
|
||||
VerifikatorId: string;
|
||||
IsPublish: boolean;
|
||||
ILNomor: string;
|
||||
ILTanggal: string;
|
||||
ILDokumen: File | null;
|
||||
ReportLocked: true;
|
||||
PerusahaanId: string;
|
||||
}
|
||||
|
||||
export interface Kawasan {
|
||||
PerusahaanId: number;
|
||||
NamaPerusahaan: string;
|
||||
}
|
||||
|
||||
export interface JenisDokIL {
|
||||
JenisDokILId: number;
|
||||
NamaJenisDokIL: string;
|
||||
}
|
||||
|
||||
export interface JenisKegiatan {
|
||||
JenisKegiatanId: number;
|
||||
NamaJenisKegiatan: string;
|
||||
}
|
||||
|
||||
export interface Verifikator {
|
||||
VerifikatorId: number;
|
||||
NamaUnitKerja: string;
|
||||
}
|
||||
|
||||
export interface Kabupaten {
|
||||
KabupatenId: number;
|
||||
NamaKabupaten: string;
|
||||
}
|
||||
|
||||
export interface Kecamatan {
|
||||
KecamatanId: number;
|
||||
NamaKecamatan: string;
|
||||
KabupatenId: number;
|
||||
}
|
||||
|
||||
export interface Kelurahan {
|
||||
KelurahanId: number;
|
||||
NamaKelurahan: string;
|
||||
KecamatanId: number;
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use App\Http\Controllers\CategoryController;
|
||||
use App\Http\Controllers\DaftarPerusahaanController;
|
||||
use App\Http\Controllers\HistoryKegiatanController;
|
||||
use App\Http\Controllers\HomeController;
|
||||
use App\Http\Controllers\HukumController;
|
||||
|
@ -9,9 +10,11 @@ use App\Http\Controllers\JenisKegiatanController;
|
|||
use App\Http\Controllers\JenisSanksiController;
|
||||
use App\Http\Controllers\KategoriController;
|
||||
use App\Http\Controllers\MenuController;
|
||||
use App\Http\Controllers\PelaporanALController;
|
||||
use App\Http\Controllers\PelaporanController;
|
||||
use App\Http\Controllers\PengumumanController;
|
||||
use App\Http\Controllers\PeraturanController;
|
||||
use App\Http\Controllers\PerusahaanController;
|
||||
use App\Http\Controllers\PostController;
|
||||
use App\Http\Controllers\ProfileController;
|
||||
use App\Http\Controllers\RefCategoryController;
|
||||
|
@ -37,7 +40,17 @@ Route::get('/', [HomeController::class, 'index'])->name('home');
|
|||
// });
|
||||
|
||||
Route::fallback(function () {
|
||||
return Inertia::render('404');
|
||||
$path = request()->path();
|
||||
|
||||
if (str_starts_with($path, 'admin')) {
|
||||
return Inertia::render('404Admin', [
|
||||
'backUrl' => route('dashboard')
|
||||
]);
|
||||
}
|
||||
|
||||
return Inertia::render('404', [
|
||||
'backUrl' => route('home')
|
||||
]);
|
||||
});
|
||||
|
||||
Route::get('/pengumuman', [PengumumanController::class, 'index'])->name('pengumuman');
|
||||
|
@ -122,6 +135,10 @@ Route::middleware(['auth'])->prefix('admin')->group(function () {
|
|||
Route::get('/pelaporan', [PelaporanController::class, 'index'])->name('admin.pelaporan.index');
|
||||
});
|
||||
|
||||
Route::middleware(['auth'])->prefix('admin')->group(function () {
|
||||
Route::get('/pelaporan/al', [PelaporanALController::class, 'index'])->name('admin.pelaporanAL.index');
|
||||
});
|
||||
|
||||
Route::middleware(['auth'])->prefix('admin')->group(function () {
|
||||
Route::get('/verifikasi', [VerifPelaporanController::class, 'index'])->name('admin.verifikasi.index');
|
||||
});
|
||||
|
@ -135,8 +152,17 @@ Route::middleware(['auth'])->prefix('admin')->group(function () {
|
|||
Route::get('/post/add', [PostController::class, 'create'])->name('admin.post.create');
|
||||
Route::post('/post', [PostController::class, 'store'])->name('admin.post.store');
|
||||
Route::get('/post/{post}', [PostController::class, 'edit'])->name('admin.post.edit');
|
||||
Route::put('/post/{post}', [PostController::class, 'update'])->name('admin.post.update');
|
||||
Route::post('/post/{post}', [PostController::class, 'update'])->name('admin.post.update');
|
||||
Route::delete('/post/{post}', [PostController::class, 'destroy'])->name('admin.post.destroy');
|
||||
});
|
||||
|
||||
Route::middleware(['auth'])->prefix('admin')->group(function () {
|
||||
Route::get('/perusahaan', [PerusahaanController::class, 'index'])->name('admin.perusahaan.index');
|
||||
Route::get('/perusahaan/add', [PerusahaanController::class, 'create'])->name('admin.perusahaan.create');
|
||||
Route::post('/perusahaan', [PerusahaanController::class, 'store'])->name('admin.perusahaan.store');
|
||||
Route::get('/perusahaan/{perusahaan}', [PerusahaanController::class, 'edit'])->name('admin.perusahaan.edit');
|
||||
Route::post('/perusahaan/{perusahaan}', [PerusahaanController::class, 'update'])->name('admin.perusahaan.update');
|
||||
Route::delete('/perusahaan/{perusahaan}', [PerusahaanController::class, 'destroy'])->name('admin.perusahaan.destroy');
|
||||
});
|
||||
|
||||
require __DIR__.'/auth.php';
|
||||
|
|
Loading…
Reference in New Issue