diff --git a/app/Enums/LayoutForm.php b/app/Enums/LayoutForm.php new file mode 100644 index 0000000..434d90a --- /dev/null +++ b/app/Enums/LayoutForm.php @@ -0,0 +1,31 @@ + 'Row', + self::COLUMN => 'Column', + self::VALUE => 'Value', + self::SECTION => 'Section', + self::HR_LINE => 'HR Line', + }; + } + + public static function options(): array + { + return collect(self::cases())->map(fn ($case) => [ + 'value' => $case->value, + 'label' => $case->label(), + ])->toArray(); + } +} diff --git a/app/Http/Controllers/Api/Core/ProgramController.php b/app/Http/Controllers/Api/Core/ProgramController.php new file mode 100644 index 0000000..34f1cec --- /dev/null +++ b/app/Http/Controllers/Api/Core/ProgramController.php @@ -0,0 +1,59 @@ +query('per_page', 10); + $search = $request->query('search', ''); + + $program = Program::where('nama', 'like', "%$search%") + ->paginate($perPage); + + return ProgramResource::collection($program); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(string $id): ProgramResource + { + $program = Program::findOrFail($id); + return new ProgramResource($program); + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} diff --git a/app/Http/Controllers/Api/Ekosistem/FormBasisDataController.php b/app/Http/Controllers/Api/Ekosistem/FormBasisDataController.php new file mode 100644 index 0000000..2aca1c6 --- /dev/null +++ b/app/Http/Controllers/Api/Ekosistem/FormBasisDataController.php @@ -0,0 +1,140 @@ +whereHas('kolom', function ($query) { + $query->where('is_active', true); + })->find($tabelId); + if (!$tabel) { + abort(404, 'Data tabel tidak ditemukan.'); + } + $kolom = KolomBasisData::where('tabel_id', $tabelId) + ->where('is_active', true)->get(); + if ($kolom->isEmpty()) { + abort(404, 'Data kolom tidak ditemukan untuk tabel ini.'); + } + + $program = null; + if (app()->has('SystemProgram')) { + $program = app('SystemProgram'); + } else { + if ($request->has('program_id') && $request->query('program_id')) { + $programId = $request->query('program_id'); + $program = Program::find($programId); + } + } + if (!$program) { + abort(400, 'Program tidak ditemukan.'); + } + + $method = $request->query('method', 'add'); + + $list_relasi = null; + if ($method == 'add' || $method == 'edit') { + $list_relasi = []; + foreach ($kolom as $k) { + if ($k->tipe == 'foreign_key') { + $relasi['kolom_id'] = $k->id; + try { + $options_kolom = DB::table($k->relasi_tabel)->selectRaw($k->relasi_kolom_label . ' as label, ' . $k->relasi_kolom_id . ' as value')->whereNull('deleted_at'); + $relasi['options'] = $options_kolom->get(); + $relasi['sql'] = $options_kolom->toSql(); + } catch (\Throwable $th) { + $relasi['options'] = []; + } + $list_relasi[] = $relasi; + } + } + $list_relasi = collect($list_relasi); + } elseif ($method == 'view') { + $id = $request->query('value_id'); + if (!$id) { + abort(400, 'value_id query parameter diperlukan untuk view method.'); + } + + $values = ValueBasisData::query() + ->where('tabel_id', $tabelId) + ->where('value_id', $id) + ->where('program_id', $program->id) + ->get(); + if ($values->isEmpty()) { + abort(404, 'Data tidak ditemukan.'); + } + + foreach ($values as $v) { + $k = $kolom->where('id', $v->kolom_id)->first(); + if ($k->tipe == 'foreign_key') { + $v->value_relasi_label = DB::table($k->relasi_tabel)->where($k->relasi_kolom_id, $v->value_relasi)->value($k->relasi_kolom_label); + } else if ($k->tipe == 'enum') { + $opsi = collect($k->enum_opsi); + $found = $opsi->first(function ($item) use ($v) { + return (string) $item['value'] === (string) $v->value_enum; + }); + $v->value_enum_label = $found['label'] ?? null; + } + } + + $value_id = $id; + } + + + $errors = $request->query('errors', null); + + return response()->json($tabel->renderHtml($method, $errors, $list_relasi)); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} diff --git a/app/Http/Controllers/Api/Ekosistem/TabelBasisDataController.php b/app/Http/Controllers/Api/Ekosistem/TabelBasisDataController.php new file mode 100644 index 0000000..f02ee4f --- /dev/null +++ b/app/Http/Controllers/Api/Ekosistem/TabelBasisDataController.php @@ -0,0 +1,78 @@ +query('per_page', 10); + $search = strtolower($request->query('search', '')); + + $tabel = TabelBasisData::with('kolom') + ->whereHas('kolom', function ($query) { + $query->where('is_active', true); + }) + ->whereRaw('lower(tabel) LIKE ?', ["%$search%"]) + ->paginate($perPage); + + return TabelBasisDataResource::collection($tabel); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + // + } + + /** + * Display the specified resource. + */ + public function show(Request $request, string $id): JsonResponse + { + if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + abort(403); + } + + $tabel = TabelBasisData::with('kolom', 'form', 'form.form', 'form.form.form', 'form.form.form.kolom')->findOrFail($id); + + return (new TabelBasisDataResource($tabel)) + ->response(); + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} diff --git a/app/Http/Controllers/Api/Ekosistem/ValueBasisDataController.php b/app/Http/Controllers/Api/Ekosistem/ValueBasisDataController.php new file mode 100644 index 0000000..2c5bf01 --- /dev/null +++ b/app/Http/Controllers/Api/Ekosistem/ValueBasisDataController.php @@ -0,0 +1,368 @@ +query('per_page', 10); + $search = strtolower($request->query('search', '')); + + $program = null; + $programList = Program::get(); + if (app()->has('SystemProgram')) { + $program = app('SystemProgram'); + } else { + if ($request->has('program_id') && $request->query('program_id')) { + $programId = $request->query('program_id'); + $program = Program::find($programId); + if (!$program) { + abort(400, 'Program tidak ditemukan.'); + } + } + } + + $tabel = TabelBasisData::with('kolom') + ->whereHas('kolom', function ($query) { + $query->where('is_active', true); + }) + ->find($tabelId); + if (!$tabel) { + abort(404, 'Data tabel tidak ditemukan.'); + } + $kolom = KolomBasisData::where('tabel_id', $tabelId) + ->where('is_active', true) + ->when('is_browse', function ($q) { + $q->where('is_browse', true); + }) + ->orderBy('urutan', 'asc') + ->get(); + if ($kolom->isEmpty()) { + abort(404, 'Data kolom tidak ditemukan untuk tabel ini.'); + } + + $selects = []; + foreach ($kolom as $k) { + if ($k->tipe == 'boolean') { + $selects[] = " + MAX( + CASE + WHEN kolom_id = {$k->id} + THEN CASE + WHEN value_boolean = true THEN 'Ya' + WHEN value_boolean = false THEN 'Tidak' + ELSE NULL + END + ELSE NULL + END + ) AS \"{$k->kolom}\" + "; + } elseif ($k->tipe == 'foreign_key') { + $selects[] = " + MAX( + CASE + WHEN kolom_id = {$k->id} + THEN (select {$k->relasi_kolom_label} from {$k->relasi_tabel} where {$k->relasi_kolom_id} = value_relasi) + ELSE NULL + END + ) AS \"{$k->kolom}\" + "; + } elseif ($k->tipe == 'enum') { + $selects[] = " + MAX( + CASE + WHEN kolom_id = {$k->id} + AND value_enum IS NOT NULL + THEN ( + SELECT elem->>'label' + FROM ekosistem_basis_data_kolom k, + jsonb_array_elements(k.enum_opsi::jsonb) AS elem + WHERE k.id = kolom_id + AND elem->>'value' = value_enum + LIMIT 1 + ) + ELSE NULL + END + ) AS \"{$k->kolom}\" + "; + } else { + $selects[] = " + MAX( + CASE + WHEN kolom_id = {$k->id} + THEN value_{$k->tipe} + ELSE NULL + END + ) AS \"{$k->kolom}\" + "; + } + } + + $base = ValueBasisData::query() + ->join('core_program', 'core_program.id', '=', 'ekosistem_basis_data_value.program_id') + ->where('tabel_id', $tabelId) + ->when($program, function ($query) use ($program) { + $query->where('program_id', $program->id); + }) + ->selectRaw(" + core_program.id AS program_id, + core_program.nama AS program_nama, + tabel_id, + value_id, + " . implode(",\n", $selects) . " + ") + ->groupBy('core_program.id', 'core_program.nama', 'tabel_id', 'value_id') + ->orderBy('value_id', 'asc'); + + $values = DB::query()->fromSub($base, 'pivot') + ->when($search, function ($query) use ($search, $kolom) { + $query->where(function ($q) use ($search, $kolom) { + foreach ($kolom as $k) { + if ($k->tipe == 'text' || $k->tipe == 'varchar') { + $q->orWhereRaw("lower({$k->kolom}) LIKE ?", ["%$search%"]); + } + } + }); + }) + ->paginate($perPage); + + return response()->json($values); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request, string $tabelId): JsonResponse + { + if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + abort(403); + } + + $tabel = TabelBasisData::whereHas('kolom', function ($query) { + $query->where('is_active', true); + })->find($tabelId); + if (!$tabel) { + abort(404, 'Data tabel tidak ditemukan.'); + } + $kolom = KolomBasisData::where('tabel_id', $tabelId) + ->where('is_active', true)->get(); + if ($kolom->isEmpty()) { + abort(404, 'Data kolom tidak ditemukan untuk tabel ini.'); + } + if (app()->has('SystemProgram')) { + $program = app('SystemProgram'); + } else { + if ($request->has('program_id') && $request->query('program_id')) { + $programId = $request->query('program_id'); + $program = Program::find($programId); + if (!$program) { + abort(400, 'Program tidak ditemukan.'); + } + } else { + abort(400, 'Program tidak ditemukan.'); + } + } + + $validationRules = []; + foreach ($kolom as $k) { + if ($k->validasi && is_array($k->validasi)) { + $validationRules[$k->kolom] = $k->validasi; + } else { + $validationRules[$k->kolom] = ['nullable']; + } + } + $validated = $request->validate($validationRules); + + DB::transaction(function () use ($program, $tabelId, $validated, $kolom) { + $maxId = ValueBasisData::where('tabel_id', $tabelId) + ->where('program_id', $program->id) + ->whereNotNull('value_id') + ->max('value_id'); + foreach ($kolom as $k) { + $values = [ + 'program_id' => $program->id, + 'tabel_id' => (int)$tabelId, + 'kolom_id' => $k->id, + 'value_id' => ($maxId ?? 0) + 1, + 'created_by' => auth()->id(), + 'updated_by' => auth()->id(), + ]; + if ($k->tipe == 'foreign_key') { + $values['value_relasi'] = $validated[$k->kolom] ?? null; + } else { + $values['value_' . $k->tipe] = $validated[$k->kolom] ?? null; + } + + ValueBasisData::create($values); + } + }); + + return response()->json(['message' => 'Data berhasil disimpan.']); + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $tabelId, string $id): JsonResponse + { + if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + abort(403); + } + + $tabel = TabelBasisData::whereHas('kolom', function ($query) { + $query->where('is_active', true); + })->find($tabelId); + if (!$tabel) { + abort(404, 'Data tabel tidak ditemukan.'); + } + $kolom = KolomBasisData::where('tabel_id', $tabelId) + ->where('is_active', true)->get(); + if ($kolom->isEmpty()) { + abort(404, 'Data kolom tidak ditemukan untuk tabel ini.'); + } + if (app()->has('SystemProgram')) { + $program = app('SystemProgram'); + } else { + if ($request->has('program_id') && $request->query('program_id')) { + $programId = $request->query('program_id'); + $program = Program::find($programId); + if (!$program) { + abort(400, 'Program tidak ditemukan.'); + } + } else { + abort(400, 'Program tidak ditemukan.'); + } + } + + $values = ValueBasisData::query() + ->where('tabel_id', $tabelId) + ->where('value_id', $id) + ->where('program_id', $program->id) + ->get(); + if ($values->isEmpty()) { + abort(404, 'Data tidak ditemukan.'); + } + + $validationRules = []; + foreach ($kolom as $k) { + if ($k->validasi && is_array($k->validasi)) { + $validationRules[$k->kolom] = $k->validasi; + } else { + $validationRules[$k->kolom] = ['nullable']; + } + } + $validated = $request->validate($validationRules); + + DB::transaction(function () use ($program, $tabelId, $validated, $kolom, $id, $values) { + foreach ($validated as $kol => $val) { + $k = $kolom->where('kolom', $kol)->first(); + if (!$k) { + continue; + } + $v = $values->where('kolom_id', $k->id)->first(); + if (!$v) { + $vals = [ + 'program_id' => $program->id, + 'tabel_id' => (int)$tabelId, + 'kolom_id' => $k->id, + 'value_id' => $id, + 'created_by' => auth()->id(), + 'updated_by' => auth()->id(), + ]; + if ($k->tipe == 'foreign_key') { + $vals['value_relasi'] = $validated[$k->kolom] ?? null; + } else { + $vals['value_' . $k->tipe] = $validated[$k->kolom] ?? null; + } + ValueBasisData::create($vals); + } else { + $v->update([ + 'value_' . ($k->tipe == 'foreign_key' ? 'relasi' : $k->tipe) => $val, + 'updated_by' => auth()->id(), + ]); + } + } + }); + + return response()->json(['message' => 'Data berhasil diperbarui.']); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(Request $request, string $tabelId, string $id): JsonResponse + { + if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { + abort(403); + } + + $tabel = TabelBasisData::whereHas('kolom', function ($query) { + $query->where('is_active', true); + })->find($tabelId); + if (!$tabel) { + abort(404, 'Data tabel tidak ditemukan.'); + } + $kolom = KolomBasisData::where('tabel_id', $tabelId)->get(); + if ($kolom->isEmpty()) { + abort(404, 'Data kolom tidak ditemukan untuk tabel ini.'); + } + if (app()->has('SystemProgram')) { + $program = app('SystemProgram'); + } else { + if ($request->has('program_id') && $request->query('program_id')) { + $programId = $request->query('program_id'); + $program = Program::find($programId); + if (!$program) { + abort(400, 'Program tidak ditemukan.'); + } + } else { + abort(400, 'Program tidak ditemukan.'); + } + } + + $values = ValueBasisData::query() + ->where('tabel_id', $tabelId) + ->where('value_id', $id) + ->where('program_id', $program->id) + ->get(); + if ($values->isEmpty()) { + abort(404, 'Data tidak ditemukan.'); + } + + ValueBasisData::query() + ->where('tabel_id', $tabelId) + ->where('value_id', $id) + ->where('program_id', $program->id) + ->delete(); + + return response()->json(['message' => 'Data berhasil dihapus.']); + } +} diff --git a/app/Http/Controllers/Api/AuthController.php b/app/Http/Controllers/Api/Konfigurasi/AuthController.php similarity index 89% rename from app/Http/Controllers/Api/AuthController.php rename to app/Http/Controllers/Api/Konfigurasi/AuthController.php index 787498a..d9e6ff0 100644 --- a/app/Http/Controllers/Api/AuthController.php +++ b/app/Http/Controllers/Api/Konfigurasi/AuthController.php @@ -1,16 +1,14 @@ ['required', 'string', 'max:255'], 'email' => ['required', 'email', 'max:255', 'unique:users'], 'password' => ['required', 'string', 'min:8', 'confirmed'], + 'username' => ['required', 'string', 'max:255'], 'role' => ['string'], + 'uid' => ['required','string', 'unique:users'], ]); $user = User::create([ 'name' => $credentials['name'], 'email' => $credentials['email'], 'password' => Hash::make($credentials['password']), + 'username' => $credentials['username'], 'role' => $credentials['role'], + 'uid' => $credentials['uid'], ]); $profile = Profile::create([ 'nama' => $credentials['name'], 'email' => $credentials['email'], 'role' => $credentials['role'], + 'user_uid' => $credentials['uid'], 'user_id' => $user->id, 'created_by' => $user->id, 'updated_by' => $user->id, diff --git a/app/Http/Controllers/Api/DesaKelurahanController.php b/app/Http/Controllers/Api/Master/DesaKelurahanController.php similarity index 98% rename from app/Http/Controllers/Api/DesaKelurahanController.php rename to app/Http/Controllers/Api/Master/DesaKelurahanController.php index ee0d285..04062fb 100644 --- a/app/Http/Controllers/Api/DesaKelurahanController.php +++ b/app/Http/Controllers/Api/Master/DesaKelurahanController.php @@ -1,6 +1,6 @@ integer('size') ?: 10; + + $jenisKomoditasList = JenisKomoditas::when($request->search, function ($q, $search) { + $search = strtolower($search); + $q->whereRaw('lower(nama) like ?', ["%{$search}%"]); + }) + ->when($request->kode, fn($q, $v) => $q->where('kode', $v)); + + if ($request->filled('sort')) { + $dir = str_starts_with($request->sort, '-') ? 'desc' : 'asc'; + $column = ltrim($request->sort, '-'); + + $allowed = ['id', 'nama', 'kode']; + + if (in_array($column, $allowed)) { + $jenisKomoditasList->orderBy($column, $dir); + } + } else { + $jenisKomoditasList->orderBy('nama', 'asc'); + } + + $jenisKomoditasList = $jenisKomoditasList->paginate($size); + + return JenisKomoditasResource::collection($jenisKomoditasList); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request): JsonResponse + { + if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) { + abort(403); + } + + $validated = $request->validate([ + 'nama' => ['required', 'string', 'max:255'], + 'kode' => ['required', 'string', 'max:50', 'unique:jenis_komoditas,kode'], + 'keterangan' => ['nullable', 'string'], + ]); + + $jenisKomoditas = JenisKomoditas::create($validated); + + return (new JenisKomoditasResource($jenisKomoditas)) + ->response() + ->setStatusCode(201); + } + + /** + * Display the specified resource. + */ + public function show(string $id): JsonResponse + { + if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) { + abort(403); + } + + $jenisKomoditas = JenisKomoditas::findOrFail($id); + + return (new JenisKomoditasResource($jenisKomoditas))->response(); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id): JsonResponse + { + if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) { + abort(403); + } + + $jenisKomoditas = JenisKomoditas::findOrFail($id); + + $validated = $request->validate([ + 'nama' => ['required', 'string', 'max:255'], + 'kode' => ['required', 'string', 'max:50', Rule::unique('jenis_komoditas', 'kode')->ignore($jenisKomoditas->id)], + 'keterangan' => ['nullable', 'string'], + ]); + + $jenisKomoditas->update($validated); + + return (new JenisKomoditasResource($jenisKomoditas))->response(); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id): JsonResponse + { + if (!Gate::any(['petani', 'fasilitator', 'super_admin'])) { + abort(403); + } + + $jenisKomoditas = JenisKomoditas::findOrFail($id); + $jenisKomoditas->delete(); + + return response()->json(null, 204); + } +} \ No newline at end of file diff --git a/app/Http/Controllers/Api/KabupatenKotaController.php b/app/Http/Controllers/Api/Master/KabupatenKotaController.php similarity index 98% rename from app/Http/Controllers/Api/KabupatenKotaController.php rename to app/Http/Controllers/Api/Master/KabupatenKotaController.php index d8a6e81..ec42e86 100644 --- a/app/Http/Controllers/Api/KabupatenKotaController.php +++ b/app/Http/Controllers/Api/Master/KabupatenKotaController.php @@ -1,6 +1,6 @@ has('profile_id')) { + $request->merge(['profile_id' => (int) $request->profile_id]); + } + $profileRule = $user->role === 'fasilitator' ? ['required', 'numeric', 'exists:profiles,id'] : ['nullable']; $validated = $request->validate([ @@ -97,6 +100,7 @@ class LahanController extends Controller 'path' => ['nullable', 'array'], 'path.*' => ['array'], 'luas_lahan' => ['nullable', 'numeric'], + 'uid' => ['required', 'string'], 'profile_id' => $profileRule, ]); diff --git a/app/Http/Controllers/Api/OfftakerController.php b/app/Http/Controllers/Api/Produktivitas/OfftakerController.php similarity index 98% rename from app/Http/Controllers/Api/OfftakerController.php rename to app/Http/Controllers/Api/Produktivitas/OfftakerController.php index f310bc1..9f477f4 100644 --- a/app/Http/Controllers/Api/OfftakerController.php +++ b/app/Http/Controllers/Api/Produktivitas/OfftakerController.php @@ -1,6 +1,6 @@ profile; + $profileId = $user->role === 'petani' ? $profile->id : $request->profile_id; + + $size = $request->integer('size') ?: 10; + + $tanamanList = Tanaman::when($request->search, function ($q, $search) { + $search = strtolower($search); + $q->whereRaw('lower(nama) like ?', ["%{$search}%"]); + }) + ->when($profileId, fn($q,$v) => $q->where('profile_id',$v)) + ->when($request->status_kepemilikan, fn($q,$v) => $q->where('status_kepemilikan',$v)) + ->when($request->lahan_id, fn($q,$v) => $q->where('lahan_id',$v)) + ->when($request->komoditas_id, fn($q,$v) => $q->where('komoditas_id',$v)) + ->when($request->desa_kelurahan_id, fn($q,$v) => + $q->whereHas('lahan', fn($k) + => $k->where('desa_kelurahan_id',$v) + ) + ) + ->when($request->kecamatan_id, fn ($q, $v) => + $q->whereHas('lahan.desaKelurahan', fn ($k) => + $k->where('kecamatan_id', $v) + ) + ) + ->when($request->kabupaten_kota_id, fn ($q, $v) => + $q->whereHas('lahan.desaKelurahan.kecamatan', fn ($k) => + $k->where('kabupaten_kota_id', $v) + ) + ) + ->when($request->provinsi_id, fn ($q, $v) => + $q->whereHas('lahan.desaKelurahan.kecamatan.kabupatenkota', fn ($k) => + $k->where('provinsi_id', $v) + ) + ); + + if ($request->filled('sort')) { + $dir = str_starts_with($request->sort, '-') ? 'desc' : 'asc'; + $column = ltrim($request->sort, '-'); + + $allowed = ['id', 'nama']; + + if (in_array($column, $allowed)) { + $tanamanList->orderBy($column, $dir); + } + } else { + $tanamanList->orderBy('nama', 'asc'); + } + + $tanamanList = $tanamanList->paginate($size); + + return TanamanResource::collection($tanamanList); + } + + /** + * Show the form for creating a new resource. + */ + public function create() + { + // + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request): JsonResponse + { + if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + abort(403); + } + + $user = Auth::user(); + $profile = $user->profile; + $profileId = $user->role === 'petani' ? $profile->id : $request->profile_id; + + $request->merge([ + 'komoditas_id' => $request->input('komoditas.id') ?? $request->input('komoditas_id'), + 'lahan_id' => $request->input('lahan.id') ?? $request->input('lahan_id'), + 'profile_id' => $profileId, + ]); + + $validated = $request->validate([ + 'nama' => ['required', 'string', 'max:255'], + 'komoditas_id' => ['required', 'exists:master_komoditas,id'], + 'lahan_id' => [ + 'required', + Rule::exists('map_lahan', 'id')->where(function ($query) use ($profileId) { + $query->where('profile_id', $profileId) + ->whereNull('deleted_at'); + }), + ], + 'latitude' => ['nullable', 'numeric', 'between:-90,90', 'required_with:longitude'], + 'longitude' => ['nullable', 'numeric', 'between:-180,180', 'required_with:latitude'], + 'kondisi_tanaman' => ['nullable', 'string', new Enum(KondisiTanaman::class)], + ]); + + $tanaman = Tanaman::create([ + ...$validated, + 'profile_id' => $profile->id, + ]); + + return (new TanamanResource($tanaman)) + ->response() + ->setStatusCode(201); + } + + /** + * Display the specified resource. + */ + public function show(string $id): JsonResponse + { + if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + abort(403); + } + + $tanaman = Tanaman::findOrFail($id); + if ($tanaman->profile_id !== Auth::user()->profile->id) { + return response()->json(['message' => 'Unauthorized'], 403); + } + + return (new TanamanResource($tanaman))->response(); + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id): JsonResponse + { + if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + abort(403); + } + $tanaman = Tanaman::findOrFail($id); + if ($tanaman->profile_id !== Auth::user()->profile->id) { + return response()->json(['message' => 'Unauthorized'], 403); + } + + $profile = Auth::user()->profile; + + $validated = $request->validate([ + 'nama' => ['required', 'string', 'max:255'], + 'komoditas_id' => ['required', 'exists:master_komoditas,id'], + 'lahan_id' => [ + 'required', + Rule::exists('map_lahan', 'id')->where(function ($query) use ($profile) { + $query->where('profile_id', $profile->id) + ->whereNull('deleted_at'); + }), + ], + 'latitude' => ['nullable', 'numeric', 'between:-90,90', 'required_with:longitude'], + 'longitude' => ['nullable', 'numeric', 'between:-180,180', 'required_with:latitude'], + 'kondisi_tanaman' => ['nullable', 'string', new Enum(KondisiTanaman::class)], + ]); + + $tanaman->update($validated); + + return (new TanamanResource($tanaman))->response(); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id): JsonResponse + { + if (!Gate::any(['petani', 'fasilitator','super_admin'])) { + abort(403); + } + $tanaman = Tanaman::findOrFail($id); + if ($tanaman->profile_id !== Auth::user()->profile->id) { + return response()->json(['message' => 'Unauthorized'], 403); + } + + $tanaman->delete(); + + return response()->json(null, 204); + } +} diff --git a/app/Http/Controllers/Api/TanamanController.php b/app/Http/Controllers/Api/TanamanController.php index 9b23bee..683d162 100644 --- a/app/Http/Controllers/Api/TanamanController.php +++ b/app/Http/Controllers/Api/TanamanController.php @@ -94,26 +94,41 @@ class TanamanController extends Controller if (!Gate::any(['petani', 'fasilitator','super_admin'])) { abort(403); } - $profile = Auth::user()->profile; + + $user = Auth::user(); + $profile = $user->profile; + $profileId = $user->role === 'petani' ? $profile->id : $request->profile_id; + + $request->merge([ + 'komoditas_id' => $request->input('komoditas.id') ?? $request->input('komoditas_id'), + 'lahan_id' => $request->input('lahan.id') ?? $request->input('lahan_id'), + 'profile_id' => $profileId, + ]); $validated = $request->validate([ 'nama' => ['required', 'string', 'max:255'], 'komoditas_id' => ['required', 'exists:master_komoditas,id'], 'lahan_id' => [ 'required', - Rule::exists('map_lahan', 'id')->where(function ($query) use ($profile) { - $query->where('profile_id', $profile->id) + Rule::exists('map_lahan', 'id')->where(function ($query) use ($profileId) { + $query->where('profile_id', $profileId) ->whereNull('deleted_at'); }), ], 'latitude' => ['nullable', 'numeric', 'between:-90,90', 'required_with:longitude'], - 'longitude' => ['nullable', 'numeric', 'between:-180,180', 'required_with:latitude'], + 'longitude' => ['nullable ', 'numeric', 'between:-180,180', 'required_with:latitude'], 'kondisi_tanaman' => ['nullable', 'string', new Enum(KondisiTanaman::class)], + 'uid' => ['required', 'string'], + 'jenis_komoditas_id' => ['nullable', 'exists:jenis_komoditas,id'], + 'komoditas_kode' => ['nullable', 'string', 'max:255'], ]); $tanaman = Tanaman::create([ ...$validated, 'profile_id' => $profile->id, + 'uid' => $validated['uid'] ?? $request->input('uid'), + 'komoditas_kode' => $validated['komoditas_kode'] ?? $request->input('komoditas_kode'), + 'jenis_komoditas_id' => $validated['jenis_komoditas_id'] ?? $request->input('jenis_komoditas_id'), ]); return (new TanamanResource($tanaman)) diff --git a/app/Http/Controllers/Web/Dashboard/SummaryController.php b/app/Http/Controllers/Web/Dashboard/SummaryController.php index 1ae24d6..77c4982 100644 --- a/app/Http/Controllers/Web/Dashboard/SummaryController.php +++ b/app/Http/Controllers/Web/Dashboard/SummaryController.php @@ -4,13 +4,8 @@ namespace App\Http\Controllers\Web\Dashboard; use App\Http\Controllers\Controller; use Illuminate\Http\Request; -use App\Models\Profile; -use App\Models\Wilayah\Provinsi; -use App\Models\wilayah\KabupatenKota; -use App\Models\Wilayah\Kecamatan; -use App\Models\Wilayah\DesaKelurahan; -use App\Models\Pertanian\Lahan; use Illuminate\Support\Facades\Gate; + class SummaryController extends Controller { public function index() @@ -21,128 +16,4 @@ class SummaryController extends Controller $title = 'Summary Dashboard'; return view('dashboard.summary', compact('title')); } - - public function produktivitasBackup(Request $request) - { - if (!Gate::allows('super_admin') && !Gate::allows('admin')) { - abort(403); - } - $title = 'Produktivitas Dashboard'; - $luasTotal = Lahan::whereNotNull('path')->sum('luas_lahan'); - $totalpetani = Profile::where('role', 'petani')->count(); - $totallahan = Lahan::whereNotNull('path')->count(); - - $petaniList = Profile::where('role', 'petani') - ->with(['lahan.tanaman.komoditas', 'desaKelurahan']) - ->paginate(20); - - $allLahan = Lahan::whereNotNull('path') - ->with(['profile', 'tanaman.komoditas', 'desaKelurahan.kecamatan.kabupatenKota.provinsi']) - ->get() - ->map(function($lahan) { - return [ - 'id' => $lahan->id, - 'polygon' => $lahan->path, - 'nama_petani' => $lahan->profile->nama ?? 'N/A', - 'luas' => $lahan->luas_lahan, - 'tanaman' => $lahan->tanaman->map(fn($t) => $t->komoditas->nama ?? null)->filter()->unique()->values() - ]; - }); - - return view('dashboard.produktivitas', compact('title', 'petaniList', 'allLahan', 'totalpetani', 'totallahan', 'luasTotal')); - } - - public function produktivitas(Request $request) - { - if (!Gate::allows('super_admin') && !Gate::allows('admin')) { - abort(403); - } - - $title = 'Produktivitas Dashboard'; - $luasTotal = Lahan::whereNotNull('path')->sum('luas_lahan'); - $totalpetani = Profile::where('role', 'petani')->count(); - $totallahan = Lahan::whereNotNull('path')->count(); - - $petaniList = Profile::where('role', 'petani') - ->with(['lahan.tanaman.komoditas', 'desaKelurahan']) - ->paginate(20); - - $allLahan = Lahan::whereNotNull('path') - ->with(['profile', 'tanaman.komoditas', 'desaKelurahan.kecamatan.kabupatenKota.provinsi']) - ->get(); - - // Penampung data per level (menggunakan ID sebagai key agar unique) - $geoProvinsi = []; - $geoKabupaten = []; - $geoKecamatan = []; - $geoDesa = []; - $geoLahan = []; - - foreach ($allLahan as $lahan) { - $desa = $lahan->desaKelurahan; - $kec = $desa ? $desa->kecamatan : null; - $kab = $kec ? $kec->kabupatenKota : null; - $prov = $kab ? $kab->provinsi : null; - - // --- Akumulasi Provinsi --- - if ($prov && $prov->path) { - if (!isset($geoProvinsi[$prov->id])) { - $geoProvinsi[$prov->id] = ['id' => $prov->id, 'nama' => $prov->nama, 'polygon' => $prov->path, 'total_lahan' => 0, 'total_luas' => 0]; - } - $geoProvinsi[$prov->id]['total_lahan']++; - $geoProvinsi[$prov->id]['total_luas'] += $lahan->luas_lahan; - } - - // --- Akumulasi Kabupaten --- - if ($kab && $kab->path) { - if (!isset($geoKabupaten[$kab->id])) { - $geoKabupaten[$kab->id] = ['id' => $kab->id, 'parent_id' => $prov->id ?? null, 'nama' => $kab->nama, 'polygon' => $kab->path, 'total_lahan' => 0, 'total_luas' => 0]; - } - $geoKabupaten[$kab->id]['total_lahan']++; - $geoKabupaten[$kab->id]['total_luas'] += $lahan->luas_lahan; - } - - // --- Akumulasi Kecamatan --- - if ($kec && $kec->path) { - if (!isset($geoKecamatan[$kec->id])) { - $geoKecamatan[$kec->id] = ['id' => $kec->id, 'parent_id' => $kab->id ?? null, 'nama' => $kec->nama, 'polygon' => $kec->path, 'total_lahan' => 0, 'total_luas' => 0]; - } - $geoKecamatan[$kec->id]['total_lahan']++; - $geoKecamatan[$kec->id]['total_luas'] += $lahan->luas_lahan; - } - - // --- Akumulasi Desa --- - if ($desa && $desa->path) { - if (!isset($geoDesa[$desa->id])) { - $geoDesa[$desa->id] = ['id' => $desa->id, 'parent_id' => $kec->id ?? null, 'nama' => $desa->nama, 'polygon' => $desa->path, 'total_lahan' => 0, 'total_luas' => 0]; - } - $geoDesa[$desa->id]['total_lahan']++; - $geoDesa[$desa->id]['total_luas'] += $lahan->luas_lahan; - } - - // Data Lahan Tetap - $geoLahan[] = [ - 'id' => $lahan->id, - 'parent_id' => $desa->id ?? null, - 'polygon' => $lahan->path, - 'nama_petani' => $lahan->profile->nama ?? 'N/A', - 'luas' => $lahan->luas_lahan, - 'tanaman' => $lahan->tanaman->map(fn($t) => $t->komoditas->nama ?? null)->filter()->unique()->values() - ]; - } - - return view('dashboard.produktivitas', [ - 'title' => $title, - 'petaniList' => $petaniList, - 'totalpetani' => $totalpetani, - 'totallahan' => $totallahan, - 'luasTotal' => $luasTotal, - // Reset key array agar menjadi format list normal untuk JSON - 'geoProvinsi' => array_values($geoProvinsi), - 'geoKabupaten' => array_values($geoKabupaten), - 'geoKecamatan' => array_values($geoKecamatan), - 'geoDesa' => array_values($geoDesa), - 'geoLahan' => $geoLahan - ]); - } } diff --git a/app/Http/Controllers/Web/Ekosistem/FormBasisDataController.php b/app/Http/Controllers/Web/Ekosistem/FormBasisDataController.php new file mode 100644 index 0000000..c2982ec --- /dev/null +++ b/app/Http/Controllers/Web/Ekosistem/FormBasisDataController.php @@ -0,0 +1,296 @@ +json([ + 'status' => 'error', + 'message' => 'Data tabel tidak ditemukan.' + ], 404); + } + + $listEnum = LayoutForm::options(); + + $tab = $request->tab ?? 'form'; + $tabel->state = 'preview'; + + return view('ekosistem.form-basis-data.index', compact('tabel', 'listEnum', 'tab')); + } + + public function create() + { + // + } + + public function store(Request $request, string $tabelId): JsonResponse + { + Gate::authorize('super_admin'); + + $tabel = TabelBasisData::find($tabelId); + if (!$tabel) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data tabel tidak ditemukan.' + ], 404); + } + + $validated = $request->validate([ + 'parent_id' => ['nullable', 'exists:ekosistem_basis_data_form,id'], + 'tipe' => ['required', Rule::enum(LayoutForm::class)], + 'column_width' => [ + Rule::requiredIf($request->tipe === LayoutForm::COLUMN->value), + 'nullable', 'integer', 'min:1', 'max:12'], + 'kolom_id' => [ + Rule::requiredIf($request->tipe === LayoutForm::VALUE->value), + 'nullable', 'exists:ekosistem_basis_data_kolom,id'], + 'section_label' => [ + Rule::requiredIf($request->tipe === LayoutForm::SECTION->value), + 'nullable', 'string'], + ]); + + FormBasisData::create([...$validated, + 'tabel_id' => $tabelId, + 'created_by' => auth()->user()->id, + 'updated_by' => auth()->user()->id + ]); + + return response()->json([ + 'status' => 'success', + 'message' => 'Form basis data berhasil ditambahkan.' + ], 201); + } + + public function show(string $tabelId, string $id): JsonResponse + { + Gate::authorize('super_admin'); + + $form = FormBasisData::where('tabel_id', $tabelId)->find($id); + if (!$form) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data formulir basis data tidak ditemukan.' + ], 404); + } + return response()->json([ + 'status' => 'success', + 'data' => $form + ], 200); + } + + public function edit(string $id) + { + // + } + + public function update(Request $request, string $tabelId, string $id) + { + Gate::authorize('super_admin'); + + $form = FormBasisData::where('tabel_id', $tabelId)->find($id); + if (!$form) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data formulir basis data tidak ditemukan.' + ], 404); + } + + $validated = $request->validate([ + 'parent_id' => ['nullable', 'exists:ekosistem_basis_data_form,id'], + 'tipe' => ['required', Rule::enum(LayoutForm::class)], + 'column_width' => [ + Rule::requiredIf($request->tipe === LayoutForm::COLUMN->value), + 'nullable', 'integer', 'min:1', 'max:12'], + 'kolom_id' => [ + Rule::requiredIf($request->tipe === LayoutForm::VALUE->value), + 'nullable', 'exists:ekosistem_basis_data_kolom,id'], + 'section_label' => [ + Rule::requiredIf($request->tipe === LayoutForm::SECTION->value), + 'nullable', 'string'], + ]); + + $form->update([...$validated, + 'updated_by' => auth()->user()->id + ]); + + return response()->json([ + 'status' => 'success', + 'message' => 'Form basis data berhasil diubah.' + ]); + } + + public function destroy(string $tabelId, string $id): JsonResponse + { + Gate::authorize('super_admin'); + + $form = FormBasisData::where('tabel_id', $tabelId)->find($id); + if (!$form) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data form basis data tidak ditemukan.' + ], 404); + } + if ($form->form->count() > 0) { + return response()->json([ + 'status' => 'error', + 'message' => 'Tidak dapat menghapus formulir basis data yang memiliki formulir.' + ], 400); + } + $form->update([ + 'deleted_by' => auth()->user()->id + ]); + $form->delete(); + + return response()->json([ + 'status' => 'success', + 'message' => 'Form basis data berhasil dihapus.' + ], 200); + } + + public function generate(Request $request, string $tabelId): RedirectResponse + { + Gate::authorize('super_admin'); + + $tabel = TabelBasisData::find($tabelId); + if (!$tabel) { + return redirect()->route('tabel.formulir.index', $tabelId)->with('error', 'Tabel basis data tidak ditemukan.'); + } + + DB::transaction(function () use ($tabel) { + + FormBasisData::where('tabel_id', $tabel->id)->delete(); + + FormBasisData::create([ + 'tabel_id' => $tabel->id, + 'tipe' => LayoutForm::SECTION, + 'section_label' => $tabel->label, + 'created_by' => auth()->user()->id, + 'updated_by' => auth()->user()->id + ]); + + FormBasisData::create([ + 'tabel_id' => $tabel->id, + 'tipe' => LayoutForm::HR_LINE, + 'created_by' => auth()->user()->id, + 'updated_by' => auth()->user()->id + ]); + + $chunks = $tabel->kolom->chunk(4); + + foreach ($chunks as $chunk) { + + $row = FormBasisData::create([ + 'tabel_id' => $tabel->id, + 'tipe' => LayoutForm::ROW, + 'created_by' => auth()->user()->id, + 'updated_by' => auth()->user()->id + ]); + + foreach ($chunk as $kolom) { + $column = FormBasisData::create([ + 'tabel_id' => $tabel->id, + 'tipe' => LayoutForm::COLUMN, + 'column_width' => 3, + 'parent_id' => $row->id, + 'created_by' => auth()->user()->id, + 'updated_by' => auth()->user()->id + ]); + + FormBasisData::create([ + 'tabel_id' => $tabel->id, + 'tipe' => LayoutForm::VALUE, + 'parent_id' => $column->id, + 'kolom_id' => $kolom->id, + 'created_by' => auth()->user()->id, + 'updated_by' => auth()->user()->id + ]); + } + } + + }); + + return redirect()->route('tabel.formulir.index', $tabelId)->with('success', 'Formulir berhasil dibuat.'); + } + + public function moveUp(Request $request, string $tabelId, string $id): JsonResponse + { + Gate::authorize('super_admin'); + + $current = FormBasisData::where('tabel_id', $tabelId)->find($id); + if (!$current) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data formulir tidak ditemukan.' + ], 404); + } + + $prev = FormBasisData::where('tabel_id', $tabelId) + ->where('urutan', '<', $current->urutan) + ->where('parent_id', $current->parent_id) + ->orderBy('urutan', 'desc') + ->first(); + + if ($prev) { + // swap + [$current->urutan, $prev->urutan] = [$prev->urutan, $current->urutan]; + $current->save(); + $prev->save(); + } + + return response()->json([ + 'status' => 'success', + 'message' => 'Formulir basis data berhasil diperbarui.' + ], 200); + } + + public function moveDown(Request $request, string $tabelId, string $id): JsonResponse + { + Gate::authorize('super_admin'); + + $current = FormBasisData::where('tabel_id', $tabelId)->find($id); + if (!$current) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data formulir tidak ditemukan.' + ], 404); + } + + $next = FormBasisData::where('tabel_id', $tabelId) + ->where('urutan', '>', $current->urutan) + ->where('parent_id', $current->parent_id) + ->orderBy('urutan', 'asc') + ->first(); + + if ($next) { + // swap + [$current->urutan, $next->urutan] = [$next->urutan, $current->urutan]; + $current->save(); + $next->save(); + } + + return response()->json([ + 'status' => 'success', + 'message' => 'Formulir basis data berhasil diperbarui.' + ], 200); + } +} diff --git a/app/Http/Controllers/Web/Ekosistem/KolomBasisDataController.php b/app/Http/Controllers/Web/Ekosistem/KolomBasisDataController.php index 3813976..fb58d87 100644 --- a/app/Http/Controllers/Web/Ekosistem/KolomBasisDataController.php +++ b/app/Http/Controllers/Web/Ekosistem/KolomBasisDataController.php @@ -145,6 +145,13 @@ class KolomBasisDataController extends Controller 'is_browse' => ['nullable', 'boolean'], ]); + if (!empty($validated['enum_opsi'])) { + $validated['enum_opsi'] = json_decode($validated['enum_opsi'], true); + } + if (!empty($validated['validasi'])) { + $validated['validasi'] = json_decode($validated['validasi'], true); + } + $kolom->update([...$validated, 'updated_by' => auth()->id(), ]); @@ -188,7 +195,7 @@ class KolomBasisDataController extends Controller ], 404); } - $prev = KolomBasisData::where('tabel_id', $current->tabel_id) + $prev = KolomBasisData::where('tabel_id', $tabelId) ->where('urutan', '<', $current->urutan) ->orderBy('urutan', 'desc') ->first(); @@ -218,7 +225,7 @@ class KolomBasisDataController extends Controller ], 404); } - $next = KolomBasisData::where('tabel_id', $current->tabel_id) + $next = KolomBasisData::where('tabel_id', $tabelId) ->where('urutan', '>', $current->urutan) ->orderBy('urutan', 'asc') ->first(); diff --git a/app/Http/Controllers/Web/Ekosistem/ValueBasisDataController.php b/app/Http/Controllers/Web/Ekosistem/ValueBasisDataController.php index 84828cb..cc4b085 100644 --- a/app/Http/Controllers/Web/Ekosistem/ValueBasisDataController.php +++ b/app/Http/Controllers/Web/Ekosistem/ValueBasisDataController.php @@ -8,6 +8,13 @@ use Illuminate\Support\Facades\Gate; use Illuminate\View\View; use Illuminate\Http\RedirectResponse; use Illuminate\Support\Facades\DB; +use Illuminate\Support\Facades\Validator; +use Illuminate\Http\JsonResponse; +use Illuminate\Support\Facades\Auth; +use Illuminate\Support\Str; +use Illuminate\Support\Arr; +use Illuminate\Support\Facades\Hash; +use Illuminate\Validation\Rule; use App\Models\Ekosistem\TabelBasisData; use App\Models\Ekosistem\KolomBasisData; @@ -17,9 +24,16 @@ use App\Models\Core\Program; class ValueBasisDataController extends Controller { + private $tableModelClassMap = [ + 'master_provinsi' => \App\Models\Wilayah\Provinsi::class, + 'master_kabupaten_kota' => \App\Models\Wilayah\KabupatenKota::class, + 'master_kecamatan' => \App\Models\Wilayah\Kecamatan::class, + 'master_desa_kelurahan' => \App\Models\Wilayah\DesaKelurahan::class, + ]; + public function index(Request $request, string $tabelId): View { - if (!Gate::any(['super_admin', 'admin'])) { + if (!Gate::any(['super_admin', 'admin', 'fasilitator'])) { abort(403); } @@ -40,13 +54,15 @@ class ValueBasisDataController extends Controller } } - $tabel = TabelBasisData::find($tabelId); + $tabel = TabelBasisData::with('kolom') + ->whereHas('kolom', function ($query) { + $query->where('is_active', true); + })->find($tabelId); if (!$tabel) { abort(404, 'Data tabel tidak ditemukan.'); } - $kolom = KolomBasisData::where('tabel_id', $tabelId) + $kolom = KolomBasisData::where('tabel_id', $tabel->id) ->where('is_active', true) - ->where('is_browse', true) ->get(); if ($kolom->isEmpty()) { abort(404, 'Data kolom tidak ditemukan untuk tabel ini.'); @@ -83,6 +99,7 @@ class ValueBasisDataController extends Controller MAX( CASE WHEN kolom_id = {$k->id} + AND value_enum IS NOT NULL THEN ( SELECT elem->>'label' FROM ekosistem_basis_data_kolom k, @@ -160,8 +177,11 @@ class ValueBasisDataController extends Controller if (!Gate::any(['super_admin', 'admin'])) { abort(403); } - - $tabel = TabelBasisData::find($tabelId); + + $tabel = TabelBasisData::with('kolom') + ->whereHas('kolom', function ($query) { + $query->where('is_active', true); + })->find($tabelId); if (!$tabel) { abort(404, 'Data tabel tidak ditemukan.'); } @@ -189,7 +209,12 @@ class ValueBasisDataController extends Controller if ($k->tipe == 'foreign_key') { $relasi['kolom_id'] = $k->id; try { - $options_kolom = DB::table($k->relasi_tabel)->selectRaw($k->relasi_kolom_label . ' as label, ' . $k->relasi_kolom_id . ' as value')->whereNull('deleted_at'); + $modelClass = Arr::get($this->tableModelClassMap, $k->relasi_tabel); + if ($modelClass) { + $options_kolom = $modelClass::select($k->relasi_kolom_label . ' as label', $k->relasi_kolom_id . ' as value')->limit(10000); + } else { + $options_kolom = DB::table($k->relasi_tabel)->selectRaw($k->relasi_kolom_label . ' as label, ' . $k->relasi_kolom_id . ' as value')->whereNull('deleted_at')->limit(10000); + } $relasi['options'] = $options_kolom->get(); $relasi['sql'] = $options_kolom->toSql(); } catch (\Throwable $th) { @@ -199,18 +224,84 @@ class ValueBasisDataController extends Controller } } $list_relasi = collect($list_relasi); - // dd($kolom->where('tipe', 'enum')->first()?->enum_opsi); - + return view('ekosistem.value-basis-data.add', compact('tabel', 'kolom', 'program', 'list_relasi')); } + private function prepareValidationRules($kolom) + { + $rules = []; + foreach ($kolom as $k) { + if ($k->validasi && is_array($k->validasi)) { + $rules[$k->kolom] = $k->validasi; + + if (!in_array('required', $k->validasi) && !in_array('nullable', $k->validasi)) { + $rules[$k->kolom][] = 'nullable'; + } + + if (!in_array('string', $k->validasi) && ($k->tipe == 'varchar' || $k->tipe == 'text')) { + $rules[$k->kolom][] = 'string'; + } + if (!in_array('size', $k->validasi) && ($k->tipe == 'varchar')) { + $rules[$k->kolom][] = 'size:255'; + } + if (!in_array('integer', $k->validasi) && $k->tipe == 'integer') { + $rules[$k->kolom][] = 'integer'; + } + if (!in_array('boolean', $k->validasi) && $k->tipe == 'boolean') { + $rules[$k->kolom][] = 'boolean'; + } + if (!in_array('date', $k->validasi) && ($k->tipe == 'date' || $k->tipe == 'datetime')) { + $rules[$k->kolom][] = 'date'; + } + if ($k->tipe == 'foreign_key') { + $rules[$k->kolom][] = 'exists:' . $k->relasi_tabel . ',' . $k->relasi_kolom_id; + } + if ($k->tipe == 'enum') { + $enumValues = collect($k->enum_opsi)->pluck('value')->toArray(); + $rules[$k->kolom][] = Rule::in($enumValues); + } + + } else { + $rules[$k->kolom] = ['nullable']; + + if ($k->tipe == 'varchar' || $k->tipe == 'text') { + $rules[$k->kolom][] = 'string'; + } + if ($k->tipe == 'varchar') { + $rules[$k->kolom][] = 'size:255'; + } + if ($k->tipe == 'integer') { + $rules[$k->kolom][] = 'integer'; + } + if ($k->tipe == 'boolean') { + $rules[$k->kolom][] = 'boolean'; + } + if ($k->tipe == 'date' || $k->tipe == 'datetime') { + $rules[$k->kolom][] = 'date'; + } + if ($k->tipe == 'foreign_key') { + $rules[$k->kolom][] = 'exists:' . $k->relasi_tabel . ',' . $k->relasi_kolom_id; + } + if ($k->tipe == 'enum') { + $enumValues = collect($k->enum_opsi)->pluck('value')->toArray(); + $rules[$k->kolom][] = Rule::in($enumValues); + } + } + } + return $rules; + } + public function store(Request $request, string $tabelId): RedirectResponse { if (!Gate::any(['super_admin', 'admin'])) { abort(403); } - $tabel = TabelBasisData::find($tabelId); + $tabel = TabelBasisData::with('kolom') + ->whereHas('kolom', function ($query) { + $query->where('is_active', true); + })->find($tabelId); if (!$tabel) { abort(404, 'Data tabel tidak ditemukan.'); } @@ -233,15 +324,7 @@ class ValueBasisDataController extends Controller } } - $validationRules = []; - foreach ($kolom as $k) { - if ($k->validasi && is_array($k->validasi)) { - $validationRules[$k->kolom] = $k->validasi; - } else { - $validationRules[$k->kolom] = ['nullable']; - } - } - $validated = $request->validate($validationRules); + $validated = $request->validate($this->prepareValidationRules($kolom)); DB::transaction(function () use ($program, $tabelId, $validated, $kolom) { $maxId = ValueBasisData::where('tabel_id', $tabelId) @@ -270,16 +353,16 @@ class ValueBasisDataController extends Controller return redirect()->route('tabel.data.index', [$tabelId, 'program_id' => $program->id])->with('success', 'Data berhasil ditambahkan.'); } - /** - * Display the specified resource. - */ public function show(Request $request, string $tabelId, string $id): View { if (!Gate::any(['super_admin', 'admin'])) { abort(403); } - $tabel = TabelBasisData::find($tabelId); + $tabel = TabelBasisData::with('kolom') + ->whereHas('kolom', function ($query) { + $query->where('is_active', true); + })->find($tabelId); if (!$tabel) { abort(404, 'Data tabel tidak ditemukan.'); } @@ -315,7 +398,9 @@ class ValueBasisDataController extends Controller foreach ($values as $v) { $k = $kolom->where('id', $v->kolom_id)->first(); if ($k->tipe == 'foreign_key') { - $v->value_relasi_label = DB::table($k->relasi_tabel)->where($k->relasi_kolom_id, $v->value_relasi)->value($k->relasi_kolom_label); + $v->value_relasi_label = DB::table($k->relasi_tabel) + ->where($k->relasi_kolom_id, $v->value_relasi) + ->value($k->relasi_kolom_label); } else if ($k->tipe == 'enum') { $opsi = collect($k->enum_opsi); $found = $opsi->first(function ($item) use ($v) { @@ -336,7 +421,10 @@ class ValueBasisDataController extends Controller abort(403); } - $tabel = TabelBasisData::find($tabelId); + $tabel = TabelBasisData::with('kolom') + ->whereHas('kolom', function ($query) { + $query->where('is_active', true); + })->find($tabelId); if (!$tabel) { abort(404, 'Data tabel tidak ditemukan.'); } @@ -376,7 +464,12 @@ class ValueBasisDataController extends Controller if ($k->tipe == 'foreign_key') { $relasi['kolom_id'] = $k->id; try { - $options_kolom = DB::table($k->relasi_tabel)->selectRaw($k->relasi_kolom_label . ' as label, ' . $k->relasi_kolom_id . ' as value')->whereNull('deleted_at'); + $modelClass = Arr::get($this->tableModelClassMap, 'master_desa_kelurahan'); + if ($modelClass) { + $options_kolom = $modelClass::select($k->relasi_kolom_label . ' as label', $k->relasi_kolom_id . ' as value')->limit(10000); + } else { + $options_kolom = DB::table($k->relasi_tabel)->selectRaw($k->relasi_kolom_label . ' as label, ' . $k->relasi_kolom_id . ' as value')->whereNull('deleted_at')->limit(10000); + } $relasi['options'] = $options_kolom->get(); $relasi['sql'] = $options_kolom->toSql(); } catch (\Throwable $th) { @@ -390,16 +483,16 @@ class ValueBasisDataController extends Controller return view('ekosistem.value-basis-data.edit', compact('tabel', 'kolom', 'program', 'values', 'value_id', 'list_relasi')); } - /** - * Update the specified resource in storage. - */ public function update(Request $request, string $tabelId, string $id): RedirectResponse { if (!Gate::any(['super_admin', 'admin'])) { abort(403); } - $tabel = TabelBasisData::find($tabelId); + $tabel = TabelBasisData::with('kolom') + ->whereHas('kolom', function ($query) { + $query->where('is_active', true); + })->find($tabelId); if (!$tabel) { abort(404, 'Data tabel tidak ditemukan.'); } @@ -431,15 +524,7 @@ class ValueBasisDataController extends Controller abort(404, 'Data tidak ditemukan.'); } - $validationRules = []; - foreach ($kolom as $k) { - if ($k->validasi && is_array($k->validasi)) { - $validationRules[$k->kolom] = $k->validasi; - } else { - $validationRules[$k->kolom] = ['nullable']; - } - } - $validated = $request->validate($validationRules); + $validated = $request->validate($this->prepareValidationRules($kolom)); DB::transaction(function () use ($program, $tabelId, $validated, $kolom, $id, $values) { foreach ($validated as $kol => $val) { diff --git a/app/Http/Controllers/Web/Konfigurasi/AuthController.php b/app/Http/Controllers/Web/Konfigurasi/AuthController.php index 63f2f12..ec89fc6 100644 --- a/app/Http/Controllers/Web/Konfigurasi/AuthController.php +++ b/app/Http/Controllers/Web/Konfigurasi/AuthController.php @@ -15,13 +15,13 @@ class AuthController extends Controller public function login() { $title = 'Login'; - return view('konfigurasi.auth.login', compact('title')); + return view('auth.login', compact('title')); } public function register() { $title = 'Register'; - return view('konfigurasi.auth.register', compact('title')); + return view('auth.register', compact('title')); } public function createAccount(Request $request) @@ -42,7 +42,7 @@ class AuthController extends Controller 'deleted_by' => null, ]); - return redirect()->route('konfigurasi.auth.login')->with('success', 'Account created successfully. Please login.'); + return redirect()->route('auth.login')->with('success', 'Account created successfully. Please login.'); } public function verify(User $user) diff --git a/app/Http/Controllers/Web/Konfigurasi/UserController.php b/app/Http/Controllers/Web/Konfigurasi/UserController.php index 52f14c8..7d79fc1 100644 --- a/app/Http/Controllers/Web/Konfigurasi/UserController.php +++ b/app/Http/Controllers/Web/Konfigurasi/UserController.php @@ -10,6 +10,7 @@ use App\Enums\Role; use Illuminate\Support\Facades\DB; use Illuminate\Support\Facades\Hash; use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Str; class UserController extends Controller { @@ -70,6 +71,7 @@ class UserController extends Controller public function store(Request $request) { $validator = Validator::make($request->all(), [ + 'username' => ['required', 'string', 'max:255', 'unique:users,name'], 'name' => ['required', 'string', 'max:255'], 'email' => ['required', 'string', 'email', 'max:255', 'unique:users'], 'password' => ['required', 'string', 'min:8', 'confirmed'], @@ -90,14 +92,17 @@ class UserController extends Controller $user = User::create([ 'name' => $request->name, + 'username' => $request->username, 'email' => $request->email, 'password' => Hash::make($request->password), 'role' => $request->role, + 'uid' => Str::random(10), 'created_by' => auth()->id(), ]); Profile::create([ 'user_id' => $user->id, + 'user_uid' => $user->uid, 'role' => $request->role, 'nama' => $request->name, 'email' => $request->email, diff --git a/app/Http/Controllers/Web/Master/JenisKomoditasController.php b/app/Http/Controllers/Web/Master/JenisKomoditasController.php new file mode 100644 index 0000000..ea6ce41 --- /dev/null +++ b/app/Http/Controllers/Web/Master/JenisKomoditasController.php @@ -0,0 +1,228 @@ +get('per_page', 10); + $search = $request->get('search'); + + $jenisKomoditas = JenisKomoditas::with('komoditas') + ->when($search, function ($query) use ($search) { + $query->where(function ($q) use ($search) { + $q->where('nama', 'ILIKE', "%{$search}%") + ->orWhere('path', 'ILIKE', "%{$search}%") + ->orWhereHas('komoditas', function ($komoditasQuery) use ($search) { + $komoditasQuery->where('nama', 'ILIKE', "%{$search}%"); + }); + }); + }) + ->latest() + ->paginate($perPage); + + $komoditas = Komoditas::orderBy('nama', 'asc')->get(); + + return view('master.jeniskomoditas.index', compact( + 'jenisKomoditas', + 'komoditas' + )); + } + + public function getByKomoditas($komoditas_id) + { + $jenisKomoditas = JenisKomoditas::where('komoditas_id', $komoditas_id) + ->orderBy('nama', 'asc') + ->get(['id', 'nama']); + + return response()->json([ + 'status' => 'success', + 'data' => $jenisKomoditas, + ]); + } + + /** + * Store a newly created resource in storage. + */ + public function store(Request $request) + { + $validated = $request->validate([ + 'komoditas_id' => [ + 'required', + 'integer', + 'exists:master_komoditas,id', + ], + 'nama' => [ + 'required', + 'string', + 'max:255', + Rule::unique('jenis_komoditas', 'nama') + ->where('komoditas_id', $request->komoditas_id), + ], + ], [ + 'komoditas_id.required' => 'Komoditas wajib dipilih.', + 'komoditas_id.exists' => 'Komoditas yang dipilih tidak valid.', + 'nama.required' => 'Nama jenis komoditas wajib diisi.', + 'nama.unique' => 'Nama jenis komoditas sudah tersedia pada komoditas ini.', + ]); + + try { + DB::beginTransaction(); + + $jenisKomoditas = JenisKomoditas::create([ + 'komoditas_id' => $validated['komoditas_id'], + 'nama' => $validated['nama'], + 'created_by' => auth()->id(), + 'updated_by' => auth()->id(), + ]); + + DB::commit(); + + return response()->json([ + 'status' => 'success', + 'message' => 'Data jenis komoditas berhasil disimpan.', + 'data' => $jenisKomoditas, + ]); + } catch (\Throwable $e) { + DB::rollBack(); + + Log::error('Gagal menyimpan jenis komoditas', [ + 'error' => $e->getMessage(), + 'request' => $request->all(), + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal menyimpan data: ' . $e->getMessage(), + ], 500); + } + } + + /** + * Display the specified resource. + */ + public function show(string $id) + { + try { + $jenisKomoditas = JenisKomoditas::with('komoditas')->findOrFail($id); + + return response()->json([ + 'status' => 'success', + 'data' => $jenisKomoditas, + ]); + } catch (\Throwable $e) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data jenis komoditas tidak ditemukan.', + ], 404); + } + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + $jenisKomoditas = JenisKomoditas::findOrFail($id); + + $validated = $request->validate([ + 'komoditas_id' => [ + 'required', + 'integer', + 'exists:master_komoditas,id', + ], + 'nama' => [ + 'required', + 'string', + 'max:255', + Rule::unique('jenis_komoditas', 'nama') + ->where('komoditas_id', $request->komoditas_id) + ->ignore($jenisKomoditas->id), + ], + 'path' => [ + 'nullable', + 'string', + ], + ], [ + 'komoditas_id.required' => 'Komoditas wajib dipilih.', + 'komoditas_id.exists' => 'Komoditas yang dipilih tidak valid.', + 'nama.required' => 'Nama jenis komoditas wajib diisi.', + 'nama.unique' => 'Nama jenis komoditas sudah tersedia pada komoditas ini.', + ]); + + try { + DB::beginTransaction(); + + $jenisKomoditas->update([ + 'komoditas_id' => $validated['komoditas_id'], + 'nama' => $validated['nama'], + 'updated_by' => auth()->id(), + ]); + + DB::commit(); + + return response()->json([ + 'status' => 'success', + 'message' => 'Data jenis komoditas berhasil diperbarui.', + 'data' => $jenisKomoditas->fresh('komoditas'), + ]); + } catch (\Throwable $e) { + DB::rollBack(); + + Log::error('Gagal update jenis komoditas', [ + 'error' => $e->getMessage(), + 'id' => $id, + 'request' => $request->all(), + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal mengupdate data: ' . $e->getMessage(), + ], 500); + } + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + try { + DB::beginTransaction(); + + $jenisKomoditas = JenisKomoditas::findOrFail($id); + $jenisKomoditas->update(['deleted_by' => auth()->id()]); + $jenisKomoditas->delete(); + + DB::commit(); + + return response()->json([ + 'status' => 'success', + 'message' => 'Data jenis komoditas berhasil dihapus.', + ]); + } catch (\Throwable $e) { + DB::rollBack(); + + Log::error('Gagal hapus jenis komoditas', [ + 'error' => $e->getMessage(), + 'id' => $id, + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal menghapus data: ' . $e->getMessage(), + ], 500); + } + } +} diff --git a/app/Http/Controllers/Web/Master/ProvinsiController.php b/app/Http/Controllers/Web/Master/ProvinsiController.php index c962629..88097d7 100644 --- a/app/Http/Controllers/Web/Master/ProvinsiController.php +++ b/app/Http/Controllers/Web/Master/ProvinsiController.php @@ -17,9 +17,12 @@ class ProvinsiController extends Controller $perPage = $request->query('per_page', 10); $search = $request->query('search', ''); - $provinsi = Provinsi::where('nama', 'like', "%$search%") - ->orWhere('kode', 'like', "%$search%") - ->orWhere('iso', 'like', "%$search%") + $provinsi = Provinsi::withCount('kabupatenKota') + ->where(function($query) use ($search) { + $query->where('nama', 'like', "%$search%") + ->orWhere('kode', 'like', "%$search%") + ->orWhere('iso', 'like', "%$search%"); + }) ->paginate($perPage); return view('master.provinsi.index', compact('title', 'provinsi')); diff --git a/app/Http/Controllers/Web/Master/VarietasController.php b/app/Http/Controllers/Web/Master/VarietasController.php new file mode 100644 index 0000000..f195ea4 --- /dev/null +++ b/app/Http/Controllers/Web/Master/VarietasController.php @@ -0,0 +1,236 @@ +get('per_page', 10); + $search = $request->get('search'); + + $varietas = Varietas::with(['komoditas', 'jenisKomoditas']) + ->when($search, function ($query) use ($search) { + $query->where(function ($q) use ($search) { + $q->where('nama', 'ILIKE', "%{$search}%") + ->orWhere('path', 'ILIKE', "%{$search}%") + ->orWhereHas('komoditas', function ($komoditasQuery) use ($search) { + $komoditasQuery->where('nama', 'ILIKE', "%{$search}%"); + }) + ->orWhereHas('jenisKomoditas', function ($jenisQuery) use ($search) { + $jenisQuery->where('nama', 'ILIKE', "%{$search}%"); + }); + }); + }) + ->latest() + ->paginate($perPage); + + $komoditas = Komoditas::orderBy('nama', 'asc')->get(); + + return view('master.varietas.index', compact( + 'varietas', + 'komoditas' + )); + } + + public function getByJenisKomoditas($jenisKomoditasId) + { + $varietas = Varietas::where('jenis_komoditas_id', $jenisKomoditasId)->get(); + + return response()->json([ + 'status' => 'success', + 'data' => $varietas, + ]); + } + + public function store(Request $request) + { + $komoditasTable = (new Komoditas)->getTable(); + $jenisKomoditasTable = (new JenisKomoditas)->getTable(); + $varietasTable = (new Varietas)->getTable(); + + $validated = $request->validate([ + 'komoditas_id' => [ + 'required', + 'integer', + Rule::exists($komoditasTable, 'id'), + ], + 'jenis_komoditas_id' => [ + 'required', + 'integer', + Rule::exists($jenisKomoditasTable, 'id') + ->where('komoditas_id', $request->komoditas_id), + ], + 'nama' => [ + 'required', + 'string', + 'max:255', + Rule::unique($varietasTable, 'nama') + ->where('komoditas_id', $request->komoditas_id) + ->where('jenis_komoditas_id', $request->jenis_komoditas_id), + ], + 'deskripsi' => [ + 'nullable', + 'string', + ], + ]); + + try { + DB::beginTransaction(); + + $varietas = Varietas::create([ + 'komoditas_id' => $validated['komoditas_id'], + 'jenis_komoditas_id' => $validated['jenis_komoditas_id'], + 'nama' => $validated['nama'], + 'deskripsi' => $validated['deskripsi'] ?? null, + 'created_by' => auth()->id(), + 'updated_by' => auth()->id(), + ]); + + DB::commit(); + + return response()->json([ + 'status' => 'success', + 'message' => 'Data varietas berhasil disimpan.', + 'data' => $varietas, + ]); + } catch (\Throwable $e) { + DB::rollBack(); + + Log::error('Gagal menyimpan varietas', [ + 'error' => $e->getMessage(), + 'request' => $request->all(), + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal menyimpan data: ' . $e->getMessage(), + ], 500); + } + } + + public function show(string $id) + { + try { + $varietas = Varietas::with(['komoditas', 'jenisKomoditas'])->findOrFail($id); + + return response()->json([ + 'status' => 'success', + 'data' => $varietas, + ]); + } catch (\Throwable $e) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data varietas tidak ditemukan.', + ], 404); + } + } + + public function update(Request $request, string $id) + { + $komoditasTable = (new Komoditas)->getTable(); + $jenisKomoditasTable = (new JenisKomoditas)->getTable(); + $varietasTable = (new Varietas)->getTable(); + + $validated = $request->validate([ + 'komoditas_id' => [ + 'required', + 'integer', + Rule::exists($komoditasTable, 'id'), + ], + 'jenis_komoditas_id' => [ + 'required', + 'integer', + Rule::exists($jenisKomoditasTable, 'id') + ->where('komoditas_id', $request->komoditas_id), + ], + 'nama' => [ + 'required', + 'string', + 'max:255', + Rule::unique($varietasTable, 'nama') + ->where('komoditas_id', $request->komoditas_id) + ->where('jenis_komoditas_id', $request->jenis_komoditas_id), + ], + 'deskripsi' => [ + 'nullable', + 'string', + ], + ]); + + try { + DB::beginTransaction(); + + $varietas = Varietas::findOrFail($id); + $varietas->update([ + 'komoditas_id' => $validated['komoditas_id'], + 'jenis_komoditas_id' => $validated['jenis_komoditas_id'], + 'nama' => $validated['nama'], + 'deskripsi' => $validated['deskripsi'] ?? null, + 'updated_by' => auth()->id(), + ]); + + DB::commit(); + + return response()->json([ + 'status' => 'success', + 'message' => 'Data varietas berhasil diperbarui.', + 'data' => $varietas->fresh(['komoditas', 'jenisKomoditas']), + ]); + } catch (\Throwable $e) { + DB::rollBack(); + + Log::error('Gagal update varietas', [ + 'error' => $e->getMessage(), + 'id' => $id, + 'request' => $request->all(), + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal mengupdate data: ' . $e->getMessage(), + ], 500); + } + } + + public function destroy(string $id) + { + try { + DB::beginTransaction(); + + $varietas = Varietas::findOrFail($id); + $varietas->update(['deleted_by' => auth()->id()]); + $varietas->delete(); + + DB::commit(); + + return response()->json([ + 'status' => 'success', + 'message' => 'Data varietas berhasil dihapus.', + ]); + } catch (\Throwable $e) { + DB::rollBack(); + + Log::error('Gagal hapus varietas', [ + 'error' => $e->getMessage(), + 'id' => $id, + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal menghapus data: ' . $e->getMessage(), + ], 500); + } + } +} diff --git a/app/Http/Controllers/Web/Produktivitas/LahanController.php b/app/Http/Controllers/Web/Produktivitas/LahanController.php index f9fddbf..09fdf9d 100644 --- a/app/Http/Controllers/Web/Produktivitas/LahanController.php +++ b/app/Http/Controllers/Web/Produktivitas/LahanController.php @@ -10,6 +10,7 @@ use App\Models\Pertanian\Adat; use App\Models\Wilayah\Provinsi; use Illuminate\Support\Facades\Storage; use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Str; class LahanController extends Controller { @@ -76,6 +77,25 @@ class LahanController extends Controller return view('produktivitas.lahan.index', compact('lahan', 'provinsi', 'profiles', 'adat', 'kabupaten', 'kecamatan', 'desa')); } + public function getByProfile(Request $request, $profileId) + { + $desaKelurahanId = $request->get('desa_kelurahan_id'); + + $lahan = Lahan::with('profile') + ->where('profile_id', $profileId) + ->when($desaKelurahanId, function ($query) use ($desaKelurahanId) { + $query->where('desa_kelurahan_id', $desaKelurahanId); + }) + ->orderBy('created_at', 'desc') + ->get(['id', 'nama', 'path', 'profile_id', 'desa_kelurahan_id']); + + return response()->json([ + 'status' => 'success', + 'data' => $lahan, + ]); + } + + public function create() { $provinsi = Provinsi::orderBy('nama', 'asc')->get(); @@ -112,8 +132,12 @@ class LahanController extends Controller return response()->json(['status' => 'error', 'message' => 'Format koordinat (path) bukan JSON array yang valid.'], 422); } } + + $userUID = Profile::where('id', $request->profile_id)->value('user_uid'); + $userUID = substr($userUID, 2); Lahan::create([ + 'uid' => 'LH' . $userUID . '-' . strtoupper(Str::random(10)), 'nama' => $request->nama, 'status_kepemilikan' => $request->status_kepemilikan, 'profile_id' => $request->profile_id, diff --git a/app/Http/Controllers/Web/Produktivitas/MapTanamanController.php b/app/Http/Controllers/Web/Produktivitas/MapTanamanController.php new file mode 100644 index 0000000..ef0e5c1 --- /dev/null +++ b/app/Http/Controllers/Web/Produktivitas/MapTanamanController.php @@ -0,0 +1,421 @@ +get('per_page', 10); + $search = $request->get('search'); + + $tanaman = Tanaman::query() + ->select([ + DB::raw('MIN(id) as id'), + 'profile_id', + 'lahan_id', + 'komoditas_id', + 'jenis_komoditas_id', + 'varietas_id', + DB::raw('COUNT(*) as total_tanaman'), + DB::raw("SUM(CASE WHEN kondisi_tanaman = 'sehat' THEN 1 ELSE 0 END) as total_sehat"), + DB::raw("SUM(CASE WHEN kondisi_tanaman = 'sakit' THEN 1 ELSE 0 END) as total_sakit"), + DB::raw("SUM(CASE WHEN kondisi_tanaman = 'mati' THEN 1 ELSE 0 END) as total_mati"), + DB::raw('MAX(created_at) as max_created_at'), + ]) + ->with([ + 'profile', + 'lahan.desaKelurahan.kecamatan.kabupatenkota.provinsi', + 'komoditas', + 'jenisKomoditas', + 'varietas', + ]) + ->when($search, function ($query) use ($search) { + $query->where(function ($q) use ($search) { + $q->whereHas('profile', fn ($q) => $q->where('nama', 'ILIKE', "%{$search}%")) + ->orWhereHas('lahan', fn ($q) => $q->where('nama', 'ILIKE', "%{$search}%")) + ->orWhereHas('komoditas', fn ($q) => $q->where('nama', 'ILIKE', "%{$search}%")) + ->orWhereHas('jenisKomoditas', fn ($q) => $q->where('nama', 'ILIKE', "%{$search}%")) + ->orWhereHas('varietas', fn ($q) => $q->where('nama', 'ILIKE', "%{$search}%")) + ->orWhereHas('lahan.desaKelurahan', fn ($q) => $q->where('nama', 'ILIKE', "%{$search}%")) + ->orWhereHas('lahan.desaKelurahan.kecamatan', fn ($q) => $q->where('nama', 'ILIKE', "%{$search}%")) + ->orWhereHas('lahan.desaKelurahan.kecamatan.kabupatenkota', fn ($q) => $q->where('nama', 'ILIKE', "%{$search}%")) + ->orWhereHas('lahan.desaKelurahan.kecamatan.kabupatenkota.provinsi', fn ($q) => $q->where('nama', 'ILIKE', "%{$search}%")); + }); + }) + ->groupBy([ + 'profile_id', + 'lahan_id', + 'komoditas_id', + 'jenis_komoditas_id', + 'varietas_id', + ]) + ->orderByDesc('max_created_at') + ->paginate($perPage); + + return view('produktivitas.mapTanaman.index', compact('tanaman')); + } + + private function getTanamanGroupByRepresentativeId($id) + { + $representative = Tanaman::findOrFail($id); + + $points = Tanaman::with([ + 'profile', + 'lahan.desaKelurahan.kecamatan.kabupatenkota.provinsi', + 'komoditas', + 'jenisKomoditas', + 'varietas', + ]) + ->where('profile_id', $representative->profile_id) + ->where('lahan_id', $representative->lahan_id) + ->where('komoditas_id', $representative->komoditas_id) + ->where('jenis_komoditas_id', $representative->jenis_komoditas_id) + ->where('varietas_id', $representative->varietas_id) + ->orderBy('id') + ->get(); + + return [ + 'representative' => $representative, + 'points' => $points, + ]; + } + + public function create() + { + $provinsi = Provinsi::select('id', 'nama', 'path') + ->orderBy('nama') + ->get(); + + $komoditas = Komoditas::orderBy('nama')->get(); + $profiles = Profile::orderBy('nama')->get(); + + return view('produktivitas.mapTanaman.add', compact( + 'provinsi', + 'komoditas', + 'profiles' + )); + } + + public function store(Request $request) + { + $desaKelurahanTable = (new DesaKelurahan)->getTable(); + $profileTable = (new Profile)->getTable(); + $lahanTable = (new Lahan)->getTable(); + $komoditasTable = (new Komoditas)->getTable(); + $jenisKomoditasTable = (new JenisKomoditas)->getTable(); + $varietasTable = (new Varietas)->getTable(); + + $validated = $request->validate([ + 'desa_kelurahan_id' => [ + 'required', + 'integer', + Rule::exists($desaKelurahanTable, 'id'), + ], + + 'profile_id' => [ + 'required', + 'integer', + Rule::exists($profileTable, 'id'), + ], + + 'lahan_id' => [ + 'required', + 'integer', + Rule::exists($lahanTable, 'id') + ->where('profile_id', $request->profile_id) + ->where('desa_kelurahan_id', $request->desa_kelurahan_id), + ], + + 'komoditas_id' => [ + 'required', + 'integer', + Rule::exists($komoditasTable, 'id'), + ], + + 'jenis_komoditas_id' => [ + 'required', + 'integer', + Rule::exists($jenisKomoditasTable, 'id') + ->where('komoditas_id', $request->komoditas_id), + ], + + 'varietas_id' => [ + 'required', + 'integer', + Rule::exists($varietasTable, 'id') + ->where('jenis_komoditas_id', $request->jenis_komoditas_id), + ], + + 'points' => [ + 'required', + 'array', + 'min:1', + ], + + 'points.*.latitude' => [ + 'required', + 'numeric', + ], + + 'points.*.longitude' => [ + 'required', + 'numeric', + ], + + 'points.*.kondisi_tanaman' => [ + 'required', + Rule::in(['sehat', 'sakit', 'mati']), + ], + + 'metode_pengumpulan' => [ + 'nullable', + 'string', + 'max:50', + ], + + 'deskripsi' => [ + 'nullable', + 'string', + ], + ], [ + 'points.required' => 'Minimal tambahkan satu titik tanaman pada peta.', + 'points.min' => 'Minimal tambahkan satu titik tanaman pada peta.', + 'points.*.latitude.required' => 'Latitude titik tanaman wajib ada.', + 'points.*.longitude.required' => 'Longitude titik tanaman wajib ada.', + 'points.*.kondisi_tanaman.required' => 'Kondisi tanaman wajib dipilih.', + ]); + + try { + DB::beginTransaction(); + + $lahan = Lahan::findOrFail($validated['lahan_id']); + $komoditas = Komoditas::findOrFail($validated['komoditas_id']); + $profile = Profile::findOrFail($validated['profile_id']); + + if (!$lahan->uid) { + throw new \Exception('UID lahan tidak ditemukan.'); + } + + $profileName = str_replace(' ', '', $profile->nama); + $lahanUidPart = substr($lahan->uid, 2); + + $existingCount = Tanaman::where('lahan_id', $validated['lahan_id']) + ->where('komoditas_id', $validated['komoditas_id']) + ->where('jenis_komoditas_id', $validated['jenis_komoditas_id']) + ->where('varietas_id', $validated['varietas_id']) + ->count(); + + $createdTanaman = []; + + foreach ($validated['points'] as $index => $point) { + $number = $existingCount + $index + 1; + + $createdTanaman[] = Tanaman::create([ + 'uid' => 'TM' . $lahanUidPart . '-' . strtoupper(Str::random(10)), + 'nama' => $komoditas->nama . ' - ' . $profileName . ' - ' . $number, + 'desa_kelurahan_id' => $validated['desa_kelurahan_id'], + + 'profile_id' => $validated['profile_id'], + 'lahan_id' => $validated['lahan_id'], + + 'komoditas_id' => $validated['komoditas_id'], + 'jenis_komoditas_id' => $validated['jenis_komoditas_id'], + 'varietas_id' => $validated['varietas_id'], + + 'latitude' => $point['latitude'], + 'longitude' => $point['longitude'], + 'kondisi_tanaman' => $point['kondisi_tanaman'], + 'metode_pengumpulan' => $validated['metode_pengumpulan'] ?? 'manual', + 'deskripsi' => $validated['deskripsi'] ?? null, + 'created_by' => auth()->id(), + 'updated_by' => auth()->id(), + ]); + } + + DB::commit(); + + return response()->json([ + 'status' => 'success', + 'message' => count($createdTanaman) . ' data tanaman berhasil disimpan.', + 'redirect' => route('map-tanaman.index'), + 'data' => $createdTanaman, + ]); + } catch (\Throwable $e) { + DB::rollBack(); + + Log::error('Gagal menyimpan tanaman bulk', [ + 'error' => $e->getMessage(), + 'request' => $request->all(), + ]); + + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal menyimpan data: ' . $e->getMessage(), + ], 500); + } + } + + public function show(string $id) + { + $group = $this->getTanamanGroupByRepresentativeId($id); + + $tanaman = $group['representative']->load([ + 'profile', + 'lahan.desaKelurahan.kecamatan.kabupatenkota.provinsi', + 'komoditas', + 'jenisKomoditas', + 'varietas', + ]); + + $points = $group['points']; + + return view('produktivitas.mapTanaman.view', compact( + 'tanaman', + 'points' + )); + } + public function edit(string $id) + { + $group = $this->getTanamanGroupByRepresentativeId($id); + + $tanaman = $group['representative']->load([ + 'profile', + 'lahan.desaKelurahan.kecamatan.kabupatenkota.provinsi', + 'komoditas', + 'jenisKomoditas', + 'varietas', + ]); + + $points = $group['points']; + + return view('produktivitas.mapTanaman.edit', compact( + 'tanaman', + 'points' + )); + } + + public function update(Request $request, string $id) + { + $group = $this->getTanamanGroupByRepresentativeId($id); + + $representative = $group['representative']; + + $validated = $request->validate([ + 'points' => ['required', 'array', 'min:1'], + 'points.*.id' => ['nullable', 'integer', 'exists:map_tanaman,id'], + 'points.*.latitude' => ['required', 'numeric'], + 'points.*.longitude' => ['required', 'numeric'], + 'points.*.kondisi_tanaman' => [ + 'required', + Rule::in(['sehat', 'sakit', 'mati']), + ], + 'points.*._delete' => ['nullable'], + ]); + + try { + DB::beginTransaction(); + + foreach ($validated['points'] as $point) { + if (!empty($point['_delete']) && !empty($point['id'])) { + $tanaman = Tanaman::where('id', $point['id']) + ->where('profile_id', $representative->profile_id) + ->where('lahan_id', $representative->lahan_id) + ->where('komoditas_id', $representative->komoditas_id) + ->where('jenis_komoditas_id', $representative->jenis_komoditas_id) + ->where('varietas_id', $representative->varietas_id) + ->first(); + + if ($tanaman) { + $tanaman->deleted_by = auth()->id(); + $tanaman->save(); + $tanaman->delete(); + } + + continue; + } + + if (!empty($point['id'])) { + $tanaman = Tanaman::where('id', $point['id']) + ->where('profile_id', $representative->profile_id) + ->where('lahan_id', $representative->lahan_id) + ->where('komoditas_id', $representative->komoditas_id) + ->where('jenis_komoditas_id', $representative->jenis_komoditas_id) + ->where('varietas_id', $representative->varietas_id) + ->firstOrFail(); + + $tanaman->update([ + 'latitude' => $point['latitude'], + 'longitude' => $point['longitude'], + 'kondisi_tanaman' => $point['kondisi_tanaman'], + 'updated_by' => auth()->id(), + ]); + } + } + + DB::commit(); + + return response()->json([ + 'status' => 'success', + 'message' => 'Data titik tanaman berhasil diperbarui.', + 'redirect' => route('map-tanaman.index'), + ]); + } catch (\Throwable $e) { + DB::rollBack(); + + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal memperbarui data: ' . $e->getMessage(), + ], 500); + } + } + + public function destroy(string $id) + { + try { + DB::beginTransaction(); + + $group = $this->getTanamanGroupByRepresentativeId($id); + $points = $group['points']; + + foreach ($points as $point) { + $point->update([ + 'deleted_by' => auth()->id(), + ]); + + $point->delete(); + } + + DB::commit(); + + return response()->json([ + 'status' => 'success', + 'message' => $points->count() . ' titik tanaman berhasil dihapus.', + ]); + } catch (\Throwable $e) { + DB::rollBack(); + + return response()->json([ + 'status' => 'error', + 'message' => 'Gagal menghapus data: ' . $e->getMessage(), + ], 500); + } + } +} diff --git a/app/Http/Controllers/Web/Produktivitas/ProfileController.php b/app/Http/Controllers/Web/Produktivitas/ProfileController.php index 4f637b0..f2ffb46 100644 --- a/app/Http/Controllers/Web/Produktivitas/ProfileController.php +++ b/app/Http/Controllers/Web/Produktivitas/ProfileController.php @@ -35,6 +35,25 @@ class ProfileController extends Controller return view('master.profile.index', compact('profiles', 'provinsi')); } + public function getByDesaKelurahan($desaId) + { + try { + $profiles = Profile::where('desa_kelurahan_id', $desaId) + ->orderBy('nama', 'asc') + ->get(['id', 'nama']); + + return response()->json([ + 'status' => 'success', + 'data' => $profiles, + ]); + } catch (\Throwable $e) { + return response()->json([ + 'status' => 'error', + 'message' => $e->getMessage(), + ], 500); + } + } + public function create() { $provinsi = Provinsi::orderBy('nama', 'asc')->get(); diff --git a/app/Http/Controllers/Web/Program/AktivitasController.php b/app/Http/Controllers/Web/Program/AktivitasController.php index 9c34155..1f88669 100644 --- a/app/Http/Controllers/Web/Program/AktivitasController.php +++ b/app/Http/Controllers/Web/Program/AktivitasController.php @@ -54,6 +54,9 @@ class AktivitasController extends Controller 'jenis_aktivitas' => ['required','string','max:255', Rule::in(['tujuan', 'aktivitas']),], 'indikator' => ['required','string'], 'deskripsi' => ['required','string'], + 'parent_id' => [ + Rule::requiredIf($request->jenis_aktivitas === 'aktivitas'), + 'nullable','exists:program_aktivitas,id',], ]); Aktivitas::create([...$validated, @@ -125,6 +128,9 @@ class AktivitasController extends Controller 'jenis_aktivitas' => ['required','string','max:255', Rule::in(['tujuan', 'aktivitas']),], 'indikator' => ['required','string'], 'deskripsi' => ['required','string'], + 'parent_id' => [ + Rule::requiredIf($request->jenis_aktivitas === 'aktivitas'), + 'nullable','exists:program_aktivitas,id'], ]); $aktivitas->update([...$validated, diff --git a/app/Http/Controllers/Web/Program/FrameworkController.php b/app/Http/Controllers/Web/Program/FrameworkController.php index 365d6fe..10df7cc 100644 --- a/app/Http/Controllers/Web/Program/FrameworkController.php +++ b/app/Http/Controllers/Web/Program/FrameworkController.php @@ -102,7 +102,8 @@ class FrameworkController extends Controller abort(403); } - $framework = Framework::findOrFail($id); + $framework = Framework::with('transformasi')->with('transformasi.tujuan') + ->findOrFail($id); $program = Program::all(); return view('program.framework.edit', compact('framework', 'program')); diff --git a/app/Http/Controllers/Web/Program/RealisasiController.php b/app/Http/Controllers/Web/Program/RealisasiController.php new file mode 100644 index 0000000..0e21a19 --- /dev/null +++ b/app/Http/Controllers/Web/Program/RealisasiController.php @@ -0,0 +1,208 @@ +has('frameworkid'), function ($query) use ($request) { + $query->where('id', $request->frameworkid); + })->latest()->first(); + if ($framework == null) { + abort(404); + } + + $ringkasanIsiRealisasi = DB::select('select count(pt.id) count_target, sum(case when rl.id > 0 then 1 else 0 end) count_realisasi + from program_target pt + inner join program_aktivitas pa on pt.aktivitas_id = pa.id + inner join program_transformasi ptr on pa.transformasi_id = ptr.id + inner join program_framework pf on ptr.framework_id = pf.id + left join + (select target_id, max(id) id from program_realisasi pr + group by target_id) rl on pt.id = rl.target_id + where + pt.deleted_at is null + and pa.deleted_at is null + and ptr.deleted_at is null + and pf.id = ? + ', [$framework->id]); + $ringkasanIsiRealisasi = collect($ringkasanIsiRealisasi)->map(function ($item) { + $item->persentase = $item->count_target > 0 ? round(($item->count_realisasi / $item->count_target) * 100, 2) : 0; + return $item; + })->first(); + + $ringkasanCapaianRealisasi = DB::select("select round(avg(percent_realisasi)::numeric * 100, 2) persentase, + sum(case when percent_realisasi >= 0.8 then 1 else 0 end) capaian_80, + sum(case when percent_realisasi >= 0.5 and percent_realisasi < 0.8 then 1 else 0 end) capaian_50_80, + sum(case when percent_realisasi < 0.5 then 1 else 0 end) capaian_50 + from + (select pt.id target_id, pr.id realisasi_id, + pt.tipe, + case + when pt.tipe = 'integer' then pt.target_integer + when pt.tipe = 'float' then pt.target_float + when pt.tipe = 'boolean' then pt.target_boolean::integer + end target, + case + when pt.tipe = 'integer' then pr.realisasi_integer + when pt.tipe = 'float' then pr.realisasi_float + when pt.tipe = 'boolean' then pr.realisasi_boolean::integer + end realisasi, + case + when pt.tipe = 'integer' then pr.realisasi_integer::float / pt.target_integer::float + when pt.tipe = 'float' then pr.realisasi_float / pt.target_float + when pt.tipe = 'boolean' then pr.realisasi_boolean::integer / pt.target_boolean::integer + end percent_realisasi + from program_target pt + inner join program_aktivitas pa on pt.aktivitas_id = pa.id + inner join program_transformasi ptr on pa.transformasi_id = ptr.id + inner join program_framework pf on ptr.framework_id = pf.id + left join + (select target_id, max(id) id from program_realisasi pr + group by target_id) rl on pt.id = rl.target_id + left join program_realisasi pr on rl.id = pr.id and pr.target_id = pt.id + where + pt.deleted_at is null + and pa.deleted_at is null + and ptr.deleted_at is null + and pf.id = ?) v", + [$framework->id]); + $ringkasanCapaianRealisasi = collect($ringkasanCapaianRealisasi)->first(); + + $ringkasanVerifikasi = DB::select("select count(pt.id) count_target, + sum(case when count_verifikasi > 0 then 1 else 0 end) count_verifikasi, + sum(case when count_verifikasi = 0 or count_verifikasi is null then 1 else 0 end) count_unverifikasi + from program_target pt + inner join program_aktivitas pa on pt.aktivitas_id = pa.id + inner join program_transformasi ptr on pa.transformasi_id = ptr.id + inner join program_framework pf on ptr.framework_id = pf.id + left join + (select target_id, max(id) id from program_realisasi pr + group by target_id) rl on pt.id = rl.target_id + left join + (select realisasi_id, count(pv.id) count_verifikasi from program_verifikasi pv + group by realisasi_id) rv + on rv.realisasi_id = rl.id + where + pt.deleted_at is null + and pa.deleted_at is null + and ptr.deleted_at is null + and pf.id = ?", + [$framework->id]); + $ringkasanVerifikasi = collect($ringkasanVerifikasi)->map(function ($item) { + $item->persentase = $item->count_target > 0 ? round(($item->count_verifikasi / $item->count_target) * 100, 2) : 0; + return $item; + })->first(); + + return view('program.realisasi.index', compact('framework', 'ringkasanIsiRealisasi', 'ringkasanCapaianRealisasi', 'ringkasanVerifikasi')); + } + + public function create() + { + // + } + + public function store(Request $request): JsonResponse + { + if (!Gate::any(['super_admin', 'admin'])) { + abort(403); + } + + $validated = $request->validate([ + 'target_id' => ['required', 'integer', 'exists:program_target,id'], + 'realisasi_integer' => ['nullable', 'integer'], + 'realisasi_float' => ['nullable', 'numeric'], + 'realisasi_boolean' => ['nullable', 'boolean'], + ]); + + $realisasi = Realisasi::create([...$validated, + 'created_by' => auth()->id(), + 'updated_by' => auth()->id() + ]); + + return response()->json([ + 'status' => 'success', + 'message' => 'Target berhasil ditambahkan.' + ], 201); + } + + public function show(string $id) + { + if (!Gate::any(['super_admin', 'admin'])) { + abort(403); + } + + $realisasi = Realisasi::with('target', 'target.aktivitas', 'target.aktivitas.transformasi', 'verifikasi') + ->find($id); + if (!$realisasi) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data realisasi tidak ditemukan.' + ], 404); + } + + return response()->json([ + 'status' => 'success', + 'data' => $realisasi + ]); + } + + public function edit(string $id) + { + // + } + + public function update(Request $request, string $id): JsonResponse + { + if (!Gate::any(['super_admin', 'admin'])) { + abort(403); + } + + $realisasi = Realisasi::find($id); + if (!$realisasi) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data realisasi tidak ditemukan.' + ], 404); + } + + $validated = $request->validate([ + 'target_id' => ['required', 'integer', 'exists:program_target,id'], + 'realisasi_integer' => ['nullable', 'integer'], + 'realisasi_float' => ['nullable', 'numeric'], + 'realisasi_boolean' => ['nullable', 'boolean'], + ]); + + $realisasi->update([...$validated, + 'updated_by' => auth()->id(), + ]); + + return response()->json([ + 'status' => 'success', + 'message' => 'Target berhasil diperbarui.' + ], 200); + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id) + { + // + } +} diff --git a/app/Http/Controllers/Web/Program/TargetController.php b/app/Http/Controllers/Web/Program/TargetController.php index b47ee4f..ac4ab67 100644 --- a/app/Http/Controllers/Web/Program/TargetController.php +++ b/app/Http/Controllers/Web/Program/TargetController.php @@ -69,6 +69,16 @@ class TargetController extends Controller 'nullable', 'boolean' ], + 'tipe_realisasi' => [ + 'nullable', + 'string', + Rule::in(['manual', 'referensi']) + ], + 'value_query' => [ + Rule::requiredIf($request->tipe_realisasi === 'referensi'), + 'nullable', + 'string' + ] ]); } @@ -77,9 +87,6 @@ class TargetController extends Controller } - /** - * Store a newly created resource in storage. - */ public function store(Request $request, string $frameworkId, string $transformasiId, string $aktivitasId): JsonResponse { if (!Gate::any(['super_admin', 'admin'])) { @@ -109,12 +116,32 @@ class TargetController extends Controller ], 201); } - /** - * Display the specified resource. - */ - public function show(string $id) + public function show(string $frameworkId, string $transformasiId, string $aktivitasId, string $id) { - // + if (!Gate::any(['super_admin', 'admin'])) { + abort(403); + } + + $target = Target::with('realisasi', 'latestRealisasi') + ->whereHas('aktivitas.transformasi', function ($query) use ($frameworkId) { + $query->where('framework_id', $frameworkId); + }) + ->whereHas('aktivitas', function ($query) use ($transformasiId) { + $query->where('transformasi_id', $transformasiId); + }) + ->where('aktivitas_id', $aktivitasId)->find($id); + + if (!$target) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data target tidak ditemukan.' + ], 404); + } + + return response()->json([ + 'status' => 'success', + 'data' => $target + ]); } /** @@ -128,16 +155,67 @@ class TargetController extends Controller /** * Update the specified resource in storage. */ - public function update(Request $request, string $id) + public function update(Request $request, string $frameworkId, string $transformasiId, string $aktivitasId, string $id) { - // + if (!Gate::any(['super_admin', 'admin'])) { + abort(403); + } + + $target = Target::whereHas('aktivitas.transformasi', function ($query) use ($frameworkId) { + $query->where('framework_id', $frameworkId); + }) + ->whereHas('aktivitas', function ($query) use ($transformasiId) { + $query->where('transformasi_id', $transformasiId); + }) + ->where('aktivitas_id', $aktivitasId)->find($id); + + if (!$target) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data target tidak ditemukan.' + ], 404); + } + + $validated = $this->validated($request); + $target->update([...$validated, + 'updated_by' => auth()->id(), + ]); + + return response()->json([ + 'status' => 'success', + 'message' => 'Target berhasil diperbarui.' + ], 200); } - /** - * Remove the specified resource from storage. - */ - public function destroy(string $id) + public function destroy(string $frameworkId, string $transformasiId, string $aktivitasId, string $id) { - // + if (!Gate::any(['super_admin', 'admin'])) { + abort(403); + } + + $target = Target::whereHas('aktivitas.transformasi', function ($query) use ($frameworkId) { + $query->where('framework_id', $frameworkId); + }) + ->whereHas('aktivitas', function ($query) use ($transformasiId) { + $query->where('transformasi_id', $transformasiId); + }) + ->where('aktivitas_id', $aktivitasId)->find($id); + + if (!$target) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data target tidak ditemukan.' + ], 404); + } + + $target->update([ + 'deleted_by' => auth()->id(), + ]); + $target->delete(); + + return response()->json([ + 'status' => 'success', + 'message' => 'Target berhasil dihapus.' + ], 200); } } diff --git a/app/Http/Controllers/Web/Program/VerifikasiController.php b/app/Http/Controllers/Web/Program/VerifikasiController.php new file mode 100644 index 0000000..932da21 --- /dev/null +++ b/app/Http/Controllers/Web/Program/VerifikasiController.php @@ -0,0 +1,107 @@ +validate([ + 'realisasi_id' => ['required', 'integer', 'exists:program_realisasi,id'], + 'dokumen' => ['required', 'string'], + 'link_dokumen' => ['required', 'url'], + ]); + + $verifikasi = Verifikasi::create([...$validated, + 'created_by' => auth()->id(), + 'updated_by' => auth()->id() + ]); + + return response()->json([ + 'status' => 'success', + 'message' => 'Dokumen bukti berhasil ditambahkan.' + ], 201); + } + + /** + * Display the specified resource. + */ + public function show(string $id): JsonResponse + { + // + } + + /** + * Show the form for editing the specified resource. + */ + public function edit(string $id) + { + // + } + + /** + * Update the specified resource in storage. + */ + public function update(Request $request, string $id) + { + // + } + + /** + * Remove the specified resource from storage. + */ + public function destroy(string $id): JsonResponse + { + if (!Gate::any(['super_admin', 'admin'])) { + abort(403); + } + + $verifikasi = Verifikasi::find($id); + if (!$verifikasi) { + return response()->json([ + 'status' => 'error', + 'message' => 'Data verifikasi tidak ditemukan.' + ], 404); + } + + $verifikasi->update(['updated_by' => auth()->id()]); + $verifikasi->delete(); + return response()->json([ + 'status' => 'success', + 'message' => 'Dokumen bukti berhasil dihapus.' + ]); + } +} diff --git a/app/Http/Controllers/Web/ProgramController.php b/app/Http/Controllers/Web/ProgramController.php index e429e4e..5a08d80 100644 --- a/app/Http/Controllers/Web/ProgramController.php +++ b/app/Http/Controllers/Web/ProgramController.php @@ -49,6 +49,7 @@ class ProgramController extends Controller 'sub_domain' => ['required', Rule::unique(Program::class, 'sub_domain')], 'tahun' => ['required', 'integer'], 'instansi' => ['nullable', 'string'], + 'nama_singkat' => ['required', 'string'], ]); if ($validator->fails()) { @@ -94,6 +95,9 @@ class ProgramController extends Controller $validator = Validator::make($request->all(), [ 'nama' => ['required'], 'sub_domain' => ['required', Rule::unique(Program::class, 'sub_domain')->ignore($id)], + 'tahun' => ['required', 'integer'], + 'instansi' => ['nullable', 'string'], + 'nama_singkat' => ['required', 'string'], ]); if ($validator->fails()) { @@ -106,9 +110,7 @@ class ProgramController extends Controller return redirect()->route('program.index')->with('error', 'Data program tidak ditemukan!'); } - $program->update([ - 'nama' => $request->nama, - 'sub_domain' => $request->sub_domain, + $program->update([...$validator->validated(), 'updated_by' => auth()->id(), ]); diff --git a/app/Http/Resources/Core/ProgramKomoditasResource.php b/app/Http/Resources/Core/ProgramKomoditasResource.php new file mode 100644 index 0000000..313e256 --- /dev/null +++ b/app/Http/Resources/Core/ProgramKomoditasResource.php @@ -0,0 +1,27 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'program_id' => $this->program_id, + 'komoditas_id' => $this->komoditas_id, + 'default' => $this->default, + 'komoditas' => $this->komoditas ? new KomoditasResource($this->komoditas) : null, + ]; + } +} diff --git a/app/Http/Resources/Core/ProgramLokasiResource.php b/app/Http/Resources/Core/ProgramLokasiResource.php new file mode 100644 index 0000000..934c296 --- /dev/null +++ b/app/Http/Resources/Core/ProgramLokasiResource.php @@ -0,0 +1,36 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'program_id' => $this->program_id, + 'provinsi_id' => $this->provinsi_id, + 'kabupaten_kota_id' => $this->kabupaten_kota_id, + 'kecamatan_id' => $this->kecamatan_id, + 'desa_kelurahan_id' => $this->desa_kelurahan_id, + 'default' => $this->default, + 'provinsi' => $this->provinsi ? new ProvinsiResource($this->provinsi) : null, + 'kabupatenKota' => $this->kabupatenKota ? new KabupatenKotaResource($this->kabupatenKota) : null, + 'kecamatan' => $this->kecamatan ? new KecamatanResource($this->kecamatan) : null, + 'desaKelurahan' => $this->desaKelurahan ? new DesaKelurahanResource($this->desaKelurahan) : null, + ]; + } +} diff --git a/app/Http/Resources/Core/ProgramResource.php b/app/Http/Resources/Core/ProgramResource.php new file mode 100644 index 0000000..2d4f05a --- /dev/null +++ b/app/Http/Resources/Core/ProgramResource.php @@ -0,0 +1,34 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'nama' => $this->nama, + 'sub_domain' => $this->sub_domain, + 'tahun' => $this->tahun, + 'instansi' => $this->instansi, + 'nama_singkat' => $this->nama_singkat, + 'komoditas' => $this->komoditas()->with('komoditas')->get()->map(function ($item) { + return new ProgramKomoditasResource($item); + }), + 'lokasi' => $this->lokasi()->with(['provinsi', 'kabupatenKota', 'kecamatan', 'desaKelurahan'])->get()->map(function ($item) { + return new ProgramLokasiResource($item); + }), + ]; + } +} diff --git a/app/Http/Resources/Ekosistem/FormBasisDataResource.php b/app/Http/Resources/Ekosistem/FormBasisDataResource.php new file mode 100644 index 0000000..9b64112 --- /dev/null +++ b/app/Http/Resources/Ekosistem/FormBasisDataResource.php @@ -0,0 +1,32 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'tabel_id' => $this->tabel_id, + 'parent_id' => $this->parent_id, + 'tipe' => $this->tipe, + 'column_width' => $this->column_width, + 'kolom_id' => $this->kolom_id, + 'section_label' => $this->section_label, + 'urutan' => $this->urutan, + 'kolom' => new KolomBasisDataResource($this->whenLoaded('kolom')), + 'form' => FormBasisDataResource::collection($this->whenLoaded('form')), + ]; + } +} diff --git a/app/Http/Resources/Ekosistem/KolomBasisDataResource.php b/app/Http/Resources/Ekosistem/KolomBasisDataResource.php new file mode 100644 index 0000000..f156a3d --- /dev/null +++ b/app/Http/Resources/Ekosistem/KolomBasisDataResource.php @@ -0,0 +1,32 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'label' => $this->label, + 'kolom' => $this->kolom, + 'tipe' => $this->tipe, + 'validasi' => $this->validasi, + 'urutan' => $this->urutan, + 'relasi_tabel' => $this->relasi_tabel, + 'relasi_kolom_id' => $this->relasi_kolom_id, + 'relasi_kolom_label' => $this->relasi_kolom_label, + 'enum_opsi' => $this->enum_opsi, + 'is_active' => $this->is_active, + 'is_browse' => $this->is_browse, + ]; + } +} diff --git a/app/Http/Resources/Ekosistem/TabelBasisDataResource.php b/app/Http/Resources/Ekosistem/TabelBasisDataResource.php new file mode 100644 index 0000000..5b4de9e --- /dev/null +++ b/app/Http/Resources/Ekosistem/TabelBasisDataResource.php @@ -0,0 +1,35 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'tabel' => $this->tabel, + 'label' => $this->label, + 'menu' => $this->menu, + 'deskripsi' => $this->deskripsi, + // 'group_id' => $this->group_id, + 'urutan' => $this->urutan, + // 'group' => new GroupBasisDataResource($this->whenLoaded('group')), + 'kolom' => KolomBasisDataResource::collection($this->whenLoaded('kolom')), + //'ringkasan' => RingkasanBasisDataResource::collection($this->whenLoaded('ringkasan')), + 'form' => FormBasisDataResource::collection($this->whenLoaded('form')), + ]; + } +} diff --git a/app/Http/Resources/Ekosistem/ValueBasisDataResource.php b/app/Http/Resources/Ekosistem/ValueBasisDataResource.php new file mode 100644 index 0000000..b377c8b --- /dev/null +++ b/app/Http/Resources/Ekosistem/ValueBasisDataResource.php @@ -0,0 +1,34 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'program_id' => $this->program_id, + 'tabel_id' => $this->tabel_id, + 'kolom_id' => $this->kolom_id, + 'value_id' => $this->value_id, + 'value_varchar' => $this->value_varchar, + 'value_integer' => $this->value_integer, + 'value_float' => $this->value_float, + 'value_boolean' => $this->value_boolean, + 'value_date' => $this->value_date, + 'value_datetime' => $this->value_datetime, + 'value_text' => $this->value_text, + 'value_relasi' => $this->value_relasi, + 'value_enum' => $this->value_enum, + ]; + } +} diff --git a/app/Http/Resources/Pertanian/JenisKomoditasResource.php b/app/Http/Resources/Pertanian/JenisKomoditasResource.php new file mode 100644 index 0000000..f35045c --- /dev/null +++ b/app/Http/Resources/Pertanian/JenisKomoditasResource.php @@ -0,0 +1,26 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'nama' => $this->nama, + 'kode' => $this->kode, + 'keterangan' => $this->keterangan, + 'created_at' => $this->created_at?->toIso8601String(), + 'updated_at' => $this->updated_at?->toIso8601String(), + ]; + } +} \ No newline at end of file diff --git a/app/Http/Resources/Pertanian/LahanResource.php b/app/Http/Resources/Pertanian/LahanResource.php index 16eb72a..c8b7e07 100644 --- a/app/Http/Resources/Pertanian/LahanResource.php +++ b/app/Http/Resources/Pertanian/LahanResource.php @@ -25,6 +25,7 @@ class LahanResource extends JsonResource 'luas_lahan' => $this->luas_lahan, 'keluarga_pengelola_id' => $this->keluarga_pengelola_id, 'adat_pemilik_id' => $this->adat_pemilik_id, + 'uid' => $this->uid, ]; } } diff --git a/app/Http/Resources/Pertanian/TanamanResource.php b/app/Http/Resources/Pertanian/TanamanResource.php index d1234fd..f2128ec 100644 --- a/app/Http/Resources/Pertanian/TanamanResource.php +++ b/app/Http/Resources/Pertanian/TanamanResource.php @@ -24,6 +24,7 @@ class TanamanResource extends JsonResource 'latitude' => $this->latitude, 'longitude' => $this->longitude, 'kondisi_tanaman' => $this->kondisi_tanaman, + 'uid' => $this->uid, ]; } } diff --git a/app/Http/Resources/ProfileResource.php b/app/Http/Resources/ProfileResource.php index e64348d..57ef1c7 100644 --- a/app/Http/Resources/ProfileResource.php +++ b/app/Http/Resources/ProfileResource.php @@ -7,6 +7,7 @@ use Illuminate\Http\Resources\Json\JsonResource; use App\Http\Resources\Wilayah\DesaKelurahanResource; use App\Http\Resources\UserResource; +use phpDocumentor\Reflection\Types\This; class ProfileResource extends JsonResource { @@ -31,6 +32,7 @@ class ProfileResource extends JsonResource 'desa_kelurahan_id' => $this->desa_kelurahan_id, 'desa_kelurahan' => $this->desaKelurahan ? new DesaKelurahanResource($this->desaKelurahan) : null, 'user' => $this->user ? new UserResource($this->user) : null, + 'user_uid' => $this->user_uid, ]; } } diff --git a/app/Http/Resources/UserResource.php b/app/Http/Resources/UserResource.php index 4af8ec0..da4db94 100644 --- a/app/Http/Resources/UserResource.php +++ b/app/Http/Resources/UserResource.php @@ -20,6 +20,7 @@ class UserResource extends JsonResource 'email' => $this->email, 'email_verified_at' => $this->email_verified_at, 'role' => $this->role, + 'uid' => $this->uid, ]; } } diff --git a/app/Models/Core/Program.php b/app/Models/Core/Program.php index e4f7240..30f7c1a 100644 --- a/app/Models/Core/Program.php +++ b/app/Models/Core/Program.php @@ -20,6 +20,7 @@ class Program extends Model "sub_domain", "tahun", "instansi", + "nama_singkat", ]; public function komoditas(): HasMany diff --git a/app/Models/Ekosistem/FormBasisData.php b/app/Models/Ekosistem/FormBasisData.php new file mode 100644 index 0000000..75ecef6 --- /dev/null +++ b/app/Models/Ekosistem/FormBasisData.php @@ -0,0 +1,383 @@ + LayoutForm::class + ]; + + protected static function booted() + { + static::creating(function ($model) { + $max = self::where('tabel_id', $model->tabel_id)->max('urutan'); + $model->urutan = ($max ?? 0) + 1; + }); + static::addGlobalScope('order', function (Builder $builder) { + $builder->orderBy('urutan'); + }); + } + + + public function tabel(): BelongsTo + { + return $this->belongsTo(TabelBasisData::class, 'tabel_id'); + } + + public function form(): HasMany + { + return $this->hasMany(FormBasisData::class, 'parent_id') + ->with('form') + ->orderBy('urutan'); + } + + public function kolom(): BelongsTo + { + return $this->belongsTo(KolomBasisData::class, 'kolom_id'); + } + + public function renderHtml(string $state, $errors = null, $optionsFK = null, $values = null, $framework = 'bootstrap'): string + { + if ($framework == 'tailwind') { + return match ($this->tipe) { + LayoutForm::COLUMN => '
' + .$this->childHtml($state, $errors, $optionsFK, $values, $framework).'
', + LayoutForm::VALUE => $this->value($state, $errors, $optionsFK, $values, $framework), + LayoutForm::SECTION => '

