diff --git a/app/Exports/ExportDataSarana.php b/app/Exports/ExportDataSarana.php
new file mode 100644
index 000000000..5a2cb2b51
--- /dev/null
+++ b/app/Exports/ExportDataSarana.php
@@ -0,0 +1,82 @@
+data = $data;
+ $this->author = $author;
+ }
+
+ public function collection()
+ {
+ return $this->data->map(function ($item) {
+ return [
+ $item->id,
+ $item->desa->nama ?? '-',
+ $item->nama,
+ $item->jumlah,
+ $item->kategori,
+ $item->keterangan,
+ ];
+ });
+ }
+
+ public function headings(): array
+ {
+ return [
+ ['Laporan Data Sarana'],
+ [''],
+ ['ID', 'Desa', 'Nama Sarana', 'Jumlah', 'Kategori', 'Keterangan'],
+ ];
+ }
+
+ public function styles(Worksheet $sheet)
+ {
+ return [
+ 1 => ['font' => ['bold' => true, 'size' => 14]],
+ 3 => ['font' => ['bold' => true, 'color' => ['rgb' => 'FFFFFF']]],
+ ];
+ }
+
+ public function registerEvents(): array
+ {
+ return [
+ AfterSheet::class => function (AfterSheet $event) {
+ $sheet = $event->sheet->getDelegate();
+
+ $sheet->mergeCells('A1:F1');
+ $sheet->getStyle('A1')->getAlignment()->setHorizontal('center');
+
+ $sheet->setCellValue('A2', 'Nama: ' . $this->author);
+ $sheet->setCellValue('F2', 'Tanggal: ' . date('d-m-Y'));
+
+ $sheet->getStyle('A2')->getAlignment()->setHorizontal('left');
+ $sheet->getStyle('F2')->getAlignment()->setHorizontal('right');
+
+ $sheet->getStyle('A3:F3')->getFill()
+ ->setFillType(\PhpOffice\PhpSpreadsheet\Style\Fill::FILL_SOLID)
+ ->getStartColor()->setARGB('4CAF50');
+
+ $lastRow = $sheet->getHighestRow();
+ $sheet->getStyle("A3:F{$lastRow}")
+ ->getBorders()
+ ->getAllBorders()
+ ->setBorderStyle(\PhpOffice\PhpSpreadsheet\Style\Border::BORDER_THIN);
+ },
+ ];
+ }
+}
diff --git a/app/Http/Controllers/Data/DataSaranaController.php b/app/Http/Controllers/Data/DataSaranaController.php
new file mode 100644
index 000000000..236d443bc
--- /dev/null
+++ b/app/Http/Controllers/Data/DataSaranaController.php
@@ -0,0 +1,169 @@
+desa_id) {
+ $query->where('desa_id', $request->desa_id);
+ }
+ if ($request->kategori) {
+ $query->where('kategori', $request->kategori);
+ }
+
+ return datatables()->of($query)
+ ->addColumn('desa', function ($row) {
+ return $row->desa ? $row->desa->nama : '-';
+ })
+ ->addColumn('aksi', function ($row) {
+ $editUrl = route('data.data-sarana.edit', $row->id);
+ $deleteUrl = route('data.data-sarana.destroy', $row->id);
+
+ return view('data.data_sarana.partials.action', compact('editUrl', 'deleteUrl'))->render();
+ })
+ ->rawColumns(['aksi'])
+ ->make(true);
+ }
+
+
+ public function create()
+ {
+ $page_title = "Tambah Sarana";
+ $page_description = "Form tambah data sarana";
+
+ $desas = DataDesa::all();
+ return view('data.data_sarana.create', compact('page_title', 'page_description', 'desas'));
+ }
+
+ public function store(Request $request)
+ {
+ try {
+ $request->validate([
+ 'desa_id' => 'required|integer:desa_id',
+ 'nama' => 'required|string|max:255',
+ 'jumlah' => 'required|integer|min:0',
+ 'kategori' => 'required|string|max:100',
+ 'keterangan' => 'required|string:max:255',
+ ]);
+ DataSarana::create($request->all());
+ } catch (\Exception $e) {
+ report($e);
+ return back()->withInput()->with('error', 'Data Sarana gagal disimpan!');
+ }
+ return redirect()->route('data.data-sarana.index')->with('success', 'Data Sarana berhasil disimpan!');
+ }
+
+ public function edit($id)
+ {
+ $page_title = 'Edit Data Sarana';
+ $page_description = 'Ubah informasi sarana desa';
+
+ $sarana = DataSarana::findOrFail($id);
+ $desas = DataDesa::all();
+
+ return view('data.data_sarana.edit', compact('page_title', 'page_description', 'sarana', 'desas'));
+ }
+
+ public function update(Request $request, $id)
+ {
+ try {
+ $request->validate([
+ 'desa_id' => 'required|integer:desa_id',
+ 'nama' => 'required|string|max:255',
+ 'jumlah' => 'required|integer|min:0',
+ 'kategori' => 'required|string|max:100',
+ 'keterangan' => 'required|string:max:255',
+ ]);
+ $sarana = DataSarana::findOrFail($id);
+ $sarana->update($request->all());
+ } catch (\Exception $e) {
+ report($e);
+ return back()->withInput()->with('error', 'Data Sarana gagal diperbarui');
+ }
+ return redirect()->route('data.data-sarana.index')->with('success', 'Data Sarana berhasil diperbarui');
+ }
+
+ public function destroy($id)
+ {
+ try {
+ $sarana = DataSarana::findOrFail($id);
+ $sarana->delete();
+ } catch (\Exception $e) {
+ report($e);
+ return back()->withInput()->with('error', 'Data Sarana gagal dihapus');
+ }
+ return redirect()->route('data.data-sarana.index')->with('success', 'Data Sarana berhasil dihapus');
+ }
+
+ public function export()
+ {
+ try {
+ $search = request('search');
+ $kategori = request('kategori');
+ $startDate = request('start_date');
+ $endDate = request('end_date');
+
+ $data = \App\Models\DataSarana::with('desa')
+ ->when($search, fn($q) => $q->where('nama', 'like', "%$search%"))
+ ->when($kategori, fn($q) => $q->where('kategori', $kategori))
+ ->when($startDate, fn($q) => $q->whereDate('created_at', '>=', $startDate))
+ ->when($endDate, fn($q) => $q->whereDate('created_at', '<=', $endDate))
+ ->latest('id')
+ ->get();
+
+ return Excel::download(new ExportDataSarana($data, 'Admin Desa'), 'data_sarana.xlsx');
+ } catch (\Exception $e) {
+ report($e);
+ return back()->withInput()->with('error', 'Data Sarana gagal dihapus');
+ }
+ }
+
+ public function import()
+ {
+ $page_title = "Import Sarana";
+ $page_description = "Upload data sarana";
+
+ $desas = DataDesa::all();
+
+ return view('data.data_sarana.import', compact('page_title', 'page_description', 'desas'));
+ }
+
+ public function importExcel(Request $request)
+ {
+ try {
+ $request->validate([
+ 'file' => 'required|mimes:xlsx,xls,csv'
+ ]);
+ Excel::import(new ImportDataSarana, $request->file('file'));
+ } catch (\Exception $e) {
+ report($e);
+ return back()->withInput()->with('error', 'Data Sarana gagal diimport');
+ }
+ return redirect()->route('data.data-sarana.index')->with('success', 'Data Sarana berhasil diimport');
+ }
+
+}
diff --git a/app/Http/Controllers/Data/DataUmumController.php b/app/Http/Controllers/Data/DataUmumController.php
index be9f7d6c4..c916a65bc 100644
--- a/app/Http/Controllers/Data/DataUmumController.php
+++ b/app/Http/Controllers/Data/DataUmumController.php
@@ -36,6 +36,7 @@
use App\Models\DataUmum;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
+use Illuminate\Support\Facades\DB;
class DataUmumController extends Controller
{
@@ -51,7 +52,12 @@ public function index()
$page_title = 'Data Umum';
$page_description = 'Ubah Data Umum';
- return view('data.data_umum.edit', compact('page_title', 'page_description', 'data_umum', 'luas_wilayah'));
+ $rekapKategori = DB::table('das_data_sarana')
+ ->select('kategori', DB::raw('SUM(jumlah) as total'))
+ ->groupBy('kategori')
+ ->pluck('total', 'kategori');
+
+ return view('data.data_umum.edit', compact('page_title', 'page_description', 'data_umum', 'luas_wilayah', 'rekapKategori'));
}
/**
diff --git a/app/Imports/ImportDataSarana.php b/app/Imports/ImportDataSarana.php
new file mode 100644
index 000000000..c026fb43c
--- /dev/null
+++ b/app/Imports/ImportDataSarana.php
@@ -0,0 +1,31 @@
+exists()) {
+ return null;
+ }
+
+ return new DataSarana([
+ 'desa_id' => $row['desa_id'],
+ 'nama' => $row['nama'],
+ 'jumlah' => $row['jumlah'],
+ 'kategori' => $row['kategori'],
+ 'keterangan' => $row['keterangan'],
+ ]);
+ }
+}
diff --git a/app/Models/DataDesa.php b/app/Models/DataDesa.php
index 832b2695e..81fe0fb20 100644
--- a/app/Models/DataDesa.php
+++ b/app/Models/DataDesa.php
@@ -181,4 +181,9 @@ public function pembangunan()
{
return $this->hasMany(Pembangunan::class, 'desa_id', 'desa_id');
}
+
+ public function saranas()
+ {
+ return $this->hasMany(DataSarana::class, 'desa_id', 'id');
+ }
}
diff --git a/app/Models/DataSarana.php b/app/Models/DataSarana.php
new file mode 100644
index 000000000..73604472e
--- /dev/null
+++ b/app/Models/DataSarana.php
@@ -0,0 +1,20 @@
+belongsTo(DataDesa::class, 'desa_id', 'id');
+ }
+}
diff --git a/database/factories/DataSaranaFactory.php b/database/factories/DataSaranaFactory.php
new file mode 100644
index 000000000..110edf463
--- /dev/null
+++ b/database/factories/DataSaranaFactory.php
@@ -0,0 +1,27 @@
+ DataDesa::factory(), // otomatis buat desa baru
+ 'nama' => $this->faker->word,
+ 'jumlah' => $this->faker->numberBetween(1, 100),
+ 'kategori' => $this->faker->randomElement([
+ 'puskesmas','puskesmas_pembantu','posyandu','pondok_bersalin',
+ 'paud','sd','smp','sma',
+ 'masjid_besar','mushola','gereja','pasar','balai_pertemuan'
+ ]),
+ 'keterangan' => $this->faker->sentence,
+ ];
+ }
+}
diff --git a/database/migrations/2025_09_23_115608_create_saranas_table.php b/database/migrations/2025_09_23_115608_create_saranas_table.php
new file mode 100644
index 000000000..eecb7a36b
--- /dev/null
+++ b/database/migrations/2025_09_23_115608_create_saranas_table.php
@@ -0,0 +1,36 @@
+bigIncrements('id');
+
+ $table->unsignedInteger('desa_id');
+ $table->foreign('desa_id')->references('id')->on('das_data_desa')->onDelete('cascade');
+
+ $table->string('kategori', 191);
+ $table->string('nama', 191)->nullable();
+ $table->integer('jumlah')->default(0);
+ $table->text('keterangan')->nullable();
+
+ $table->timestamps();
+ });
+ }
+
+ /**
+ * Reverse the migrations.
+ */
+ public function down(): void
+ {
+ Schema::dropIfExists('das_data_sarana');
+ }
+};
diff --git a/database/seeders/DasDataSaranaTableSeeder.php b/database/seeders/DasDataSaranaTableSeeder.php
new file mode 100644
index 000000000..fcdc0c90c
--- /dev/null
+++ b/database/seeders/DasDataSaranaTableSeeder.php
@@ -0,0 +1,60 @@
+delete();
+ DB::statement('ALTER TABLE das_data_desa AUTO_INCREMENT = 1'); // reset id
+
+ DB::table('das_data_desa')->insert([
+ 'id' => 1,
+ 'profil_id' => 1,
+ 'desa_id' => 1333222,
+ 'nama' => 'Desa Contoh',
+ 'sebutan_desa' => 'Desa',
+ 'website' => 'https://desa-contoh.id',
+ 'path' => json_encode(['desa-contoh']), // ✅ JSON valid
+ 'luas_wilayah' => 1234,
+ 'created_at' => now(),
+ 'updated_at' => now(),
+ ]);
+
+ // Untuk das_data_sarana bisa truncate karena hanya referensi ke desa_id
+ DB::table('das_data_sarana')->truncate();
+
+ DB::table('das_data_sarana')->insert([
+ [
+ 'id' => 2,
+ 'desa_id' => 1,
+ 'kategori' => 'puskesmas',
+ 'nama' => 'Puskesmas',
+ 'jumlah' => 1,
+ 'keterangan' => 'Puskesmas induk di pusat desa',
+ 'created_at' => now(),
+ 'updated_at' => now(),
+ ],
+ [
+ 'id' => 3,
+ 'desa_id' => 1,
+ 'kategori' => 'masjid_besar',
+ 'nama' => 'Masjid',
+ 'jumlah' => 7,
+ 'keterangan' => null,
+ 'created_at' => now(),
+ 'updated_at' => now(),
+ ],
+ ]);
+ }
+}
diff --git a/resources/views/data/data_sarana/create.blade.php b/resources/views/data/data_sarana/create.blade.php
new file mode 100644
index 000000000..1b436eaa2
--- /dev/null
+++ b/resources/views/data/data_sarana/create.blade.php
@@ -0,0 +1,99 @@
+@extends('layouts.dashboard_template')
+
+@section('content')
+
+
+
+
+
+
Tambah Data Sarana
+
+
+
+
+@endsection
diff --git a/resources/views/data/data_sarana/edit.blade.php b/resources/views/data/data_sarana/edit.blade.php
new file mode 100644
index 000000000..a49b8bc72
--- /dev/null
+++ b/resources/views/data/data_sarana/edit.blade.php
@@ -0,0 +1,103 @@
+@extends('layouts.dashboard_template')
+
+@section('content')
+
+
+
+
+
+
Edit Data Sarana
+
+
+
+
+@endsection
diff --git a/resources/views/data/data_sarana/import.blade.php b/resources/views/data/data_sarana/import.blade.php
new file mode 100644
index 000000000..4f851c8ae
--- /dev/null
+++ b/resources/views/data/data_sarana/import.blade.php
@@ -0,0 +1,170 @@
+@extends('layouts.dashboard_template')
+
+@section('content')
+
+
+
+
+
+
Import Data Sarana
+
+
+
+
+
+
+
Contoh Format Import Excel
+
+
+
+ desa_id
+ nama
+ jumlah
+ kategori
+ keterangan
+
+
+
+
+ 1
+ Posyandu Melati
+ 3
+ puskesmas
+ Bangunan permanen
+
+
+ 2
+ PAUD Tunas Bangsa
+ 2
+ paud
+ Kondisi baik
+
+
+ 3
+ Pasar
+ 1
+ pasar
+ Perlu perbaikan pagar
+
+
+
+
List Kategori Data Sarana
+
+
+
+ Kategori
+ Sub Kategori
+ Value (Untuk kategori import)
+
+
+
+
+
+ Sarana Kesehatan
+ Puskesmas
+ puskesmas
+
+
+ Puskesmas Pembantu
+ puskesmas_pembantu
+
+
+ Posyandu
+ posyandu
+
+
+ Pondok Bersalin
+ pondok_bersalin
+
+
+
+
+ Sarana Pendidikan
+ PAUD/Sederajat
+ paud
+
+
+ SD/Sederajat
+ sd
+
+
+ SMP/Sederajat
+ smp
+
+
+ SMA/Sederajat
+ sma
+
+
+
+
+ Sarana Umum
+ Masjid Besar
+ masjid_besar
+
+
+ Mushola
+ mushola
+
+
+ Gereja
+ gereja
+
+
+ Pasar
+ pasar
+
+
+ Balai Pertemuan
+ balai_pertemuan
+
+
+
+
+
List Id Desa
+
+
+
+ ID
+ Nama Desa
+
+
+
+ @forelse($desas as $desa)
+
+ {{ $desa->id }}
+ {{ $desa->nama }}
+
+ @empty
+
+ Belum ada data desa
+
+ @endforelse
+
+
+
+
+
+@endsection
diff --git a/resources/views/data/data_sarana/index.blade.php b/resources/views/data/data_sarana/index.blade.php
new file mode 100644
index 000000000..9dd5e76c4
--- /dev/null
+++ b/resources/views/data/data_sarana/index.blade.php
@@ -0,0 +1,125 @@
+@extends('layouts.dashboard_template')
+
+@section('content')
+
+
+
+ @include('partials.flash_message')
+
+
+
+
+
+
+
+
+
+ Aksi
+ Nama Sarana
+ Jumlah
+ Kategori
+ Desa
+
+
+
+
+
+
+
+@endsection
+
+@include('partials.asset_datatables')
+
+@push('scripts')
+
+@include('forms.datatable-vertical')
+@include('forms.delete-modal')
+@endpush
diff --git a/resources/views/data/data_sarana/partials/action.blade.php b/resources/views/data/data_sarana/partials/action.blade.php
new file mode 100644
index 000000000..780fa61e9
--- /dev/null
+++ b/resources/views/data/data_sarana/partials/action.blade.php
@@ -0,0 +1,18 @@
+
+
\ No newline at end of file
diff --git a/resources/views/data/data_umum/form_edit.blade.php b/resources/views/data/data_umum/form_edit.blade.php
index 73c48b5cd..bf2594475 100644
--- a/resources/views/data/data_umum/form_edit.blade.php
+++ b/resources/views/data/data_umum/form_edit.blade.php
@@ -94,7 +94,8 @@
Puskesmas *
- {!! Form::number('jml_puskesmas', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_puskesmas', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -102,7 +103,8 @@
Puskesmas Pembantu *
- {!! Form::number('jml_puskesmas_pembantu', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_puskesmas_pembantu', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -110,7 +112,8 @@
Posyandu *
- {!! Form::number('jml_posyandu', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_posyandu', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -118,7 +121,8 @@
Pondok Bersalin *
- {!! Form::number('jml_pondok_bersalin', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_pondok_bersalin', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -128,7 +132,8 @@
PAUD/Sederajat *
- {!! Form::number('jml_paud', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_paud', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -136,7 +141,8 @@
SD/Sederajat *
- {!! Form::number('jml_sd', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_sd', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -144,7 +150,8 @@
SMP/Sederajat *
- {!! Form::number('jml_smp', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_smp', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -152,7 +159,8 @@
SMA/Sederajat *
- {!! Form::number('jml_sma', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_sma', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -162,7 +170,8 @@
Masjid Besar *
- {!! Form::number('jml_masjid_besar', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_masjid_besar', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -170,7 +179,8 @@
Mushola *
- {!! Form::number('jml_mushola', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_mushola', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -178,7 +188,8 @@
Gereja *
- {!! Form::number('jml_gereja', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_gereja', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -186,7 +197,8 @@
Pasar *
- {!! Form::number('jml_pasar', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_pasar', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
@@ -194,7 +206,8 @@
Balai Pertemuan *
- {!! Form::number('jml_balai_pertemuan', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!}
+ {{-- {!! Form::number('jml_balai_pertemuan', null, ['placeholder' => '0', 'class' => 'form-control', 'required', 'style' => 'text-align:right;']) !!} --}}
+
diff --git a/resources/views/layouts/fragments/sidebar.blade.php b/resources/views/layouts/fragments/sidebar.blade.php
index 22afbc22a..7dc04a89f 100644
--- a/resources/views/layouts/fragments/sidebar.blade.php
+++ b/resources/views/layouts/fragments/sidebar.blade.php
@@ -136,7 +136,7 @@