Update Kelompok Tani
This commit is contained in:
+1
-1
@@ -681,7 +681,7 @@ Status: `Belum` / `Proses` / `Selesai` / `Dikecualikan`
|
|||||||
| Produktivitas | Tanaman | B | Belum | |
|
| Produktivitas | Tanaman | B | Belum | |
|
||||||
| Produktivitas | MapTanaman | ? | Belum | |
|
| Produktivitas | MapTanaman | ? | Belum | |
|
||||||
| Produktivitas | Produksi | B | Belum | |
|
| Produktivitas | Produksi | B | Belum | |
|
||||||
| Produktivitas | Survey Kelompok Tani (+ tab) | C | Belum | |
|
| Produktivitas | Survey Kelompok Tani (+ tab) | C | Proses | Daftar: Form Request filter, service query, dan JS terpisah; ringkasan profil disiapkan service; form/tab lama belum direfaktor |
|
||||||
| Produktivitas | Sebaran Petani | ? | Belum | Sedang dikerjakan, konfirmasi dulu |
|
| Produktivitas | Sebaran Petani | ? | Belum | Sedang dikerjakan, konfirmasi dulu |
|
||||||
| Program | Program, Program Komoditas, Program Lokasi | B | Belum | Pindah dari root ke `Program/` |
|
| Program | Program, Program Komoditas, Program Lokasi | B | Belum | Pindah dari root ke `Program/` |
|
||||||
| Program | Framework → Transformasi → Aktivitas → Target | B | Belum | Nested shallow |
|
| Program | Framework → Transformasi → Aktivitas → Target | B | Belum | Nested shallow |
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum JenisKelompokTani: string
|
||||||
|
{
|
||||||
|
case POKTAN = 'Kelompok Tani (Poktan)';
|
||||||
|
case KWT = 'Kelompok Wanita Tani (KWT)';
|
||||||
|
case GAPOKTAN = 'Gabungan Kelompok Tani (Gapoktan)';
|
||||||
|
case PEMUDA_TANI = 'Kelompok Pemuda Tani';
|
||||||
|
case ASOSIASI = 'Asosiasi Petani';
|
||||||
|
case LAINNYA = 'Lainnya';
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->mapWithKeys(fn (self $jenis) => [$jenis->value => $jenis->value])->all();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Enums;
|
||||||
|
|
||||||
|
enum StatusLegalitasKelompokTani: string
|
||||||
|
{
|
||||||
|
case BELUM_ADA = 'Belum ada SK/dokumen apa pun';
|
||||||
|
case PROSES = 'Sedang proses pengajuan';
|
||||||
|
case SK_DESA_KECAMATAN = 'Sudah ada SK dari desa/kecamatan';
|
||||||
|
case SK_DINAS_KABUPATEN = 'Sudah ada SK dan terdaftar di Dinas/Kabupaten';
|
||||||
|
case TIDAK_TAHU = 'Tidak tahu';
|
||||||
|
|
||||||
|
public static function options(): array
|
||||||
|
{
|
||||||
|
return collect(self::cases())->mapWithKeys(fn (self $status) => [$status->value => $status->value])->all();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,9 @@
|
|||||||
namespace App\Http\Controllers\Web\Produktivitas;
|
namespace App\Http\Controllers\Web\Produktivitas;
|
||||||
|
|
||||||
use App\Http\Controllers\Controller;
|
use App\Http\Controllers\Controller;
|
||||||
|
use App\Http\Requests\Produktivitas\IndexKelompokTaniRequest;
|
||||||
|
use App\Services\Produktivitas\KelompokTaniService;
|
||||||
|
use Illuminate\View\View;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\Gate;
|
use Illuminate\Support\Facades\Gate;
|
||||||
use Illuminate\Support\Str;
|
use Illuminate\Support\Str;
|
||||||
@@ -32,94 +35,10 @@ class SurveyKelompokTaniController extends Controller
|
|||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function index(Request $request)
|
public function index(IndexKelompokTaniRequest $request): View
|
||||||
{
|
{
|
||||||
$validated = $request->validate([
|
return view('produktivitas.survey-kelompok-tani.index', app(KelompokTaniService::class)->indexData($request->validated()));
|
||||||
'search' => ['nullable', 'string', 'max:255'],
|
|
||||||
'kabupaten_kota_id' => ['nullable', 'integer', 'exists:master_kabupaten_kota,id'],
|
|
||||||
'kecamatan_id' => ['nullable', 'integer', 'exists:master_kecamatan,id'],
|
|
||||||
'desa_kelurahan_id' => ['nullable', 'integer', 'exists:master_desa_kelurahan,id'],
|
|
||||||
'komoditas_id' => ['nullable', 'integer', 'exists:master_komoditas,id'],
|
|
||||||
'jenis_kelompok' => ['nullable', 'string', 'max:50'],
|
|
||||||
'sort_by' => ['nullable', 'string'],
|
|
||||||
'per_page' => ['nullable', 'integer', 'in:10,25'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$defaultKabupaten = KabupatenKota::select('id', 'nama')
|
|
||||||
->whereRaw('lower(nama) like ?', ['%ngada%'])->orderBy('nama')->first();
|
|
||||||
$allowedSorts = ['nama_asc', 'nama_desc', 'anggota_desc', 'anggota_asc'];
|
|
||||||
$filters = [
|
|
||||||
'search' => $validated['search'] ?? null,
|
|
||||||
'kabupaten_kota_id' => $validated['kabupaten_kota_id'] ?? $defaultKabupaten?->id,
|
|
||||||
'kecamatan_id' => $validated['kecamatan_id'] ?? null,
|
|
||||||
'desa_kelurahan_id' => $validated['desa_kelurahan_id'] ?? null,
|
|
||||||
'komoditas_id' => $validated['komoditas_id'] ?? null,
|
|
||||||
'jenis_kelompok' => $validated['jenis_kelompok'] ?? null,
|
|
||||||
'sort_by' => in_array($validated['sort_by'] ?? null, $allowedSorts, true)
|
|
||||||
? $validated['sort_by'] : null,
|
|
||||||
];
|
|
||||||
|
|
||||||
$query = KelompokTani::query()
|
|
||||||
->when(filled($filters['search']), function ($query) use ($filters) {
|
|
||||||
$search = '%' . Str::lower($filters['search']) . '%';
|
|
||||||
$query->where(function ($query) use ($search) {
|
|
||||||
$query->whereRaw('lower(nama_kelompok) like ?', [$search])
|
|
||||||
->orWhereRaw('lower(nama_responden) like ?', [$search]);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
->when($filters['kabupaten_kota_id'], function ($query, $id) {
|
|
||||||
$query->whereHas('desaKelurahan.kecamatan', fn ($query) => $query->where('kabupaten_kota_id', $id));
|
|
||||||
})
|
|
||||||
->when($filters['kecamatan_id'], function ($query, $id) {
|
|
||||||
$query->whereHas('desaKelurahan', fn ($query) => $query->where('kecamatan_id', $id));
|
|
||||||
})
|
|
||||||
->when($filters['desa_kelurahan_id'], fn ($query, $id) => $query->where('desa_kelurahan_id', $id))
|
|
||||||
->when($filters['komoditas_id'], function ($query, $id) {
|
|
||||||
$query->where(function ($query) use ($id) {
|
|
||||||
$query->whereJsonContains('komoditas_id', (string) $id)
|
|
||||||
->orWhereJsonContains('komoditas_id', (int) $id);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
->when($filters['jenis_kelompok'], fn ($query, $jenis) => $query->where('jenis_kelompok', $jenis));
|
|
||||||
|
|
||||||
$summary = (clone $query)->selectRaw('
|
|
||||||
COUNT(*) AS total_kelompok,
|
|
||||||
COUNT(DISTINCT jenis_kelompok) AS total_jenis,
|
|
||||||
COALESCE(SUM(jumlah_pengurus), 0) AS total_pengurus,
|
|
||||||
COALESCE(SUM(jumlah_pengurus_aktif), 0) AS pengurus_aktif,
|
|
||||||
COALESCE(SUM(jumlah_anggota_terdaftar), 0) AS total_anggota,
|
|
||||||
COALESCE(SUM(jumlah_anggota_aktif), 0) AS anggota_aktif
|
|
||||||
')->first();
|
|
||||||
|
|
||||||
match ($filters['sort_by']) {
|
|
||||||
'nama_asc' => $query->orderBy('nama_kelompok'),
|
|
||||||
'nama_desc' => $query->orderByDesc('nama_kelompok'),
|
|
||||||
'anggota_desc' => $query->orderByRaw('COALESCE(jumlah_anggota_terdaftar, 0) DESC'),
|
|
||||||
'anggota_asc' => $query->orderByRaw('COALESCE(jumlah_anggota_terdaftar, 0) ASC'),
|
|
||||||
default => $query->latest(),
|
|
||||||
};
|
|
||||||
|
|
||||||
$perPage = (int) ($validated['per_page'] ?? 10);
|
|
||||||
$kelompokTani = $query->with('desaKelurahan.kecamatan')->orderByDesc('id')
|
|
||||||
->paginate($perPage)->appends([...$filters, 'per_page' => $perPage]);
|
|
||||||
$selectedKabupaten = $filters['kabupaten_kota_id']
|
|
||||||
? KabupatenKota::select('id', 'nama')->find($filters['kabupaten_kota_id']) : null;
|
|
||||||
$kecamatanOptions = $filters['kabupaten_kota_id']
|
|
||||||
? Kecamatan::select('id', 'nama')->where('kabupaten_kota_id', $filters['kabupaten_kota_id'])->orderBy('nama')->get()
|
|
||||||
: collect();
|
|
||||||
$desaOptions = $filters['kecamatan_id']
|
|
||||||
? DesaKelurahan::select('id', 'nama')->where('kecamatan_id', $filters['kecamatan_id'])->orderBy('nama')->get()
|
|
||||||
: collect();
|
|
||||||
$komoditasOptions = Komoditas::select('id', 'nama')->orderBy('nama')->get();
|
|
||||||
$jenisKelompokOptions = KelompokTani::query()->whereNotNull('jenis_kelompok')
|
|
||||||
->where('jenis_kelompok', '!=', '')->distinct()->orderBy('jenis_kelompok')->pluck('jenis_kelompok');
|
|
||||||
|
|
||||||
return view('produktivitas.survey-kelompok-tani.index', compact(
|
|
||||||
'kelompokTani', 'summary', 'filters', 'selectedKabupaten',
|
|
||||||
'kecamatanOptions', 'desaOptions', 'komoditasOptions', 'jenisKelompokOptions'
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function create()
|
public function create()
|
||||||
{
|
{
|
||||||
$this->authorizeAccess();
|
$this->authorizeAccess();
|
||||||
@@ -129,6 +48,7 @@ class SurveyKelompokTaniController extends Controller
|
|||||||
|
|
||||||
return view('produktivitas.survey-kelompok-tani.add', [
|
return view('produktivitas.survey-kelompok-tani.add', [
|
||||||
...compact('provinsi', 'komoditas'),
|
...compact('provinsi', 'komoditas'),
|
||||||
|
'ringkasanProfil' => app(KelompokTaniService::class)->profileSummary(),
|
||||||
...$this->profileLocationOptions(),
|
...$this->profileLocationOptions(),
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
@@ -178,15 +98,18 @@ class SurveyKelompokTaniController extends Controller
|
|||||||
public function edit(Request $request, KelompokTani $kelompokTani)
|
public function edit(Request $request, KelompokTani $kelompokTani)
|
||||||
{
|
{
|
||||||
$this->authorizeAccess();
|
$this->authorizeAccess();
|
||||||
|
$ringkasanProfil = app(KelompokTaniService::class)->profileSummary($kelompokTani);
|
||||||
|
|
||||||
return match ($request->query('section', 'identitas')) {
|
return match ($request->query('section', 'identitas')) {
|
||||||
'kelembagaan' => view('produktivitas.survey-kelompok-tani.kelembagaan', compact('kelompokTani')),
|
'kelembagaan' => view('produktivitas.survey-kelompok-tani.kelembagaan', compact('kelompokTani', 'ringkasanProfil')),
|
||||||
'aktivitas-fungsi' => view('produktivitas.survey-kelompok-tani.aktivitas-fungsi', [
|
'aktivitas-fungsi' => view('produktivitas.survey-kelompok-tani.aktivitas-fungsi', [
|
||||||
'kelompokTani' => $kelompokTani->loadMissing(['aset', 'bantuan']),
|
'kelompokTani' => $kelompokTani->loadMissing(['aset', 'bantuan']),
|
||||||
|
'ringkasanProfil' => $ringkasanProfil,
|
||||||
]),
|
]),
|
||||||
'sosial-konflik' => view('produktivitas.survey-kelompok-tani.sosial-konflik', compact('kelompokTani')),
|
'sosial-konflik' => view('produktivitas.survey-kelompok-tani.sosial-konflik', compact('kelompokTani', 'ringkasanProfil')),
|
||||||
default => view('produktivitas.survey-kelompok-tani.edit', [
|
default => view('produktivitas.survey-kelompok-tani.edit', [
|
||||||
'kelompokTani' => $kelompokTani->loadMissing('pengurus'),
|
'kelompokTani' => $kelompokTani->loadMissing('pengurus'),
|
||||||
|
'ringkasanProfil' => $ringkasanProfil,
|
||||||
'provinsi' => Provinsi::orderBy('kode', 'asc')->get(),
|
'provinsi' => Provinsi::orderBy('kode', 'asc')->get(),
|
||||||
'komoditas' => Komoditas::where('is_utama', true)->get(),
|
'komoditas' => Komoditas::where('is_utama', true)->get(),
|
||||||
...$this->profileLocationOptions($kelompokTani),
|
...$this->profileLocationOptions($kelompokTani),
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Requests\Produktivitas;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Http\FormRequest;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
|
||||||
|
class IndexKelompokTaniRequest extends FormRequest
|
||||||
|
{
|
||||||
|
public function authorize(): bool
|
||||||
|
{
|
||||||
|
return Gate::any(['super_admin', 'admin', 'enumerator']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'search' => ['nullable', 'string', 'max:255'],
|
||||||
|
'kabupaten_kota_id' => ['bail', 'nullable', 'integer', 'exists:master_kabupaten_kota,id'],
|
||||||
|
'kecamatan_id' => ['bail', 'nullable', 'integer', 'exists:master_kecamatan,id'],
|
||||||
|
'desa_kelurahan_id' => ['bail', 'nullable', 'integer', 'exists:master_desa_kelurahan,id'],
|
||||||
|
'komoditas_id' => ['bail', 'nullable', 'integer', 'exists:master_komoditas,id'],
|
||||||
|
'jenis_kelompok' => ['nullable', 'string', 'max:50'],
|
||||||
|
'status_legalitas' => ['nullable', 'string', 'max:50'],
|
||||||
|
'sort_by' => ['nullable', 'string', 'max:50'],
|
||||||
|
'per_page' => ['nullable', 'integer', 'in:10,25'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function messages(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'integer' => ':attribute harus berupa angka bulat.',
|
||||||
|
'exists' => ':attribute tidak ditemukan.',
|
||||||
|
'string' => ':attribute harus berupa teks.',
|
||||||
|
'max' => ':attribute terlalu panjang.',
|
||||||
|
'in' => 'Pilihan :attribute tidak valid.',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attributes(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'search' => 'Pencarian',
|
||||||
|
'kabupaten_kota_id' => 'Kabupaten/Kota',
|
||||||
|
'kecamatan_id' => 'Kecamatan',
|
||||||
|
'desa_kelurahan_id' => 'Desa/Kelurahan',
|
||||||
|
'komoditas_id' => 'Komoditas',
|
||||||
|
'jenis_kelompok' => 'Jenis kelompok',
|
||||||
|
'status_legalitas' => 'Status SK/Dokumen Pendaftaran',
|
||||||
|
'sort_by' => 'Urutan data',
|
||||||
|
'per_page' => 'Jumlah data per halaman',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
|||||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||||
|
|
||||||
use App\Models\Wilayah\DesaKelurahan;
|
use App\Models\Wilayah\DesaKelurahan;
|
||||||
|
use App\Models\User;
|
||||||
|
|
||||||
class KelompokTani extends Model
|
class KelompokTani extends Model
|
||||||
{
|
{
|
||||||
@@ -125,6 +126,11 @@ class KelompokTani extends Model
|
|||||||
return $this->belongsTo(DesaKelurahan::class, 'desa_kelurahan_id');
|
return $this->belongsTo(DesaKelurahan::class, 'desa_kelurahan_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function creator(): BelongsTo
|
||||||
|
{
|
||||||
|
return $this->belongsTo(User::class, 'created_by');
|
||||||
|
}
|
||||||
|
|
||||||
public function pengurus()
|
public function pengurus()
|
||||||
{
|
{
|
||||||
return $this->hasMany(PengurusKelompokTani::class, 'survey_kelompok_tani_id');
|
return $this->hasMany(PengurusKelompokTani::class, 'survey_kelompok_tani_id');
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Services\Produktivitas;
|
||||||
|
|
||||||
|
use App\Enums\JenisKelompokTani;
|
||||||
|
use App\Enums\StatusLegalitasKelompokTani;
|
||||||
|
use App\Models\Pertanian\KelompokTani;
|
||||||
|
use App\Models\Pertanian\Komoditas;
|
||||||
|
use App\Models\Wilayah\DesaKelurahan;
|
||||||
|
use App\Models\Wilayah\KabupatenKota;
|
||||||
|
use App\Models\Wilayah\Kecamatan;
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Support\Facades\DB;
|
||||||
|
use Illuminate\Support\Facades\Gate;
|
||||||
|
use Illuminate\Support\Str;
|
||||||
|
|
||||||
|
class KelompokTaniService
|
||||||
|
{
|
||||||
|
public function profileSummary(?KelompokTani $kelompokTani = null): array
|
||||||
|
{
|
||||||
|
$kelompokTani?->loadMissing('desaKelurahan.kecamatan.kabupatenKota');
|
||||||
|
$desa = $kelompokTani?->desaKelurahan;
|
||||||
|
$statusSk = $kelompokTani?->status_legalitas;
|
||||||
|
$memilikiSk = in_array($statusSk, KelompokTani::STATUS_LEGALITAS_DENGAN_SK, true);
|
||||||
|
$komoditas = $kelompokTani?->komoditas_id
|
||||||
|
? Komoditas::whereIn('id', $kelompokTani->komoditas_id)->orderBy('nama')->pluck('nama')->unique()->values()->all()
|
||||||
|
: [];
|
||||||
|
$tanggalPerubahan = collect([$kelompokTani?->updated_at ?? $kelompokTani?->created_at])->filter();
|
||||||
|
if ($kelompokTani?->exists) {
|
||||||
|
foreach (['pengurus', 'aset', 'bantuan'] as $relation) {
|
||||||
|
// Penghapusan data bagian juga termasuk pembaruan profil kelompok.
|
||||||
|
$tanggal = $kelompokTani->{$relation}()->withTrashed()->max(DB::raw('COALESCE(updated_at, created_at)'));
|
||||||
|
if ($tanggal) {
|
||||||
|
$tanggalPerubahan->push(Carbon::parse($tanggal));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'judul' => $kelompokTani ? ($kelompokTani->nama_kelompok ?: 'Profil Kelompok Tani') : 'Profil Kelompok Tani Baru',
|
||||||
|
'lokasi' => collect([$desa?->nama, $desa?->kecamatan?->nama, $desa?->kecamatan?->kabupatenKota?->nama])
|
||||||
|
->filter()->implode(' · ') ?: 'Lokasi belum terisi',
|
||||||
|
'jenis' => $kelompokTani?->jenis_kelompok ?: 'Belum terisi',
|
||||||
|
'legalitas' => [
|
||||||
|
'label' => $memilikiSk ? 'Sudah ada SK' : ($statusSk ?: 'Belum terisi'),
|
||||||
|
'detail' => $statusSk ?: 'Belum terisi',
|
||||||
|
'warna' => $memilikiSk ? 'success' : 'secondary',
|
||||||
|
],
|
||||||
|
'pengurus' => $kelompokTani?->jumlah_pengurus !== null ? number_format($kelompokTani->jumlah_pengurus, 0, ',', '.') : 'Belum terisi',
|
||||||
|
'anggota' => $kelompokTani?->jumlah_anggota_terdaftar !== null ? number_format($kelompokTani->jumlah_anggota_terdaftar, 0, ',', '.') : 'Belum terisi',
|
||||||
|
'komoditas' => $komoditas,
|
||||||
|
'responden' => $kelompokTani?->nama_responden ?: 'Belum terisi',
|
||||||
|
'jabatan' => $kelompokTani?->jabatan_responden ?: 'Belum terisi',
|
||||||
|
'kontak' => $kelompokTani?->kontak_responden ?: 'Belum terisi',
|
||||||
|
'tahun_berdiri' => $kelompokTani?->tahun_berdiri ?: 'Belum terisi',
|
||||||
|
'diperbarui' => $tanggalPerubahan->sortByDesc(fn ($tanggal) => $tanggal->getTimestamp())->first(),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function indexData(array $validated): array
|
||||||
|
{
|
||||||
|
$defaultKabupaten = KabupatenKota::select('id', 'nama')
|
||||||
|
->whereRaw('lower(nama) like ?', ['%ngada%'])->orderBy('nama')->first();
|
||||||
|
$filters = [
|
||||||
|
'search' => $validated['search'] ?? null,
|
||||||
|
'kabupaten_kota_id' => $validated['kabupaten_kota_id'] ?? $defaultKabupaten?->id,
|
||||||
|
'kecamatan_id' => $validated['kecamatan_id'] ?? null,
|
||||||
|
'desa_kelurahan_id' => $validated['desa_kelurahan_id'] ?? null,
|
||||||
|
'komoditas_id' => $validated['komoditas_id'] ?? null,
|
||||||
|
'jenis_kelompok' => $validated['jenis_kelompok'] ?? null,
|
||||||
|
'status_legalitas' => $validated['status_legalitas'] ?? null,
|
||||||
|
'sort_by' => in_array($validated['sort_by'] ?? null, ['nama_asc', 'nama_desc', 'anggota_desc', 'anggota_asc'], true)
|
||||||
|
? $validated['sort_by'] : null,
|
||||||
|
];
|
||||||
|
|
||||||
|
$query = KelompokTani::query()
|
||||||
|
->when(filled($filters['search']), function ($query) use ($filters) {
|
||||||
|
$search = '%'.Str::lower($filters['search']).'%';
|
||||||
|
$query->where(function ($query) use ($search) {
|
||||||
|
$query->whereRaw('lower(nama_kelompok) like ?', [$search])
|
||||||
|
->orWhereRaw('lower(nama_responden) like ?', [$search])
|
||||||
|
->orWhereHas('desaKelurahan', function ($query) use ($search) {
|
||||||
|
$query->where(function ($query) use ($search) {
|
||||||
|
$query->whereRaw('lower(nama) like ?', [$search])
|
||||||
|
->orWhereHas('kecamatan', fn ($query) => $query->whereRaw('lower(nama) like ?', [$search]));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
})
|
||||||
|
->when($filters['kabupaten_kota_id'], fn ($query, $id) => $query
|
||||||
|
->whereHas('desaKelurahan.kecamatan', fn ($query) => $query->where('kabupaten_kota_id', $id)))
|
||||||
|
->when($filters['kecamatan_id'], fn ($query, $id) => $query
|
||||||
|
->whereHas('desaKelurahan', fn ($query) => $query->where('kecamatan_id', $id)))
|
||||||
|
->when($filters['desa_kelurahan_id'], fn ($query, $id) => $query->where('desa_kelurahan_id', $id))
|
||||||
|
->when($filters['komoditas_id'], fn ($query, $id) => $query->where(function ($query) use ($id) {
|
||||||
|
$query->whereJsonContains('komoditas_id', (string) $id)->orWhereJsonContains('komoditas_id', (int) $id);
|
||||||
|
}))
|
||||||
|
->when($filters['jenis_kelompok'], function ($query, $value) {
|
||||||
|
if ($value === JenisKelompokTani::LAINNYA->value) {
|
||||||
|
$jenisStandar = array_diff(JenisKelompokTani::options(), [JenisKelompokTani::LAINNYA->value]);
|
||||||
|
$query->whereNotNull('jenis_kelompok')->where('jenis_kelompok', '!=', '')->whereNotIn('jenis_kelompok', $jenisStandar);
|
||||||
|
} else {
|
||||||
|
$query->where('jenis_kelompok', $value);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
->when($filters['status_legalitas'], fn ($query, $value) => $query->where('status_legalitas', $value));
|
||||||
|
|
||||||
|
$summary = (clone $query)->selectRaw('
|
||||||
|
COUNT(*) AS total_kelompok,
|
||||||
|
COUNT(DISTINCT desa_kelurahan_id) AS total_desa,
|
||||||
|
COUNT(DISTINCT jenis_kelompok) AS total_jenis,
|
||||||
|
COALESCE(SUM(jumlah_pengurus), 0) AS total_pengurus,
|
||||||
|
COALESCE(SUM(jumlah_pengurus_aktif), 0) AS pengurus_aktif,
|
||||||
|
COALESCE(SUM(jumlah_anggota_terdaftar), 0) AS total_anggota,
|
||||||
|
COALESCE(SUM(jumlah_anggota_aktif), 0) AS anggota_aktif
|
||||||
|
')->first();
|
||||||
|
$summary->memiliki_sk = (clone $query)->whereIn('status_legalitas', KelompokTani::STATUS_LEGALITAS_DENGAN_SK)->count();
|
||||||
|
|
||||||
|
match ($filters['sort_by']) {
|
||||||
|
'nama_asc' => $query->orderBy('nama_kelompok'),
|
||||||
|
'nama_desc' => $query->orderByDesc('nama_kelompok'),
|
||||||
|
'anggota_desc' => $query->orderByRaw('COALESCE(jumlah_anggota_terdaftar, 0) DESC'),
|
||||||
|
'anggota_asc' => $query->orderByRaw('COALESCE(jumlah_anggota_terdaftar, 0) ASC'),
|
||||||
|
default => $query->latest(),
|
||||||
|
};
|
||||||
|
|
||||||
|
$showEnumerator = Gate::any(['super_admin', 'admin']);
|
||||||
|
$query->with('desaKelurahan.kecamatan');
|
||||||
|
if ($showEnumerator) {
|
||||||
|
$query->with('creator:id,name');
|
||||||
|
}
|
||||||
|
$perPage = (int) ($validated['per_page'] ?? 10);
|
||||||
|
$kelompokTani = $query->orderByDesc('id')->paginate($perPage)->appends([...$filters, 'per_page' => $perPage]);
|
||||||
|
$komoditasOptions = Komoditas::select('id', 'nama')->orderBy('nama')->get();
|
||||||
|
$komoditasById = $komoditasOptions->keyBy('id');
|
||||||
|
$ringkasanKelompok = $kelompokTani->getCollection()->mapWithKeys(function (KelompokTani $item) use ($komoditasById) {
|
||||||
|
$memilikiSk = in_array($item->status_legalitas, KelompokTani::STATUS_LEGALITAS_DENGAN_SK, true);
|
||||||
|
|
||||||
|
return [$item->id => [
|
||||||
|
'lokasi' => collect([$item->desaKelurahan?->nama, $item->desaKelurahan?->kecamatan?->nama])
|
||||||
|
->filter()->implode(' · ') ?: 'Lokasi belum terisi',
|
||||||
|
'komoditas' => collect($item->komoditas_id ?? [])->map(fn ($id) => $komoditasById->get($id)?->nama)->filter()->unique()->values()->all(),
|
||||||
|
'legalitas' => [
|
||||||
|
'label' => $memilikiSk ? 'Sudah ada SK' : ($item->status_legalitas ?: 'Belum terisi'),
|
||||||
|
'warna' => $memilikiSk ? 'success' : 'secondary',
|
||||||
|
],
|
||||||
|
]];
|
||||||
|
});
|
||||||
|
|
||||||
|
$selectedKabupaten = $filters['kabupaten_kota_id'] ? KabupatenKota::select('id', 'nama')->find($filters['kabupaten_kota_id']) : null;
|
||||||
|
$kecamatanOptions = $filters['kabupaten_kota_id']
|
||||||
|
? Kecamatan::select('id', 'nama')->where('kabupaten_kota_id', $filters['kabupaten_kota_id'])->orderBy('nama')->get() : collect();
|
||||||
|
$desaOptions = $filters['kecamatan_id']
|
||||||
|
? DesaKelurahan::select('id', 'nama')->where('kecamatan_id', $filters['kecamatan_id'])->orderBy('nama')->get() : collect();
|
||||||
|
$jenisKelompokOptions = collect(JenisKelompokTani::options())->values();
|
||||||
|
$statusLegalitasOptions = collect(StatusLegalitasKelompokTani::options())->values();
|
||||||
|
|
||||||
|
return compact('kelompokTani', 'summary', 'filters', 'ringkasanKelompok', 'showEnumerator', 'selectedKabupaten',
|
||||||
|
'kecamatanOptions', 'desaOptions', 'komoditasOptions', 'jenisKelompokOptions', 'statusLegalitasOptions');
|
||||||
|
}
|
||||||
|
}
|
||||||
+56
-13
@@ -246,15 +246,6 @@ body > .select2-container--open {
|
|||||||
border-color: var(--bs-form-invalid-border-color);
|
border-color: var(--bs-form-invalid-border-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.kelompok-tani-page .kelompok-tani-summary-name {
|
|
||||||
font-size: .9rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kelompok-tani-page .kelompok-tani-summary-note {
|
|
||||||
font-size: .75rem;
|
|
||||||
line-height: 1.3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.kelompok-tani-page .kelompok-tani-help {
|
.kelompok-tani-page .kelompok-tani-help {
|
||||||
margin-top: -.75rem;
|
margin-top: -.75rem;
|
||||||
}
|
}
|
||||||
@@ -400,17 +391,20 @@ body > .select2-container--open {
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bumdes-profile-summary .bumdes-summary-content {
|
.bumdes-profile-summary .bumdes-summary-content,
|
||||||
|
.kelompok-profile-summary .kelompok-summary-content {
|
||||||
min-inline-size: 0;
|
min-inline-size: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bumdes-profile-summary .bumdes-summary-label {
|
.bumdes-profile-summary .bumdes-summary-label,
|
||||||
|
.kelompok-profile-summary .kelompok-summary-label {
|
||||||
color: var(--bs-secondary-color);
|
color: var(--bs-secondary-color);
|
||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bumdes-profile-summary .badge {
|
.bumdes-profile-summary .badge,
|
||||||
|
.kelompok-profile-summary .badge {
|
||||||
max-inline-size: 100%;
|
max-inline-size: 100%;
|
||||||
line-height: 1.45;
|
line-height: 1.45;
|
||||||
white-space: normal;
|
white-space: normal;
|
||||||
@@ -418,7 +412,56 @@ body > .select2-container--open {
|
|||||||
overflow-wrap: anywhere;
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.bumdes-profile-summary .bumdes-summary-more {
|
.bumdes-profile-summary .bumdes-summary-more,
|
||||||
|
.kelompok-profile-summary .kelompok-summary-more {
|
||||||
min-block-size: 1.5rem;
|
min-block-size: 1.5rem;
|
||||||
font-size: 0.8125rem;
|
font-size: 0.8125rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Daftar kelompok tani. */
|
||||||
|
@media (min-width: 768px) {
|
||||||
|
.kelompok-tani-page .nav-pills-custom-spacing { display: flex; width: 100%; gap: .5rem; flex-wrap: nowrap !important; overflow-x: auto; }
|
||||||
|
.kelompok-tani-page .nav-pills-custom-spacing::-webkit-scrollbar { display: none; }
|
||||||
|
.kelompok-tani-page .nav-pills-custom-spacing .nav-item { margin-right: 0 !important; flex: 1; }
|
||||||
|
.kelompok-tani-page .nav-pills-custom-spacing .nav-link { display: flex; justify-content: center; align-items: center; padding-left: .5rem !important; padding-right: .5rem !important; width: 100%; white-space: nowrap; font-size: var(--bs-body-font-size) !important; }
|
||||||
|
.kelompok-tani-page .nav-pills-custom-spacing .nav-link i { margin-right: .4rem !important; }
|
||||||
|
}
|
||||||
|
.kelompok-tani-row { cursor: pointer; }
|
||||||
|
.kelompok-filter-row { padding-block: 1.5rem; }
|
||||||
|
.kelompok-filter-line { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; }
|
||||||
|
.kelompok-filter-label { display: flex; align-items: center; font-weight: 500; margin-right: .5rem; white-space: nowrap; }
|
||||||
|
.kelompok-filter-line .form-select,
|
||||||
|
.kelompok-filter-line .btn { min-height: 31px; }
|
||||||
|
.kelompok-filter-line .form-select {
|
||||||
|
background-color: var(--bs-paper-bg);
|
||||||
|
border-color: var(--bs-border-color);
|
||||||
|
border-radius: var(--bs-border-radius);
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
}
|
||||||
|
.kelompok-filter-line .form-select:focus {
|
||||||
|
border-color: rgba(40, 167, 69, .55);
|
||||||
|
box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .12);
|
||||||
|
}
|
||||||
|
.kelompok-filter-line .btn { white-space: nowrap; }
|
||||||
|
.kelompok-filter-static {
|
||||||
|
display: flex; align-items: center; min-height: 31px;
|
||||||
|
padding: .25rem .75rem; font-size: .8125rem;
|
||||||
|
background-color: var(--bs-paper-bg);
|
||||||
|
border: 1px solid var(--bs-border-color);
|
||||||
|
border-radius: var(--bs-border-radius);
|
||||||
|
color: var(--bs-body-color);
|
||||||
|
}
|
||||||
|
.kelompok-filter-line .input-group-sm { width: 240px; }
|
||||||
|
.kelompok-data-filters > div:not(.kelompok-filter-label) { flex: 1 1 12rem; min-inline-size: 0; }
|
||||||
|
.kelompok-data-filters .form-select { text-overflow: ellipsis; }
|
||||||
|
.kelompok-data-filters .input-group-sm { width: 100%; }
|
||||||
|
.kelompok-list-table .kelompok-list-identity { min-inline-size: 15rem; max-inline-size: 21rem; }
|
||||||
|
.kelompok-list-table .kelompok-list-commodities { min-inline-size: 10rem; max-inline-size: 15rem; }
|
||||||
|
.kelompok-list-table .kelompok-list-status { min-inline-size: 10rem; max-inline-size: 15rem; }
|
||||||
|
@media (max-width: 767.98px) {
|
||||||
|
.kelompok-filter-line { display: grid; grid-template-columns: minmax(0, 1fr); width: 100%; }
|
||||||
|
.kelompok-filter-label { margin-right: 0; }
|
||||||
|
.kelompok-filter-line .btn { justify-content: center; width: 100%; }
|
||||||
|
.kelompok-filter-line .input-group-sm { width: 100%; }
|
||||||
|
.kelompok-filter-line .input-group .btn { width: auto; }
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
$(function () {
|
||||||
|
const filterForm = document.getElementById('kelompok-filter-form');
|
||||||
|
if (!filterForm) return;
|
||||||
|
|
||||||
|
const headers = {
|
||||||
|
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content,
|
||||||
|
'Accept': 'application/json'
|
||||||
|
};
|
||||||
|
const $desa = $('#filter-desa');
|
||||||
|
let desaRequest;
|
||||||
|
|
||||||
|
$('#filter-kecamatan').on('change', function () {
|
||||||
|
if (desaRequest) desaRequest.abort();
|
||||||
|
$desa.empty().append(new Option('Semua Desa', '')).prop('disabled', true);
|
||||||
|
if (!this.value) return;
|
||||||
|
|
||||||
|
desaRequest = $.ajax({
|
||||||
|
url: filterForm.dataset.desaUrl.replace(':id', this.value),
|
||||||
|
method: 'GET',
|
||||||
|
headers,
|
||||||
|
success: function (data) {
|
||||||
|
data.forEach(desa => $desa.append(new Option(desa.nama, desa.id)));
|
||||||
|
$desa.prop('disabled', false);
|
||||||
|
},
|
||||||
|
error: function (xhr, status) {
|
||||||
|
if (status !== 'abort') $desa.empty().append(new Option('Gagal memuat desa', ''));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('#kelompok-per-page').on('change', function () { this.form.submit(); });
|
||||||
|
|
||||||
|
$(document).on('click', '.kelompok-tani-row', function (event) {
|
||||||
|
if ($(event.target).closest('a, button, input, select, textarea, label').length) return;
|
||||||
|
window.location.href = this.querySelector('.edit-record').href;
|
||||||
|
});
|
||||||
|
|
||||||
|
$(document).on('click', '.kelompok-tani-page .delete-record', function () {
|
||||||
|
const url = this.dataset.deleteUrl;
|
||||||
|
const name = this.dataset.nama;
|
||||||
|
Swal.fire({
|
||||||
|
title: 'Hapus kelompok tani?',
|
||||||
|
text: `Data kelompok tani "${name}" akan dihapus.`,
|
||||||
|
icon: 'warning',
|
||||||
|
showCancelButton: true,
|
||||||
|
confirmButtonText: 'Ya, hapus',
|
||||||
|
cancelButtonText: 'Batal',
|
||||||
|
customClass: { confirmButton: 'btn btn-danger me-3', cancelButton: 'btn btn-label-secondary' },
|
||||||
|
buttonsStyling: false
|
||||||
|
}).then(function (result) {
|
||||||
|
if (!result.isConfirmed) return;
|
||||||
|
$.ajax({
|
||||||
|
url,
|
||||||
|
method: 'DELETE',
|
||||||
|
headers,
|
||||||
|
success: function (response) {
|
||||||
|
Swal.fire({ icon: 'success', title: 'Terhapus', text: response.message, showConfirmButton: false, timer: 1500 })
|
||||||
|
.then(() => window.location.reload());
|
||||||
|
},
|
||||||
|
error: () => Swal.fire({ icon: 'error', title: 'Gagal menghapus', text: 'Data kelompok tani gagal dihapus. Silakan coba lagi.' })
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -103,12 +103,9 @@
|
|||||||
<label class="form-label" for="add-kt-jenis-kelompok">Terdaftar/Dikenal Sebagai<span class="text-danger">*</span></label>
|
<label class="form-label" for="add-kt-jenis-kelompok">Terdaftar/Dikenal Sebagai<span class="text-danger">*</span></label>
|
||||||
<select class="form-select @error('jenis_kelompok') is-invalid @enderror" id="add-kt-jenis-kelompok" name="jenis_kelompok" required>
|
<select class="form-select @error('jenis_kelompok') is-invalid @enderror" id="add-kt-jenis-kelompok" name="jenis_kelompok" required>
|
||||||
<option value="">Pilih Jenis Kelompok</option>
|
<option value="">Pilih Jenis Kelompok</option>
|
||||||
<option value="Kelompok Tani (Poktan)" {{ old('jenis_kelompok') == 'Kelompok Tani (Poktan)' ? 'selected' : '' }}>Kelompok Tani (Poktan)</option>
|
@foreach(\App\Enums\JenisKelompokTani::options() as $value => $label)
|
||||||
<option value="Kelompok Wanita Tani (KWT)" {{ old('jenis_kelompok') == 'Kelompok Wanita Tani (KWT)' ? 'selected' : '' }}>Kelompok Wanita Tani (KWT)</option>
|
<option value="{{ $value }}" @selected(old('jenis_kelompok') === $value)>{{ $label }}</option>
|
||||||
<option value="Gabungan Kelompok Tani (Gapoktan)" {{ old('jenis_kelompok') == 'Gabungan Kelompok Tani (Gapoktan)' ? 'selected' : '' }}>Gabungan Kelompok Tani (Gapoktan)</option>
|
@endforeach
|
||||||
<option value="Kelompok Pemuda Tani" {{ old('jenis_kelompok') == 'Kelompok Pemuda Tani' ? 'selected' : '' }}>Kelompok Pemuda Tani</option>
|
|
||||||
<option value="Asosiasi Petani" {{ old('jenis_kelompok') == 'Asosiasi Petani' ? 'selected' : '' }}>Asosiasi Petani</option>
|
|
||||||
<option value="Lainnya" {{ old('jenis_kelompok') == 'Lainnya' ? 'selected' : '' }}>Lainnya</option>
|
|
||||||
</select>
|
</select>
|
||||||
@error('jenis_kelompok')<div class="error invalid-feedback">{{ $message }}</div>@enderror
|
@error('jenis_kelompok')<div class="error invalid-feedback">{{ $message }}</div>@enderror
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -103,13 +103,7 @@
|
|||||||
<div class="col-md-6 mb-4 form-control-validation">
|
<div class="col-md-6 mb-4 form-control-validation">
|
||||||
<label class="form-label" for="edit-kt-jenis-kelompok">Terdaftar/Dikenal Sebagai<span class="text-danger">*</span></label>
|
<label class="form-label" for="edit-kt-jenis-kelompok">Terdaftar/Dikenal Sebagai<span class="text-danger">*</span></label>
|
||||||
@php
|
@php
|
||||||
$jenisKelompokOptions = [
|
$jenisKelompokOptions = array_diff(\App\Enums\JenisKelompokTani::options(), [\App\Enums\JenisKelompokTani::LAINNYA->value]);
|
||||||
'Kelompok Tani (Poktan)',
|
|
||||||
'Kelompok Wanita Tani (KWT)',
|
|
||||||
'Gabungan Kelompok Tani (Gapoktan)',
|
|
||||||
'Kelompok Pemuda Tani',
|
|
||||||
'Asosiasi Petani',
|
|
||||||
];
|
|
||||||
$currentJenisKelompok = old('jenis_kelompok', $kelompokTani->jenis_kelompok);
|
$currentJenisKelompok = old('jenis_kelompok', $kelompokTani->jenis_kelompok);
|
||||||
$isJenisKelompokLainnya = $currentJenisKelompok && !in_array($currentJenisKelompok, $jenisKelompokOptions);
|
$isJenisKelompokLainnya = $currentJenisKelompok && !in_array($currentJenisKelompok, $jenisKelompokOptions);
|
||||||
@endphp
|
@endphp
|
||||||
|
|||||||
@@ -1,152 +1,113 @@
|
|||||||
@include('produktivitas.survey-kelompok-tani._alert')
|
@include('produktivitas.survey-kelompok-tani._alert')
|
||||||
|
|
||||||
<style>
|
<div class="card kelompok-profile-summary mb-4" id="kelompok-tani-summary" role="region" aria-label="Ringkasan profil kelompok tani">
|
||||||
@media (min-width: 768px) {
|
<div class="card-body p-3 p-lg-4">
|
||||||
.nav-pills-custom-spacing {
|
<div class="row g-3 align-items-start">
|
||||||
display: flex;
|
<div class="col-12 col-lg-5">
|
||||||
width: 100%;
|
<div class="d-flex align-items-start gap-3">
|
||||||
gap: 0.5rem;
|
<span class="avatar flex-shrink-0" aria-hidden="true">
|
||||||
flex-wrap: nowrap !important;
|
<span class="avatar-initial rounded bg-label-primary">
|
||||||
overflow-x: auto;
|
<i class="icon-base ti tabler-users-group icon-22px"></i>
|
||||||
}
|
</span>
|
||||||
|
</span>
|
||||||
.nav-pills-custom-spacing::-webkit-scrollbar {
|
<div class="kelompok-summary-content">
|
||||||
display: none;
|
<h5 class="mb-1 text-break kelompok-tani-summary-name">{{ $ringkasanProfil['judul'] }}</h5>
|
||||||
}
|
<p class="small text-muted mb-1 text-break">
|
||||||
|
<i class="icon-base ti tabler-map-pin icon-16px me-1" aria-hidden="true"></i>{{ $ringkasanProfil['lokasi'] }}
|
||||||
.nav-pills-custom-spacing .nav-item {
|
</p>
|
||||||
margin-right: 0 !important;
|
<p class="small mb-0 text-break"><span class="text-muted">Tahun Berdiri:</span> <span data-summary-field="tahun_berdiri">{{ $ringkasanProfil['tahun_berdiri'] }}</span></p>
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-pills-custom-spacing .nav-link {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding-left: 0.5rem !important;
|
|
||||||
padding-right: 0.5rem !important;
|
|
||||||
width: 100%;
|
|
||||||
white-space: nowrap;
|
|
||||||
font-size: var(--bs-body-font-size) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.nav-pills-custom-spacing .nav-link i {
|
|
||||||
margin-right: 0.4rem !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
|
|
||||||
@php
|
|
||||||
$namaKomoditas = isset($kelompokTani) && $kelompokTani->komoditas_id
|
|
||||||
? \App\Models\Pertanian\Komoditas::whereIn('id', $kelompokTani->komoditas_id)->pluck('nama')
|
|
||||||
: collect();
|
|
||||||
$lokasiKelompok = isset($kelompokTani)
|
|
||||||
? collect([$kelompokTani->dusun, $kelompokTani->desaKelurahan?->nama, $kelompokTani->desaKelurahan?->kecamatan?->nama])
|
|
||||||
->filter()
|
|
||||||
->implode(', ')
|
|
||||||
: null;
|
|
||||||
@endphp
|
|
||||||
|
|
||||||
<div class="card mb-6" id="kelompok-tani-summary">
|
|
||||||
<div class="card-body p-3">
|
|
||||||
<div class="row align-items-center gy-3">
|
|
||||||
<div class="{{ isset($kelompokTani) ? 'col-lg-8' : 'col-12' }} d-flex align-items-center">
|
|
||||||
<div class="avatar avatar-lg me-3">
|
|
||||||
<span class="avatar-initial rounded bg-label-primary">
|
|
||||||
<i class="icon-base ti tabler-users-group icon-28px"></i>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div class="d-flex flex-column text-break" style="min-width: 0;">
|
|
||||||
<h6 class="mb-0 kelompok-tani-summary-name">{{ isset($kelompokTani) ? $kelompokTani->nama_kelompok : 'Tambah Kelompok Tani Baru' }}</h6>
|
|
||||||
<span class="text-muted kelompok-tani-summary-note">
|
|
||||||
{{ $lokasiKelompok ?: 'Lengkapi profil untuk memulai' }}
|
|
||||||
</span>
|
|
||||||
@if(isset($kelompokTani) && ($kelompokTani->jenis_kelompok || $namaKomoditas->isNotEmpty() || $kelompokTani->nama_gapoktan))
|
|
||||||
<div class="d-flex flex-wrap gap-1 mt-2">
|
|
||||||
@if($kelompokTani->jenis_kelompok)
|
|
||||||
<span class="badge bg-label-secondary">{{ $kelompokTani->jenis_kelompok }}</span>
|
|
||||||
@endif
|
|
||||||
@if($kelompokTani->nama_gapoktan)
|
|
||||||
<span class="badge bg-label-info">Gapoktan: {{ $kelompokTani->nama_gapoktan }}</span>
|
|
||||||
@endif
|
|
||||||
@foreach($namaKomoditas as $nama)
|
|
||||||
<span class="badge bg-label-success">{{ $nama }}</span>
|
|
||||||
@endforeach
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@if(isset($kelompokTani))
|
|
||||||
<div class="col-lg-4">
|
|
||||||
<p class="mb-2 fw-medium">Jumlah Pengurus & Anggota</p>
|
|
||||||
<div class="row g-2">
|
|
||||||
@foreach([
|
|
||||||
'jumlah_pengurus' => 'Total Pengurus',
|
|
||||||
'jumlah_anggota_terdaftar' => 'Total Anggota',
|
|
||||||
] as $field => $label)
|
|
||||||
<div class="col-6">
|
|
||||||
<div class="border rounded p-2 h-100">
|
|
||||||
<p class="mb-1 text-muted kelompok-tani-summary-note">{{ $label }}</p>
|
|
||||||
<h5 class="mb-0">{{ $kelompokTani->{$field} !== null ? number_format($kelompokTani->{$field}, 0, ',', '.') : '-' }}</h5>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endforeach
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if(isset($kelompokTani))
|
|
||||||
<hr class="my-3">
|
|
||||||
<div class="row gy-3">
|
|
||||||
<div class="col-sm-6 col-lg-3 d-flex align-items-center">
|
|
||||||
<span class="avatar avatar-sm me-2">
|
|
||||||
<span class="avatar-initial rounded-circle bg-label-primary">
|
|
||||||
<i class="icon-base ti tabler-user icon-16px"></i>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<div>
|
|
||||||
<p class="mb-0 text-muted kelompok-tani-summary-note">Nama Responden</p>
|
|
||||||
<span class="fw-medium small">{{ $kelompokTani->nama_responden ?: '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 col-lg-3 d-flex align-items-center">
|
|
||||||
<span class="avatar avatar-sm me-2">
|
|
||||||
<span class="avatar-initial rounded-circle bg-label-primary">
|
|
||||||
<i class="icon-base ti tabler-briefcase icon-16px"></i>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<div>
|
|
||||||
<p class="mb-0 text-muted kelompok-tani-summary-note">Jabatan Responden</p>
|
|
||||||
<span class="fw-medium small">{{ $kelompokTani->jabatan_responden ?: '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 col-lg-3 d-flex align-items-center">
|
|
||||||
<span class="avatar avatar-sm me-2">
|
|
||||||
<span class="avatar-initial rounded-circle bg-label-primary">
|
|
||||||
<i class="icon-base ti tabler-phone icon-16px"></i>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<div>
|
|
||||||
<p class="mb-0 text-muted kelompok-tani-summary-note">Kontak Responden</p>
|
|
||||||
<span class="fw-medium small">{{ $kelompokTani->kontak_responden ?: '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm-6 col-lg-3 d-flex align-items-center">
|
|
||||||
<span class="avatar avatar-sm me-2">
|
|
||||||
<span class="avatar-initial rounded-circle bg-label-primary">
|
|
||||||
<i class="icon-base ti tabler-calendar-event icon-16px"></i>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
<div>
|
|
||||||
<p class="mb-0 text-muted kelompok-tani-summary-note">Tahun Berdiri</p>
|
|
||||||
<span class="fw-medium small">{{ $kelompokTani->tahun_berdiri ?: '-' }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
@endif
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-12 col-lg-7">
|
||||||
|
<div class="row g-3">
|
||||||
|
<div class="col-12 col-sm-6">
|
||||||
|
<p class="kelompok-summary-label mb-1">Jenis Kelompok</p>
|
||||||
|
<span class="badge bg-label-primary" data-summary-field="jenis">{{ $ringkasanProfil['jenis'] }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-6">
|
||||||
|
<p class="kelompok-summary-label mb-1">Status SK</p>
|
||||||
|
<span class="badge bg-label-{{ $ringkasanProfil['legalitas']['warna'] }}" title="{{ $ringkasanProfil['legalitas']['detail'] }}" data-summary-field="legalitas">{{ $ringkasanProfil['legalitas']['label'] }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-6" data-summary-field="komoditas">
|
||||||
|
<p class="kelompok-summary-label mb-1">Komoditas Utama</p>
|
||||||
|
@if(count($ringkasanProfil['komoditas']))
|
||||||
|
<div class="d-flex flex-wrap align-items-start gap-2">
|
||||||
|
@foreach(array_slice($ringkasanProfil['komoditas'], 0, 2) as $nama)
|
||||||
|
<span class="badge bg-label-primary">{{ $nama }}</span>
|
||||||
|
@endforeach
|
||||||
|
@if(count($ringkasanProfil['komoditas']) > 2)
|
||||||
|
<button class="btn btn-sm btn-text-primary p-0 kelompok-summary-more" type="button" data-bs-toggle="collapse" data-bs-target="#kelompok-summary-extra-komoditas" aria-expanded="false" aria-controls="kelompok-summary-extra-komoditas">+{{ count($ringkasanProfil['komoditas']) - 2 }} lainnya</button>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
@if(count($ringkasanProfil['komoditas']) > 2)
|
||||||
|
<div class="collapse" id="kelompok-summary-extra-komoditas">
|
||||||
|
<div class="d-flex flex-wrap gap-2 pt-2">
|
||||||
|
@foreach(array_slice($ringkasanProfil['komoditas'], 2) as $nama)
|
||||||
|
<span class="badge bg-label-primary">{{ $nama }}</span>
|
||||||
|
@endforeach
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endif
|
||||||
|
@else
|
||||||
|
<p class="small mb-0 text-muted">Belum terisi</p>
|
||||||
|
@endif
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-sm-6">
|
||||||
|
<div class="row g-2">
|
||||||
|
<div class="col-6">
|
||||||
|
<p class="kelompok-summary-label mb-1">Total Pengurus</p>
|
||||||
|
<span class="fw-medium text-break" data-summary-field="pengurus">{{ $ringkasanProfil['pengurus'] }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
<p class="kelompok-summary-label mb-1">Total Anggota</p>
|
||||||
|
<span class="fw-medium text-break" data-summary-field="anggota">{{ $ringkasanProfil['anggota'] }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="card-footer border-top p-3 py-lg-2 px-lg-4">
|
||||||
|
<div class="row gy-3">
|
||||||
|
@foreach([
|
||||||
|
'responden' => ['Nama Responden', 'user'],
|
||||||
|
'jabatan' => ['Jabatan Responden', 'briefcase'],
|
||||||
|
'kontak' => ['Kontak Responden', 'phone'],
|
||||||
|
] as $field => [$label, $icon])
|
||||||
|
<div class="col-sm-6 col-lg-3 d-flex align-items-center">
|
||||||
|
<span class="avatar avatar-sm me-2 flex-shrink-0" aria-hidden="true">
|
||||||
|
<span class="avatar-initial rounded-circle bg-label-primary">
|
||||||
|
<i class="icon-base ti tabler-{{ $icon }} icon-16px"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div class="kelompok-summary-content">
|
||||||
|
<p class="kelompok-summary-label mb-0">{{ $label }}</p>
|
||||||
|
<span class="fw-medium small text-break" data-summary-field="{{ $field }}">{{ $ringkasanProfil[$field] }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
@endforeach
|
||||||
|
<div class="col-sm-6 col-lg-3 d-flex align-items-center">
|
||||||
|
<span class="avatar avatar-sm me-2 flex-shrink-0" aria-hidden="true">
|
||||||
|
<span class="avatar-initial rounded-circle bg-label-primary">
|
||||||
|
<i class="icon-base ti tabler-calendar-event icon-16px"></i>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
<div class="kelompok-summary-content">
|
||||||
|
<p class="kelompok-summary-label mb-0">Diperbarui</p>
|
||||||
|
<span class="fw-medium small text-break" data-summary-field="diperbarui">
|
||||||
|
@if($ringkasanProfil['diperbarui'])
|
||||||
|
<time datetime="{{ $ringkasanProfil['diperbarui']->toIso8601String() }}">{{ $ringkasanProfil['diperbarui']->locale('id')->translatedFormat('d M Y, H:i') }}</time>
|
||||||
|
@else
|
||||||
|
{{ isset($kelompokTani) ? 'Belum terisi' : 'Belum tersimpan' }}
|
||||||
|
@endif
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="nav-align-top">
|
<div class="nav-align-top">
|
||||||
<ul class="nav nav-pills flex-column flex-md-row mb-6 row-gap-2 nav-pills-custom-spacing">
|
<ul class="nav nav-pills flex-column flex-md-row mb-6 row-gap-2 nav-pills-custom-spacing">
|
||||||
|
|||||||
@@ -1,74 +1,45 @@
|
|||||||
@extends('layout.main')
|
@extends('layout.main')
|
||||||
|
|
||||||
|
|
||||||
@push('style')
|
|
||||||
<style>
|
|
||||||
.kelompok-tani-row { cursor: pointer; }
|
|
||||||
.kelompok-filter-row { padding-block: 1.5rem; }
|
|
||||||
.kelompok-filter-line { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; }
|
|
||||||
.kelompok-filter-label { display: flex; align-items: center; font-weight: 500; margin-right: .5rem; white-space: nowrap; }
|
|
||||||
.kelompok-filter-line .form-select,
|
|
||||||
.kelompok-filter-line .btn { min-height: 31px; }
|
|
||||||
.kelompok-filter-line .form-select {
|
|
||||||
background-color: var(--bs-paper-bg);
|
|
||||||
border-color: var(--bs-border-color);
|
|
||||||
border-radius: var(--bs-border-radius);
|
|
||||||
color: var(--bs-body-color);
|
|
||||||
}
|
|
||||||
.kelompok-filter-line .form-select:focus {
|
|
||||||
border-color: rgba(40, 167, 69, .55);
|
|
||||||
box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .12);
|
|
||||||
}
|
|
||||||
.kelompok-filter-line .btn { white-space: nowrap; }
|
|
||||||
.kelompok-filter-static {
|
|
||||||
display: flex; align-items: center; min-height: 31px;
|
|
||||||
padding: .25rem .75rem; font-size: .8125rem;
|
|
||||||
background-color: var(--bs-paper-bg);
|
|
||||||
border: 1px solid var(--bs-border-color);
|
|
||||||
border-radius: var(--bs-border-radius);
|
|
||||||
color: var(--bs-body-color);
|
|
||||||
}
|
|
||||||
.kelompok-filter-line .input-group-sm { width: 240px; }
|
|
||||||
@media (max-width: 767.98px) {
|
|
||||||
.kelompok-filter-line { display: grid; grid-template-columns: minmax(0, 1fr); width: 100%; }
|
|
||||||
.kelompok-filter-label { margin-right: 0; }
|
|
||||||
.kelompok-filter-line .btn { justify-content: center; width: 100%; }
|
|
||||||
.kelompok-filter-line .input-group-sm { width: 100%; }
|
|
||||||
.kelompok-filter-line .input-group .btn { width: auto; }
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@endpush
|
|
||||||
|
|
||||||
@section('content')
|
@section('content')
|
||||||
<div class="container-fluid flex-grow-1 container-p-y kelompok-tani-page">
|
<div class="container-fluid flex-grow-1 container-p-y kelompok-tani-page">
|
||||||
@include('produktivitas.survey-kelompok-tani._alert')
|
@include('produktivitas.survey-kelompok-tani._alert')
|
||||||
|
|
||||||
<div class="row g-3 mb-4">
|
<div class="row g-3 mb-4" id="kelompok-overview" aria-label="Ringkasan kelompok tani">
|
||||||
<div class="col-12 col-md-4">
|
<div class="col-12 col-md-6 col-xl-3">
|
||||||
<x-summary-card
|
<x-summary-card
|
||||||
title="Kelompok Tani"
|
title="Total Kelompok Tani"
|
||||||
count="{{ number_format($summary->total_kelompok, 0, ',', '.') }}"
|
count="{{ number_format($summary->total_kelompok, 0, ',', '.') }}"
|
||||||
subtitle="{{ number_format($summary->total_jenis, 0, ',', '.') }} jenis kelompok"
|
subtitle="{{ number_format($summary->total_desa, 0, ',', '.') }} desa/kelurahan terdata"
|
||||||
icon="users-group"
|
icon="users-group"
|
||||||
color="primary"
|
color="primary"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4">
|
<div class="col-12 col-md-6 col-xl-3">
|
||||||
|
<x-summary-card
|
||||||
|
title="Memiliki SK"
|
||||||
|
count="{{ number_format($summary->memiliki_sk, 0, ',', '.') }}"
|
||||||
|
subtitle="SK dari desa/kecamatan atau Dinas/Kabupaten"
|
||||||
|
icon="certificate"
|
||||||
|
color="success"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-12 col-md-6 col-xl-3">
|
||||||
<x-summary-card
|
<x-summary-card
|
||||||
title="Total Pengurus"
|
title="Total Pengurus"
|
||||||
count="{{ number_format($summary->total_pengurus, 0, ',', '.') }}"
|
count="{{ number_format($summary->total_pengurus, 0, ',', '.') }}"
|
||||||
subtitle="{{ number_format($summary->pengurus_aktif, 0, ',', '.') }} pengurus aktif"
|
subtitle="{{ number_format($summary->pengurus_aktif, 0, ',', '.') }} pengurus aktif"
|
||||||
icon="user-star"
|
icon="user-star"
|
||||||
color="success"
|
color="info"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-12 col-md-4">
|
<div class="col-12 col-md-6 col-xl-3">
|
||||||
<x-summary-card
|
<x-summary-card
|
||||||
title="Total Anggota"
|
title="Total Anggota"
|
||||||
count="{{ number_format($summary->total_anggota, 0, ',', '.') }}"
|
count="{{ number_format($summary->total_anggota, 0, ',', '.') }}"
|
||||||
subtitle="{{ number_format($summary->anggota_aktif, 0, ',', '.') }} anggota aktif"
|
subtitle="{{ number_format($summary->anggota_aktif, 0, ',', '.') }} anggota aktif"
|
||||||
icon="users"
|
icon="users"
|
||||||
color="info"
|
color="warning"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -77,7 +48,7 @@
|
|||||||
<div class="col-12">
|
<div class="col-12">
|
||||||
<div class="card card-border-shadow-primary">
|
<div class="card card-border-shadow-primary">
|
||||||
<div class="card-body py-3">
|
<div class="card-body py-3">
|
||||||
<form method="GET" action="{{ route('kelompok-tani.index') }}" id="kelompok-filter-form" aria-label="Filter data kelompok tani">
|
<form method="GET" action="{{ route('kelompok-tani.index') }}" id="kelompok-filter-form" data-desa-url="{{ route('desakelurahan.bykecamatan', ':id') }}" aria-label="Filter data kelompok tani">
|
||||||
<input type="hidden" name="per_page" value="{{ $kelompokTani->perPage() }}">
|
<input type="hidden" name="per_page" value="{{ $kelompokTani->perPage() }}">
|
||||||
<div class="kelompok-filter-line">
|
<div class="kelompok-filter-line">
|
||||||
<div class="kelompok-filter-label">
|
<div class="kelompok-filter-label">
|
||||||
@@ -109,7 +80,7 @@
|
|||||||
|
|
||||||
<hr class="my-3">
|
<hr class="my-3">
|
||||||
|
|
||||||
<div class="kelompok-filter-line">
|
<div class="kelompok-filter-line kelompok-data-filters">
|
||||||
<div class="kelompok-filter-label">
|
<div class="kelompok-filter-label">
|
||||||
<i class="icon-base ti tabler-adjustments-horizontal me-2"></i><span>Data</span>
|
<i class="icon-base ti tabler-adjustments-horizontal me-2"></i><span>Data</span>
|
||||||
</div>
|
</div>
|
||||||
@@ -129,6 +100,14 @@
|
|||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
<div>
|
||||||
|
<select name="status_legalitas" class="form-select form-select-sm" aria-label="Filter Status SK/Dokumen Pendaftaran">
|
||||||
|
<option value="">Semua Status SK</option>
|
||||||
|
@foreach($statusLegalitasOptions as $status)
|
||||||
|
<option value="{{ $status }}" @selected($filters['status_legalitas'] === $status)>{{ $status }}</option>
|
||||||
|
@endforeach
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<select name="sort_by" class="form-select form-select-sm" aria-label="Urutkan data kelompok tani">
|
<select name="sort_by" class="form-select form-select-sm" aria-label="Urutkan data kelompok tani">
|
||||||
<option value="">Urut Terbaru</option>
|
<option value="">Urut Terbaru</option>
|
||||||
@@ -138,14 +117,16 @@
|
|||||||
<option value="anggota_asc" @selected($filters['sort_by'] === 'anggota_asc')>Anggota Tersedikit</option>
|
<option value="anggota_asc" @selected($filters['sort_by'] === 'anggota_asc')>Anggota Tersedikit</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
</div>
|
||||||
<div class="input-group input-group-sm">
|
<div class="kelompok-filter-search d-flex flex-column flex-md-row gap-2 mt-3">
|
||||||
<input type="text" name="search" class="form-control" placeholder="Cari kelompok / responden..." value="{{ $filters['search'] }}" aria-label="Cari nama kelompok atau responden">
|
<div class="input-group input-group-sm flex-grow-1">
|
||||||
<button type="submit" class="btn btn-outline-secondary" aria-label="Cari data kelompok tani"><i class="icon-base ti tabler-search"></i></button>
|
<input type="text" name="search" class="form-control" placeholder="Cari kelompok / lokasi / responden..." value="{{ $filters['search'] }}" aria-label="Cari nama kelompok, desa, kecamatan, atau responden">
|
||||||
</div>
|
<button type="submit" class="btn btn-outline-secondary" aria-label="Cari data kelompok tani"><i class="icon-base ti tabler-search"></i></button>
|
||||||
|
</div>
|
||||||
|
<div class="kelompok-filter-actions d-flex gap-2 flex-shrink-0">
|
||||||
|
<button type="submit" class="btn btn-outline-secondary btn-sm flex-fill"><i class="icon-base ti tabler-filter me-1"></i>Filter</button>
|
||||||
|
<a href="{{ route('kelompok-tani.index') }}" class="btn btn-outline-secondary btn-sm flex-fill"><i class="icon-base ti tabler-refresh me-1"></i>Reset</a>
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-outline-secondary btn-sm"><i class="icon-base ti tabler-filter me-1"></i>Filter</button>
|
|
||||||
<a href="{{ route('kelompok-tani.index') }}" class="btn btn-outline-secondary btn-sm"><i class="icon-base ti tabler-refresh me-1"></i>Reset</a>
|
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@@ -165,12 +146,12 @@
|
|||||||
@endforeach
|
@endforeach
|
||||||
<div class="col-md-auto">
|
<div class="col-md-auto">
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<label class="me-2 text-nowrap">Show</label>
|
<label class="me-2 text-nowrap" for="kelompok-per-page">Tampilkan</label>
|
||||||
<select name="per_page" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
|
<select name="per_page" id="kelompok-per-page" class="form-select form-select-sm w-auto">
|
||||||
<option value="10" @selected($kelompokTani->perPage() === 10)>10</option>
|
<option value="10" @selected($kelompokTani->perPage() === 10)>10</option>
|
||||||
<option value="25" @selected($kelompokTani->perPage() === 25)>25</option>
|
<option value="25" @selected($kelompokTani->perPage() === 25)>25</option>
|
||||||
</select>
|
</select>
|
||||||
<label class="ms-2 text-nowrap">entries</label>
|
<span class="ms-2 text-nowrap">data</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-auto ms-md-auto">
|
<div class="col-md-auto ms-md-auto">
|
||||||
@@ -182,36 +163,63 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="table-responsive text-nowrap">
|
<div class="table-responsive text-nowrap">
|
||||||
<table class="table table-hover">
|
<table class="table table-hover align-middle kelompok-list-table">
|
||||||
<thead class="border-top">
|
<thead class="border-top">
|
||||||
<tr>
|
<tr>
|
||||||
<th>No</th>
|
<th>No</th>
|
||||||
<th>Nama Kelompok</th>
|
<th>Kelompok Tani</th>
|
||||||
<th>Desa/Kelurahan</th>
|
<th>Komoditas</th>
|
||||||
<th>Jenis Kelompok</th>
|
<th>Status SK</th>
|
||||||
<th>Tahun Berdiri</th>
|
<th>Pengurus</th>
|
||||||
|
<th>Anggota</th>
|
||||||
<th>Nama Responden</th>
|
<th>Nama Responden</th>
|
||||||
|
@if($showEnumerator)
|
||||||
|
<th>Nama Enumerator</th>
|
||||||
|
@endif
|
||||||
<th>Aksi</th>
|
<th>Aksi</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
@forelse($kelompokTani as $index => $item)
|
@forelse($kelompokTani as $item)
|
||||||
|
@php($ringkasan = $ringkasanKelompok[$item->id])
|
||||||
<tr class="kelompok-tani-row">
|
<tr class="kelompok-tani-row">
|
||||||
<td>{{ $kelompokTani->firstItem() + $loop->index }}</td>
|
<td>{{ $kelompokTani->firstItem() + $loop->index }}</td>
|
||||||
<td><a href="{{ route('kelompok-tani.edit', $item->id) }}" class="text-heading d-block">{{ $item->nama_kelompok }}</a></td>
|
<td class="text-wrap kelompok-list-identity">
|
||||||
<td><span class="text-muted">{{ $item->desaKelurahan?->nama }}</span></td>
|
<a href="{{ route('kelompok-tani.edit', $item->id) }}" class="text-heading d-block fw-medium">{{ $item->nama_kelompok }}</a>
|
||||||
<td><span class="text-muted">{{ $item->jenis_kelompok }}</span></td>
|
<div class="small text-muted">{{ $ringkasan['lokasi'] }}</div>
|
||||||
<td><span class="text-muted">{{ $item->tahun_berdiri }}</span></td>
|
<div class="small text-muted">{{ $item->jenis_kelompok ?: 'Jenis kelompok belum terisi' }}</div>
|
||||||
<td><span class="text-muted">{{ $item->nama_responden }}</span></td>
|
</td>
|
||||||
|
<td class="text-wrap kelompok-list-commodities">
|
||||||
|
<span class="text-muted">{{ implode(', ', $ringkasan['komoditas']) ?: 'Belum terisi' }}</span>
|
||||||
|
</td>
|
||||||
|
<td class="text-wrap kelompok-list-status">
|
||||||
|
<span class="text-muted" title="{{ $item->status_legalitas }}">{{ $ringkasan['legalitas']['label'] }}</span>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="fw-medium">{{ $item->jumlah_pengurus !== null ? number_format($item->jumlah_pengurus, 0, ',', '.') : 'Belum terisi' }}</div>
|
||||||
|
@if($item->jumlah_pengurus_aktif !== null)
|
||||||
|
<span class="small text-muted">{{ number_format($item->jumlah_pengurus_aktif, 0, ',', '.') }} aktif</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<div class="fw-medium">{{ $item->jumlah_anggota_terdaftar !== null ? number_format($item->jumlah_anggota_terdaftar, 0, ',', '.') : 'Belum terisi' }}</div>
|
||||||
|
@if($item->jumlah_anggota_aktif !== null)
|
||||||
|
<span class="small text-muted">{{ number_format($item->jumlah_anggota_aktif, 0, ',', '.') }} aktif</span>
|
||||||
|
@endif
|
||||||
|
</td>
|
||||||
|
<td class="text-wrap"><span class="text-muted">{{ $item->nama_responden ?: 'Belum terisi' }}</span></td>
|
||||||
|
@if($showEnumerator)
|
||||||
|
<td class="text-wrap"><span class="text-muted">{{ $item->creator?->name ?? '-' }}</span></td>
|
||||||
|
@endif
|
||||||
<td>
|
<td>
|
||||||
<div class="d-flex align-items-center">
|
<div class="d-flex align-items-center">
|
||||||
<a href="{{ route('kelompok-tani.edit', $item->id) }}" class="btn btn-icon btn-text-secondary rounded-pill edit-record"><i class="ti tabler-edit ti-md"></i></a>
|
<a href="{{ route('kelompok-tani.edit', $item->id) }}" class="btn btn-icon btn-text-secondary rounded-pill edit-record" aria-label="Ubah {{ $item->nama_kelompok }}"><i class="ti tabler-edit ti-md"></i></a>
|
||||||
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill delete-record" data-id="{{ $item->id }}" data-nama="{{ $item->nama_kelompok }}"><i class="ti tabler-trash ti-md"></i></a>
|
<button type="button" class="btn btn-icon btn-text-secondary rounded-pill delete-record" data-delete-url="{{ route('kelompok-tani.destroy', $item->id) }}" data-nama="{{ $item->nama_kelompok }}" aria-label="Hapus {{ $item->nama_kelompok }}"><i class="ti tabler-trash ti-md"></i></button>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
@empty
|
@empty
|
||||||
<tr><td colspan="7" class="text-center py-4">Tidak ada data ditemukan.</td></tr>
|
<tr><td colspan="{{ $showEnumerator ? 9 : 8 }}" class="text-center py-4">Tidak ada data ditemukan.</td></tr>
|
||||||
@endforelse
|
@endforelse
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
@@ -224,69 +232,5 @@
|
|||||||
@endsection
|
@endsection
|
||||||
|
|
||||||
@push('scripts')
|
@push('scripts')
|
||||||
<script>
|
<script src="{{ asset('assets/js/kelompok-tani.js') }}?v={{ filemtime(public_path('assets/js/kelompok-tani.js')) }}"></script>
|
||||||
'use strict';
|
|
||||||
const csrfToken = "{{ csrf_token() }}";
|
|
||||||
|
|
||||||
$(document).ready(function () {
|
|
||||||
const $kecamatanFilter = $('#filter-kecamatan');
|
|
||||||
const $desaFilter = $('#filter-desa');
|
|
||||||
const desaUrlTemplate = '{{ route("desakelurahan.bykecamatan", ":id") }}';
|
|
||||||
let desaRequest;
|
|
||||||
|
|
||||||
$kecamatanFilter.on('change', function () {
|
|
||||||
if (desaRequest) desaRequest.abort();
|
|
||||||
$desaFilter.empty().append(new Option('Semua Desa', '')).prop('disabled', true);
|
|
||||||
const kecamatanId = $(this).val();
|
|
||||||
if (!kecamatanId) return;
|
|
||||||
|
|
||||||
desaRequest = $.get(desaUrlTemplate.replace(':id', kecamatanId), function (data) {
|
|
||||||
data.forEach(function (desa) {
|
|
||||||
$desaFilter.append(new Option(desa.nama, desa.id));
|
|
||||||
});
|
|
||||||
$desaFilter.prop('disabled', false);
|
|
||||||
}).fail(function (xhr, status) {
|
|
||||||
if (status !== 'abort') {
|
|
||||||
$desaFilter.empty().append(new Option('Gagal memuat desa', ''));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on('click', '.kelompok-tani-row', function (event) {
|
|
||||||
if ($(event.target).closest('a, button, input, select, textarea, label').length) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.location.href = this.querySelector('.edit-record').href;
|
|
||||||
});
|
|
||||||
|
|
||||||
$(document).on('click', '.delete-record', function () {
|
|
||||||
const id = $(this).data('id');
|
|
||||||
const name = $(this).data('nama');
|
|
||||||
|
|
||||||
Swal.fire({
|
|
||||||
title: 'Apakah Anda yakin?',
|
|
||||||
text: `Data kelompok tani "${name}" akan dihapus secara permanen!`,
|
|
||||||
icon: 'warning',
|
|
||||||
showCancelButton: true,
|
|
||||||
confirmButtonText: 'Ya, Hapus!',
|
|
||||||
customClass: { confirmButton: 'btn btn-primary me-3', cancelButton: 'btn btn-label-secondary' },
|
|
||||||
buttonsStyling: false
|
|
||||||
}).then(function (result) {
|
|
||||||
if (result.value) {
|
|
||||||
$.ajax({
|
|
||||||
url: '{{ route('kelompok-tani.destroy', ':id') }}'.replace(':id', id),
|
|
||||||
type: 'DELETE',
|
|
||||||
headers: { 'X-CSRF-TOKEN': csrfToken },
|
|
||||||
success: function (res) {
|
|
||||||
Swal.fire({ icon: 'success', title: 'Terhapus!', text: res.message, showConfirmButton: false, timer: 1500 })
|
|
||||||
.then(() => window.location.reload());
|
|
||||||
},
|
|
||||||
error: () => Swal.fire({ icon: 'error', title: 'Gagal!', text: 'Terjadi kesalahan sistem.' })
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
@endpush
|
@endpush
|
||||||
|
|||||||
@@ -42,13 +42,7 @@
|
|||||||
<label class="form-label" for="klb-status-legalitas">Status SK/Dokumen Pendaftaran</label>
|
<label class="form-label" for="klb-status-legalitas">Status SK/Dokumen Pendaftaran</label>
|
||||||
<select class="form-select @error('status_legalitas') is-invalid @enderror" id="klb-status-legalitas" name="status_legalitas">
|
<select class="form-select @error('status_legalitas') is-invalid @enderror" id="klb-status-legalitas" name="status_legalitas">
|
||||||
<option value="">Pilih Status Legalitas</option>
|
<option value="">Pilih Status Legalitas</option>
|
||||||
@foreach([
|
@foreach(\App\Enums\StatusLegalitasKelompokTani::options() as $option)
|
||||||
'Belum ada SK/dokumen apa pun',
|
|
||||||
'Sedang proses pengajuan',
|
|
||||||
'Sudah ada SK dari desa/kecamatan',
|
|
||||||
'Sudah ada SK dan terdaftar di Dinas/Kabupaten',
|
|
||||||
'Tidak tahu',
|
|
||||||
] as $option)
|
|
||||||
<option value="{{ $option }}" {{ old('status_legalitas', $kelompokTani->status_legalitas) == $option ? 'selected' : '' }}>{{ $option }}</option>
|
<option value="{{ $option }}" {{ old('status_legalitas', $kelompokTani->status_legalitas) == $option ? 'selected' : '' }}>{{ $option }}</option>
|
||||||
@endforeach
|
@endforeach
|
||||||
</select>
|
</select>
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
use App\Http\Controllers\Web\Produktivitas\SurveyKelompokTaniController;
|
use App\Http\Controllers\Web\Produktivitas\SurveyKelompokTaniController;
|
||||||
|
use App\Http\Requests\Produktivitas\IndexKelompokTaniRequest;
|
||||||
|
use App\Models\User;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Http\Request;
|
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
use Illuminate\Support\Facades\Schema;
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
|
use Illuminate\Support\ViewErrorBag;
|
||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
@@ -27,6 +30,9 @@ beforeEach(function () {
|
|||||||
$table->string('nama_responden')->nullable();
|
$table->string('nama_responden')->nullable();
|
||||||
$table->integer('desa_kelurahan_id');
|
$table->integer('desa_kelurahan_id');
|
||||||
$table->string('jenis_kelompok')->nullable();
|
$table->string('jenis_kelompok')->nullable();
|
||||||
|
$table->string('status_legalitas')->nullable();
|
||||||
|
$table->boolean('pernah_mendapatkan_bantuan')->nullable();
|
||||||
|
$table->integer('created_by')->nullable();
|
||||||
$table->json('komoditas_id')->nullable();
|
$table->json('komoditas_id')->nullable();
|
||||||
foreach (['jumlah_pengurus', 'jumlah_pengurus_aktif', 'jumlah_anggota_terdaftar', 'jumlah_anggota_aktif'] as $field) {
|
foreach (['jumlah_pengurus', 'jumlah_pengurus_aktif', 'jumlah_anggota_terdaftar', 'jumlah_anggota_aktif'] as $field) {
|
||||||
$table->integer($field)->nullable();
|
$table->integer($field)->nullable();
|
||||||
@@ -34,6 +40,17 @@ beforeEach(function () {
|
|||||||
$table->timestamps();
|
$table->timestamps();
|
||||||
$table->softDeletes();
|
$table->softDeletes();
|
||||||
});
|
});
|
||||||
|
Schema::create('users', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('name');
|
||||||
|
$table->softDeletes();
|
||||||
|
});
|
||||||
|
DB::table('users')->insert(['id' => 7, 'name' => 'Enumerator Uji']);
|
||||||
|
$user = new User(['name' => 'Admin Uji', 'email' => 'admin@example.test', 'role' => 'admin']);
|
||||||
|
$user->id = 7;
|
||||||
|
$this->be($user);
|
||||||
|
Event::forget('composing: *');
|
||||||
|
view()->share('errors', new ViewErrorBag());
|
||||||
|
|
||||||
DB::table('master_kabupaten_kota')->insert([
|
DB::table('master_kabupaten_kota')->insert([
|
||||||
['id' => 1, 'nama' => 'Kabupaten Ngada'], ['id' => 2, 'nama' => 'Kabupaten Lain'],
|
['id' => 1, 'nama' => 'Kabupaten Ngada'], ['id' => 2, 'nama' => 'Kabupaten Lain'],
|
||||||
@@ -53,11 +70,11 @@ beforeEach(function () {
|
|||||||
['id' => 1, 'nama' => 'Kakao'], ['id' => 2, 'nama' => 'Kopi'], ['id' => 11, 'nama' => 'Vanili'],
|
['id' => 1, 'nama' => 'Kakao'], ['id' => 2, 'nama' => 'Kopi'], ['id' => 11, 'nama' => 'Vanili'],
|
||||||
]);
|
]);
|
||||||
foreach ([
|
foreach ([
|
||||||
[1, 'Alpha', 'Budi', 1, 'Poktan', ['1', '2'], 3, 2, 30, 25, null],
|
[1, 'Alpha', 'Budi', 1, 'Kelompok Tani (Poktan)', ['1', '2'], 3, 2, 30, 25, null],
|
||||||
[2, 'Beta', 'Siti', 2, 'KWT', [1], 5, 4, 40, 35, null],
|
[2, 'Beta', 'Siti', 2, 'Kelompok Wanita Tani (KWT)', [1], 5, 4, 40, 35, null],
|
||||||
[3, 'Citra', 'Ani', 3, 'Poktan', [2, 11], null, null, 0, 0, null],
|
[3, 'Citra', 'Ani', 3, 'Kelompok Tani (Poktan)', [2, 11], null, null, 0, 0, null],
|
||||||
[4, 'Diluar', 'Toni', 4, 'Poktan', [1], 99, 99, 900, 900, null],
|
[4, 'Diluar', 'Toni', 4, 'Kelompok Tani (Poktan)', [1], 99, 99, 900, 900, null],
|
||||||
[5, 'Dihapus', 'Dewi', 1, 'Poktan', [1], 99, 99, 900, 900, '2026-01-01'],
|
[5, 'Dihapus', 'Dewi', 1, 'Kelompok Tani (Poktan)', [1], 99, 99, 900, 900, '2026-01-01'],
|
||||||
] as [$id, $nama, $responden, $desa, $jenis, $komoditas, $pengurus, $pengurusAktif, $anggota, $anggotaAktif, $deletedAt]) {
|
] as [$id, $nama, $responden, $desa, $jenis, $komoditas, $pengurus, $pengurusAktif, $anggota, $anggotaAktif, $deletedAt]) {
|
||||||
DB::table('survey_kelompok_tani')->insert([
|
DB::table('survey_kelompok_tani')->insert([
|
||||||
'id' => $id, 'nama_kelompok' => $nama, 'nama_responden' => $responden,
|
'id' => $id, 'nama_kelompok' => $nama, 'nama_responden' => $responden,
|
||||||
@@ -65,12 +82,22 @@ beforeEach(function () {
|
|||||||
'komoditas_id' => json_encode($komoditas), 'jumlah_pengurus' => $pengurus,
|
'komoditas_id' => json_encode($komoditas), 'jumlah_pengurus' => $pengurus,
|
||||||
'jumlah_pengurus_aktif' => $pengurusAktif, 'jumlah_anggota_terdaftar' => $anggota,
|
'jumlah_pengurus_aktif' => $pengurusAktif, 'jumlah_anggota_terdaftar' => $anggota,
|
||||||
'jumlah_anggota_aktif' => $anggotaAktif, 'created_at' => "2026-01-0{$id}", 'deleted_at' => $deletedAt,
|
'jumlah_anggota_aktif' => $anggotaAktif, 'created_at' => "2026-01-0{$id}", 'deleted_at' => $deletedAt,
|
||||||
|
'status_legalitas' => match ($id) {
|
||||||
|
1 => 'Sudah ada SK dari desa/kecamatan',
|
||||||
|
2 => 'Sedang proses pengajuan',
|
||||||
|
4, 5 => 'Sudah ada SK dan terdaftar di Dinas/Kabupaten',
|
||||||
|
default => null,
|
||||||
|
},
|
||||||
|
'pernah_mendapatkan_bantuan' => match ($id) { 1 => true, 2 => false, default => null },
|
||||||
|
'created_by' => 7,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->listKelompok = function (array $filters = []) {
|
$this->listKelompok = function (array $filters = []) {
|
||||||
$request = Request::create('/survey/kelompok-tani', 'GET', $filters);
|
$request = IndexKelompokTaniRequest::create('/survey/kelompok-tani', 'GET', $filters);
|
||||||
|
$request->setContainer($this->app)->setRedirector(app('redirect'));
|
||||||
$this->app->instance('request', $request);
|
$this->app->instance('request', $request);
|
||||||
|
$request->validateResolved();
|
||||||
|
|
||||||
return app(SurveyKelompokTaniController::class)->index($request)->getData();
|
return app(SurveyKelompokTaniController::class)->index($request)->getData();
|
||||||
};
|
};
|
||||||
@@ -86,6 +113,8 @@ test('kelompok cards summarize the default region and exclude deleted groups', f
|
|||||||
->and((int) $data['summary']->pengurus_aktif)->toBe(6)
|
->and((int) $data['summary']->pengurus_aktif)->toBe(6)
|
||||||
->and((int) $data['summary']->total_anggota)->toBe(70)
|
->and((int) $data['summary']->total_anggota)->toBe(70)
|
||||||
->and((int) $data['summary']->anggota_aktif)->toBe(60);
|
->and((int) $data['summary']->anggota_aktif)->toBe(60);
|
||||||
|
expect((int) $data['summary']->total_desa)->toBe(3);
|
||||||
|
expect((int) $data['summary']->memiliki_sk)->toBe(1);
|
||||||
expect($data['kecamatanOptions']->pluck('id')->all())->toBe([1, 2]);
|
expect($data['kecamatanOptions']->pluck('id')->all())->toBe([1, 2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -102,8 +131,8 @@ test('kelompok commodity filter matches string and numeric JSON ids exactly', fu
|
|||||||
$data = ($this->listKelompok)(['komoditas_id' => '1']);
|
$data = ($this->listKelompok)(['komoditas_id' => '1']);
|
||||||
expect($data['kelompokTani']->pluck('id')->all())->toBe([2, 1]);
|
expect($data['kelompokTani']->pluck('id')->all())->toBe([2, 1]);
|
||||||
expect((int) $data['summary']->total_kelompok)->toBe(2);
|
expect((int) $data['summary']->total_kelompok)->toBe(2);
|
||||||
expect(($this->listKelompok)(['komoditas_id' => 2, 'jenis_kelompok' => 'Poktan'])['kelompokTani']->pluck('id')->all())->toBe([3, 1]);
|
expect(($this->listKelompok)(['komoditas_id' => 2, 'jenis_kelompok' => 'Kelompok Tani (Poktan)'])['kelompokTani']->pluck('id')->all())->toBe([3, 1]);
|
||||||
expect(($this->listKelompok)(['jenis_kelompok' => 'KWT'])['kelompokTani']->pluck('id')->all())->toBe([2]);
|
expect(($this->listKelompok)(['jenis_kelompok' => 'Kelompok Wanita Tani (KWT)'])['kelompokTani']->pluck('id')->all())->toBe([2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('kelompok search matches group and respondent names without escaping region filters', function () {
|
test('kelompok search matches group and respondent names without escaping region filters', function () {
|
||||||
@@ -128,7 +157,7 @@ test('kelompok totals cover every result and pagination preserves all filters',
|
|||||||
for ($index = 0; $index < 12; $index++) {
|
for ($index = 0; $index < 12; $index++) {
|
||||||
DB::table('survey_kelompok_tani')->insert([
|
DB::table('survey_kelompok_tani')->insert([
|
||||||
'nama_kelompok' => 'Kelompok tambahan ' . $index, 'desa_kelurahan_id' => 1,
|
'nama_kelompok' => 'Kelompok tambahan ' . $index, 'desa_kelurahan_id' => 1,
|
||||||
'jenis_kelompok' => 'Poktan', 'komoditas_id' => '["1"]', 'jumlah_anggota_terdaftar' => 10,
|
'jenis_kelompok' => 'Kelompok Tani (Poktan)', 'komoditas_id' => '["1"]', 'jumlah_anggota_terdaftar' => 10,
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
$data = ($this->listKelompok)(['komoditas_id' => 1, 'sort_by' => 'nama_asc', 'page' => 2, 'per_page' => '10']);
|
$data = ($this->listKelompok)(['komoditas_id' => 1, 'sort_by' => 'nama_asc', 'page' => 2, 'per_page' => '10']);
|
||||||
@@ -144,4 +173,111 @@ test('kelompok filters reject malformed ids and unsupported page sizes', functio
|
|||||||
expect(fn () => ($this->listKelompok)($filters))->toThrow(ValidationException::class);
|
expect(fn () => ($this->listKelompok)($filters))->toThrow(ValidationException::class);
|
||||||
})->with([
|
})->with([
|
||||||
[['kecamatan_id' => 'invalid']], [['komoditas_id' => 999]], [['per_page' => 0]], [['search' => ['invalid']]],
|
[['kecamatan_id' => 'invalid']], [['komoditas_id' => 999]], [['per_page' => 0]], [['search' => ['invalid']]],
|
||||||
|
[['status_legalitas' => ['invalid']]],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
test('legal status filters keep cards and rows consistent', function () {
|
||||||
|
$sk = ($this->listKelompok)(['status_legalitas' => 'Sudah ada SK dari desa/kecamatan']);
|
||||||
|
expect($sk['kelompokTani']->pluck('id')->all())->toBe([1]);
|
||||||
|
expect((int) $sk['summary']->memiliki_sk)->toBe(1);
|
||||||
|
expect((int) $sk['summary']->total_pengurus)->toBe(3);
|
||||||
|
$proses = ($this->listKelompok)(['status_legalitas' => 'Sedang proses pengajuan']);
|
||||||
|
expect($proses['kelompokTani']->pluck('id')->all())->toBe([2]);
|
||||||
|
expect((int) $proses['summary']->total_anggota)->toBe(40);
|
||||||
|
expect((int) $proses['summary']->memiliki_sk)->toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('group summaries resolve commodities locations and legal status from recorded data', function () {
|
||||||
|
$data = ($this->listKelompok)();
|
||||||
|
expect($data['ringkasanKelompok'][1]['komoditas'])->toBe(['Kakao', 'Kopi']);
|
||||||
|
expect($data['ringkasanKelompok'][3]['komoditas'])->toBe(['Kopi', 'Vanili']);
|
||||||
|
expect($data['ringkasanKelompok'][1]['lokasi'])->toBe('Desa A · Kecamatan A');
|
||||||
|
expect($data['ringkasanKelompok'][1]['legalitas'])->toBe(['label' => 'Sudah ada SK', 'warna' => 'success']);
|
||||||
|
expect($data['ringkasanKelompok'][3]['legalitas']['label'])->toBe('Belum terisi');
|
||||||
|
expect($data['kelompokTani']->first()->creator->name)->toBe('Enumerator Uji');
|
||||||
|
expect(($this->listKelompok)(['search' => 'Desa B'])['kelompokTani']->pluck('id')->all())->toBe([2]);
|
||||||
|
expect(($this->listKelompok)(['search' => 'Kecamatan B'])['kelompokTani']->pluck('id')->all())->toBe([3]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('new filters survive pagination and filtered page totals cover every matching group', function () {
|
||||||
|
$filters = ['status_legalitas' => 'Sedang proses pengajuan', 'jenis_kelompok' => 'Kelompok Wanita Tani (KWT)', 'komoditas_id' => 1, 'sort_by' => 'anggota_desc', 'per_page' => 25];
|
||||||
|
$data = ($this->listKelompok)($filters);
|
||||||
|
parse_str(parse_url($data['kelompokTani']->url(2), PHP_URL_QUERY), $params);
|
||||||
|
foreach ($filters as $field => $value) expect((string) $params[$field])->toBe((string) $value);
|
||||||
|
expect((int) $data['summary']->total_kelompok)->toBe(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('list cards filters columns and permissions match the group data', function (string $role, bool $showEnumerator) {
|
||||||
|
$user = new User(['name' => 'Pengguna Uji', 'email' => 'user@example.test', 'role' => $role]);
|
||||||
|
$user->id = 7;
|
||||||
|
$this->be($user);
|
||||||
|
$data = ($this->listKelompok)(['status_legalitas' => 'Sedang proses pengajuan']);
|
||||||
|
$html = view('produktivitas.survey-kelompok-tani.index', $data)->render();
|
||||||
|
$dom = new DOMDocument();
|
||||||
|
libxml_use_internal_errors(true);
|
||||||
|
$dom->loadHTML($html);
|
||||||
|
libxml_clear_errors();
|
||||||
|
$xpath = new DOMXPath($dom);
|
||||||
|
expect($xpath->query('//div[@id="kelompok-overview"]/div')->length)->toBe(4);
|
||||||
|
foreach (['Kelompok Tani', 'Komoditas', 'Status SK', 'Pengurus', 'Anggota', 'Nama Responden'] as $heading) {
|
||||||
|
expect($xpath->query('//th[normalize-space()="'.$heading.'"]')->length)->toBe(1);
|
||||||
|
}
|
||||||
|
expect($xpath->query('//th[normalize-space()="Nama Enumerator"]')->length)->toBe($showEnumerator ? 1 : 0);
|
||||||
|
expect($data['showEnumerator'])->toBe($showEnumerator);
|
||||||
|
expect($xpath->query('//*[@name="pernah_mendapatkan_bantuan"]')->length)->toBe(0);
|
||||||
|
expect($xpath->query('//form[@id="kelompok-filter-form"]')->item(0)->getAttribute('data-desa-url'))->toContain(':id');
|
||||||
|
expect($xpath->query('//button[contains(@class,"delete-record")]')->item(0)->getAttribute('data-delete-url'))->toBe(route('kelompok-tani.destroy', 2));
|
||||||
|
expect($html)->toContain('kelompok-tani.js');
|
||||||
|
})->with([['admin', true], ['super_admin', true], ['enumerator', false]]);
|
||||||
|
|
||||||
|
test('roles outside group management cannot request the list', function () {
|
||||||
|
$user = new User(['name' => 'Fasilitator Uji', 'email' => 'fasilitator@example.test', 'role' => 'fasilitator']);
|
||||||
|
$this->be($user);
|
||||||
|
expect(fn () => ($this->listKelompok)())->toThrow(\Illuminate\Auth\Access\AuthorizationException::class);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('all standard type and SK choices remain available with no matching or recorded groups', function () {
|
||||||
|
$data = ($this->listKelompok)(['search' => 'Tidak ada kelompok']);
|
||||||
|
expect($data['kelompokTani']->total())->toBe(0);
|
||||||
|
expect($data['jenisKelompokOptions']->all())->toBe([
|
||||||
|
'Kelompok Tani (Poktan)', 'Kelompok Wanita Tani (KWT)', 'Gabungan Kelompok Tani (Gapoktan)',
|
||||||
|
'Kelompok Pemuda Tani', 'Asosiasi Petani', 'Lainnya',
|
||||||
|
]);
|
||||||
|
expect($data['statusLegalitasOptions']->all())->toBe([
|
||||||
|
'Belum ada SK/dokumen apa pun', 'Sedang proses pengajuan', 'Sudah ada SK dari desa/kecamatan',
|
||||||
|
'Sudah ada SK dan terdaftar di Dinas/Kabupaten', 'Tidak tahu',
|
||||||
|
]);
|
||||||
|
DB::table('survey_kelompok_tani')->update(['deleted_at' => now()]);
|
||||||
|
$empty = ($this->listKelompok)();
|
||||||
|
expect($empty['jenisKelompokOptions']->all())->toBe($data['jenisKelompokOptions']->all());
|
||||||
|
expect($empty['statusLegalitasOptions']->all())->toBe($data['statusLegalitasOptions']->all());
|
||||||
|
$html = view('produktivitas.survey-kelompok-tani.index', $empty)->render();
|
||||||
|
expect($html)->toContain('Gabungan Kelompok Tani (Gapoktan)', 'Tidak tahu');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('Lainnya type filter includes custom descriptions and literal Lainnya', function () {
|
||||||
|
DB::table('survey_kelompok_tani')->where('id', 3)->update(['jenis_kelompok' => 'Komunitas Petani Lokal']);
|
||||||
|
DB::table('survey_kelompok_tani')->where('id', 2)->update(['jenis_kelompok' => 'Lainnya']);
|
||||||
|
$data = ($this->listKelompok)(['jenis_kelompok' => 'Lainnya']);
|
||||||
|
expect($data['kelompokTani']->pluck('id')->all())->toBe([3, 2]);
|
||||||
|
expect((int) $data['summary']->total_kelompok)->toBe(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('removed aid filter neither constrains results nor persists in pagination', function () {
|
||||||
|
$data = ($this->listKelompok)(['pernah_mendapatkan_bantuan' => '0']);
|
||||||
|
expect($data['kelompokTani']->pluck('id')->all())->toBe([3, 2, 1]);
|
||||||
|
expect($data['filters'])->not->toHaveKey('pernah_mendapatkan_bantuan');
|
||||||
|
expect($data['kelompokTani']->url(2))->not->toContain('pernah_mendapatkan_bantuan');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('unfilled membership totals remain distinct from a recorded zero in the table', function () {
|
||||||
|
$data = ($this->listKelompok)(['search' => 'Citra']);
|
||||||
|
$html = view('produktivitas.survey-kelompok-tani.index', $data)->render();
|
||||||
|
$dom = new DOMDocument();
|
||||||
|
libxml_use_internal_errors(true);
|
||||||
|
$dom->loadHTML($html);
|
||||||
|
libxml_clear_errors();
|
||||||
|
$xpath = new DOMXPath($dom);
|
||||||
|
expect(trim($xpath->query('//tr[contains(@class,"kelompok-tani-row")]/td[5]/div')->item(0)->textContent))->toBe('Belum terisi');
|
||||||
|
expect(trim($xpath->query('//tr[contains(@class,"kelompok-tani-row")]/td[6]/div')->item(0)->textContent))->toBe('0');
|
||||||
|
});
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ use App\Models\Pertanian\KelompokTani;
|
|||||||
use App\Models\Pertanian\AsetKelompokTani;
|
use App\Models\Pertanian\AsetKelompokTani;
|
||||||
use App\Models\Pertanian\BantuanKelompokTani;
|
use App\Models\Pertanian\BantuanKelompokTani;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Services\Produktivitas\KelompokTaniService;
|
||||||
use Illuminate\Database\Schema\Blueprint;
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
use Illuminate\Http\Request;
|
use Illuminate\Http\Request;
|
||||||
use Illuminate\Support\Facades\DB;
|
use Illuminate\Support\Facades\DB;
|
||||||
@@ -17,6 +18,17 @@ use Illuminate\Support\ViewErrorBag;
|
|||||||
use Illuminate\Validation\ValidationException;
|
use Illuminate\Validation\ValidationException;
|
||||||
|
|
||||||
beforeEach(function () {
|
beforeEach(function () {
|
||||||
|
Schema::create('activity_log', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('log_name')->nullable();
|
||||||
|
$table->text('description');
|
||||||
|
$table->nullableMorphs('subject');
|
||||||
|
$table->nullableMorphs('causer');
|
||||||
|
$table->string('event')->nullable();
|
||||||
|
$table->json('properties')->nullable();
|
||||||
|
$table->uuid('batch_uuid')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
foreach ([
|
foreach ([
|
||||||
'master_provinsi' => null,
|
'master_provinsi' => null,
|
||||||
'master_kabupaten_kota' => 'provinsi_id',
|
'master_kabupaten_kota' => 'provinsi_id',
|
||||||
@@ -126,6 +138,10 @@ beforeEach(function () {
|
|||||||
return $request;
|
return $request;
|
||||||
};
|
};
|
||||||
$this->profileDom = function ($view) {
|
$this->profileDom = function ($view) {
|
||||||
|
$data = $view->getData();
|
||||||
|
if (!array_key_exists('ringkasanProfil', $data)) {
|
||||||
|
$view->with('ringkasanProfil', app(KelompokTaniService::class)->profileSummary($data['kelompokTani'] ?? null));
|
||||||
|
}
|
||||||
$dom = new DOMDocument();
|
$dom = new DOMDocument();
|
||||||
$previous = libxml_use_internal_errors(true);
|
$previous = libxml_use_internal_errors(true);
|
||||||
$dom->loadHTML($view->render());
|
$dom->loadHTML($view->render());
|
||||||
@@ -159,6 +175,85 @@ test('profile saves identity and membership together on creation and update', fu
|
|||||||
expect($response->getTargetUrl())->toBe(route('kelompok-tani.edit', $group->id) . '#section-pengurus');
|
expect($response->getTargetUrl())->toBe(route('kelompok-tani.edit', $group->id) . '#section-pengurus');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('profile summary uses saved group details and preserves recorded zero totals', function () {
|
||||||
|
DB::table('master_komoditas')->insert([
|
||||||
|
['id' => 1, 'nama' => 'Kopi'], ['id' => 2, 'nama' => 'Kakao'], ['id' => 3, 'nama' => 'Vanili'],
|
||||||
|
]);
|
||||||
|
$group = ($this->kelompok)([
|
||||||
|
'jumlah_pengurus' => 0, 'jumlah_anggota_terdaftar' => null, 'komoditas_id' => [1, '2', 3, 1],
|
||||||
|
'status_legalitas' => 'Sudah ada SK dari desa/kecamatan',
|
||||||
|
'jabatan_responden' => 'Ketua', 'kontak_responden' => '08123456789', 'tahun_berdiri' => 2020,
|
||||||
|
]);
|
||||||
|
$summary = app(KelompokTaniService::class)->profileSummary($group);
|
||||||
|
expect($summary['judul'])->toBe('Kelompok A');
|
||||||
|
expect($summary['lokasi'])->toBe('Desa A · Kecamatan A · Ngada');
|
||||||
|
expect($summary['jenis'])->toBe('Kelompok Tani (Poktan)');
|
||||||
|
expect($summary['pengurus'])->toBe('0');
|
||||||
|
expect($summary['anggota'])->toBe('Belum terisi');
|
||||||
|
expect($summary['komoditas'])->toBe(['Kakao', 'Kopi', 'Vanili']);
|
||||||
|
expect($summary['legalitas']['label'])->toBe('Sudah ada SK');
|
||||||
|
expect($summary['legalitas']['detail'])->toBe('Sudah ada SK dari desa/kecamatan');
|
||||||
|
expect($summary['kontak'])->toBe('08123456789');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('all profile tabs receive the same saved summary with expandable commodities', function () {
|
||||||
|
DB::table('master_komoditas')->insert([
|
||||||
|
['id' => 1, 'nama' => 'Kopi'], ['id' => 2, 'nama' => 'Kakao'], ['id' => 3, 'nama' => 'Vanili'],
|
||||||
|
]);
|
||||||
|
$group = ($this->kelompok)(['jumlah_pengurus' => 4, 'jumlah_anggota_terdaftar' => 5, 'komoditas_id' => [1, 2, 3]]);
|
||||||
|
$controller = app(SurveyKelompokTaniController::class);
|
||||||
|
$expected = app(KelompokTaniService::class)->profileSummary($group);
|
||||||
|
foreach (['identitas', 'kelembagaan', 'aktivitas-fungsi', 'sosial-konflik'] as $section) {
|
||||||
|
$view = $controller->edit(($this->profileRequest)(['section' => $section], 'GET'), $group);
|
||||||
|
expect($view->getData()['ringkasanProfil'])->toEqual($expected);
|
||||||
|
$dom = ($this->profileDom)($view);
|
||||||
|
expect($dom->query('//div[@id="kelompok-tani-summary"]/div[contains(@class,"card-footer")]')->length)->toBe(1);
|
||||||
|
expect($dom->query('//div[contains(@class,"card-footer") and contains(@class,"border-top")]')->length)->toBe(1);
|
||||||
|
expect($dom->query('//div[@id="kelompok-tani-summary"]/div[contains(@class,"card-body")]//*[@data-summary-field="tahun_berdiri"]')->length)->toBe(1);
|
||||||
|
expect($dom->query('//div[contains(@class,"card-footer")]//*[@data-summary-field="tahun_berdiri"]')->length)->toBe(0);
|
||||||
|
expect($dom->query('//div[contains(@class,"card-footer")]//*[@data-summary-field="diperbarui"]/time')->length)->toBe(1);
|
||||||
|
expect($dom->query('//*[@data-summary-field="pengurus"]')->item(0)->textContent)->toBe('4');
|
||||||
|
expect($dom->query('//*[@data-summary-field="anggota"]')->item(0)->textContent)->toBe('5');
|
||||||
|
expect(trim($dom->query('//button[@data-bs-target="#kelompok-summary-extra-komoditas"]')->item(0)->textContent))->toBe('+1 lainnya');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
test('new group summary is empty and rendering the header performs no database queries', function () {
|
||||||
|
($this->profileRequest)([], 'GET');
|
||||||
|
$summary = app(KelompokTaniService::class)->profileSummary();
|
||||||
|
expect($summary['judul'])->toBe('Profil Kelompok Tani Baru');
|
||||||
|
expect($summary['pengurus'])->toBe('Belum terisi');
|
||||||
|
expect($summary['anggota'])->toBe('Belum terisi');
|
||||||
|
expect($summary['komoditas'])->toBe([]);
|
||||||
|
DB::enableQueryLog();
|
||||||
|
DB::flushQueryLog();
|
||||||
|
$html = view('produktivitas.survey-kelompok-tani.header', ['kelompokTani' => null, 'ringkasanProfil' => $summary])->render();
|
||||||
|
expect(DB::getQueryLog())->toBe([]);
|
||||||
|
expect($html)->toContain('Belum tersimpan');
|
||||||
|
DB::disableQueryLog();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('summary update time includes changes to group sections and removal of a recorded aid', function () {
|
||||||
|
$group = ($this->kelompok)();
|
||||||
|
DB::table('survey_kelompok_tani')->where('id', $group->id)->update([
|
||||||
|
'created_at' => '2026-01-01 08:00:00', 'updated_at' => '2026-01-01 08:00:00',
|
||||||
|
]);
|
||||||
|
$group->refresh();
|
||||||
|
$service = app(KelompokTaniService::class);
|
||||||
|
expect($service->profileSummary($group)['diperbarui']->format('Y-m-d H:i:s'))->toBe('2026-01-01 08:00:00');
|
||||||
|
|
||||||
|
foreach ([
|
||||||
|
['survey_pengurus_kelompok_tani', ['nama_pengurus' => 'Ketua', 'jabatan' => 'Ketua', 'kontak' => '08123456789', 'status' => true, 'updated_at' => null], '2026-01-02 09:00:00'],
|
||||||
|
['survey_aset_kelompok_tani', ['jenis_aset' => 'Timbangan', 'status_aset' => true, 'status_pengelolaan_aset' => 'Swadaya', 'kondisi_aset' => 'Berfungsi', 'updated_at' => '2026-01-03 10:00:00'], '2026-01-03 10:00:00'],
|
||||||
|
['survey_bantuan_kelompok_tani', ['nama_program' => 'Program A', 'penyelenggara' => 'Dinas', 'bentuk_bantuan' => 'Bibit', 'updated_at' => '2026-01-04 11:00:00', 'deleted_at' => '2026-01-04 11:00:00'], '2026-01-04 11:00:00'],
|
||||||
|
] as [$table, $attributes, $expected]) {
|
||||||
|
DB::table($table)->insert(['uid' => 'TEST-UPDATED', 'survey_kelompok_tani_id' => $group->id, 'created_at' => $expected, ...$attributes]);
|
||||||
|
expect($service->profileSummary($group)['diperbarui']->format('Y-m-d H:i:s'))->toBe($expected);
|
||||||
|
}
|
||||||
|
DB::table('survey_kelompok_tani')->where('id', $group->id)->update(['updated_at' => '2026-01-05 12:00:00']);
|
||||||
|
expect($service->profileSummary($group->refresh())['diperbarui']->format('Y-m-d H:i:s'))->toBe('2026-01-05 12:00:00');
|
||||||
|
});
|
||||||
|
|
||||||
test('invalid membership prevents partial profile saves', function () {
|
test('invalid membership prevents partial profile saves', function () {
|
||||||
$group = ($this->kelompok)(['jumlah_anggota_terdaftar' => 20]);
|
$group = ($this->kelompok)(['jumlah_anggota_terdaftar' => 20]);
|
||||||
$request = ($this->profileRequest)([
|
$request = ($this->profileRequest)([
|
||||||
|
|||||||
Reference in New Issue
Block a user