'.$this->section_label.'

', + LayoutForm::ROW => '
'.$this->childHtml($state, $errors, $optionsFK, $values, $framework).'
', + LayoutForm::HR_LINE => '
', + default => '' + }; + } else { + return match ($this->tipe) { + LayoutForm::COLUMN => '
' + .$this->childHtml($state, $errors, $optionsFK, $values, $framework).'
', + LayoutForm::VALUE => $this->value($state, $errors, $optionsFK, $values, $framework), + LayoutForm::SECTION => '
'.$this->section_label.'
', + LayoutForm::ROW => '
'.$this->childHtml($state, $errors, $optionsFK, $values, $framework).'
', + LayoutForm::HR_LINE => '
', + default => '' + }; + } + } + + + private function childHtml(string $state, $errors = null, $optionsFK = null, $values = null, $framework = 'bootstrap'): string + { + $html = ''; + foreach ($this->form as $form) { + $html .= $form->renderHtml($state, $errors, $optionsFK, $values, $framework); + } + return $html; + } + + private function value(string $state, $errors = null, $optionsFK = null, $values = null, $framework = 'bootstrap'): string + { + $html = ''; + if ($this->kolom) { + $k = $this->kolom; + + $value = $values ? $values->where('kolom_id', $k->id)->first() : null; + $oldValue = old($k->kolom); + + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + + if($k->tipe == 'varchar') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } + elseif($k->tipe == 'text') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } + elseif($k->tipe == 'integer') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } + elseif($k->tipe == 'float') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } + elseif($k->tipe == 'date') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } + elseif($k->tipe == 'datetime'){ + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } + elseif($k->tipe == 'boolean') { + if ($state == 'preview' || $state == 'view') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } elseif ($state == 'add') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } elseif ($state == 'edit') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } + } + elseif($k->tipe == 'foreign_key') { + if ($state == 'preview' || $state == 'view') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } elseif ($state == 'add') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } elseif ($state == 'edit') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } + } + elseif($k->tipe == 'enum') { + if ($state == 'preview' || $state == 'view') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } elseif ($state == 'add') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } elseif ($state == 'edit') { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } + } + else { + if ($framework == 'tailwind') { + $html .= ''; + } else { + $html .= ''; + } + } + + $errorClass = $errors && $errors->has($k->kolom) + ? 'is-invalid' + : ''; + + if ($state == 'preview') { + $html = Str::of($html) + ->replace('kolom_value', '') + ->replace('kolom_name', $k->kolom) + ->replace('kolom_error', '') + ->replace('kolom_required', ''); + } elseif ($state == 'add') { + $html = Str::of($html) + ->replace('kolom_value', e($oldValue)) + ->replace('kolom_name', $k->kolom) + ->replace('kolom_error', $errorClass) + ->replace('kolom_required', '') + ->replace('readonly', ''); + } elseif ($state == 'edit') { + $html = Str::of($html) + ->replace('kolom_value', e($oldValue ?? ($value ? ($value['value_'.$k->tipe]) : null))) + ->replace('kolom_name', $k->kolom) + ->replace('kolom_error', $errorClass) + ->replace('kolom_required', '') + ->replace('readonly', ''); + } elseif ($state == 'view') { + $html = Str::of($html) + ->replace('kolom_name', $k->kolom) + ->replace('kolom_error', '') + ->replace('kolom_required', ''); + + if ($k->tipe == 'boolean') { + $html = Str::of($html) + ->replace('kolom_value', e($value ? ($value['value_'.$k->tipe] == 1 ? 'Ya' : 'Tidak') : null)); + } elseif($k->tipe == 'foreign_key') { + $html = Str::of($html) + ->replace('kolom_value', e($value ? $value['value_relasi_label'] : null)); + } elseif($k->tipe == 'enum') { + $html = Str::of($html) + ->replace('kolom_value', e($value ? $value['value_enum_label'] : null)); + } else { + $html = Str::of($html) + ->replace('kolom_value', e($value ? $value['value_'.$k->tipe] : null)); + } + } + } + + return $html; + } +} diff --git a/app/Models/Ekosistem/TabelBasisData.php b/app/Models/Ekosistem/TabelBasisData.php index b238269..5d04b2e 100644 --- a/app/Models/Ekosistem/TabelBasisData.php +++ b/app/Models/Ekosistem/TabelBasisData.php @@ -7,10 +7,14 @@ use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasOne; +use Illuminate\Database\Eloquent\Factories\HasFactory; +use Illuminate\Database\Eloquent\Casts\Attribute; use App\Models\Ekosistem\KolomBasisData; use App\Models\Ekosistem\GroupBasisData; use App\Models\Ekosistem\RingkasanBasisData; +use App\Models\Ekosistem\FormBasisData; class TabelBasisData extends Model { @@ -53,4 +57,20 @@ class TabelBasisData extends Model { return $this->hasMany(RingkasanBasisData::class, 'tabel_id')->orderBy('urutan'); } + + public function form(): HasMany + { + return $this->hasMany(FormBasisData::class, 'tabel_id') + ->whereNull('parent_id') + ->orderBy('urutan'); + } + + public function renderHtml(string $state, $errors = null, $optionsFK = null, $values = null): string + { + $html = ''; + foreach ($this->form as $form) { + $html .= $form->renderHtml($state, $errors, $optionsFK, $values); + } + return $html; + } } diff --git a/app/Models/Map/Lahan.php b/app/Models/Map/Lahan.php index 78534c7..f0dcc6c 100644 --- a/app/Models/Map/Lahan.php +++ b/app/Models/Map/Lahan.php @@ -17,6 +17,7 @@ class Lahan extends Model 'status_kepemilikan', 'profile_id', 'desa_kelurahan_id', + 'uid', 'path', 'luas_lahan', 'created_by', diff --git a/app/Models/Master/JenisKomoditas.php b/app/Models/Master/JenisKomoditas.php new file mode 100644 index 0000000..8632301 --- /dev/null +++ b/app/Models/Master/JenisKomoditas.php @@ -0,0 +1,33 @@ +belongsTo(Komoditas::class, 'komoditas_id'); + } + + public function varietas() + { + return $this->hasMany(Varietas::class, 'jenis_komoditas_id'); + } +} diff --git a/app/Models/Master/Varietas.php b/app/Models/Master/Varietas.php new file mode 100644 index 0000000..b8d867f --- /dev/null +++ b/app/Models/Master/Varietas.php @@ -0,0 +1,33 @@ +belongsTo(JenisKomoditas::class, 'jenis_komoditas_id'); + } + + public function komoditas() + { + return $this->belongsTo(Komoditas::class, 'komoditas_id'); + } +} diff --git a/app/Models/Pertanian/JenisKomoditas.php b/app/Models/Pertanian/JenisKomoditas.php new file mode 100644 index 0000000..5ae17ab --- /dev/null +++ b/app/Models/Pertanian/JenisKomoditas.php @@ -0,0 +1,52 @@ + + */ + protected $fillable = [ + 'nama', + 'kode', + 'keterangan', + ]; + + /** + * Atribut yang harus dikonversi ke tipe data lain (casting). + * + * @var array + */ + protected $casts = [ + 'created_at' => 'datetime', + 'updated_at' => 'datetime', + 'deleted_at' => 'datetime', + ]; + + /** + * Relasi ke model Komoditas atau Tanaman (jika ada). + * Contoh: Satu Jenis Komoditas memiliki banyak data Tanaman/Komoditas turunannya. + */ + public function tanaman(): HasMany + { + return $this->hasMany(Tanaman::class, 'jenis_komoditas_id', 'id'); + } +} \ No newline at end of file diff --git a/app/Models/Pertanian/Komoditas.php b/app/Models/Pertanian/Komoditas.php index a0b5858..d40315b 100644 --- a/app/Models/Pertanian/Komoditas.php +++ b/app/Models/Pertanian/Komoditas.php @@ -5,6 +5,8 @@ namespace App\Models\Pertanian; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Builder; +use App\Models\Master\JenisKomoditas; +use App\Models\Master\Varietas; class Komoditas extends Model { @@ -30,4 +32,14 @@ class Komoditas extends Model $builder->whereIn('id', app('SystemProgram')->komoditas()->pluck('id')); }); } + + public function jenisKomoditas() + { + return $this->hasMany(JenisKomoditas::class, 'komoditas_id'); + } + + public function varietas() + { + return $this->hasMany(Varietas::class, 'komoditas_id'); + } } diff --git a/app/Models/Pertanian/Lahan.php b/app/Models/Pertanian/Lahan.php index 7bfc525..0658438 100644 --- a/app/Models/Pertanian/Lahan.php +++ b/app/Models/Pertanian/Lahan.php @@ -19,6 +19,7 @@ class Lahan extends Model protected $table = 'map_lahan'; protected $fillable = [ + 'uid', 'nama', 'status_kepemilikan', 'profile_id', @@ -37,22 +38,22 @@ class Lahan extends Model 'path' => 'array', ]; - public function profile() + public function profile(): BelongsTo { return $this->belongsTo(Profile::class, 'profile_id'); } - public function desaKelurahan() + public function desaKelurahan(): BelongsTo { return $this->belongsTo(DesaKelurahan::class, 'desa_kelurahan_id'); } - public function adat() + public function adat(): BelongsTo { return $this->belongsTo(Adat::class, 'adat_pemilik_id'); } - public function tanaman() + public function tanaman(): HasMany { return $this->hasMany(Tanaman::class, 'lahan_id'); } diff --git a/app/Models/Pertanian/Tanaman.php b/app/Models/Pertanian/Tanaman.php index 3ce7eea..5b89bb2 100644 --- a/app/Models/Pertanian/Tanaman.php +++ b/app/Models/Pertanian/Tanaman.php @@ -4,12 +4,11 @@ namespace App\Models\Pertanian; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; -use App\Models\Pertanian\Komoditas; -use App\Models\Pertanian\Lahan; use App\Models\Profile; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\BelongsTo; - +use App\Models\Pertanian\Lahan; +use App\Models\Pertanian\Komoditas; +use App\Models\Master\JenisKomoditas; +use App\Models\Master\Varietas; class Tanaman extends Model { @@ -18,13 +17,18 @@ class Tanaman extends Model protected $table = 'map_tanaman'; protected $fillable = [ + 'uid', 'nama', + 'jenis_komoditas_id', + 'varietas_id', 'komoditas_id', 'profile_id', 'lahan_id', 'latitude', 'longitude', 'kondisi_tanaman', + 'metode_pengumpulan', + 'deskripsi', 'created_by', 'updated_by', 'deleted_by', @@ -35,6 +39,16 @@ class Tanaman extends Model return $this->belongsTo(Komoditas::class, 'komoditas_id'); } + public function jenisKomoditas() + { + return $this->belongsTo(JenisKomoditas::class, 'jenis_komoditas_id'); + } + + public function varietas() + { + return $this->belongsTo(Varietas::class, 'varietas_id'); + } + public function profile() { return $this->belongsTo(Profile::class, 'profile_id'); diff --git a/app/Models/Profile.php b/app/Models/Profile.php index e961336..2752038 100644 --- a/app/Models/Profile.php +++ b/app/Models/Profile.php @@ -4,11 +4,9 @@ namespace App\Models; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; + use App\Models\User; -use App\Models\Pertanian\Lahan; use App\Models\Wilayah\DesaKelurahan; -use Illuminate\Database\Eloquent\Relations\HasMany; -use Illuminate\Database\Eloquent\Relations\BelongsTo; class Profile extends Model { @@ -17,13 +15,14 @@ class Profile extends Model protected $fillable = [ 'user_id', 'role', + 'user_uid', 'nama', 'email', 'telepon', 'alamat', 'kk', 'ktp', - + 'uid', 'desa_kelurahan_id', 'path_kk', 'file_kk', @@ -44,9 +43,4 @@ class Profile extends Model { return $this->belongsTo(User::class, 'user_id'); } - - public function lahan(): HasMany - { - return $this->hasMany(Lahan::class, 'profile_id'); - } } diff --git a/app/Models/Program/Aktivitas.php b/app/Models/Program/Aktivitas.php index 4e471d0..47cd02d 100644 --- a/app/Models/Program/Aktivitas.php +++ b/app/Models/Program/Aktivitas.php @@ -23,6 +23,9 @@ class Aktivitas extends Model "indikator", "deskripsi", "transformasi_id", + "parent_id", + "created_by", + "updated_by" ]; public function transformasi(): BelongsTo @@ -32,6 +35,20 @@ class Aktivitas extends Model public function target(): HasMany { - return $this->hasMany(Target::class, 'aktivitas_id', 'id'); + return $this->hasMany(Target::class, 'aktivitas_id', 'id') + ->orderBy('id', 'asc'); + } + + public function parent(): BelongsTo + { + return $this->belongsTo(Aktivitas::class, 'parent_id', 'id'); + } + + public function aktivitas(): HasMany + { + return $this->hasMany(Aktivitas::class, 'parent_id', 'id') + ->where('jenis_aktivitas', 'aktivitas') + ->orderBy('kode', 'asc') + ->orderBy('id', 'asc'); } } diff --git a/app/Models/Program/Framework.php b/app/Models/Program/Framework.php index 5740a89..e7d1d2a 100644 --- a/app/Models/Program/Framework.php +++ b/app/Models/Program/Framework.php @@ -20,6 +20,8 @@ class Framework extends Model "nama", "tahun", "program_id", + "created_by", + "updated_by", ]; public function program(): BelongsTo @@ -29,6 +31,8 @@ class Framework extends Model public function transformasi(): HasMany { - return $this->hasMany(Transformasi::class, 'framework_id', 'id'); + return $this->hasMany(Transformasi::class, 'framework_id', 'id') + ->orderBy('kode', 'asc') + ->orderBy('id', 'asc'); } } diff --git a/app/Models/Program/Realisasi.php b/app/Models/Program/Realisasi.php new file mode 100644 index 0000000..ba56efa --- /dev/null +++ b/app/Models/Program/Realisasi.php @@ -0,0 +1,37 @@ +belongsTo(Target::class, 'target_id', 'id'); + } + + public function verifikasi(): HasMany + { + return $this->hasMany(Verifikasi::class, 'realisasi_id', 'id')->oldest(); + } +} diff --git a/app/Models/Program/Target.php b/app/Models/Program/Target.php index a1a791a..8bf0f80 100644 --- a/app/Models/Program/Target.php +++ b/app/Models/Program/Target.php @@ -5,6 +5,8 @@ namespace App\Models\Program; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\SoftDeletes; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\HasMany; +use Illuminate\Database\Eloquent\Relations\HasOne; use App\Models\Program\Aktivitas; @@ -27,10 +29,25 @@ class Target extends Model "realisasi_float", "realisasi_boolean", "aktivitas_id", + "tipe_realisasi", + "value_query", + "created_by", + "updated_by", + "deleted_by", ]; public function aktivitas(): BelongsTo { return $this->belongsTo(Aktivitas::class, 'aktivitas_id', 'id'); } + + public function realisasi(): HasMany + { + return $this->hasMany(Realisasi::class, 'target_id', 'id')->latest(); + } + + public function latestRealisasi(): HasOne + { + return $this->hasOne(Realisasi::class, 'target_id', 'id')->latest(); + } } diff --git a/app/Models/Program/Transformasi.php b/app/Models/Program/Transformasi.php index 9e2bd68..504c5f8 100644 --- a/app/Models/Program/Transformasi.php +++ b/app/Models/Program/Transformasi.php @@ -27,8 +27,12 @@ class Transformasi extends Model return $this->belongsTo(Framework::class, 'framework_id', 'id'); } - public function aktivitas(): HasMany + public function tujuan(): HasMany { - return $this->hasMany(Aktivitas::class, 'transformasi_id', 'id'); + return $this->hasMany(Aktivitas::class, 'transformasi_id', 'id') + ->whereNull('parent_id') + ->where('jenis_aktivitas', 'tujuan') + ->orderBy('kode', 'asc') + ->orderBy('id', 'asc'); } } diff --git a/app/Models/Program/Verifikasi.php b/app/Models/Program/Verifikasi.php new file mode 100644 index 0000000..837e48b --- /dev/null +++ b/app/Models/Program/Verifikasi.php @@ -0,0 +1,30 @@ +belongsTo(Realisasi::class, 'realisasi_id', 'id'); + } +} diff --git a/app/Models/User.php b/app/Models/User.php index 8351492..ae21e48 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -21,6 +21,8 @@ class User extends Authenticatable 'password', 'email_verified_at', 'role', + 'username', + 'uid' ]; protected $hidden = [ diff --git a/app/Models/Wilayah/DesaKelurahan.php b/app/Models/Wilayah/DesaKelurahan.php index ac8beef..123a402 100644 --- a/app/Models/Wilayah/DesaKelurahan.php +++ b/app/Models/Wilayah/DesaKelurahan.php @@ -1,6 +1,6 @@ get(); + $tabel_menu = TabelBasisData::whereHas('kolom', function ($query) { + $query->where('is_active', true); + })->get(); $view->with('tabel_menu', $tabel_menu); }); diff --git a/app/View/Components/WilayahDashboard.php b/app/View/Components/WilayahDashboard.php new file mode 100644 index 0000000..774251b --- /dev/null +++ b/app/View/Components/WilayahDashboard.php @@ -0,0 +1,70 @@ +count(); + + $provinsiKosong = $totalProvinsi - $provinsiAktif; + + $totalKabupaten = KabupatenKota::whereRaw("SUBSTRING(kode, 3, 2) < '70'")->count(); + $totalKota = KabupatenKota::whereRaw("SUBSTRING(kode, 3, 2) >= '70'")->count(); + + if ($totalKabupaten == 0 && $totalKota == 0 && $totalKabKota > 0) { + $totalKabupaten = $totalKabKota; + } + + $kecamatanPerdesaan = Kecamatan::whereHas('kabupatenKota', function($query) { + $query->whereRaw("SUBSTRING(kode, 3, 2) < '70'"); + })->count(); + + $kecamatanPerkotaan = max(0, $totalKecamatan - $kecamatanPerdesaan); + + $terpetakan = DesaKelurahan::whereNotNull('path')->count(); + $belumTerpetakan = max(0, $totalDesaKelurahan - $terpetakan); + + $totalDesa = DesaKelurahan::where('nama', 'like', 'DESA%')->count() ?: $totalDesaKelurahan; + $totalKelurahan = DesaKelurahan::where('nama', 'like', 'KEL%')->count(); + + $data = [ + 'totalProvinsi' => $totalProvinsi, + 'provinsiAktif' => $provinsiAktif, + 'provinsiKosong' => $provinsiKosong, + + 'totalKabKota' => $totalKabKota, + 'totalKabupaten' => $totalKabupaten, + 'totalKota' => $totalKota, + + 'totalKecamatan' => $totalKecamatan, + 'kecamatanPerdesaan' => $kecamatanPerdesaan, + 'kecamatanPerkotaan' => $kecamatanPerkotaan, + + 'totalDesaKelurahan' => $totalDesaKelurahan, + 'totalDesa' => $totalDesa, + 'totalKelurahan' => $totalKelurahan, + 'terpetakan' => $terpetakan, + 'belumTerpetakan' => $belumTerpetakan, + ]; + + return view('components.wilayah-dashboard', $data); + } +} \ No newline at end of file diff --git a/database/migrations/0001_01_01_000000_create_users_table.php b/database/migrations/0001_01_01_000000_create_users_table.php index 2872310..cf2f279 100644 --- a/database/migrations/0001_01_01_000000_create_users_table.php +++ b/database/migrations/0001_01_01_000000_create_users_table.php @@ -10,8 +10,10 @@ return new class extends Migration { Schema::create('users', function (Blueprint $table) { $table->id(); + $table->string('uid')->unique(); $table->string('name'); - $table->string('email')->unique(); + $table->string('username')->unique(); + $table->string('email')->unique()->nullable(); $table->timestamp('email_verified_at')->nullable(); $table->string('password'); $table->rememberToken(); @@ -23,17 +25,6 @@ return new class extends Migration $table->softDeletes(); }); - // Schema::create('user_roles', function (Blueprint $table) { - // $table->id(); - // $table->foreignId('user_id')->unique()->constrained('users')->onDelete('cascade'); - // $table->enum('role_name', ['Super Admin', 'Admin', 'Koordinator', 'Penyuluh', 'Pendamping', 'Petani'])->default('Petani'); - // $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); - // $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); - // $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); - // $table->timestamps(); - // $table->softDeletes(); - // }); - Schema::create('password_reset_tokens', function (Blueprint $table) { $table->string('email')->primary(); $table->string('token'); @@ -53,7 +44,6 @@ return new class extends Migration public function down(): void { Schema::dropIfExists('users'); - // Schema::dropIfExists('user_roles'); Schema::dropIfExists('password_reset_tokens'); Schema::dropIfExists('sessions'); } diff --git a/database/migrations/2026_02_19_172905_create_profiles_table.php b/database/migrations/2026_02_19_172905_create_profiles_table.php index 1423340..b2fae24 100644 --- a/database/migrations/2026_02_19_172905_create_profiles_table.php +++ b/database/migrations/2026_02_19_172905_create_profiles_table.php @@ -67,6 +67,7 @@ return new class extends Migration $table->string('file_kk')->nullable(); $table->string('path_ktp', 500)->nullable(); $table->string('file_ktp')->nullable(); + $table->string('user_uid'); $table->foreignId('keluarga_id')->nullable()->constrained('keluarga')->onDelete('set null'); diff --git a/database/migrations/2026_02_20_110207_create_komoditas_table.php b/database/migrations/2026_02_20_110207_create_komoditas_table.php index 4238135..1f7e0bb 100644 --- a/database/migrations/2026_02_20_110207_create_komoditas_table.php +++ b/database/migrations/2026_02_20_110207_create_komoditas_table.php @@ -22,13 +22,39 @@ return new class extends Migration $table->timestamps(); $table->softDeletes(); }); - } - /** - * Reverse the migrations. - */ + Schema::create('jenis_komoditas', function (Blueprint $table) { + $table->id(); + $table->foreignId('komoditas_id')->constrained('master_komoditas')->onDelete('cascade'); + $table->string('nama'); + $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); + + $table->timestamps(); + $table->softDeletes(); + }); + + Schema::create('master_varietas', function (Blueprint $table) { + $table->id(); + $table->foreignId('komoditas_id')->constrained('master_komoditas')->onDelete('cascade'); + $table->foreignId('jenis_komoditas_id')->constrained('jenis_komoditas')->onDelete('cascade'); + $table->string('nama'); + $table->text('deskripsi')->unique(); + + $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); + + $table->timestamps(); + $table->softDeletes(); + }); + } + public function down(): void { + Schema::dropIfExists('master_varietas'); + Schema::dropIfExists('jenis_komoditas'); Schema::dropIfExists('master_komoditas'); } }; diff --git a/database/migrations/2026_02_20_131504_create_lahans_table.php b/database/migrations/2026_02_20_131504_create_lahans_table.php index 70f30e5..13dc242 100644 --- a/database/migrations/2026_02_20_131504_create_lahans_table.php +++ b/database/migrations/2026_02_20_131504_create_lahans_table.php @@ -13,6 +13,7 @@ return new class extends Migration { Schema::create('map_lahan', function (Blueprint $table) { $table->id(); + $table->string('uid')->unique(); $table->string('nama'); $table->string('status_kepemilikan'); @@ -21,7 +22,13 @@ return new class extends Migration $table->foreignId('adat_pemilik_id')->nullable()->constrained('adat')->onDelete('set null'); $table->foreignId('desa_kelurahan_id')->nullable()->constrained('master_desa_kelurahan'); - $table->text('path')->nullable(); + $table->string('lat')->nullable(); + $table->string('long')->nullable(); + $table->jsonb('path')->nullable(); + + $table->boolean('sertifikat')->default(false); + $table->boolean('konflik')->default(false); + $table->text('deskripsi')->nullable(); $table->float('luas_lahan', 8)->nullable(); @@ -33,11 +40,32 @@ return new class extends Migration $table->softDeletes(); }); + Schema::create('sertifikat_lahan', function (Blueprint $table) { + $table->id(); + $table->string('uid')->unique(); + $table->string('nomor_sertifikat'); + $table->date('tanggal_terbit'); + $table->date('tanggal_kadaluarsa')->nullable(); + $table->string('lembaga_penerbit')->nullable(); + + $table->foreignId('lahan_id')->constrained('map_lahan')->onDelete('cascade'); + + $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); + + $table->timestamps(); + $table->softDeletes(); + }); + Schema::create('map_tanaman', function (Blueprint $table) { $table->id(); + $table->string('uid')->unique(); $table->string('nama'); $table->foreignId('komoditas_id')->constrained('master_komoditas')->onDelete('set null'); + $table->foreignId('jenis_komoditas_id')->constrained('jenis_komoditas')->onDelete('set null'); + $table->foreignId('varietas_id')->constrained('master_varietas')->onDelete('set null'); $table->foreignId('profile_id')->constrained('profiles')->onDelete('set null'); $table->foreignId('lahan_id')->constrained('map_lahan')->onDelete('set null'); @@ -45,6 +73,8 @@ return new class extends Migration $table->float('longitude')->nullable(); $table->string('kondisi_tanaman')->nullable(); + $table->string('metode_pengumpulan')->nullable(); + $table->text('deskripsi')->nullable(); $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); @@ -61,6 +91,7 @@ return new class extends Migration public function down(): void { Schema::dropIfExists('map_tanaman'); + Schema::dropIfExists('sertifikat_lahan'); Schema::dropIfExists('map_lahan'); } }; diff --git a/database/migrations/2026_04_10_131504_update_jsonb_path.php b/database/migrations/2026_04_10_131504_update_jsonb_path.php deleted file mode 100644 index d7534c8..0000000 --- a/database/migrations/2026_04_10_131504_update_jsonb_path.php +++ /dev/null @@ -1,42 +0,0 @@ -jsonb('path')->nullable()->change(); - }); - - Schema::table('master_provinsi', function (Blueprint $table) { - $table->jsonb('path')->nullable()->change(); - }); - - Schema::table('master_kabupaten_kota', function (Blueprint $table) { - $table->jsonb('path')->nullable()->change(); - }); - - Schema::table('master_kecamatan', function (Blueprint $table) { - $table->jsonb('path')->nullable()->change(); - }); - - Schema::table('master_desa_kelurahan', function (Blueprint $table) { - $table->jsonb('path')->nullable()->change(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - - } -}; diff --git a/database/migrations/2026_04_11_105538_create_programs_table.php b/database/migrations/2026_04_11_105538_create_programs_table.php index 9af40e2..8fc965b 100644 --- a/database/migrations/2026_04_11_105538_create_programs_table.php +++ b/database/migrations/2026_04_11_105538_create_programs_table.php @@ -18,6 +18,7 @@ return new class extends Migration $table->string('sub_domain'); $table->integer('tahun'); $table->string('instansi')->nullable(); + $table->string('nama_singkat', length: 50); $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); diff --git a/database/migrations/2026_04_15_103739_create_basis_data_table.php b/database/migrations/2026_04_15_103739_create_basis_data_table.php index e8d3fa8..35aeedb 100644 --- a/database/migrations/2026_04_15_103739_create_basis_data_table.php +++ b/database/migrations/2026_04_15_103739_create_basis_data_table.php @@ -29,9 +29,15 @@ return new class extends Migration $table->foreignId('tabel_id')->constrained('ekosistem_basis_data_tabel')->restrictOnDelete(); $table->string('kolom'); $table->string('label'); - $table->enum('tipe', ['varchar', 'integer', 'float', 'boolean', 'date', 'datetime', 'text'])->default('varchar'); + $table->enum('tipe', ['varchar', 'integer', 'float', 'boolean', 'date', 'datetime', 'text', 'foreign_key', 'enum'])->default('varchar'); $table->jsonb('validasi')->nullable(); $table->integer('urutan')->default(1); + $table->string('relasi_tabel')->nullable(); + $table->string('relasi_kolom_id')->nullable(); + $table->string('relasi_kolom_label')->nullable(); + $table->boolean('is_active')->default('true'); + $table->boolean('is_browse')->default('false'); + $table->jsonb('enum_opsi')->nullable(); $table->timestamps(); $table->softDeletes(); @@ -60,6 +66,8 @@ return new class extends Migration $table->date('value_date')->nullable(); $table->datetime('value_datetime')->nullable(); $table->text('value_text')->nullable(); + $table->integer('value_relasi')->nullable(); + $table->string('value_enum')->nullable(); $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); diff --git a/database/migrations/2026_04_16_172905_update_profiles_table.php b/database/migrations/2026_04_16_172905_update_profiles_table.php deleted file mode 100644 index f2ee0fb..0000000 --- a/database/migrations/2026_04_16_172905_update_profiles_table.php +++ /dev/null @@ -1,25 +0,0 @@ -foreignId('adat_id')->nullable()->constrained('adat'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - } -}; diff --git a/database/migrations/2026_04_27_092222_create_petani_table.php b/database/migrations/2026_04_27_092222_create_petani_table.php index 13df24f..46a432c 100644 --- a/database/migrations/2026_04_27_092222_create_petani_table.php +++ b/database/migrations/2026_04_27_092222_create_petani_table.php @@ -13,9 +13,11 @@ return new class extends Migration { Schema::create('ekosistem_petani_cpm', function (Blueprint $table) { $table->id(); - + $table->string('uid')->unique(); + $table->foreignId('program_id')->nullable()->constrained('core_program')->nullOnDelete(); $table->string('nama'); $table->boolean('jenis_kelamin'); + $table->string('tempat_lahir')->nullable(); $table->date('tanggal_lahir'); $table->string('pendidikan_terakhir')->nullable(); @@ -28,11 +30,17 @@ return new class extends Migration $table->float('lama_bertani', 8)->nullable(); $table->float('luas_lahan', 8)->nullable(); + $table->string('dusun_rt_rw')->nullable(); + $table->string('nomor_telepon')->nullable(); + $table->string('foto')->nullable(); + $table->string('pekerjaan_sampingan')->nullable(); $table->foreignId('komoditas_sampingan_id')->nullable()->constrained('master_komoditas'); $table->float('lama_bertani_sampingan', 8)->nullable(); $table->float('luas_lahan_sampingan', 8)->nullable(); + + $table->string('status_perkawinan')->nullable(); $table->string('status_keluarga')->nullable(); $table->integer('jumlah_tanggungan')->nullable(); @@ -48,9 +56,6 @@ return new class extends Migration }); } - /** - * Reverse the migrations. - */ public function down(): void { Schema::dropIfExists('ekosistem_petani_cpm'); diff --git a/database/migrations/2026_05_05_103739_update_basis_data_table.php b/database/migrations/2026_05_05_103739_update_basis_data_table.php deleted file mode 100644 index cee687b..0000000 --- a/database/migrations/2026_05_05_103739_update_basis_data_table.php +++ /dev/null @@ -1,55 +0,0 @@ -string('relasi_tabel')->nullable(); - $table->string('relasi_kolom_id')->nullable(); - $table->string('relasi_kolom_label')->nullable(); - - $table->jsonb('enum_opsi')->nullable(); - }); - - DB::statement("alter table public.ekosistem_basis_data_kolom drop constraint ekosistem_basis_data_kolom_tipe_check;"); - DB::statement(" - alter table public.ekosistem_basis_data_kolom add constraint ekosistem_basis_data_kolom_tipe_check check (((tipe)::text = any - ((array[ - 'varchar'::character varying, - 'integer'::character varying, - 'float'::character varying, - 'boolean'::character varying, - 'date'::character varying, - 'datetime'::character varying, - 'text'::character varying, - 'foreign_key'::character varying, - 'enum'::character varying - ])::text[]))) - "); - - Schema::table('ekosistem_basis_data_value', function (Blueprint $table) { - $table->integer('value_relasi')->nullable(); - $table->string('value_enum')->nullable(); - }); - - Schema::table('ekosistem_petani_cpm', function (Blueprint $table) { - $table->foreignId('program_id')->nullable()->constrained('core_program')->nullOnDelete(); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - } -}; diff --git a/database/migrations/2026_05_06_113516_create_ekosistem_lahan_cpcl_table.php b/database/migrations/2026_05_06_113516_create_ekosistem_lahan_cpcl_table.php index d2d866f..db86702 100644 --- a/database/migrations/2026_05_06_113516_create_ekosistem_lahan_cpcl_table.php +++ b/database/migrations/2026_05_06_113516_create_ekosistem_lahan_cpcl_table.php @@ -13,6 +13,7 @@ return new class extends Migration { Schema::create('ekosistem_lahan_cpcl', function (Blueprint $table) { $table->id(); + $table->string('uid')->unique(); $table->string('nama'); $table->string('status_kepemilikan'); diff --git a/database/migrations/2026_05_07_103739_update_basis_data_table_2.php b/database/migrations/2026_05_07_103739_update_basis_data_table_2.php deleted file mode 100644 index a187a6b..0000000 --- a/database/migrations/2026_05_07_103739_update_basis_data_table_2.php +++ /dev/null @@ -1,27 +0,0 @@ -boolean('is_active')->default('true'); - $table->boolean('is_browse')->default('false'); - }); - } - - /** - * Reverse the migrations. - */ - public function down(): void - { - } -}; diff --git a/database/migrations/2026_05_12_161821_create_form_basis_data_table.php b/database/migrations/2026_05_12_161821_create_form_basis_data_table.php new file mode 100644 index 0000000..2502189 --- /dev/null +++ b/database/migrations/2026_05_12_161821_create_form_basis_data_table.php @@ -0,0 +1,47 @@ +id(); + + $table->foreignId('tabel_id')->constrained('ekosistem_basis_data_tabel')->restrictOnDelete(); + + $table->foreignId('parent_id')->nullable()->constrained('ekosistem_basis_data_form')->restrictOnDelete(); + + $table->enum('tipe', ['form-row', 'form-column', 'form-value', 'form-section', 'form-hr-line'])->default('form-value'); + + $table->integer('column_width')->nullable(); + + $table->foreignId('kolom_id')->nullable()->constrained('ekosistem_basis_data_kolom')->nullOnDelete(); + + $table->string('section_label')->nullable(); + + $table->integer('urutan')->default(1); + + $table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null'); + $table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null'); + + $table->timestamps(); + $table->softDeletes(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('ekosistem_basis_data_form'); + } +}; diff --git a/database/migrations/2026_05_15_161421_update_frameworks_table.php b/database/migrations/2026_05_15_161421_update_frameworks_table.php new file mode 100644 index 0000000..dc94b1b --- /dev/null +++ b/database/migrations/2026_05_15_161421_update_frameworks_table.php @@ -0,0 +1,36 @@ +foreignId('parent_id')->nullable()->constrained('program_aktivitas')->nullOnDelete(); + }); + + Schema::table('program_target', function (Blueprint $table) { + $table->enum('tipe_realisasi', ['manual', 'referensi'])->default('manual'); + $table->text('value_query')->nullable(); + }); + + Schema::table('program_verifikasi', function (Blueprint $table) { + $table->foreignId('realisasi_id')->nullable()->constrained('program_realisasi')->nullOnDelete(); + $table->dropColumn('aktivitas_id'); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + + } +}; \ No newline at end of file diff --git a/database/seeders/AdminUserSeeder.php b/database/seeders/AdminUserSeeder.php index c99c647..d1193be 100644 --- a/database/seeders/AdminUserSeeder.php +++ b/database/seeders/AdminUserSeeder.php @@ -15,7 +15,9 @@ class AdminUserSeeder extends Seeder public function run(): void { DB::table('users')->insert([ + 'uid' => 'AD-1234567891', 'name' => 'Admin', + 'username' => 'admin', 'email' => 'admin@example.com', 'email_verified_at' => now(), 'password' => Hash::make('Asalada123'), @@ -24,7 +26,7 @@ class AdminUserSeeder extends Seeder 'updated_at' => now(), ]); - Db::table('profiles')->insert([ + DB::table('profiles')->insert([ 'user_id' => 1, 'role' => 'super_admin', 'nama' => 'Admin', @@ -43,7 +45,7 @@ class AdminUserSeeder extends Seeder 'updated_by' => 1, 'created_at' => now(), 'updated_at' => now(), - 'adat_id' => null, + 'user_uid' => 'AD-1234567891', ]); } } diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php index 6b901f8..f5f3a82 100644 --- a/database/seeders/DatabaseSeeder.php +++ b/database/seeders/DatabaseSeeder.php @@ -20,6 +20,7 @@ class DatabaseSeeder extends Seeder User::factory()->create([ 'name' => 'Test User', 'email' => 'test@example.com', + 'uid' => 'AD-1234567890', ]); } } diff --git a/public/assets/img/pages/profile-banner.png b/public/assets/img/pages/profile-banner.png deleted file mode 100644 index bdb9ba7..0000000 Binary files a/public/assets/img/pages/profile-banner.png and /dev/null differ diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index b88a647..4eda5f9 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -4,7 +4,7 @@
+
+
+ + +
+
@@ -51,12 +57,14 @@ rules: { nama: { required: true }, sub_domain: { required: true }, - tahun: { required: true } + tahun: { required: true }, + nama_singkat: { required: true } }, messages: { nama: { required: "Nama program wajib diisi" }, sub_domain: { required: "Subdomain wajib diisi" }, - tahun: { required: "Tahun wajib diisi" } + tahun: { required: "Tahun wajib diisi" }, + nama_singkat: { required: "Nama singkat wajib diisi" } }, errorElement: 'div', errorPlacement: function (error, element) { diff --git a/resources/views/core/program/edit.blade.php b/resources/views/core/program/edit.blade.php index 30603a3..6d36b74 100644 --- a/resources/views/core/program/edit.blade.php +++ b/resources/views/core/program/edit.blade.php @@ -19,6 +19,12 @@
+
+
+ + +
+
@@ -56,12 +62,14 @@ rules: { nama: { required: true }, sub_domain: { required: true }, - tahun: { required: true } + tahun: { required: true }, + nama_singkat: { required: true } }, messages: { nama: { required: "Nama program wajib diisi" }, sub_domain: { required: "Subdomain wajib diisi" }, - tahun: { required: "Tahun wajib diisi" } + tahun: { required: "Tahun wajib diisi" }, + nama_singkat: { required: "Nama singkat wajib diisi" } }, errorElement: 'div', errorPlacement: function (error, element) { diff --git a/resources/views/core/program/view.blade.php b/resources/views/core/program/view.blade.php index 93a838c..c9821ed 100644 --- a/resources/views/core/program/view.blade.php +++ b/resources/views/core/program/view.blade.php @@ -17,6 +17,12 @@
+
+
+ + +
+
diff --git a/resources/views/dashboard/produktivitas.blade.php b/resources/views/dashboard/produktivitas.blade.php deleted file mode 100644 index 4e9e7b7..0000000 --- a/resources/views/dashboard/produktivitas.blade.php +++ /dev/null @@ -1,619 +0,0 @@ -@extends('layout.main') - -@push('style') - - -@endpush - -@section('content') -
-
-
-
-
-
-
- -
-
- Total Land Area -

{{ number_format($luasTotal, 1) }} ha

-
-
-
- -
-
-
-
- -
-
- Total Lahan -

{{ $totallahan }}

-
-
-
- -
-
-
-
- Active -
-
- Registered Farmers -

{{ $totalpetani }}

-
-
-
- -
-
Distribusi Komoditas
-
- @foreach(['Vanilla' => 45, 'Candlenut' => 30, 'Cocoa' => 15, 'Coffee' => 10] as $name => $val) -
-
- {{ $name }} - {{ $val }}% -
-
-
-
-
- @endforeach -
-
-
- -
-
-
-
Peta Sebaran Lahan
-
- - - -
-
-
- -
-
- -
-
-
Produktivitas Petani Regional
- -
-
- - - - - - - - - - - - @foreach($petaniList as $p) - - - - - - - - @endforeach - -
Nama PetaniID LahanKomoditasLuas (HA)Status
-
-
{{ substr($p->nama, 0, 1) }}
- {{ $p->nama }} -
-
TR-{{ $p->id }}Vanilla{{ number_format($p->lahan->sum('luas_lahan'), 1) }}Verified
-
-
-
-
-
-@endsection - -@push('scripts') - - -@endpush \ No newline at end of file diff --git a/resources/views/dashboard/summary.blade.php b/resources/views/dashboard/summary.blade.php index 16a4eac..de7af17 100644 --- a/resources/views/dashboard/summary.blade.php +++ b/resources/views/dashboard/summary.blade.php @@ -33,14 +33,14 @@
-

- Ringkasan M&E (Theory of Change) -

- -

- Capaian Utama Transformasi Bioekonomi Desa Ngada -

-
+

+ Ringkasan M&E (Theory of Change) +

+ +

+ Capaian Utama Transformasi Bioekonomi Desa Ngada +

+
+
+
+
+ @csrf + + + + +
+ + +
+ + + +
+
+
+ +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/ekosistem/form-basis-data/add-hr-line.blade.php b/resources/views/ekosistem/form-basis-data/add-hr-line.blade.php new file mode 100644 index 0000000..22475be --- /dev/null +++ b/resources/views/ekosistem/form-basis-data/add-hr-line.blade.php @@ -0,0 +1,27 @@ +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/ekosistem/form-basis-data/add-row.blade.php b/resources/views/ekosistem/form-basis-data/add-row.blade.php new file mode 100644 index 0000000..8693842 --- /dev/null +++ b/resources/views/ekosistem/form-basis-data/add-row.blade.php @@ -0,0 +1,27 @@ +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/ekosistem/form-basis-data/add-section.blade.php b/resources/views/ekosistem/form-basis-data/add-section.blade.php new file mode 100644 index 0000000..a9837ec --- /dev/null +++ b/resources/views/ekosistem/form-basis-data/add-section.blade.php @@ -0,0 +1,77 @@ +
+
+
Tambah Section
+ +
+
+
+ @csrf + + +
+ + +
+ + + +
+
+
+ +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/ekosistem/form-basis-data/add-value.blade.php b/resources/views/ekosistem/form-basis-data/add-value.blade.php new file mode 100644 index 0000000..f80e27a --- /dev/null +++ b/resources/views/ekosistem/form-basis-data/add-value.blade.php @@ -0,0 +1,85 @@ +
+
+
Tambah Formulir Nilai
+ +
+
+
+ @csrf + + + + +
+ + +
+ + + +
+
+
+ +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/ekosistem/form-basis-data/edit-column.blade.php b/resources/views/ekosistem/form-basis-data/edit-column.blade.php new file mode 100644 index 0000000..99d8e01 --- /dev/null +++ b/resources/views/ekosistem/form-basis-data/edit-column.blade.php @@ -0,0 +1,94 @@ +
+
+
Edit Kolom
+ +
+
+
+ @csrf + @method('PUT') + + + + + +
+ + +
+ + + +
+
+
+ +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/ekosistem/form-basis-data/edit-section.blade.php b/resources/views/ekosistem/form-basis-data/edit-section.blade.php new file mode 100644 index 0000000..509079a --- /dev/null +++ b/resources/views/ekosistem/form-basis-data/edit-section.blade.php @@ -0,0 +1,89 @@ +
+
+
Edit Section
+ +
+
+
+ @csrf + @method('PUT') + + + +
+ + +
+ + + +
+
+
+ +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/ekosistem/form-basis-data/edit-value.blade.php b/resources/views/ekosistem/form-basis-data/edit-value.blade.php new file mode 100644 index 0000000..14384e9 --- /dev/null +++ b/resources/views/ekosistem/form-basis-data/edit-value.blade.php @@ -0,0 +1,97 @@ +
+
+
Edit Formulir Nilai
+ +
+
+
+ @csrf + @method('PUT') + + + + + +
+ + +
+ + + +
+
+
+ +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/ekosistem/form-basis-data/index.blade.php b/resources/views/ekosistem/form-basis-data/index.blade.php new file mode 100644 index 0000000..27fceeb --- /dev/null +++ b/resources/views/ekosistem/form-basis-data/index.blade.php @@ -0,0 +1,283 @@ +@extends('layout.main') + +@section('content') +
+
+
+
Formulir untuk Tabel Basis Data Ekosistem: {{ $tabel->label }}
+ +
+
+ @csrf + +
+ +
+
+ +
+ + +
+ + +
+ +
+ Kembali +
+
+
+
+@include('ekosistem.form-basis-data.add-row') +@include('ekosistem.form-basis-data.add-section') +@include('ekosistem.form-basis-data.edit-section') +@include('ekosistem.form-basis-data.add-hr-line') +@include('ekosistem.form-basis-data.add-column') +@include('ekosistem.form-basis-data.edit-column') +@include('ekosistem.form-basis-data.move-column') +@include('ekosistem.form-basis-data.add-value') +@include('ekosistem.form-basis-data.edit-value') +@endsection + +@push('scripts') + + + +@endpush \ No newline at end of file diff --git a/resources/views/ekosistem/form-basis-data/move-column.blade.php b/resources/views/ekosistem/form-basis-data/move-column.blade.php new file mode 100644 index 0000000..ad27ef6 --- /dev/null +++ b/resources/views/ekosistem/form-basis-data/move-column.blade.php @@ -0,0 +1,98 @@ +
+
+
Edit Kolom
+ +
+
+
+ @csrf + @method('PUT') + + + + + +
+ + +
+ + + +
+
+
+ +@push('scripts') + +@endpush \ No newline at end of file diff --git a/resources/views/ekosistem/kolom-basis-data/edit.blade.php b/resources/views/ekosistem/kolom-basis-data/edit.blade.php index 94af81d..a38468d 100644 --- a/resources/views/ekosistem/kolom-basis-data/edit.blade.php +++ b/resources/views/ekosistem/kolom-basis-data/edit.blade.php @@ -37,9 +37,15 @@ - +
+ + +
- +