This commit is contained in:
2026-06-08 10:09:54 +07:00
160 changed files with 11667 additions and 1688 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
<div class="card-body">
<!-- Logo -->
<div class="app-brand justify-content-center mb-6">
<a href="{{ route('dashboard.produktivitas') }}" class="app-brand-link">
<a href="{{ route('dashboard.summary') }}" class="app-brand-link">
<span class="app-brand-logo demo">
<span class="text-primary">
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -0,0 +1,28 @@
@props(['title', 'mainCount', 'icon', 'color' => 'primary', 'subLabel1', 'subCount1', 'subLabel2', 'subCount2'])
<div class="card h-100 shadow-sm border-0">
<div class="card-body p-3">
<div class="d-flex justify-content-between align-items-start mb-3">
<div>
<p class="text-muted fw-medium mb-1 small text-uppercase">{{ $title }}</p>
<h3 class="mb-0 fw-bold text-secondary">{{ number_format($mainCount, 0, ',', '.') }}</h3>
</div>
<div class="badge bg-label-{{ $color }} p-2 rounded">
<i class="ti tabler-{{ $icon }}" style="font-size: 1.5rem;"></i>
</div>
</div>
<div class="bg-lighter rounded p-2 mt-auto">
<div class="row text-center gx-0">
<div class="col-6 border-end">
<span class="text-muted d-block" style="font-size: 0.75rem;">{{ $subLabel1 }}</span>
<span class="fw-semibold text-dark small">{{ number_format($subCount1, 0, ',', '.') }}</span>
</div>
<div class="col-6">
<span class="text-muted d-block" style="font-size: 0.75rem;">{{ $subLabel2 }}</span>
<span class="fw-semibold text-dark small">{{ number_format($subCount2, 0, ',', '.') }}</span>
</div>
</div>
</div>
</div>
</div>
@@ -0,0 +1,50 @@
<div class="row g-3 mb-4">
<div class="col-12 col-sm-6 col-xl-3">
<x-wilayah-card
title="TOTAL PROVINSI"
:mainCount="$totalProvinsi"
icon="map-pin"
color="primary"
subLabel1="Data Aktif"
:subCount1="$provinsiAktif"
subLabel2="Data Kosong"
:subCount2="$provinsiKosong"
/>
</div>
<div class="col-12 col-sm-6 col-xl-3">
<x-wilayah-card
title="Kabupaten & Kota"
mainCount="{{ $totalKabKota }}"
icon="building-skyscraper"
color="info"
subLabel1="Kabupaten"
subCount1="{{ $totalKabupaten }}"
subLabel2="Kota"
subCount2="{{ $totalKota }}"
/>
</div>
<div class="col-12 col-sm-6 col-xl-3">
<x-wilayah-card
title="TOTAL KECAMATAN"
:mainCount="$totalKecamatan"
icon="map-pin"
color="warning"
subLabel1="Zona Perdesaan"
:subCount1="$kecamatanPerdesaan"
subLabel2="Zona Perkotaan"
:subCount2="$kecamatanPerkotaan"
/>
</div>
<div class="col-12 col-sm-6 col-xl-3">
<x-wilayah-card
title="DESA & KELURAHAN"
:mainCount="$totalDesaKelurahan"
icon="home-2"
color="success"
subLabel1="Terpetakan"
:subCount1="$terpetakan"
subLabel2="Blm Pemetaan"
:subCount2="$belumTerpetakan"
/>
</div>
</div>
+10 -2
View File
@@ -18,6 +18,12 @@
<input type="text" class="form-control" id="add-program-nama" name="nama" placeholder="Cth: Program Pertanian" />
</div>
</div>
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label" for="add-program-nama-singkat">Nama Singkat<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-program-nama-singkat" name="nama_singkat" placeholder="Cth: PP" />
</div>
</div>
<div class="row">
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-program-subdomain">Sub Domain<span class="text-danger">*</span></label>
@@ -51,12 +57,14 @@
rules: {
nama: { required: true },
sub_domain: { required: true },
tahun: { required: true }
tahun: { required: true },
nama_singkat: { required: true }
},
messages: {
nama: { required: "Nama program wajib diisi" },
sub_domain: { required: "Subdomain wajib diisi" },
tahun: { required: "Tahun wajib diisi" }
tahun: { required: "Tahun wajib diisi" },
nama_singkat: { required: "Nama singkat wajib diisi" }
},
errorElement: 'div',
errorPlacement: function (error, element) {
+10 -2
View File
@@ -19,6 +19,12 @@
<input type="text" class="form-control" id="add-program-nama" name="nama" placeholder="Cth: Program Pertanian" value="{{ old('nama', $program->nama) }}" />
</div>
</div>
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label" for="add-program-nama-singkat">Nama Singkat<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-program-nama-singkat" name="nama_singkat" placeholder="Cth: PP" value="{{ old('nama_singkat', $program->nama_singkat) }}" />
</div>
</div>
<div class="row">
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-program-subdomain">Sub Domain<span class="text-danger">*</span></label>
@@ -56,12 +62,14 @@
rules: {
nama: { required: true },
sub_domain: { required: true },
tahun: { required: true }
tahun: { required: true },
nama_singkat: { required: true }
},
messages: {
nama: { required: "Nama program wajib diisi" },
sub_domain: { required: "Subdomain wajib diisi" },
tahun: { required: "Tahun wajib diisi" }
tahun: { required: "Tahun wajib diisi" },
nama_singkat: { required: "Nama singkat wajib diisi" }
},
errorElement: 'div',
errorPlacement: function (error, element) {
@@ -17,6 +17,12 @@
<input type="text" class="form-control" id="add-program-nama" name="nama" placeholder="Cth: Program Pertanian" value="{{ old('nama', $program->nama) }}" readonly />
</div>
</div>
<div class="row">
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label" for="add-program-nama-singkat">Nama Singkat<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-program-nama-singkat" name="nama_singkat" placeholder="Cth: PP" value="{{ old('nama_singkat', $program->nama_singkat) }}" readonly />
</div>
</div>
<div class="row">
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-program-subdomain">Sub Domain<span class="text-danger">*</span></label>
@@ -1,619 +0,0 @@
@extends('layout.main')
@push('style')
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<style>
body { background-color: #f8faf9; font-family: 'Public Sans', sans-serif; }
.card { border: none; border-radius: 15px; box-shadow: 0 4px 20px rgba(0,0,0,0.05); }
/* Map Container */
#map { height: 600px; width: 100%; border-radius: 15px; }
/* Sidebar Mini Stats */
.stat-card-mini { transition: all 0.3s ease; border-left: 5px solid transparent; }
.stat-card-mini:hover { transform: translateX(5px); border-left-color: var(--agri-green); }
/* Map Switcher Manual */
.map-controls-custom {
position: absolute; top: 20px; right: 20px; z-index: 1000;
background: white; padding: 5px; border-radius: 10px; box-shadow: 0 2px 10px rgba(0,0,0,0.1);
}
/* Table Styling */
.table thead th { background-color: #f8f9fa; text-transform: uppercase; font-size: 11px; letter-spacing: 1px; }
.avatar-label { width: 40px; height: 40px; display: flex; align-items: center; justify-content: center; border-radius: 10px; font-weight: bold; }
.leaflet-tooltip.leaflet-modern-tooltip {
background-color: transparent;
border: none;
box-shadow: none;
padding: 0;
color: #333;
font-family: 'Public Sans', sans-serif; /* Pastikan font dashboard Anda */
}
/* Menghilangkan panah kecil di bawah tooltip */
.leaflet-modern-tooltip:before {
border: none !important;
}
/* 2. Gaya untuk Modern Map Card kita */
.modern-map-card {
background: #ffffff;
border-radius: 12px;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.12);
padding: 16px;
width: 190px; /* Lebar card */
border: 1px solid rgba(0,0,0,0.05);
}
/* Bagian Header */
.card-header-region {
margin-bottom: 8px;
}
.region-label {
font-size: 11px;
text-transform: uppercase;
color: #888;
font-weight: 500;
letter-spacing: 0.5px;
display: block;
margin-bottom: 2px;
}
.region-name {
font-weight: 700;
font-size: 15px;
margin: 0;
color: #1e1e1e;
line-height: 1.2;
}
/* Bagian Stats Row (Flexbox untuk menyusun ikon & teks) */
.d-flex { display: flex; }
.align-items-center { align-items: center; }
.mb-2 { margin-bottom: 8px; }
.me-2 { margin-right: 10px; }
.fw-bold { font-weight: 700; }
.small { font-size: 12px; }
/* Gaya Ikon Baris Statistik */
.stat-icon {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
font-size: 16px;
}
/* Warna Latar Belakang Ringan untuk Ikon (Bootstrap style) */
.bg-success-light { background-color: rgba(40, 167, 69, 0.1); }
.bg-primary-light { background-color: rgba(0, 123, 255, 0.1); }
.text-success { color: #28a745; }
.text-primary { color: #007bff; }
.stat-info {
flex: 1;
}
.stat-value {
font-size: 14px;
color: #222;
line-height: 1;
margin-top: 2px;
}
.text-muted { color: #6c757d; }
/* Bagian Footer Action */
.action-prompt {
font-size: 12px;
}
/* Sedikit animasi pada chevron saat di-hover */
.action-prompt i {
transition: transform 0.2s ease;
}
.leaflet-modern-tooltip:hover .action-prompt i {
transform: translateX(3px);
}
/* Gaya Bootstrap Helper Ringan (untuk jaga-jaga jika dashboard tidak memuatnya) */
.border-top { border-top: 1px solid rgba(0,0,0,0.08) !important; }
.border-bottom { border-bottom: 1px solid rgba(0,0,0,0.08) !important; }
.py-2 { padding-top: 8px !important; padding-bottom: 8px !important; }
.my-2 { margin-top: 8px !important; margin-bottom: 8px !important; }
.leaflet-popup-wrapper.leaflet-modern-popup-wrapper {
box-shadow: none;
}
.leaflet-modern-popup-wrapper .leaflet-popup-content-wrapper {
border-radius: 12px;
padding: 0 !important; /* Hapus padding default Leaflet agar full width */
overflow: hidden;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.12);
border: 1px solid rgba(0,0,0,0.05);
}
.leaflet-modern-popup-wrapper .leaflet-popup-content {
margin: 0 !important;
padding: 0 !important;
}
/* Penyesuaian panah kecil di bawah */
.leaflet-modern-popup-wrapper .leaflet-popup-tip {
background: #f8f9fa; /* Sesuaikan dengan warna body footer popup (bg-light) */
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
/* 2. Gaya untuk Modern Lahan Popup kita */
.modern-lahan-popup {
font-family: 'Public Sans', sans-serif;
min-width: 270px; /* Lebar card */
}
/* Typography Hierarki */
.modern-lahan-popup .h5 { font-size: 16px; margin: 0; }
.modern-lahan-popup .fs-5 { font-size: 1.25rem; }
.modern-lahan-popup small { font-size: 11px; }
/* Header Farmer */
.rounded-top-12 { border-radius: 12px 12px 0 0; }
.header-farmer {
border-bottom: 1px solid rgba(0,0,0,0.06);
}
.label-header {
letter-spacing: 0.5px;
}
.farmer-name {
line-height: 1.2;
}
/* Body Details */
.details-body {
background-color: #f8f9fa;
}
.rounded-bottom-12 { border-radius: 0 0 12px 12px; }
.label-detail {
margin-bottom: 0px;
}
.value-detail {
font-size: 14px;
margin-top: 1px;
line-height: 1.1;
}
.value-commodity {
font-size: 13px;
line-height: 1.2;
}
/* Ikon Container (derived from stats card style) */
.icon-stat-container {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
}
/* Helper Colors (for icons & backgrounds) */
.text-danger-light { color: #dc3545 !important; }
.text-danger { color: #dc3545 !important; }
.text-success { color: #28a745 !important; }
/* Background Ringan (Bootstrap Style) */
.text-success-light { background-color: rgba(40, 167, 69, 0.1) !important; }
.text-danger-light { background-color: rgba(220, 53, 69, 0.1) !important; }
.bg-primary-light { background-color: rgba(0, 123, 255, 0.1) !important; }
/* Bootstrap-like Utilities (untuk berjaga-jaga jika dash belum memuatnya) */
.me-2 { margin-right: 10px !important; }
.mb-3 { margin-bottom: 16px !important; }
.ps-2 { padding-left: 12px !important; }
.col-6 { width: 50%; }
.col-12 { width: 100%; }
.border { border: 1px solid rgba(0,0,0,0.08) !important; }
</style>
@endpush
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="row">
<div class="col-lg-3">
<div class="card mb-3 stat-card-mini">
<div class="card-body">
<div class="d-flex justify-content-between">
<div class="avatar bg-label-success rounded p-2"><i class="ti tabler-mountain fs-3"></i></div>
<!-- <span class="badge bg-label-success">+2.4%</span> -->
</div>
<div class="mt-3">
<small class="text-muted text-uppercase">Total Land Area</small>
<h4 class="mb-0 fw-bold">{{ number_format($luasTotal, 1) }} <small class="text-muted">ha</small></h4>
</div>
</div>
</div>
<div class="card mb-3 stat-card-mini">
<div class="card-body">
<div class="d-flex justify-content-between">
<div class="avatar bg-label-success rounded p-2"><i class="ti tabler-map fs-3"></i></div>
<!-- <span class="badge bg-label-success"></span> -->
</div>
<div class="mt-3">
<small class="text-muted text-uppercase">Total Lahan</small>
<h4 class="mb-0 fw-bold">{{ $totallahan }}</h4>
</div>
</div>
</div>
<div class="card mb-3 stat-card-mini">
<div class="card-body">
<div class="d-flex justify-content-between">
<div class="avatar bg-label-primary rounded p-2"><i class="ti tabler-users fs-3"></i></div>
<span class="badge bg-success text-white text-center">Active</span>
</div>
<div class="mt-3">
<small class="text-muted text-uppercase">Registered Farmers</small>
<h4 class="mb-0 fw-bold">{{ $totalpetani }}</h4>
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-header"><h6 class="mb-0 fw-bold">Distribusi Komoditas</h6></div>
<div class="card-body">
@foreach(['Vanilla' => 45, 'Candlenut' => 30, 'Cocoa' => 15, 'Coffee' => 10] as $name => $val)
<div class="mb-3">
<div class="d-flex justify-content-between mb-1">
<small class="fw-bold">{{ $name }}</small>
<small class="text-muted">{{ $val }}%</small>
</div>
<div class="progress" style="height: 6px;">
<div class="progress-bar bg-success" style="width: {{ $val }}%"></div>
</div>
</div>
@endforeach
</div>
</div>
</div>
<div class="col-lg-9">
<div class="card mb-4 overflow-hidden position-relative">
<div class="card-header d-flex justify-content-between align-items-center bg-white py-3">
<h6 class="mb-0 fw-bold"><i class="ti tabler-map-2 me-2"></i>Peta Sebaran Lahan</h6>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-secondary active" onclick="setMapLayer('satellite')">Satellite</button>
<button class="btn btn-outline-secondary" onclick="setMapLayer('topo')">Topographic</button>
<button class="btn btn-outline-secondary" onclick="setMapLayer('parcels')">Parcels</button>
</div>
</div>
<div id="map">
<button id="btnBackMap" class="btn btn-sm btn-dark shadow" style="display:none; position:absolute; top:20px; left:50px; z-index:1000;">
<i class="ti tabler-arrow-left me-1"></i> Kembali ke Atas
</button>
</div>
</div>
<div class="card">
<div class="card-header border-bottom d-flex justify-content-between align-items-center">
<h6 class="mb-0 fw-bold">Produktivitas Petani Regional</h6>
<button class="btn btn-sm btn-success"><i class="ti tabler-plus me-1"></i> Bulk Action</button>
</div>
<div class="table-responsive">
<table class="table table-hover mb-0">
<thead>
<tr>
<th>Nama Petani</th>
<th>ID Lahan</th>
<th>Komoditas</th>
<th>Luas (HA)</th>
<th>Status</th>
</tr>
</thead>
<tbody>
@foreach($petaniList as $p)
<tr>
<td>
<div class="d-flex align-items-center">
<div class="avatar-label bg-label-secondary me-2">{{ substr($p->nama, 0, 1) }}</div>
<span class="fw-bold">{{ $p->nama }}</span>
</div>
</td>
<td><span class="text-muted small">TR-{{ $p->id }}</span></td>
<td><span class="badge bg-label-info">Vanilla</span></td>
<td class="fw-bold">{{ number_format($p->lahan->sum('luas_lahan'), 1) }}</td>
<td><span class="badge bg-success">Verified</span></td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
var satellite = L.tileLayer('https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', { maxZoom: 22 });
var topo = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png');
var parcels = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png');
var map = L.map('map', { center: [-0.789, 113.921], zoom: 5, layers: [satellite], zoomControl: false });
L.control.zoom({ position: 'topleft' }).addTo(map);
function setMapLayer(type) {
map.removeLayer(satellite); map.removeLayer(topo); map.removeLayer(parcels);
if(type === 'satellite') satellite.addTo(map);
if(type === 'topo') topo.addTo(map);
if(type === 'parcels') parcels.addTo(map);
event.target.parentElement.querySelectorAll('.btn').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
}
// 2. Data dari Server
var dataLevels = {
'provinsi': @json($geoProvinsi),
'kabupaten': @json($geoKabupaten),
'kecamatan': @json($geoKecamatan),
'desa': @json($geoDesa),
'lahan': @json($geoLahan)
};
var currentLayerGroup = L.featureGroup().addTo(map);
var mapHistory = [];
var btnBack = document.getElementById('btnBackMap');
function ensureLatLon(coords) {
if (typeof coords[0] === 'number') {
if (Math.abs(coords[0]) > 90) {
return [coords[1], coords[0]];
}
return [coords[0], coords[1]];
}
return coords.map(ensureLatLon);
}
function renderLevel(level, parentId = null, fallbackBounds = null) {
var dataToRender = dataLevels[level];
if (parentId !== null) {
dataToRender = dataToRender.filter(item => item.parent_id == parentId);
}
if (dataToRender.length === 0) {
alert(`Belum ada data pemetaan ${level} yang terdaftar di wilayah ini.`);
if (mapHistory.length > 0) {
mapHistory.pop();
}
return;
}
currentLayerGroup.clearLayers();
if (level === 'lahan' && parentId !== null) {
var parentDesa = dataLevels['desa'].find(d => d.id == parentId);
if (parentDesa && parentDesa.polygon) {
try {
var desaGeojson = typeof parentDesa.polygon === 'string' ? JSON.parse(parentDesa.polygon) : parentDesa.polygon;
var desaLayer;
var desaStyle = {
color: '#dc3545',
weight: 2,
dashArray: '5, 8',
fillColor: 'none',
interactive: false
};
if (desaGeojson !== null && typeof desaGeojson === 'object' && desaGeojson.type) {
desaLayer = L.geoJSON(desaGeojson, {
style: desaStyle,
coordsToLatLng: function (coords) {
var a = coords[0], b = coords[1];
if (Math.abs(a) > 90) return L.latLng(b, a);
if (Math.abs(b) > 90) return L.latLng(a, b);
return L.latLng(b, a);
}
});
} else if (Array.isArray(desaGeojson)) {
var safeCoordsDesa = ensureLatLon(desaGeojson);
desaLayer = L.polygon(safeCoordsDesa, desaStyle);
}
if (desaLayer) {
currentLayerGroup.addLayer(desaLayer);
}
} catch (e) {
console.error("Gagal render batas desa:", e);
}
}
}
dataToRender.forEach(function(item) {
if (!item.polygon) return;
try {
var geojsonData = typeof item.polygon === 'string' ? JSON.parse(item.polygon) : item.polygon;
var layer;
var styleOpts = level === 'lahan'
? { color: '#28a745', weight: 2, fillColor: '#28a745', fillOpacity: 0.6 }
: { color: '#f39c12', weight: 2, fillColor: '#f1c40f', fillOpacity: 0.3 };
if (geojsonData !== null && typeof geojsonData === 'object' && geojsonData.type) {
layer = L.geoJSON(geojsonData, {
style: styleOpts,
coordsToLatLng: function (coords) {
var a = coords[0], b = coords[1];
if (Math.abs(a) > 90) return L.latLng(b, a);
if (Math.abs(b) > 90) return L.latLng(a, b);
return L.latLng(b, a);
}
});
} else if (Array.isArray(geojsonData)) {
var safeCoords = ensureLatLon(geojsonData);
layer = L.polygon(safeCoords, styleOpts);
}
if (layer) {
if (level === 'lahan') {
var listTanaman = item.tanaman && item.tanaman.length > 0 ? item.tanaman.join(', ') : null;
var komoditasText = listTanaman
? listTanaman
: '<span class="text-danger-light fw-medium small">Belum terdaftar</span>';
var cropIcon = listTanaman ? 'ti tabler-plant' : 'ti tabler-leaf-off';
var cropIconColor = listTanaman ? 'text-success' : 'text-danger';
var popupContent = `
<div class="modern-lahan-popup">
<div class="p-3 bg-white rounded-top-12 header-farmer">
<div class="d-flex align-items-center mb-1">
<small class="label-header text-uppercase text-muted fw-semibold">Pemilik Lahan</small>
</div>
<h6 class="mb-0 fw-bold farmer-name h5">${item.nama_petani}</h6>
</div>
<div class="p-3 bg-light rounded-bottom-12 details-body">
<div class="row g-2 mb-3">
<div class="col-12 d-flex align-items-center">
<div class="icon-stat-container rounded-circle bg-white text-muted border me-2">
<i class="ti tabler-hash fs-5"></i>
</div>
<div>
<small class="d-block text-muted text-uppercase fw-medium label-detail">ID Lahan</small>
<span class="fw-bold text-dark value-detail">TR-${item.id}</span>
</div>
</div>
</div>
<div class="row g-1">
<div class="col-6 d-flex align-items-center">
<div class="icon-stat-container rounded ${cropIconColor}-light me-2">
<i class="${cropIcon} fs-5 ${cropIconColor}"></i>
</div>
<div class="flex-grow-1">
<small class="d-block text-muted text-uppercase fw-medium label-detail">Komoditas</small>
<span class="fw-semibold text-dark value-commodity">${komoditasText}</span>
</div>
</div>
<div class="col-6 d-flex align-items-center border-start ps-2">
<div class="icon-stat-container rounded bg-primary-light me-2">
<i class="ti tabler-dimensions fs-5 text-primary"></i>
</div>
<div>
<small class="d-block text-muted text-uppercase fw-medium label-detail">Luas Lahan</small>
<span class="fw-bold text-dark value-detail">${parseFloat(item.luas).toFixed(1)} Ha</span>
</div>
</div>
</div>
</div>
</div>
`;
layer.bindPopup(popupContent, {
maxWidth: 300,
closeButton: true,
offset: [0, 0],
className: 'leaflet-modern-popup-wrapper'
});
} else {
var tooltipContent = `
<div class="modern-map-card">
<div class="card-header-region">
<span class="region-label">Wilayah</span>
<h6 class="region-name">${item.nama}</h6>
</div>
<div class="card-body-stats border-top border-bottom py-2 my-2">
<div class="stat-row d-flex align-items-center mb-2">
<div class="stat-icon bg-success-light text-success me-2 rounded-p-2">
<i class="ti tabler-map"></i>
</div>
<div class="stat-info">
<small class="text-muted text-uppercase fw-medium" style="font-size: 10px;">Jumlah Lahan</small>
<div class="stat-value fw-bold">${parseInt(item.total_lahan)}</div>
</div>
</div>
<div class="stat-row d-flex align-items-center">
<div class="stat-icon bg-primary-light text-primary me-2 rounded-p-2">
<i class="ti tabler-area"></i>
</div>
<div class="stat-info">
<small class="text-muted text-uppercase fw-medium" style="font-size: 10px;">Total Luas</small>
<div class="stat-value fw-bold">${parseFloat(item.total_luas).toFixed(2)} <small class="text-muted">Ha</small></div>
</div>
</div>
</div>
<div class="card-footer-action text-center mt-2">
<span class="action-prompt text-primary fw-medium small">
Klik untuk detail <i class="ti tabler-chevron-right ms-1"></i>
</span>
</div>
</div>
`;
layer.bindTooltip(tooltipContent, {
sticky: true,
direction: 'top',
className: 'leaflet-modern-tooltip',
offset: [0, -10]
});
layer.on('click', function(e) {
L.DomEvent.stopPropagation(e);
var nextLevel = level === 'provinsi' ? 'kabupaten'
: level === 'kabupaten' ? 'kecamatan'
: level === 'kecamatan' ? 'desa' : 'lahan';
mapHistory.push({ level: level, parentId: parentId, bounds: currentLayerGroup.getBounds() });
renderLevel(nextLevel, item.id, e.target.getBounds());
});
}
currentLayerGroup.addLayer(layer);
}
} catch (e) {
console.error("Gagal memproses data level", level, "ID", item.id, e);
}
});
if (btnBack) btnBack.style.display = mapHistory.length > 0 ? 'block' : 'none';
if (currentLayerGroup.getLayers().length > 0) {
var bounds = currentLayerGroup.getBounds();
if (bounds.isValid()) {
map.fitBounds(bounds, { padding: [30, 30], maxZoom: 18 });
}
} else if (fallbackBounds && fallbackBounds.isValid()) {
map.fitBounds(fallbackBounds, { padding: [30, 30], maxZoom: 18 });
}
}
if (btnBack) {
btnBack.addEventListener('click', function() {
var prevState = mapHistory.pop();
if (prevState) {
renderLevel(prevState.level, prevState.parentId);
if (prevState.bounds && prevState.bounds.isValid()) {
map.fitBounds(prevState.bounds, { padding: [30, 30] });
}
}
});
}
setTimeout(() => {
map.invalidateSize();
// Render Awal: Provinsi
renderLevel('provinsi', null);
}, 300);
</script>
@endpush
+8 -8
View File
@@ -33,14 +33,14 @@
<!-- Header Section -->
<div class="flex flex-col md:flex-row justify-between items-start md:items-center gap-4 mb-6">
<div>
<h2 class="text-2xl font-bold teks-header-judul">
Ringkasan M&E (Theory of Change)
</h2>
<p class="text-sm mt-1 teks-header-sub">
Capaian Utama Transformasi Bioekonomi Desa Ngada
</p>
</div>
<h2 class="text-2xl font-bold teks-header-judul">
Ringkasan M&E (Theory of Change)
</h2>
<p class="text-sm mt-1 teks-header-sub">
Capaian Utama Transformasi Bioekonomi Desa Ngada
</p>
</div>
<div class="flex gap-2">
<button class="bg-white border border-gray-200 text-gray-600 px-4 py-2 rounded-lg text-sm font-medium hover:bg-gray-50 flex items-center gap-2">
<i data-lucide="download" class="w-4 h-4"></i> Export PDF
@@ -0,0 +1,81 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasAddColumn"
aria-labelledby="offcanvasAddColumnLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasAddColumnLabel" class="offcanvas-title">Tambah Kolom</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="add-new-column pt-0" id="addColumnForm" action="{{ route('tabel.formulir.store', $tabel->id) }}" method="POST">
@csrf
<input type="hidden" id="add-column-tipe" name="tipe" value="form-column" />
<input type="hidden" id="add-column-parent" name="parent_id" value="" />
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-column-width">Lebar Kolom (1-12)</label>
<input type="number" min="1" max="12" step="1" value="3"
class="form-control" id="add-column-width" name="column_width" placeholder="Cth: 3" />
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasAddColumn = new bootstrap.Offcanvas(document.getElementById('offcanvasAddColumn'));
$('#addColumnForm').validate({
rules: {
column_width: { required: true, number: true },
},
messages: {
column_width: { required: "Lebar kolom wajib diisi" },
},
submitHandler: function (form) {
$.ajax({
url: $(form).attr('action'),
type: 'POST',
data: $(form).serialize(),
success: function () {
offcanvasAddColumn.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
$('.add-column').click(function(e) {
e.preventDefault();
$('#add-column-parent').val($(this).data('parent'));
offcanvasAddColumn.show();
});
});
</script>
@endpush
@@ -0,0 +1,27 @@
@push('scripts')
<script>
$(document).ready(function() {
function addForm(tipe) {
$.ajax({
url: '{{ route('tabel.formulir.store', [$tabel->id]) }}',
type: 'POST',
data: {
tipe: tipe,
_token: '{{ csrf_token() }}',
},
success: function () {
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
}
$(".add-hr-line").click(function (e) {
e.preventDefault();
addForm('form-hr-line');
});
});
</script>
@endpush
@@ -0,0 +1,27 @@
@push('scripts')
<script>
$(document).ready(function() {
function addForm(tipe) {
$.ajax({
url: '{{ route('tabel.formulir.store', [$tabel->id]) }}',
type: 'POST',
data: {
tipe: tipe,
_token: '{{ csrf_token() }}',
},
success: function () {
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
}
$(".add-row").click(function (e) {
e.preventDefault();
addForm('form-row');
});
});
</script>
@endpush
@@ -0,0 +1,77 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasAddSection"
aria-labelledby="offcanvasAddSectionLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasAddSectionLabel" class="offcanvas-title">Tambah Section</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="add-new-section pt-0" id="addSectionForm" action="{{ route('tabel.formulir.store', $tabel->id) }}" method="POST">
@csrf
<input type="hidden" id="add-section-tipe" name="tipe" value="form-section" />
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-section-label">Label Section</label>
<input type="text" class="form-control" id="add-section-label" name="section_label" placeholder="Cth: Nama Petani" />
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasAddSection = new bootstrap.Offcanvas(document.getElementById('offcanvasAddSection'));
$('#addSectionForm').validate({
rules: {
section_label: { required: true }
},
messages: {
section_label: { required: "Label wajib diisi" }
},
submitHandler: function (form) {
$.ajax({
url: $(form).attr('action'),
type: 'POST',
data: $(form).serialize(),
success: function () {
offcanvasAddSection.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
$('.add-section').click(function(e) {
e.preventDefault();
offcanvasAddSection.show();
});
});
</script>
@endpush
@@ -0,0 +1,85 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasAddValue"
aria-labelledby="offcanvasAddValueLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasAddValueLabel" class="offcanvas-title">Tambah Formulir Nilai</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="add-new-value pt-0" id="addValueForm" action="{{ route('tabel.formulir.store', $tabel->id) }}" method="POST">
@csrf
<input type="hidden" id="add-value-tipe" name="tipe" value="form-value" />
<input type="hidden" id="add-value-parent" name="parent_id" value="" />
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-value-kolom">Kolom</label>
<select class="form-select" id="add-value-kolom" name="kolom_id">
<option value="">Pilih Kolom</option>
@foreach ($tabel->kolom as $kolom)
<option value="{{ $kolom->id }}">{{ $kolom->label }}</option>
@endforeach
</select>
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasAddValue = new bootstrap.Offcanvas(document.getElementById('offcanvasAddValue'));
$('#addValueForm').validate({
rules: {
kolom_id: { required: true },
},
messages: {
kolom_id: { required: "Kolom wajib dipilih" },
},
submitHandler: function (form) {
$.ajax({
url: $(form).attr('action'),
type: 'POST',
data: $(form).serialize(),
success: function () {
offcanvasAddValue.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
$('.add-value').click(function(e) {
e.preventDefault();
$('#add-value-parent').val($(this).data('parent'));
offcanvasAddValue.show();
});
});
</script>
@endpush
@@ -0,0 +1,94 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasEditColumn"
aria-labelledby="offcanvasEditColumnLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasEditColumnLabel" class="offcanvas-title">Edit Kolom</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="pt-0" id="editColumnForm" action="{{ route('tabel.formulir.update', [$tabel->id, ':id']) }}" method="POST">
@csrf
@method('PUT')
<input type="hidden" id="edit-column-id" name="id" />
<input type="hidden" id="edit-column-tipe" name="tipe" value="form-column" />
<input type="hidden" id="edit-column-parent" name="parent_id" value="" />
<div class="mb-6 form-control-validation">
<label class="form-label" for="edit-column-width">Lebar Kolom (1-12)</label>
<input type="number" min="1" max="12" step="1" value="3"
class="form-control" id="edit-column-width" name="column_width" placeholder="Cth: 3" />
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasEditColumn = new bootstrap.Offcanvas(document.getElementById('offcanvasEditColumn'));
$('.edit-column').click(function(e) {
console.log('edit column');
e.preventDefault();
const id = $(this).data('id');
$.get("{{ route('tabel.formulir.show', [$tabel->id, ':id']) }}".replace(':id', id), function (res) {
if (res.status !== 'success') {
return;
}
$('#edit-column-id').val(res.data.id);
$('#edit-column-parent').val(res.data.parent_id);
$('#edit-column-width').val(res.data.column_width);
offcanvasEditColumn.show();
}).fail(function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
});
});
$('#editColumnForm').validate({
rules: {
column_width: { required: true, number: true },
},
messages: {
column_width: { required: "Lebar kolom wajib diisi" },
},
submitHandler: function (form) {
console.log($('#edit-column-id').val());
$.ajax({
url: $(form).attr('action').replace(':id', $('#edit-column-id').val()),
type: 'PUT',
data: $(form).serialize(),
success: function () {
offcanvasEditColumn.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -0,0 +1,89 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasEditSection"
aria-labelledby="offcanvasEditSectionLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasEditSectionLabel" class="offcanvas-title">Edit Section</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="pt-0" id="editSectionForm" action="{{ route('tabel.formulir.update', [$tabel->id, ':id']) }}" method="POST">
@csrf
@method('PUT')
<input type="hidden" id="edit-section-id" name="id" />
<input type="hidden" id="edit-section-tipe" name="tipe" value="form-section" />
<div class="mb-6 form-control-validation">
<label class="form-label" for="edit-section-label">Label Section</label>
<input type="text" class="form-control" id="edit-section-label" name="section_label" placeholder="Cth: Nama Petani" />
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasEditSection = new bootstrap.Offcanvas(document.getElementById('offcanvasEditSection'));
$('.edit-section').click(function(e) {
console.log('edit section');
e.preventDefault();
const id = $(this).data('id');
$.get("{{ route('tabel.formulir.show', [$tabel->id, ':id']) }}".replace(':id', id), function (res) {
if (res.status !== 'success') {
return;
}
$('#edit-section-id').val(res.data.id);
$('#edit-section-label').val(res.data.section_label);
offcanvasEditSection.show();
}).fail(function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
});
});
$('#editSectionForm').validate({
rules: {
section_label: { required: true }
},
messages: {
section_label: { required: "Label wajib diisi" }
},
submitHandler: function (form) {
$.ajax({
url: $(form).attr('action').replace(':id', $('#edit-section-id').val()),
type: 'PUT',
data: $(form).serialize(),
success: function () {
offcanvasEditSection.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -0,0 +1,97 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasEditValue"
aria-labelledby="offcanvasEditValueLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasEditValueLabel" class="offcanvas-title">Edit Formulir Nilai</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="pt-0" id="editValueForm" action="{{ route('tabel.formulir.update', [$tabel->id, ':id']) }}" method="POST">
@csrf
@method('PUT')
<input type="hidden" id="edit-value-id" name="id" />
<input type="hidden" id="edit-value-tipe" name="tipe" value="form-value" />
<input type="hidden" id="edit-value-parent" name="parent_id" value="" />
<div class="mb-6 form-control-validation">
<label class="form-label" for="edit-value-kolom">Kolom</label>
<select class="form-select" id="edit-value-kolom" name="kolom_id">
<option value="">Pilih Kolom</option>
@foreach ($tabel->kolom as $kolom)
<option value="{{ $kolom->id }}">{{ $kolom->label }}</option>
@endforeach
</select>
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasEditValue = new bootstrap.Offcanvas(document.getElementById('offcanvasEditValue'));
$('.edit-value').click(function(e) {
console.log('edit value');
e.preventDefault();
const id = $(this).data('id');
$.get("{{ route('tabel.formulir.show', [$tabel->id, ':id']) }}".replace(':id', id), function (res) {
if (res.status !== 'success') {
return;
}
$('#edit-value-id').val(res.data.id);
$('#edit-value-parent').val(res.data.parent_id);
$('#edit-value-kolom').val(res.data.kolom_id);
offcanvasEditValue.show();
}).fail(function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
});
});
$('#editValueForm').validate({
rules: {
kolom_id: { required: true },
},
messages: {
kolom_id: { required: "Kolom wajib dipilih" },
},
submitHandler: function (form) {
$.ajax({
url: $(form).attr('action').replace(':id', $('#edit-value-id').val()),
type: 'PUT',
data: $(form).serialize(),
success: function () {
offcanvasEditValue.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -0,0 +1,283 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<div class="card-header border-bottom d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">Formulir untuk Tabel Basis Data Ekosistem: {{ $tabel->label }}</h5>
<div class="d-flex gap-2">
<form action="{{ route('tabel.formulir.generate', $tabel->id) }}" method="POST">
@csrf
<button class="btn btn-sm btn-label-primary" type="submit">
<i class="ti tabler-automation me-1"></i> Generate Formulir
</button>
</form>
</div>
</div>
<div class="card-body">
<div class="nav-align-top pt-4">
<ul class="nav nav-pills" role="tablist">
<li class="nav-item">
<button
type="button"
class="nav-link active"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-pills-form"
aria-controls="navs-pills-form"
aria-selected="true">
<i class="ti tabler-forms me-1"></i> Rancangan
</button>
</li>
<li class="nav-item">
<button
type="button"
class="nav-link"
role="tab"
data-bs-toggle="tab"
data-bs-target="#navs-pills-preview"
aria-controls="navs-pills-preview"
aria-selected="false">
<i class="ti tabler-eye me-1"></i> Pratinjau
</button>
</li>
</ul>
</div>
<div class="tab-content p-0">
<div class="tab-pane fade show active" id="navs-pills-form" role="tabpanel">
<div class="row pt-4">
<div class="col-6 mb-4">
<h6>Rancangan Form</h6>
<ul class="list-group list-group-flush" id="list-form">
<li class="list-group-item drag-item cursor-move ">
<div class="d-flex justify-content-between align-items-center">
<span class="lead"><strong>Formulir: {{ $tabel->label }}</strong></span>
<div class="align-text-bottom">
<a href="javascript:;" class="btn btn-icon btn-text-primary rounded-pill add-row"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Tambah Baris">
<i class="ti tabler-row-insert-bottom ti-md"></i></a>
<a href="javascript:;" class="btn btn-icon btn-text-primary rounded-pill add-section"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Tambah Section">
<i class="ti tabler-new-section ti-md"></i></a>
<a href="javascript:;" class="btn btn-icon btn-text-primary rounded-pill add-hr-line"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Tambah Garis Pembatas">
<i class="ti tabler-separator ti-md"></i></a>
</div>
</div>
</li>
@foreach($tabel->form as $form)
<li class="list-group-item drag-item cursor-move pl-4">
<div class="d-flex justify-content-between align-items-center">
<span>{{ $form->tipe->label() }}
@if($form->tipe->value == 'form-section')
: {{ $form->section_label }}
@endif
</span>
<div class="align-text-bottom">
@if (!$loop->first)
<button type="button"
class="btn btn-icon btn-text-secondary move-up"
data-id="{{ $form->id }}" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Pindah ke Urutan Sebelumnya">
<i class="ti tabler-arrow-up"></i>
</button>
@endif
@if (!$loop->last)
<button type="button"
class="btn btn-icon btn-text-secondary move-down"
data-id="{{ $form->id }}" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Pindah ke Urutan Selanjutnya">
<i class="ti tabler-arrow-down"></i>
</button>
@endif
@if($form->tipe->value == 'form-row')
<a href="javascript:;" class="btn btn-icon btn-text-primary rounded-pill add-column"
data-parent="{{ $form->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Tambah Kolom">
<i class="ti tabler-column-insert-right ti-md"></i></a>
@elseif($form->tipe->value == 'form-section')
<a href="javascript:;" class="btn btn-icon btn-text-primary rounded-pill edit-section" data-id="{{ $form->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Edit Section">
<i class="ti tabler-section ti-md"></i></a>
@endif
<a href="javascript:;" class="btn btn-icon btn-text-danger rounded-pill delete-record"
data-id="{{ $form->id }}" data-nama="{{ $form->tipe->label() }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Hapus">
<i class="ti tabler-trash ti-md"></i></a>
</div>
</div>
@if($form->tipe->value == 'form-row')
<ul class="list-group list-group-flush nested-list">
@foreach($form->form as $childForm)
<li class="list-group-item drag-item cursor-move">
<div class="d-flex justify-content-between align-items-center">
<span>{{ $childForm->tipe->label() }}
@if($childForm->tipe->value == 'form-column')
: col-{{ $childForm->column_width }}
@endif
</span>
<div class="align-text-bottom">
@if (!$loop->first)
<button type="button"
class="btn btn-icon btn-text-secondary move-up"
data-id="{{ $childForm->id }}" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Pindah ke Urutan Sebelumnya">
<i class="ti tabler-arrow-up"></i>
</button>
@endif
@if (!$loop->last)
<button type="button"
class="btn btn-icon btn-text-secondary move-down"
data-id="{{ $childForm->id }}" data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Pindah ke Urutan Selanjutnya">
<i class="ti tabler-arrow-down"></i>
</button>
@endif
@if($childForm->tipe->value == 'form-column')
<a href="javascript:;" class="btn btn-icon btn-text-primary rounded-pill edit-column"
data-parent="{{ $form->id }}" data-id="{{ $childForm->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Edit Kolom">
<i class="ti tabler-columns ti-md"></i></a>
<a href="javascript:;" class="btn btn-icon btn-text-primary rounded-pill move-column"
data-parent="{{ $form->id }}" data-id="{{ $childForm->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Pindah Kolom ke Row Berbeda">
<i class="ti tabler-transition-top ti-md"></i></a>
<a href="javascript:;" class="btn btn-icon btn-text-primary rounded-pill add-value"
data-parent="{{ $childForm->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Tambah Form Nilai">
<i class="ti tabler-file-plus ti-md"></i></a>
@endif
<a href="javascript:;" class="btn btn-icon btn-text-danger rounded-pill delete-record"
data-id="{{ $childForm->id }}" data-nama="{{ $childForm->tipe->label() }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Hapus">
<i class="ti tabler-trash ti-md"></i></a>
</div>
</div>
@if($childForm->tipe->value == 'form-column')
<ul class="list-group list-group-flush nested-list">
@foreach($childForm->form as $grandchildForm)
<li class="list-group-item drag-item cursor-move">
<div class="d-flex justify-content-between align-items-center">
<span>{{ $grandchildForm->tipe->label() }}
@if($grandchildForm->tipe->value == 'form-value')
: {{ $grandchildForm->kolom->label }}
@endif
</span>
<div class="align-text-bottom">
<a href="javascript:;" class="btn btn-icon btn-text-primary rounded-pill edit-value"
data-parent="{{ $childForm->id }}" data-id="{{ $grandchildForm->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Edit Form Nilai">
<i class="ti tabler-file-text ti-md"></i></a>
<a href="javascript:;" class="btn btn-icon btn-text-danger rounded-pill delete-record"
data-id="{{ $grandchildForm->id }}" data-nama="{{ $grandchildForm->tipe->label() }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Hapus">
<i class="ti tabler-trash ti-md"></i></a>
</div>
</div>
</li>
@endforeach
</ul>
@endif
</li>
@endforeach
</ul>
@endif
</li>
@endforeach
</ul>
</div>
</div>
</div>
<div class="tab-pane fade" id="navs-pills-preview" role="tabpanel">
<div class="row pt-4">
<div class="col-12 mb-4">
<h6>Pratinjau Formulir</h6>
{!! $tabel->renderHtml('preview') !!}
</div>
</div>
</div>
</div>
<div class="mt-4 text-end">
<a href="{{ route('tabel.show', $tabel->id) }}" class="btn btn-label-secondary me-2">Kembali</a>
</div>
</div>
</div>
</div>
@include('ekosistem.form-basis-data.add-row')
@include('ekosistem.form-basis-data.add-section')
@include('ekosistem.form-basis-data.edit-section')
@include('ekosistem.form-basis-data.add-hr-line')
@include('ekosistem.form-basis-data.add-column')
@include('ekosistem.form-basis-data.edit-column')
@include('ekosistem.form-basis-data.move-column')
@include('ekosistem.form-basis-data.add-value')
@include('ekosistem.form-basis-data.edit-value')
@endsection
@push('scripts')
<script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.7/Sortable.min.js"></script>
<script>
$(document).ready(function() {
$(".move-up").click(function (e) {
e.preventDefault();
let id = $(this).data('id');
$.post("{{ route('tabel.formulir.move-up', [$tabel->id, ':id']) }}".replace(':id', id), {
_token: '{{ csrf_token() }}',
_method: 'PUT'
}, function (res) {
if (res.status === 'success') {
location.reload();
}
});
});
$(".move-down").click(function (e) {
e.preventDefault();
let id = $(this).data('id');
$.post("{{ route('tabel.formulir.move-down', [$tabel->id, ':id']) }}".replace(':id', id), {
_token: '{{ csrf_token() }}',
_method: 'PUT'
}, function (res) {
if (res.status === 'success') {
location.reload();
}
});
});
$(".delete-record").click(function (e) {
e.preventDefault();
const id = $(this).data('id');
const name = $(this).data('nama');
Swal.fire({
title: 'Hapus?',
text: `Form ${name} akan dihapus!`,
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, Hapus!',
customClass: { confirmButton: 'btn btn-danger me-3', cancelButton: 'btn btn-label-secondary' },
buttonsStyling: false
}).then(function (result) {
if (result.value) {
$.ajax({
url: "{{ route('tabel.formulir.destroy', [$tabel->id, ':id']) }}".replace(':id', id),
type: 'POST',
data: {
_token: '{{ csrf_token() }}',
_method: 'DELETE'
},
success: function () {
Swal.fire({ icon: 'success', title: 'Terhapus!', showConfirmButton: false, timer: 1500 })
.then(() => location.reload());
}
});
}
});
});
});
</script>
@endpush
@@ -0,0 +1,98 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasMoveColumn"
aria-labelledby="offcanvasMoveColumnLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasMoveColumnLabel" class="offcanvas-title">Edit Kolom</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="pt-0" id="moveColumnForm" action="{{ route('tabel.formulir.update', [$tabel->id, ':id']) }}" method="POST">
@csrf
@method('PUT')
<input type="hidden" id="move-column-id" name="id" />
<input type="hidden" id="move-column-tipe" name="tipe" value="form-column" />
<input type="hidden" id="move-column-width" name="column_width" value="" />
<div class="mb-6 form-control-validation">
<label class="form-label" for="move-column-parent">Row Induk</label>
<select class="form-select" id="move-column-parent" name="parent_id">
<option value="">Pilih Row Induk</option>
@foreach ($tabel->form->where('tipe', \App\Enums\LayoutForm::ROW) as $row)
<option value="{{ $row->id }}">Row: {{ $row->urutan }}</option>
@endforeach
</select>
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasMoveColumn = new bootstrap.Offcanvas(document.getElementById('offcanvasMoveColumn'));
$('.move-column').click(function(e) {
console.log('move column');
e.preventDefault();
const id = $(this).data('id');
$.get("{{ route('tabel.formulir.show', [$tabel->id, ':id']) }}".replace(':id', id), function (res) {
if (res.status !== 'success') {
return;
}
$('#move-column-id').val(res.data.id);
$('#move-column-parent').val(res.data.parent_id);
$('#move-column-width').val(res.data.column_width);
offcanvasMoveColumn.show();
}).fail(function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
});
});
$('#moveColumnForm').validate({
rules: {
parent_id: { required: true },
},
messages: {
parent_id: { required: "Row induk wajib diisi" },
},
submitHandler: function (form) {
console.log($('#move-column-id').val());
$.ajax({
url: $(form).attr('action').replace(':id', $('#move-column-id').val()),
type: 'PUT',
data: $(form).serialize(),
success: function () {
offcanvasMoveColumn.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -37,9 +37,15 @@
<option value="date">Date</option>
<option value="datetime">Datetime</option>
<option value="text">Text</option>
<option value="foreign_key">Relasi</option>
<option value="enum">Enum</option> <option value="foreign_key">Relasi</option>
</select>
</div>
<div class="mb-6 form-control-validation" id="enum-form-opsi-update" style="display: none;">
<label class="form-label" for="update-kolom-opsi-enum">Opsi Enum<span class="text-danger">*</span></label>
<textarea class="form-control" id="update-kolom-opsi-enum" name="enum_opsi" placeholder='Cth: [{"label": "Ya", "value": "ya"}]'></textarea>
</div>
<div class="mb-6 form-control-validation" id="relasi-form-tabel-update" style="display: none;">
<label class="form-label" for="update-kolom-relasi-tabel">Tabel Relasi<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="update-kolom-relasi-tabel" name="relasi_tabel" placeholder="Cth: Nama Petani" />
@@ -54,7 +60,7 @@
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="update-kolom-validasi">Validasi<span class="text-danger">*</span></label>
<textarea class="form-control" id="update-kolom-validasi" name="validasi" value="1" placeholder="Cth: required"></textarea>
<textarea class="form-control" id="update-kolom-validasi" name="validasi" placeholder="Cth: {&quot;required&quot;: true}"></textarea>
</div>
<div class="mb-6 form-control-validation">
<label class="switch">
@@ -89,17 +95,39 @@
<script>
$(document).ready(function() {
$('#update-kolom-jenis_data').select2({
dropdownParent: $('#offcanvasUpdateKolom'),
width: '100%',
});
$('#update-kolom-jenis_data').on('change', function() {
var tipe = $(this).val();
if (tipe === 'foreign_key') {
$('#relasi-form-tabel-update').show();
$('#relasi-form-kolom-update').show();
$('#relasi-form-kolom-label-update').show();
} else {
$('#relasi-form-tabel-update').hide();
$('#relasi-form-kolom-update').hide();
$('#relasi-form-kolom-label-update').hide();
}
if (tipe === 'enum') {
$('#enum-form-opsi-update').show();
} else {
$('#enum-form-opsi-update').hide();
}
});
$.validator.addMethod("snakecase", function(value, element) {
return this.optional(element) || /^[a-z0-9]+(_[a-z0-9]+)*$/.test(value);
}, "Gunakan format snake_case (huruf kecil dan underscore)");
const offcanvasUpdate = new bootstrap.Offcanvas(document.getElementById('offcanvasUpdateKolom'));
$(".edit-record").click(function () {
const id = $(this).data('id');
console.log("Edit record with ID:", id);
@@ -114,21 +142,25 @@
$('#update-kolom-id').val(res.data.id);
$('#update-kolom-nama').val(res.data.kolom);
$('#update-kolom-label').val(res.data.label);
$('#update-kolom-jenis_data').val(res.data.tipe).trigger('change');
$('#update-kolom-validasi').val(res.data.required ? 'required' : '');
let validasiVal = (typeof res.data.validasi === 'object' && res.data.validasi !== null)
? JSON.stringify(res.data.validasi)
: res.data.validasi;
$('#update-kolom-validasi').val(validasiVal);
let opsiEnumVal = (typeof res.data.enum_opsi === 'object' && res.data.enum_opsi !== null)
? JSON.stringify(res.data.enum_opsi)
: res.data.enum_opsi;
$('#update-kolom-opsi-enum').val(opsiEnumVal);
if (res.data.tipe === 'foreign_key') {
console.log("Data relasi:", res.data);
$('#update-kolom-relasi-tabel').val(res.data.relasi_tabel);
$('#update-kolom-relasi-kolom-id').val(res.data.relasi_kolom_id);
$('#update-kolom-relasi-kolom-label').val(res.data.relasi_kolom_label);
$('#relasi-form-tabel-update').show();
$('#relasi-form-kolom-update').show();
$('#relasi-form-kolom-label-update').show();
} else {
$('#relasi-form-tabel-update').hide();
$('#relasi-form-kolom-update').hide();
$('#relasi-form-kolom-label-update').hide();
}
$('#update-kolom-jenis_data').val(res.data.tipe).trigger('change');
$('#update-kolom-is_active').prop('checked', res.data.is_active === 1 || res.data.is_active === true);
$('#update-kolom-is_browse').prop('checked', res.data.is_browse === 1 || res.data.is_browse === true);
offcanvasUpdate.show();
@@ -177,4 +209,4 @@
});
});
</script>
@endpush
@endpush
@@ -86,13 +86,13 @@
<div class="col-md-auto d-flex gap-2">
@if (app()->has('SystemProgram'))
@else
<div class="col-md-auto">
<div class="col-6">
<div class="d-flex align-items-center">
<label class="me-2 text-nowrap">Program</label>
<select name="program_id" class="form-select form-select-sm w-auto" onchange="this.form.submit()" id="select-program">
<option value="">Pilih Program</option>
<option value="">--- Pilih Program ---</option>
@foreach($programList as $prog)
<option value="{{ $prog->id }}" {{ request('program_id') == $prog->id ? 'selected' : '' }}>{{ $prog->nama }}</option>
<option value="{{ $prog->id }}" {{ request('program_id') == $prog->id ? 'selected' : '' }}>{{ $prog->nama_singkat }}</option>
@endforeach
</select>
</div>
@@ -102,9 +102,8 @@
<input type="text" name="search" class="form-control" placeholder="Cari Nama Petani..." value="{{ request('search') }}">
<button type="submit" class="btn btn-outline-primary"><i class="ti tabler-search"></i></button>
</div>
<a href="{{ route('petani-cpm.create', ['program_id' => request('program_id')]) }}" class="btn btn-sm btn-primary"
style="display: {{ request('program_id') ? 'flex' : 'none' }};">
<i class="ti tabler-plus me-1"></i> Tambah
<a href="{{ route('petani-cpm.create', ['program_id' => request('program_id')]) }}" class="btn btn-sm btn-primary flex-shrink-0">
<i class="icon-base ti tabler-plus me-1"></i> Tambah
</a>
</div>
</form>
@@ -159,6 +158,7 @@
'use strict';
$(document).ready(function () {
$('#select-program').select2();
$(".delete-record").click(function (e) {
e.preventDefault();
@@ -4,7 +4,7 @@
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<div class="card-header border-bottom mb-4">
<h5 class="card-title mb-0">Edit Petani (CPM)</h5>
<h5 class="card-title mb-0">Data Petani (CPM)</h5>
</div>
<div class="card-body">
@@ -159,6 +159,24 @@
</div>
</div>
<h6 class="mb-3">Informasi Formulir Tabel</h6>
<hr class="my-4 mx-n4" />
<div class="row mb-4">
<div class="col-md-auto d-flex gap-2">
<a href="{{ route('tabel.formulir.index', ['tabel' => $tabel->id]) }}" class="btn btn-sm btn-primary" >
<i class="ti tabler-edit me-1"></i> Edit
</a>
</div>
</div>
<div class="row mb-4">
<div class="col-12">
<h6>Pratinjau Formulir</h6>
{!! $tabel->renderHtml('preview') !!}
</div>
</div>
<div class="mt-4 text-end">
<a href="{{ route('tabel.index') }}" class="btn btn-label-secondary me-2">Kembali</a>
</div>
@@ -0,0 +1,107 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<div class="card-header border-bottom mb-4">
<h5 class="card-title mb-0">Tambah Data {{ $tabel->label }}</h5>
</div>
<div class="card-body">
<form id="addNewDataForm" action="{{ route('tabel.data.store', [$tabel->id, 'program_id' => $program->id]) }}" method="POST" >
@csrf
<h6 class="mb-3">Informasi {{ $tabel->label }}</h6>
<div class="row">
@foreach($kolom as $k)
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-data-{{ $k->kolom }}">{{ $k->label }}<span class="text-danger">*</span></label>
@if($k->tipe == 'varchar')
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" />
@elseif($k->tipe == 'text')
<textarea class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}">{{ old($k->kolom) }}</textarea>
@elseif($k->tipe == 'integer')
<input type="number" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" step="1" />
@elseif($k->tipe == 'float')
<input type="number" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" step="any" />
@elseif($k->tipe == 'date')
<input type="date" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" />
@elseif($k->tipe == 'datetime')
<input type="datetime-local" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" />
@elseif($k->tipe == 'boolean')
<select class="form-select tipe-boolean @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
<option value="1" {{ old($k->kolom) == 1 ? 'selected' : '' }}>Ya</option>
<option value="0" {{ old($k->kolom) == 0 ? 'selected' : '' }}>Tidak</option>
</select>
@elseif($k->tipe == 'foreign_key')
<select class="form-select tipe-fk @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
@if ($list_relasi->where('kolom_id', $k->id)->first() != null)
@foreach($list_relasi->where('kolom_id', $k->id)->first()['options'] as $opsi_relasi)
<option value="{{ $opsi_relasi->value }}" {{ old($k->kolom) == $opsi_relasi->value ? 'selected' : '' }}>{{ $opsi_relasi->label }}</option>
@endforeach
@endif
</select>
@elseif($k->tipe == 'enum')
<select class="form-select tipe-fk @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
@foreach($k->enum_opsi as $opsi)
<option value="{{ $opsi['value'] }}" {{ old($k->kolom) == $opsi['value'] ? 'selected' : '' }}>{{ $opsi['label'] }}</option>
@endforeach
</select>
@else
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" />
@endif
@error($k->kolom)<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
@endforeach
</div>
<div class="mt-4 text-end">
<a href="{{ route('tabel.data.index', [$tabel->id, 'program_id' => $program->id]) }}" class="btn btn-label-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success data-submit">Simpan Data</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
'use strict';
$(document).ready(function () {
$('#add-data-program,.tipe-boolean, .tipe-fk').select2();
$('#addNewDataForm').validate({
rules: {
tabel: { required: true, snakecase: true },
label: { required: true },
menu: { required: true }
},
messages: {
tabel: { required: "Nama tabel wajib diisi", snakecase: "Gunakan format snake_case (huruf kecil dan underscore)" },
label: { required: "Label wajib diisi" },
menu: { required: "Judul menu wajib diisi" }
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -10,58 +10,8 @@
<div class="card-body">
<form id="addNewDataForm" action="{{ route('tabel.data.store', [$tabel->id, 'program_id' => $program->id]) }}" method="POST" >
@csrf
<h6 class="mb-3">Informasi {{ $tabel->label }}</h6>
<div class="row">
@foreach($kolom as $k)
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-data-{{ $k->kolom }}">{{ $k->label }}<span class="text-danger">*</span></label>
@if($k->tipe == 'varchar')
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" />
@elseif($k->tipe == 'text')
<textarea class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}">{{ old($k->kolom) }}</textarea>
@elseif($k->tipe == 'integer')
<input type="number" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" step="1" />
@elseif($k->tipe == 'float')
<input type="number" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" step="any" />
@elseif($k->tipe == 'date')
<input type="date" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" />
@elseif($k->tipe == 'datetime')
<input type="datetime-local" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" />
@elseif($k->tipe == 'boolean')
<select class="form-select tipe-boolean @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
<option value="1" {{ old($k->kolom) == 1 ? 'selected' : '' }}>Ya</option>
<option value="0" {{ old($k->kolom) == 0 ? 'selected' : '' }}>Tidak</option>
</select>
@elseif($k->tipe == 'foreign_key')
<select class="form-select tipe-fk @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
@if ($list_relasi->where('kolom_id', $k->id)->first() != null)
@foreach($list_relasi->where('kolom_id', $k->id)->first()['options'] as $opsi_relasi)
<option value="{{ $opsi_relasi->value }}" {{ old($k->kolom) == $opsi_relasi->value ? 'selected' : '' }}>{{ $opsi_relasi->label }}</option>
@endforeach
@endif
</select>
@elseif($k->tipe == 'enum')
<select class="form-select tipe-fk @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
@foreach($k->enum_opsi as $opsi)
<option value="{{ $opsi['value'] }}" {{ old($k->kolom) == $opsi['value'] ? 'selected' : '' }}>{{ $opsi['label'] }}</option>
@endforeach
</select>
@else
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) }}" />
@endif
@error($k->kolom)<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
@endforeach
</div>
{!! $tabel->renderHtml('add', $errors, $list_relasi) !!}
<div class="mt-4 text-end">
<a href="{{ route('tabel.data.index', [$tabel->id, 'program_id' => $program->id]) }}" class="btn btn-label-secondary me-2">Kembali</a>
@@ -77,7 +27,7 @@
<script>
'use strict';
$(document).ready(function () {
$('#add-data-program,.tipe-boolean, .tipe-fk').select2();
$('#add-data-program,.tipe-boolean, .tipe-foreign_key, .tipe-enum').select2();
$('#addNewDataForm').validate({
rules: {
@@ -0,0 +1,124 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<div class="card-header border-bottom mb-4">
<h5 class="card-title mb-0">Tambah Data {{ $tabel->label }}</h5>
</div>
<div class="card-body">
<form id="addUpdateDataForm" action="{{ route('tabel.data.update', [$tabel->id, $value_id, 'program_id' => $program->id]) }}" method="POST" >
@csrf
@method('PUT')
<h6 class="mb-3">Informasi {{ $tabel->label }}</h6>
@if (!$program)
<div class="row">
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-data-program">Program<span class="text-danger">*</span></label>
<select name="program_id" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
<option value="">Pilih Program</option>
@foreach($programList as $prog)
<option value="{{ $prog->id }}" {{ request('program_id') == $prog->id ? 'selected' : '' }}>{{ $prog->nama }}</option>
@endforeach
</select>
</div>
</div>
@endif
<div class="row">
@foreach($kolom as $k)
<?php
$value = $values->where('kolom_id', $k->id)->first();
?>
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-data-{{ $k->kolom }}">{{ $k->label }}<span class="text-danger">*</span></label>
@if($k->tipe == 'varchar')
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@elseif($k->tipe == 'text')
<textarea class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}">{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}</textarea>
@elseif($k->tipe == 'integer')
<input type="number" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" step="1" />
@elseif($k->tipe == 'float')
<input type="number" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" step="any" />
@elseif($k->tipe == 'date')
<input type="date" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@elseif($k->tipe == 'datetime')
<input type="datetime-local" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@elseif($k->tipe == 'boolean')
<select class="form-select tipe-boolean @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
<option value="1" {{ (old($k->kolom) ?? ($value['value_'.$k->tipe] ?? null)) == 1 ? 'selected' : '' }}>Ya</option>
<option value="0" {{ (old($k->kolom) ?? ($value['value_'.$k->tipe] ?? null)) == 0 ? 'selected' : '' }}>Tidak</option>
</select>
@elseif($k->tipe == 'foreign_key')
<select class="form-select tipe-fk @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
@if ($list_relasi->where('kolom_id', $k->id)->first() != null)
@foreach($list_relasi->where('kolom_id', $k->id)->first()['options'] as $opsi_relasi)
<option value="{{ $opsi_relasi->value }}" {{ old($k->kolom) ?? $value['value_'.$k->tipe] == $opsi_relasi->value ? 'selected' : '' }}>{{ $opsi_relasi->label }}</option>
@endforeach
@endif
</select>
@elseif($k->tipe == 'enum')
<select class="form-select tipe-fk @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
@foreach($k->enum_opsi as $opsi)
<option value="{{ $opsi['value'] }}" {{ old($k->kolom) ?? $value['value_'.$k->tipe] == $opsi['value'] ? 'selected' : '' }}>{{ $opsi['label'] }}</option>
@endforeach
</select>
@else
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@endif
@error($k->kolom)<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
@endforeach
</div>
<div class="mt-4 text-end">
<a href="{{ route('tabel.data.index', [$tabel->id, 'program_id' => $program->id]) }}" class="btn btn-label-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success data-submit">Simpan Data</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
'use strict';
$(document).ready(function () {
$('#add-data-program,.tipe-boolean, .tipe-fk').select2();
$('#addNewDataForm').validate({
rules: {
tabel: { required: true, snakecase: true },
label: { required: true },
menu: { required: true }
},
messages: {
tabel: { required: "Nama tabel wajib diisi", snakecase: "Gunakan format snake_case (huruf kecil dan underscore)" },
label: { required: "Label wajib diisi" },
menu: { required: "Judul menu wajib diisi" }
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -10,76 +10,10 @@
<div class="card-body">
<form id="addUpdateDataForm" action="{{ route('tabel.data.update', [$tabel->id, $value_id, 'program_id' => $program->id]) }}" method="POST" >
@csrf
@method('PUT')
@method('PUT')
<h6 class="mb-3">Informasi {{ $tabel->label }}</h6>
@if (!$program)
<div class="row">
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-data-program">Program<span class="text-danger">*</span></label>
<select name="program_id" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
<option value="">Pilih Program</option>
@foreach($programList as $prog)
<option value="{{ $prog->id }}" {{ request('program_id') == $prog->id ? 'selected' : '' }}>{{ $prog->nama }}</option>
@endforeach
</select>
</div>
</div>
@endif
<div class="row">
@foreach($kolom as $k)
<?php
$value = $values->where('kolom_id', $k->id)->first();
?>
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-data-{{ $k->kolom }}">{{ $k->label }}<span class="text-danger">*</span></label>
@if($k->tipe == 'varchar')
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@elseif($k->tipe == 'text')
<textarea class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}">{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}</textarea>
@elseif($k->tipe == 'integer')
<input type="number" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" step="1" />
@elseif($k->tipe == 'float')
<input type="number" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" step="any" />
@elseif($k->tipe == 'date')
<input type="date" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@elseif($k->tipe == 'datetime')
<input type="datetime-local" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@elseif($k->tipe == 'boolean')
<select class="form-select tipe-boolean @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
<option value="1" {{ (old($k->kolom) ?? ($value['value_'.$k->tipe] ?? null)) == 1 ? 'selected' : '' }}>Ya</option>
<option value="0" {{ (old($k->kolom) ?? ($value['value_'.$k->tipe] ?? null)) == 0 ? 'selected' : '' }}>Tidak</option>
</select>
@elseif($k->tipe == 'foreign_key')
<select class="form-select tipe-fk @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
@if ($list_relasi->where('kolom_id', $k->id)->first() != null)
@foreach($list_relasi->where('kolom_id', $k->id)->first()['options'] as $opsi_relasi)
<option value="{{ $opsi_relasi->value }}" {{ old($k->kolom) ?? $value['value_'.$k->tipe] == $opsi_relasi->value ? 'selected' : '' }}>{{ $opsi_relasi->label }}</option>
@endforeach
@endif
</select>
@elseif($k->tipe == 'enum')
<select class="form-select tipe-fk @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" name="{{ $k->kolom }}">
@foreach($k->enum_opsi as $opsi)
<option value="{{ $opsi['value'] }}" {{ old($k->kolom) ?? $value['value_'.$k->tipe] == $opsi['value'] ? 'selected' : '' }}>{{ $opsi['label'] }}</option>
@endforeach
</select>
@else
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}"
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@endif
@error($k->kolom)<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
@endforeach
</div>
{!! $tabel->renderHtml('edit', $errors, $list_relasi, $values) !!}
<div class="mt-4 text-end">
<a href="{{ route('tabel.data.index', [$tabel->id, 'program_id' => $program->id]) }}" class="btn btn-label-secondary me-2">Kembali</a>
<button type="submit" class="btn btn-success data-submit">Simpan Data</button>
@@ -49,16 +49,15 @@
<div class="col-md-auto d-flex gap-2">
@if (app()->has('SystemProgram'))
@else
<div class="col-md-auto">
<div class="col-6">
<div class="d-flex align-items-center">
<label class="me-2 text-nowrap">Program</label>
<select name="program_id" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
<option value="">Pilih Program</option>
<select name="program_id" class="form-select form-select-sm w-auto" onchange="this.form.submit()" id="program_id">
<option value="">--- Pilih Program ---</option>
@foreach($programList as $prog)
<option value="{{ $prog->id }}" {{ request('program_id') == $prog->id ? 'selected' : '' }}>{{ $prog->nama }}</option>
<option value="{{ $prog->id }}" {{ request('program_id') == $prog->id ? 'selected' : '' }}>{{ $prog->nama_singkat }}</option>
@endforeach
</select>
<label class="ms-2 text-nowrap">entries</label>
</div>
</div>
@endif
@@ -66,9 +65,8 @@
<input type="text" name="search" class="form-control" placeholder="Cari Nama Tabel..." value="{{ request('search') }}">
<button type="submit" class="btn btn-outline-primary"><i class="ti tabler-search"></i></button>
</div>
<a href="{{ route('tabel.data.create', [$tabel->id, 'program_id' => request('program_id')]) }}" class="btn btn-sm btn-primary add-new"
style="display: {{ request('program_id') ? 'flex' : 'none' }};">
<i class="ti tabler-plus me-1"></i> Tambah
<a href="{{ route('tabel.data.create', [$tabel->id, 'program_id' => request('program_id')]) }}" class="btn btn-sm btn-primary flex-shrink-0 add-new">
<i class="icon-xs icon-base ti tabler-plus me-1"></i> Tambah
</a>
</div>
</form>
@@ -123,6 +121,8 @@
'use strict';
$(document).ready(function () {
$('#program_id').select2();
$(".delete-record").click(function (e) {
e.preventDefault();
const id = $(this).data('id');
@@ -12,63 +12,7 @@
@csrf
@method('PUT')
<h6 class="mb-3">Informasi {{ $tabel->label }}</h6>
@if (!$program)
<div class="row">
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-data-program">Program<span class="text-danger">*</span></label>
<select name="program_id" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
<option value="">Pilih Program</option>
@foreach($programList as $prog)
<option value="{{ $prog->id }}" {{ request('program_id') == $prog->id ? 'selected' : '' }}>{{ $prog->nama }}</option>
@endforeach
</select>
</div>
</div>
@endif
<div class="row">
@foreach($kolom as $k)
<?php
$value = $values->where('kolom_id', $k->id)->first();
?>
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="add-data-{{ $k->kolom }}">{{ $k->label }}<span class="text-danger">*</span></label>
@if($k->tipe == 'varchar')
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" readonly
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@elseif($k->tipe == 'text')
<textarea class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" readonly
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}">{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}</textarea>
@elseif($k->tipe == 'integer')
<input type="number" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" readonly
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" step="1" />
@elseif($k->tipe == 'float')
<input type="number" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" readonly
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" step="any" />
@elseif($k->tipe == 'date')
<input type="date" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" readonly
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@elseif($k->tipe == 'datetime')
<input type="datetime-local" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" readonly
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@elseif($k->tipe == 'boolean')
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" readonly
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ (old($k->kolom) ?? $value['value_'.$k->tipe]) == 1 ? 'Ya' : 'Tidak' }}" />
@elseif($k->tipe == 'foreign_key')
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" readonly
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ (old($k->kolom) ?? $value['value_relasi_label']) }}" />
@elseif($k->tipe == 'enum')
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" readonly
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ (old($k->kolom) ?? $value['value_enum_label']) }}" />
@else
<input type="text" class="form-control @error($k->kolom) is-invalid @enderror" id="add-data-{{ $k->kolom }}" readonly
name="{{ $k->kolom }}" placeholder="Cth: {{ $k->label }}" value="{{ old($k->kolom) ?? $value['value_'.$k->tipe] }}" />
@endif
@error($k->kolom)<div class="error invalid-feedback">{{ $message }}</div>@enderror
</div>
@endforeach
</div>
{!! $tabel->renderHtml('view', $errors, null, $values) !!}
<div class="mt-4 text-end">
<a href="{{ route('tabel.data.index', [$tabel->id, 'program_id' => $program->id]) }}" class="btn btn-label-secondary me-2">Kembali</a>
@@ -1,118 +0,0 @@
<!doctype html>
<html
lang="en"
class=" layout-wide customizer-hide"
dir="ltr"
data-skin="default"
data-bs-theme="light"
data-assets-path="../../assets/"
data-template="vertical-menu-template">
<head>
<meta charset="utf-8" />
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0" />
<meta name="robots" content="noindex, nofollow" />
<title>Agomonev | {{ $title ?? 'Auth' }} </title>
<meta name="description" content="" />
<!-- Favicon -->
<link rel="icon" type="image/x-icon" href="../../assets/img/favicon/favicon.ico" />
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Public+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&ampdisplay=swap"
rel="stylesheet" />
<link rel="stylesheet" href="{{ asset('assets/vendor/fonts/iconify-icons.css') }}" />
<script src="{{ asset('assets/vendor/libs/@algolia/autocomplete-js.js') }}"></script>
<!-- Core CSS -->
<!-- build:css assets/vendor/css/theme.css -->
<link rel="stylesheet" href="{{ asset('assets/vendor/libs/node-waves/node-waves.css') }}" />
<link rel="stylesheet" href="{{ asset('assets/vendor/libs/pickr/pickr-themes.css') }}" />
<link rel="stylesheet" href="{{ asset('assets/vendor/css/core.css') }}" />
<link rel="stylesheet" href="{{ asset('assets/css/demo.css') }}" />
<!-- Vendors CSS -->
<link rel="stylesheet" href="{{ asset('assets/vendor/libs/perfect-scrollbar/perfect-scrollbar.css') }}" />
<!-- endbuild -->
<!-- Vendor -->
<link rel="stylesheet" href="{{ asset('assets/vendor/libs/@form-validation/form-validation.css') }}" />
<!-- Page CSS -->
<!-- Page -->
<link rel="stylesheet" href="{{ asset('assets/vendor/css/pages/page-auth.css') }}" />
<!-- Helpers -->
<script src="{{ asset('assets/vendor/js/helpers.js') }}"></script>
<!--! Template customizer & Theme config files MUST be included after core stylesheets and helpers.js in the <head> section -->
<!--? Template customizer: To hide customizer set displayCustomizer value false in config.js. -->
<script src="{{ asset('assets/vendor/js/template-customizer.js') }}"></script>
<!--? Config: Mandatory theme config file contain global vars & default theme options, Set your preferred theme option in this file. -->
<script src="{{ asset('assets/js/config.js') }}"></script>
</head>
<body>
<!-- Content -->
<div class="container-xxl">
<div class="authentication-wrapper authentication-basic container-p-y">
<div class="authentication-inner py-6">
<!-- Register Card -->
@yield('content')
<!-- Register Card -->
</div>
</div>
</div>
<!-- / Content -->
<!-- Core JS -->
<!-- build:js assets/vendor/js/theme.js -->
<script src="{{ asset('assets/vendor/libs/jquery/jquery.js') }}"></script>
<script src="{{ asset('assets/vendor/libs/popper/popper.js') }}"></script>
<script src="{{ asset('assets/vendor/js/bootstrap.js') }}"></script>
<script src="{{ asset('assets/vendor/libs/node-waves/node-waves.js') }}"></script>
<script src="{{ asset('assets/vendor/libs/pickr/pickr.js') }}"></script>
<script src="{{ asset('assets/vendor/libs/perfect-scrollbar/perfect-scrollbar.js') }}"></script>
<script src="{{ asset('assets/vendor/libs/hammer/hammer.js') }}"></script>
<script src="{{ asset('assets/vendor/libs/i18n/i18n.js') }}"></script>
<script src="{{ asset('assets/vendor/js/menu.js') }}"></script>
<!-- endbuild -->
<!-- Vendors JS -->
<script src="{{ asset('assets/vendor/libs/@form-validation/popular.js') }}"></script>
<script src="{{ asset('assets/vendor/libs/@form-validation/bootstrap5.js') }}"></script>
<script src="{{ asset('assets/vendor/libs/@form-validation/auto-focus.js') }}"></script>
<!-- Main JS -->
<script src="{{ asset('assets/js/main.js') }}"></script>
<!-- Page JS -->
<script src="{{ asset('assets/js/pages-auth.js') }}"></script>
</body>
</html>
@@ -1,89 +0,0 @@
@extends('auth.layout')
@section('content')
<div class="card">
<div class="card-body">
<!-- Logo -->
<div class="app-brand justify-content-center mb-6">
<a href="{{ route('dashboard.produktivitas') }}" class="app-brand-link">
<span class="app-brand-logo demo">
<span class="text-primary">
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Daun Bagian Kiri -->
<path opacity="0.3" d="M16 2.5C16 2.5 5 6.5 5 15.5C5 20.5 11 23.5 16 23.5V2.5Z" fill="currentColor" />
<!-- Daun Bagian Kanan -->
<path opacity="0.6" d="M16 2.5C16 2.5 27 6.5 27 15.5C27 20.5 21 23.5 16 23.5V2.5Z" fill="currentColor" />
<!-- Grafik Pertumbuhan (Monev) -->
<path d="M2 19L10 11L15 15L27 4" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" />
<!-- Titik Target/Pencapaian -->
<circle cx="27" cy="4" r="2.5" fill="currentColor" />
</svg>
</span>
</span>
<span class="app-brand-text demo menu-text fw-bold ms-3" style="text-transform: capitalize;">AgroMonev</span>
</a>
</div>
<!-- /Logo -->
<h4 class="mb-1">Selamat datang di AgroMonev!</h4>
<p class="mb-6">Silakan masuk ke akun Anda</p>
@if ($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
@foreach ($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<form id="formAuthentication" class="mb-4" action="{{ route('auth.authenticate') }}" method="POST">
@csrf
<div class="mb-6 form-control-validation">
<label for="email" class="form-label">Email atau Username</label>
<input
type="text"
class="form-control"
id="email"
name="email_username"
placeholder="Masukkan email atau username Anda"
autofocus />
</div>
<div class="mb-6 form-password-toggle form-control-validation">
<label class="form-label" for="password">Password</label>
<div class="input-group input-group-merge">
<input
type="password"
id="password"
class="form-control"
name="password"
placeholder="&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;&#xb7;"
aria-describedby="password" />
<span class="input-group-text cursor-pointer"><i class="icon-base ti tabler-eye-off"></i></span>
</div>
</div>
<div class="my-8">
<div class="d-flex justify-content-between">
<div class="form-check mb-0 ms-2">
<input class="form-check-input" type="checkbox" id="remember-me" />
<label class="form-check-label" for="remember-me"> Remember Me </label>
</div>
<a href="auth-forgot-password-basic.html">
<p class="mb-0">Lupa Password?</p>
</a>
</div>
</div>
<div class="mb-6">
<button class="btn btn-primary d-grid w-100" type="submit">Login</button>
</div>
</form>
<p class="text-center">
<span>Anda belum terdaftar?</span>
<a href="{{ route('register') }}">
<span>Buat akun baru</span>
</a>
</p>
</div>
</div>
@endsection
@@ -10,6 +10,10 @@
<label class="form-label" for="add-user-fullname">Nama Lengkap</label>
<input type="text" class="form-control" id="add-user-fullname" placeholder="John Doe" name="name" required />
</div>
<div class="mb-4">
<label class="form-label" for="add-user-username">Nama akun</label>
<input type="text" class="form-control" id="add-user-username" placeholder="john" name="username" required />
</div>
<div class="mb-4">
<label class="form-label" for="add-user-email">Email</label>
<input type="email" id="add-user-email" class="form-control" placeholder="john.doe@example.com" name="email" required />
+1 -1
View File
@@ -361,7 +361,7 @@
<div class="dropdown-divider my-1 mx-n2"></div>
</li>
<li>
<a class="dropdown-item" href="{{ route('userProfile.index') }}">
<a class="dropdown-item" href="pages-profile-user.html">
<i class="icon-base ti tabler-user me-3 icon-md"></i
><span class="align-middle">Profil Saya</span>
</a>
+82 -31
View File
@@ -1,6 +1,6 @@
<aside id="layout-menu" class="layout-menu menu-vertical menu">
<div class="app-brand demo ">
<a href="{{ route('dashboard.produktivitas') }}" class="app-brand-link">
<a href="{{ route('dashboard.summary') }}" class="app-brand-link">
<span class="app-brand-logo demo">
<span>
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
@@ -58,8 +58,28 @@
</a>
<ul class="menu-sub">
<li class="menu-item">
<a href="{{ route('dashboard.produktivitas') }}" class="menu-link">
<div data-i18n="Produktivitas">Produktivitas</div>
<a href="{{ route('dashboard.summary') }}" class="menu-link">
<div data-i18n="Summary">Summary</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('dashboard.resiliensi') }}" class="menu-link">
<div data-i18n="Reseliensi">Reseliensi</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('dashboard.hilirisasi') }}" class="menu-link">
<div data-i18n="Hilirisasi">Hilirisasi</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('dashboard.konektivitas') }}" class="menu-link">
<div data-i18n="Konektivitas">Konektivitas</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('dashboard.akselerasi') }}" class="menu-link">
<div data-i18n="Akselerasi">Akselerasi</div>
</a>
</li>
</ul>
@@ -109,7 +129,7 @@
</li>
</ul>
</li>
<li class="menu-item {{ request()->routeIs(['petani-cpm.*','tabel.data.*']) ? 'active open' : '' }}">
<li class="menu-item {{ request()->routeIs(['petani-cpm.*', 'lahan-cpcl.*', 'tabel.data.*']) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-folder-root"></i>
<div data-i18n="Basis Data Ekosistem">Basis Data Ekosistem</div>
@@ -117,7 +137,14 @@
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('petani-cpm.*') ? 'active' : '' }}">
<a href="{{ route('petani-cpm.index') }}" class="menu-link">
<div data-i18n="Petani">Petani</div>
<div data-i18n="Petani CPM">Petani CPM</div>
</a>
</li>
</ul>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('lahan-cpcl.*') ? 'active' : '' }}">
<a href="{{ route('lahan-cpcl.index') }}" class="menu-link">
<div data-i18n="Lahan CPCL">Lahan CPCL</div>
</a>
</li>
</ul>
@@ -151,11 +178,18 @@
</li>
</ul>
</li> -->
<li class="menu-item">
<li class="menu-item {{ request()->routeIs(['realisasi.*', 'kelompok-belajar-tani.*', 'workshop.*']) ? 'active open' : '' }}">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-webhook"></i>
<div data-i18n="Program & Komunitas">Program & Komunitas</div>
</a>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('realisasi.*') ? 'active' : '' }}">
<a href="{{ route('realisasi.index') }}" class="menu-link">
<div data-i18n="Capaian Program">Capaian Program</div>
</a>
</li>
</ul>
<ul class="menu-sub">
<li class="menu-item {{ request()->routeIs('kelompok-belajar-tani.*') ? 'active' : '' }}">
<a href="{{ route('kelompok-belajar-tani.index') }}" class="menu-link">
@@ -164,19 +198,19 @@
</li>
</ul>
<ul class="menu-sub">
<li class="menu-item">
<li class="menu-item {{ request()->routeIs('workshop.*') ? 'active' : '' }}">
<a href="{{ route('workshop.index') }}" class="menu-link">
<div data-i18n="Workshop & Lokakarya">Workshop & Lokakarya</div>
</a>
</li>
</ul>
<ul class="menu-sub">
<!-- <ul class="menu-sub">
<li class="menu-item">
<a href="#" class="menu-link">
<div data-i18n="Desiminasi & Replikasi">Desiminasi & Replikasi</div>
</a>
</li>
</ul>
</ul> -->
</li>
<!-- Master Data -->
<li class="menu-header small">
@@ -189,26 +223,26 @@
<div data-i18n="Wilayah">Wilayah</div>
</a>
<ul class="menu-sub">
<li class="menu-item">
<a href="{{ route('provinsi.index') }}" class="menu-link">
<div data-i18n="Provinsi">provinsi</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('kabupatenkota.index') }}" class="menu-link">
<div data-i18n="Kabupaten Kota">Kabupaten Kota</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('kecamatan.index') }}" class="menu-link">
<div data-i18n="Kecamatan">Kecamatan</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('desakelurahan.index') }}" class="menu-link">
<div data-i18n="Desa Kelurahan">Desa Kelurahan</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('provinsi.index') }}" class="menu-link">
<div data-i18n="Provinsi">provinsi</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('kabupatenkota.index') }}" class="menu-link">
<div data-i18n="Kabupaten Kota">Kabupaten Kota</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('kecamatan.index') }}" class="menu-link">
<div data-i18n="Kecamatan">Kecamatan</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('desakelurahan.index') }}" class="menu-link">
<div data-i18n="Desa Kelurahan">Desa Kelurahan</div>
</a>
</li>
</ul>
</li>
<li class="menu-item">
@@ -224,10 +258,27 @@
</a>
</li>
<li class="menu-item">
<a href="{{ route('komoditas.index') }}" class="menu-link">
<a href="javascript:void(0);" class="menu-link menu-toggle">
<i class="menu-icon icon-base ti tabler-plant"></i>
<div data-i18n="Komoditas">Komoditas</div>
<div data-i18n="Tanaman">Tanaman</div>
</a>
<ul class="menu-sub">
<li class="menu-item">
<a href="{{ route('komoditas.index') }}" class="menu-link">
<div data-i18n="Komoditas">Komoditas</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('jenis-komoditas.index') }}" class="menu-link">
<div data-i18n="Jenis Komoditas">Jenis Komoditas</div>
</a>
</li>
<li class="menu-item">
<a href="{{ route('varietas.index') }}" class="menu-link">
<div data-i18n="Varietas">Varietas</div>
</a>
</li>
</ul>
</li>
<!-- Konfigurasi -->
@@ -2,6 +2,9 @@
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="mb-6">
<x-wilayah-dashboard />
</div>
<div class="card">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">Data Desa/Kelurahan</h5>
@@ -0,0 +1,42 @@
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasAddJenisKomoditas" aria-labelledby="offcanvasAddJenisKomoditasLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasAddJenisKomoditasLabel" class="offcanvas-title">Tambah Jenis Komoditas</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="add-new-jeniskomoditas pt-0" id="addNewJenisKomoditasForm" action="{{ route('jenis-komoditas.store') }}" method="POST">
@csrf
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-jenis-komoditas-komoditas">
Komoditas <span class="text-danger">*</span>
</label>
<select id="add-jenis-komoditas-komoditas" name="komoditas_id" class="select2 form-select" data-placeholder="Pilih Komoditas">
<option value=""></option>
@foreach($komoditas as $item)
<option value="{{ $item->id }}">{{ $item->nama }}</option>
@endforeach
</select>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-jenis-komoditas-name">
Nama Jenis Komoditas <span class="text-danger">*</span>
</label>
<input
type="text"
class="form-control"
id="add-jenis-komoditas-name"
placeholder="Contoh: Kopi Arabika"
name="nama"
autocomplete="off" />
</div>
<div class="mt-8">
<button type="submit" class="btn btn-primary me-3 data-submit">Submit</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Cancel</button>
</div>
</form>
</div>
</div>
@@ -0,0 +1,45 @@
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasEditJenisKomoditas" aria-labelledby="offcanvasEditJenisKomoditasLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasEditJenisKomoditasLabel" class="offcanvas-title">Edit Jenis Komoditas</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="edit-jeniskomoditas pt-0" id="editJenisKomoditasForm" method="POST">
@csrf
@method('PUT')
<input type="hidden" id="edit-jenis-komoditas-id" name="id">
<div class="mb-6 form-control-validation">
<label class="form-label" for="edit-jenis-komoditas-komoditas">
Komoditas <span class="text-danger">*</span>
</label>
<select id="edit-jenis-komoditas-komoditas" name="komoditas_id" class="select2 form-select" data-placeholder="Pilih Komoditas">
<option value=""></option>
@foreach($komoditas as $item)
<option value="{{ $item->id }}">{{ $item->nama }}</option>
@endforeach
</select>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="edit-jenis-komoditas-name">
Nama Jenis Komoditas <span class="text-danger">*</span>
</label>
<input
type="text"
class="form-control"
id="edit-jenis-komoditas-name"
placeholder="Contoh: Kopi Arabika"
name="nama"
autocomplete="off" />
</div>
<div class="mt-8">
<button type="submit" class="btn btn-primary me-3 data-submit">Update</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Cancel</button>
</div>
</form>
</div>
</div>
@@ -0,0 +1,485 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<div class="card-header border-bottom">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center gap-3">
<div>
<h5 class="card-title mb-0">Jenis Komoditas</h5>
<small class="text-muted">Kelola data jenis komoditas</small>
</div>
<button
type="button"
class="btn btn-primary"
data-bs-toggle="offcanvas"
data-bs-target="#offcanvasAddJenisKomoditas">
<i class="ti tabler-plus me-1"></i>
Tambah Jenis Komoditas
</button>
</div>
</div>
<div class="card-body border-bottom">
<form
method="GET"
action="{{ route('jenis-komoditas.index') }}"
class="row g-3 align-items-center justify-content-between">
<div class="col-md-auto">
<div class="d-flex align-items-center">
<label class="me-2 text-nowrap">Show</label>
<select name="per_page" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
@foreach([10, 25, 50, 100] as $limit)
<option value="{{ $limit }}" {{ (int) request('per_page', 10) === $limit ? 'selected' : '' }}>
{{ $limit }}
</option>
@endforeach
</select>
<label class="ms-2 text-nowrap">entries</label>
</div>
</div>
<div class="col-md-auto">
<div class="input-group input-group-sm">
<input
type="text"
name="search"
class="form-control"
placeholder="Cari jenis komoditas..."
value="{{ request('search') }}">
@if(request('search'))
<a href="{{ route('jenis-komoditas.index', ['per_page' => request('per_page', 10)]) }}"
class="btn btn-outline-secondary">
<i class="ti tabler-x"></i>
</a>
@endif
<button type="submit" class="btn btn-outline-primary">
<i class="ti tabler-search"></i>
</button>
</div>
</div>
</form>
</div>
<div class="table-responsive text-nowrap">
<table class="table table-hover">
<thead class="border-top">
<tr>
<th style="width: 70px;">No</th>
<th>Nama Jenis Komoditas</th>
<th>Komoditas</th>
<th class="text-center" style="width: 160px;">Actions</th>
</tr>
</thead>
<tbody>
@forelse($jenisKomoditas as $index => $jenis)
@php
$komoditasNama = $jenis->komoditas->nama ?? '-';
$words = preg_split('/\s+/', trim($komoditasNama));
$initials = collect($words)
->filter()
->map(fn($word) => strtoupper(substr($word, 0, 1)))
->take(2)
->implode('');
$states = ['success', 'danger', 'warning', 'info', 'primary', 'secondary'];
$state = $states[$jenis->id % count($states)];
@endphp
<tr>
<td>{{ $jenisKomoditas->firstItem() + $index }}</td>
<td>
<div class="d-flex justify-content-start align-items-center">
<div class="avatar-wrapper me-3">
<div class="avatar avatar-sm">
<span class="avatar-initial rounded-circle bg-label-{{ $state }}">
{{ $initials ?: 'JK' }}
</span>
</div>
</div>
<div class="d-flex flex-column">
<span class="fw-medium text-heading">{{ $jenis->nama }}</span>
<small class="text-muted">ID: {{ $jenis->id }}</small>
</div>
</div>
</td>
<td>
<span class="badge bg-label-secondary">
{{ $komoditasNama }}
</span>
</td>
<td>
<div class="d-flex justify-content-center align-items-center">
<button
type="button"
class="btn btn-icon btn-text-secondary rounded-pill view-record"
data-id="{{ $jenis->id }}"
title="Lihat">
<i class="ti tabler-eye ti-md"></i>
</button>
<button
type="button"
class="btn btn-icon btn-text-secondary rounded-pill edit-record"
data-id="{{ $jenis->id }}"
title="Edit">
<i class="ti tabler-edit ti-md"></i>
</button>
<button
type="button"
class="btn btn-icon btn-text-secondary rounded-pill delete-record"
data-id="{{ $jenis->id }}"
data-nama="{{ $jenis->nama }}"
title="Hapus">
<i class="ti tabler-trash ti-md"></i>
</button>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center py-5">
<div class="d-flex flex-column align-items-center">
<i class="ti tabler-database-off ti-lg mb-2 text-muted"></i>
<span class="text-muted">Tidak ada data ditemukan.</span>
</div>
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($jenisKomoditas->hasPages())
<div class="card-footer d-flex justify-content-center justify-content-md-end border-top">
{{ $jenisKomoditas->appends(request()->query())->links('pagination::bootstrap-5') }}
</div>
@endif
</div>
</div>
@include('master.jeniskomoditas.add')
@include('master.jeniskomoditas.edit')
@include('master.jeniskomoditas.view')
@endsection
@push('scripts')
<script>
'use strict';
$(function () {
const csrfToken = "{{ csrf_token() }}";
const baseUrl = "{{ url('jenis-komoditas') }}";
const offcanvasAdd = new bootstrap.Offcanvas(document.getElementById('offcanvasAddJenisKomoditas'));
const offcanvasEdit = new bootstrap.Offcanvas(document.getElementById('offcanvasEditJenisKomoditas'));
const offcanvasView = new bootstrap.Offcanvas(document.getElementById('offcanvasViewJenisKomoditas'));
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': csrfToken
}
});
$('.select2').each(function () {
const $this = $(this);
$this.wrap('<div class="position-relative"></div>').select2({
dropdownParent: $this.parent(),
placeholder: $this.data('placeholder') || 'Pilih data',
allowClear: true,
width: '100%'
});
});
$.validator.setDefaults({
errorElement: 'div',
errorClass: 'invalid-feedback',
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
},
errorPlacement: function (error, element) {
if (element.hasClass('select2-hidden-accessible')) {
error.insertAfter(element.next('.select2-container'));
} else {
error.appendTo(element.closest('.form-control-validation'));
}
}
});
$('.select2').on('change', function () {
const $form = $(this).closest('form');
if ($form.data('validator')) {
$(this).valid();
}
});
function showSuccess(title = 'Berhasil!', text = 'Data berhasil diproses') {
return Swal.fire({
icon: 'success',
title,
text,
showConfirmButton: false,
timer: 1500,
customClass: {
confirmButton: 'btn btn-primary'
},
buttonsStyling: false
});
}
function showError(xhr, fallback = 'Terjadi kesalahan sistem.') {
let message = fallback;
if (xhr.responseJSON?.message) {
message = xhr.responseJSON.message;
}
if (xhr.responseJSON?.errors) {
message = Object.values(xhr.responseJSON.errors).flat().join('<br>');
}
return Swal.fire({
icon: 'error',
title: 'Gagal!',
html: message,
customClass: {
confirmButton: 'btn btn-primary'
},
buttonsStyling: false
});
}
function resetForm($form) {
if (!$form.length) return;
$form[0].reset();
if ($form.data('validator')) {
$form.validate().resetForm();
}
$form.find('.is-invalid').removeClass('is-invalid');
$form.find('.select2').each(function () {
$(this).val('').trigger('change.select2');
});
}
function setButtonLoading($button, isLoading, text = 'Menyimpan...') {
if (isLoading) {
$button
.data('original-text', $button.html())
.prop('disabled', true)
.html(`<span class="spinner-border spinner-border-sm me-1"></span>${text}`);
} else {
$button
.prop('disabled', false)
.html($button.data('original-text'));
}
}
$('#addNewJenisKomoditasForm').validate({
rules: {
komoditas_id: {
required: true
},
nama: {
required: true,
maxlength: 255
}
},
messages: {
komoditas_id: {
required: 'Komoditas wajib dipilih'
},
nama: {
required: 'Nama jenis komoditas wajib diisi',
maxlength: 'Nama maksimal 255 karakter'
}
},
submitHandler: function (form, event) {
event.preventDefault();
const $form = $(form);
const $button = $form.find('button[type="submit"]');
setButtonLoading($button, true);
$.ajax({
url: $form.attr('action'),
type: 'POST',
data: $form.serialize(),
success: function (res) {
offcanvasAdd.hide();
showSuccess(
'Berhasil!',
res.message || 'Data jenis komoditas berhasil disimpan'
).then(() => window.location.reload());
},
error: function (xhr) {
showError(xhr, 'Gagal menyimpan data jenis komoditas');
},
complete: function () {
setButtonLoading($button, false);
}
});
}
});
$('#offcanvasAddJenisKomoditas').on('hidden.bs.offcanvas', function () {
resetForm($('#addNewJenisKomoditasForm'));
});
$(document).on('click', '.edit-record', function () {
const id = $(this).data('id');
const $form = $('#editJenisKomoditasForm');
resetForm($form);
$.ajax({
url: `${baseUrl}/${id}`,
type: 'GET',
success: function (res) {
const data = res.data || res;
$('#edit-jenis-komoditas-id').val(data.id);
$('#edit-jenis-komoditas-komoditas').val(data.komoditas_id).trigger('change');
$('#edit-jenis-komoditas-name').val(data.nama);
offcanvasEdit.show();
},
error: function (xhr) {
showError(xhr, 'Terjadi kesalahan saat mengambil data edit');
}
});
});
$('#editJenisKomoditasForm').validate({
rules: {
komoditas_id: {
required: true
},
nama: {
required: true,
maxlength: 255
}
},
messages: {
komoditas_id: {
required: 'Komoditas wajib dipilih'
},
nama: {
required: 'Nama jenis komoditas wajib diisi',
maxlength: 'Nama maksimal 255 karakter'
}
},
submitHandler: function (form, event) {
event.preventDefault();
const $form = $(form);
const id = $('#edit-jenis-komoditas-id').val();
const $button = $form.find('button[type="submit"]');
setButtonLoading($button, true, 'Mengupdate...');
$.ajax({
url: `${baseUrl}/${id}`,
type: 'POST',
data: $form.serialize(),
success: function (res) {
offcanvasEdit.hide();
showSuccess(
'Updated!',
res.message || 'Data jenis komoditas berhasil diperbarui'
).then(() => window.location.reload());
},
error: function (xhr) {
showError(xhr, 'Gagal mengupdate data jenis komoditas');
},
complete: function () {
setButtonLoading($button, false);
}
});
}
});
$('#offcanvasEditJenisKomoditas').on('hidden.bs.offcanvas', function () {
resetForm($('#editJenisKomoditasForm'));
});
$(document).on('click', '.view-record', function () {
const id = $(this).data('id');
$.ajax({
url: `${baseUrl}/${id}`,
type: 'GET',
success: function (res) {
const data = res.data || res;
$('#view-jenis-komoditas-komoditas').val(data.komoditas?.nama || data.nama_komoditas || '-');
$('#view-jenis-komoditas-name').val(data.nama || '-');
offcanvasView.show();
},
error: function (xhr) {
showError(xhr, 'Terjadi kesalahan saat mengambil detail data');
}
});
});
$(document).on('click', '.delete-record', function () {
const id = $(this).data('id');
const nama = $(this).data('nama') || 'data ini';
Swal.fire({
title: 'Apakah Anda yakin?',
text: `Jenis Komoditas "${nama}" akan dihapus secara permanen.`,
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal',
customClass: {
confirmButton: 'btn btn-primary me-3',
cancelButton: 'btn btn-label-secondary'
},
buttonsStyling: false
}).then(function (result) {
if (!result.isConfirmed) return;
$.ajax({
url: `${baseUrl}/${id}`,
type: 'DELETE',
success: function (res) {
showSuccess(
'Terhapus!',
res.message || 'Data jenis komoditas berhasil dihapus'
).then(() => window.location.reload());
},
error: function (xhr) {
showError(xhr, 'Gagal menghapus data jenis komoditas');
}
});
});
});
});
</script>
@endpush
@@ -0,0 +1,24 @@
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasViewJenisKomoditas" aria-labelledby="offcanvasViewJenisKomoditasLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasViewJenisKomoditasLabel" class="offcanvas-title">Detail Jenis Komoditas</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<div class="mb-6">
<label class="form-label">Komoditas</label>
<input type="text" class="form-control" id="view-jenis-komoditas-komoditas" readonly>
</div>
<div class="mb-6">
<label class="form-label">Nama Jenis Komoditas</label>
<input type="text" class="form-control" id="view-jenis-komoditas-name" readonly>
</div>
<div class="mt-8">
<button type="button" class="btn btn-label-secondary" data-bs-dismiss="offcanvas">
Tutup
</button>
</div>
</div>
</div>
@@ -2,6 +2,9 @@
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="mb-6">
<x-wilayah-dashboard />
</div>
<div class="card">
<div class="card-body border-bottom">
<form method="GET" action="{{ route('kabupatenkota.index') }}" class="d-flex justify-content-between align-items-center row gap-3 gap-md-0">
@@ -2,6 +2,9 @@
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="mb-6">
<x-wilayah-dashboard />
</div>
<div class="card">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">Data Kecamatan</h5>
@@ -2,6 +2,9 @@
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="mb-6">
<x-wilayah-dashboard />
</div>
<div class="card">
<div class="card-body border-bottom">
<form method="GET" action="{{ route('provinsi.index') }}" class="d-flex justify-content-between align-items-center row gap-3 gap-md-0">
@@ -35,6 +38,7 @@
<th>No</th>
<th>Kode Provinsi</th>
<th>Nama Provinsi</th>
<th class="text-center">Status Kelengkapan</th>
<th>Actions</th>
</tr>
</thead>
@@ -62,6 +66,19 @@
</div>
</div>
</td>
<td class="text-center">
@if($prov->kabupaten_kota_count > 0)
<span class="badge bg-label-success d-inline-flex align-items-center" style="border-radius: 20px; padding: 6px 10px;">
<i class="ti tabler-check me-1" style="font-size: 16px;"></i> Aktif (Ada Kabupaten)
</span>
@else
<span class="badge bg-label-warning d-inline-flex align-items-center" style="border-radius: 20px; padding: 6px 10px;">
<i class="ti tabler-alert-circle me-1" style="font-size: 16px;"></i> Belum Ada Wilayah
</span>
@endif
</td>
<td>
<div class="d-flex align-items-center">
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill edit-record" data-id="{{ $prov->id }}"><i class="ti tabler-edit ti-md"></i></a>
@@ -72,7 +89,7 @@
</tr>
@empty
<tr>
<td colspan="4" class="text-center py-4">Tidak ada data ditemukan.</td>
<td colspan="5" class="text-center py-4">Tidak ada data ditemukan.</td>
</tr>
@endforelse
</tbody>
@@ -0,0 +1,61 @@
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasAddVarietas" aria-labelledby="offcanvasAddVarietasLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasAddVarietasLabel" class="offcanvas-title">Tambah Varietas</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="add-new-varietas pt-0" id="addNewVarietasForm" action="{{ route('varietas.store') }}" method="POST">
@csrf
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-varietas-komoditas">
Komoditas <span class="text-danger">*</span>
</label>
<select id="add-varietas-komoditas" name="komoditas_id" class="select2 form-select" data-placeholder="Pilih Komoditas">
<option value=""></option>
@foreach($komoditas as $item)
<option value="{{ $item->id }}">{{ $item->nama }}</option>
@endforeach
</select>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-varietas-jenis-komoditas">
Jenis Komoditas <span class="text-danger">*</span>
</label>
<select id="add-varietas-jenis-komoditas" name="jenis_komoditas_id" class="select2 form-select" data-placeholder="Pilih Jenis Komoditas">
<option value=""></option>
</select>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-varietas-name">
Nama Varietas <span class="text-danger">*</span>
</label>
<input
type="text"
class="form-control"
id="add-varietas-name"
placeholder="Contoh: Ciherang"
name="nama"
autocomplete="off">
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-varietas-description">Deskripsi</label>
<textarea
class="form-control"
id="add-varietas-description"
placeholder="Opsional"
name="deskripsi"
rows="3"></textarea>
</div>
<div class="mt-8">
<button type="submit" class="btn btn-primary me-3 data-submit">Submit</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Cancel</button>
</div>
</form>
</div>
</div>
@@ -0,0 +1,64 @@
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasEditVarietas" aria-labelledby="offcanvasEditVarietasLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasEditVarietasLabel" class="offcanvas-title">Edit Varietas</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="edit-varietas pt-0" id="editVarietasForm" method="POST">
@csrf
@method('PUT')
<input type="hidden" id="edit-varietas-id" name="id">
<div class="mb-6 form-control-validation">
<label class="form-label" for="edit-varietas-komoditas">
Komoditas <span class="text-danger">*</span>
</label>
<select id="edit-varietas-komoditas" name="komoditas_id" class="select2 form-select" data-placeholder="Pilih Komoditas">
<option value=""></option>
@foreach($komoditas as $item)
<option value="{{ $item->id }}">{{ $item->nama }}</option>
@endforeach
</select>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="edit-varietas-jenis-komoditas">
Jenis Komoditas <span class="text-danger">*</span>
</label>
<select id="edit-varietas-jenis-komoditas" name="jenis_komoditas_id" class="select2 form-select" data-placeholder="Pilih Jenis Komoditas">
<option value=""></option>
</select>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="edit-varietas-name">
Nama Varietas <span class="text-danger">*</span>
</label>
<input
type="text"
class="form-control"
id="edit-varietas-name"
placeholder="Contoh: Ciherang"
name="nama"
autocomplete="off">
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="edit-varietas-description">Deskripsi</label>
<textarea
class="form-control"
id="edit-varietas-description"
placeholder="Opsional"
name="deskripsi"
rows="3"></textarea>
</div>
<div class="mt-8">
<button type="submit" class="btn btn-primary me-3 data-submit">Update</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Cancel</button>
</div>
</form>
</div>
</div>
@@ -0,0 +1,547 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<div class="card-body border-bottom">
<form method="GET" action="{{ route('varietas.index') }}" class="d-flex justify-content-between align-items-center row gap-3 gap-md-0">
<div class="col-md-auto">
<div class="d-flex align-items-center">
<label class="me-2 text-nowrap">Show</label>
<select name="per_page" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
<option value="10" {{ request('per_page') == 10 ? 'selected' : '' }}>10</option>
<option value="25" {{ request('per_page') == 25 ? 'selected' : '' }}>25</option>
<option value="50" {{ request('per_page') == 50 ? 'selected' : '' }}>50</option>
<option value="100" {{ request('per_page') == 100 ? 'selected' : '' }}>100</option>
</select>
<label class="ms-2 text-nowrap">entries</label>
</div>
</div>
<div class="col-md-auto d-flex gap-2">
<div class="input-group input-group-sm w-auto">
<input
type="text"
name="search"
class="form-control"
placeholder="Cari Varietas..."
value="{{ request('search') }}">
@if(request('search'))
<a href="{{ route('varietas.index', ['per_page' => request('per_page', 10)]) }}" class="btn btn-outline-secondary">
<i class="ti tabler-x"></i>
</a>
@endif
<button type="submit" class="btn btn-outline-primary">
<i class="ti tabler-search"></i>
</button>
</div>
<button
type="button"
class="btn btn-sm btn-primary add-new"
data-bs-toggle="offcanvas"
data-bs-target="#offcanvasAddVarietas">
<i class="ti tabler-plus me-1"></i> Tambah
</button>
</div>
</form>
</div>
<div class="table-responsive text-nowrap">
<table class="table table-hover">
<thead class="border-top">
<tr>
<th>No</th>
<th>Nama Varietas</th>
<th>Komoditas</th>
<th>Jenis Komoditas</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
@forelse($varietas as $index => $item)
@php
$namaVarietas = $item->nama ?? '-';
$words = preg_split('/\s+/', trim($namaVarietas));
$initials = collect($words)
->filter()
->map(fn($word) => strtoupper(substr($word, 0, 1)))
->take(2)
->implode('');
$states = ['success', 'danger', 'warning', 'info', 'primary', 'secondary'];
$state = $states[$item->id % count($states)];
@endphp
<tr>
<td>{{ $varietas->firstItem() + $index }}</td>
<td>
<div class="d-flex justify-content-start align-items-center">
<div class="avatar-wrapper me-3">
<div class="avatar avatar-sm">
<span class="avatar-initial rounded-circle bg-label-{{ $state }}">
{{ $initials ?: 'VR' }}
</span>
</div>
</div>
<div class="d-flex flex-column">
<span class="fw-medium text-heading">{{ $item->nama }}</span>
<small class="text-muted">{{ $item->deskripsi }}</small>
</div>
</div>
</td>
<td>
<span class="badge bg-label-secondary">
{{ $item->komoditas->nama ?? '-' }}
</span>
</td>
<td>
<span class="badge bg-label-info">
{{ $item->jenisKomoditas->nama ?? '-' }}
</span>
</td>
<td>
<div class="d-flex justify-content-center align-items-center">
<button
type="button"
class="btn btn-icon btn-text-secondary rounded-pill view-record"
data-id="{{ $item->id }}"
title="Lihat">
<i class="ti tabler-eye ti-md"></i>
</button>
<button
type="button"
class="btn btn-icon btn-text-secondary rounded-pill edit-record"
data-id="{{ $item->id }}"
title="Edit">
<i class="ti tabler-edit ti-md"></i>
</button>
<button
type="button"
class="btn btn-icon btn-text-secondary rounded-pill delete-record"
data-id="{{ $item->id }}"
data-nama="{{ $item->nama }}"
title="Hapus">
<i class="ti tabler-trash ti-md"></i>
</button>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="6" class="text-center py-4">
Tidak ada data ditemukan.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($varietas->hasPages())
<div class="card-footer d-flex justify-content-center justify-content-md-end border-top">
{{ $varietas->appends(request()->query())->links('pagination::bootstrap-5') }}
</div>
@endif
</div>
</div>
@include('master.varietas.add')
@include('master.varietas.edit')
@include('master.varietas.view')
@endsection
@push('scripts')
<script>
'use strict';
$(function () {
const csrfToken = "{{ csrf_token() }}";
const baseUrl = "{{ url('varietas') }}";
const offcanvasAdd = new bootstrap.Offcanvas(document.getElementById('offcanvasAddVarietas'));
const offcanvasEdit = new bootstrap.Offcanvas(document.getElementById('offcanvasEditVarietas'));
const offcanvasView = new bootstrap.Offcanvas(document.getElementById('offcanvasViewVarietas'));
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': csrfToken
}
});
$('.select2').each(function () {
const $this = $(this);
$this.wrap('<div class="position-relative"></div>').select2({
dropdownParent: $this.parent(),
placeholder: $this.data('placeholder') || 'Pilih data',
allowClear: true,
width: '100%'
});
});
$.validator.setDefaults({
errorElement: 'div',
errorClass: 'invalid-feedback',
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
},
errorPlacement: function (error, element) {
if (element.hasClass('select2-hidden-accessible')) {
error.insertAfter(element.next('.select2-container'));
} else {
error.appendTo(element.closest('.form-control-validation'));
}
}
});
$('.select2').on('change', function () {
const $form = $(this).closest('form');
if (!$form.length) return;
if (!$form.data('validator')) return;
$(this).valid();
});
function showSuccess(title = 'Berhasil!', text = 'Data berhasil diproses') {
return Swal.fire({
icon: 'success',
title,
text,
showConfirmButton: false,
timer: 1500,
customClass: {
confirmButton: 'btn btn-primary'
},
buttonsStyling: false
});
}
function showError(xhr, fallback = 'Terjadi kesalahan sistem.') {
let message = fallback;
if (xhr.responseJSON?.message) {
message = xhr.responseJSON.message;
}
if (xhr.responseJSON?.errors) {
message = Object.values(xhr.responseJSON.errors).flat().join('<br>');
}
return Swal.fire({
icon: 'error',
title: 'Gagal!',
html: message,
customClass: {
confirmButton: 'btn btn-primary'
},
buttonsStyling: false
});
}
function setButtonLoading($button, isLoading, text = 'Menyimpan...') {
if (isLoading) {
$button
.data('original-text', $button.html())
.prop('disabled', true)
.html(`<span class="spinner-border spinner-border-sm me-1"></span>${text}`);
} else {
$button
.prop('disabled', false)
.html($button.data('original-text'));
}
}
function resetForm($form) {
if (!$form.length) return;
$form[0].reset();
if ($form.data('validator')) {
$form.validate().resetForm();
}
$form.find('.is-invalid').removeClass('is-invalid');
$form.find('.select2').each(function () {
$(this).val('').trigger('change.select2');
});
}
function loadJenisKomoditas(komoditasId, targetSelector, selectedId = null) {
const $target = $(targetSelector);
$target.empty().append('<option value=""></option>').trigger('change.select2');
if (!komoditasId) return;
$.ajax({
url: `/jenis-komoditas-by-komoditas/${komoditasId}`,
type: 'GET',
success: function (res) {
const data = res.data || [];
data.forEach(function (item) {
const option = new Option(item.nama, item.id, false, false);
$target.append(option);
});
if (selectedId) {
$target.val(selectedId);
}
$target.trigger('change.select2');
},
error: function (xhr) {
showError(xhr, 'Gagal mengambil data jenis komoditas.');
}
});
}
$('#add-varietas-komoditas').on('change', function () {
const komoditasId = $(this).val();
loadJenisKomoditas(komoditasId, '#add-varietas-jenis-komoditas');
});
$('#edit-varietas-komoditas').on('change', function () {
const komoditasId = $(this).val();
loadJenisKomoditas(komoditasId, '#edit-varietas-jenis-komoditas');
});
$('#addNewVarietasForm').validate({
rules: {
komoditas_id: {
required: true
},
jenis_komoditas_id: {
required: true
},
nama: {
required: true,
maxlength: 255
},
deskripsi: {
maxlength: 10000
}
},
messages: {
komoditas_id: {
required: 'Komoditas wajib dipilih'
},
jenis_komoditas_id: {
required: 'Jenis komoditas wajib dipilih'
},
nama: {
required: 'Nama varietas wajib diisi',
maxlength: 'Nama maksimal 255 karakter'
},
deskripsi: {
maxlength: 'Deskripsi terlalu panjang'
}
},
submitHandler: function (form, event) {
event.preventDefault();
const $form = $(form);
const $button = $form.find('button[type="submit"]');
setButtonLoading($button, true);
$.ajax({
url: $form.attr('action'),
type: 'POST',
data: $form.serialize(),
success: function (res) {
offcanvasAdd.hide();
showSuccess(
'Berhasil!',
res.message || 'Data varietas berhasil disimpan'
).then(() => window.location.reload());
},
error: function (xhr) {
showError(xhr, 'Gagal menyimpan data varietas');
},
complete: function () {
setButtonLoading($button, false);
}
});
}
});
$('#offcanvasAddVarietas').on('hidden.bs.offcanvas', function () {
resetForm($('#addNewVarietasForm'));
$('#add-varietas-jenis-komoditas').empty().append('<option value=""></option>').trigger('change.select2');
});
$(document).on('click', '.view-record', function () {
const id = $(this).data('id');
$.ajax({
url: `${baseUrl}/${id}`,
type: 'GET',
success: function (res) {
const data = res.data || res;
$('#view-varietas-komoditas').val(data.komoditas?.nama || '-');
$('#view-varietas-jenis-komoditas').val(data.jenis_komoditas?.nama || data.jenis_komoditas?.nama || data.jenisKomoditas?.nama || '-');
$('#view-varietas-name').val(data.nama || '-');
$('#view-varietas-description').val(data.deskripsi || '-');
offcanvasView.show();
},
error: function (xhr) {
showError(xhr, 'Terjadi kesalahan saat mengambil detail data.');
}
});
});
$(document).on('click', '.edit-record', function () {
const id = $(this).data('id');
const $form = $('#editVarietasForm');
resetForm($form);
$.ajax({
url: `${baseUrl}/${id}`,
type: 'GET',
success: function (res) {
const data = res.data || res;
$('#edit-varietas-id').val(data.id);
$('#edit-varietas-komoditas').val(data.komoditas_id).trigger('change.select2');
$('#edit-varietas-name').val(data.nama || '');
$('#edit-varietas-description').val(data.deskripsi || '');
loadJenisKomoditas(
data.komoditas_id,
'#edit-varietas-jenis-komoditas',
data.jenis_komoditas_id
);
offcanvasEdit.show();
},
error: function (xhr) {
showError(xhr, 'Terjadi kesalahan saat mengambil data edit.');
}
});
});
$('#editVarietasForm').validate({
rules: {
komoditas_id: {
required: true
},
jenis_komoditas_id: {
required: true
},
nama: {
required: true,
maxlength: 255
},
deskripsi: {
maxlength: 10000
}
},
messages: {
komoditas_id: {
required: 'Komoditas wajib dipilih'
},
jenis_komoditas_id: {
required: 'Jenis komoditas wajib dipilih'
},
nama: {
required: 'Nama varietas wajib diisi',
maxlength: 'Nama maksimal 255 karakter'
},
deskripsi: {
maxlength: 'Deskripsi terlalu panjang'
}
},
submitHandler: function (form, event) {
event.preventDefault();
const $form = $(form);
const id = $('#edit-varietas-id').val();
const $button = $form.find('button[type="submit"]');
setButtonLoading($button, true, 'Mengupdate...');
$.ajax({
url: `${baseUrl}/${id}`,
type: 'POST',
data: $form.serialize(),
success: function (res) {
offcanvasEdit.hide();
showSuccess(
'Updated!',
res.message || 'Data varietas berhasil diperbarui'
).then(() => window.location.reload());
},
error: function (xhr) {
showError(xhr, 'Gagal mengupdate data varietas');
},
complete: function () {
setButtonLoading($button, false);
}
});
}
});
$('#offcanvasEditVarietas').on('hidden.bs.offcanvas', function () {
resetForm($('#editVarietasForm'));
$('#edit-varietas-jenis-komoditas').empty().append('<option value=""></option>').trigger('change.select2');
});
$(document).on('click', '.delete-record', function () {
const id = $(this).data('id');
const nama = $(this).data('nama') || 'data ini';
Swal.fire({
title: 'Apakah Anda yakin?',
text: `Varietas "${nama}" akan dihapus secara permanen.`,
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal',
customClass: {
confirmButton: 'btn btn-primary me-3',
cancelButton: 'btn btn-label-secondary'
},
buttonsStyling: false
}).then(function (result) {
if (!result.isConfirmed) return;
$.ajax({
url: `${baseUrl}/${id}`,
type: 'DELETE',
success: function (res) {
showSuccess(
'Terhapus!',
res.message || 'Data varietas berhasil dihapus'
).then(() => window.location.reload());
},
error: function (xhr) {
showError(xhr, 'Gagal menghapus data varietas');
}
});
});
});
});
</script>
@endpush
@@ -0,0 +1,34 @@
<div class="offcanvas offcanvas-end" tabindex="-1" id="offcanvasViewVarietas" aria-labelledby="offcanvasViewVarietasLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasViewVarietasLabel" class="offcanvas-title">Detail Varietas</h5>
<button type="button" class="btn-close text-reset" data-bs-dismiss="offcanvas" aria-label="Close"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<div class="mb-6">
<label class="form-label">Komoditas</label>
<input type="text" class="form-control" id="view-varietas-komoditas" readonly>
</div>
<div class="mb-6">
<label class="form-label">Jenis Komoditas</label>
<input type="text" class="form-control" id="view-varietas-jenis-komoditas" readonly>
</div>
<div class="mb-6">
<label class="form-label">Nama Varietas</label>
<input type="text" class="form-control" id="view-varietas-name" readonly>
</div>
<div class="mb-6">
<label class="form-label">Deskripsi</label>
<textarea class="form-control" id="view-varietas-description" rows="3" readonly></textarea>
</div>
<div class="mt-8">
<button type="button" class="btn btn-label-secondary" data-bs-dismiss="offcanvas">
Tutup
</button>
</div>
</div>
</div>
@@ -140,11 +140,12 @@
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
@endsection
@push('scripts')
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
@@ -159,7 +160,7 @@
let regionBoundaryLayer = L.featureGroup().addTo(map);
function focusToRegionBoundary(selectElement) {
regionBoundaryLayer.clearLayers();
regionBoundaryLayer.clearLayers(); // Bersihkan garis wilayah sebelumnya
let selectedOption = selectElement.options[selectElement.selectedIndex];
if (!selectedOption || !selectedOption.hasAttribute('data-path')) return;
@@ -174,7 +175,7 @@
function formatCoordinates(coords) {
if (coords.length === 2 && typeof coords[0] === 'number') {
return [coords[0], coords[1]]; // JANGAN DIBALIK (Leaflet Draw butuh format asli)
return [coords[0], coords[1]]; // JANGAN DIBALIK
}
return coords.map(formatCoordinates);
}
@@ -182,11 +183,11 @@
let latLngs = formatCoordinates(rawData);
let polygon = L.polygon(latLngs, {
color: '#ffc107',
fillColor: '#ffc107',
fillOpacity: 0.15,
weight: 3,
dashArray: '8, 6',
color: '#3b5998',
fillColor: '#3b5998',
fillOpacity: 0.1,
weight: 2,
dashArray: '5, 5',
interactive: false
});
@@ -228,8 +229,10 @@
map.on(L.Draw.Event.CREATED, function (e) {
let layer = e.layer;
drawnItems.clearLayers();
drawnItems.addLayer(layer);
updatePathTextarea(layer);
});
@@ -241,32 +244,14 @@
map.on(L.Draw.Event.DELETED, function () {
document.getElementById('path').value = '';
let luasInput = document.querySelector('input[name="luas_lahan"]');
if (luasInput) luasInput.value = '';
});
function updatePathTextarea(layer) {
let latlngs = layer.getLatLngs()[0];
let pathData = latlngs.map(function(latlng) {
return [latlng.lng, latlng.lat];
});
document.getElementById('path').value = JSON.stringify(pathData);
let areaSquareMeters = L.GeometryUtil.geodesicArea(latlngs);
let areaHectares = areaSquareMeters / 10000;
let luasInput = document.querySelector('input[name="luas_lahan"]');
if (luasInput) {
luasInput.value = areaHectares.toFixed(2);
luasInput.style.transition = "background-color 0.5s ease";
luasInput.style.backgroundColor = "#e8fdf0";
setTimeout(() => {
luasInput.style.backgroundColor = "";
}, 1500);
}
}
let statusSelect = document.getElementById('status_kepemilikan');
@@ -162,11 +162,12 @@
</div>
</div>
</div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
@endsection
@push('scripts')
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
<script>
document.addEventListener("DOMContentLoaded", function() {
@@ -203,11 +204,11 @@
let latLngs = formatCoordinates(rawData);
let polygon = L.polygon(latLngs, {
color: '#ffc107',
fillColor: '#ffc107',
fillOpacity: 0.15,
weight: 3,
dashArray: '8, 6',
color: '#3b5998',
fillColor: '#3b5998',
fillOpacity: 0.1,
weight: 2,
dashArray: '5, 5',
interactive: false
});
@@ -275,32 +276,14 @@
map.on(L.Draw.Event.DELETED, function () {
document.getElementById('path').value = '';
let luasInput = document.querySelector('input[name="luas_lahan"]');
if (luasInput) luasInput.value = '';
});
function updatePathTextarea(layer) {
let latlngs = layer.getLatLngs()[0];
let pathData = latlngs.map(function(latlng) {
return [latlng.lng, latlng.lat];
});
document.getElementById('path').value = JSON.stringify(pathData);
let areaSquareMeters = L.GeometryUtil.geodesicArea(latlngs);
let areaHectares = areaSquareMeters / 10000;
let luasInput = document.querySelector('input[name="luas_lahan"]');
if (luasInput) {
luasInput.value = areaHectares.toFixed(2);
luasInput.style.transition = "background-color 0.5s ease";
luasInput.style.backgroundColor = "#e8fdf0";
setTimeout(() => {
luasInput.style.backgroundColor = "";
}, 1500);
}
}
let statusSelect = document.getElementById('status_kepemilikan');
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,832 @@
@extends('layout.main')
@push('style')
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<style>
#map {
height: 100%;
min-height: 620px;
width: 100%;
background-color: var(--bs-body-bg);
z-index: 1;
border-radius: 0.375rem;
}
.map-wrapper {
min-height: 620px;
position: relative;
}
.points-table-wrapper {
max-height: 320px;
overflow-y: auto;
}
.leaflet-popup-content {
color: #2f2b3d;
}
.plant-marker-icon {
width: 32px;
height: 32px;
border-radius: 50%;
background: rgba(var(--bs-success-rgb), 0.95);
border: 2px solid #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 18px;
}
.plant-marker-icon.sakit {
background: rgba(var(--bs-danger-rgb), 0.95);
}
.plant-marker-icon.mati {
background: rgba(var(--bs-dark-rgb), 0.95);
}
.plant-dot-marker {
width: 11px;
height: 11px;
border-radius: 50%;
background: rgba(var(--bs-success-rgb), 0.95);
border: 2px solid #fff;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.45);
}
.plant-dot-marker.sakit {
background: rgba(var(--bs-danger-rgb), 0.95);
}
.plant-dot-marker.mati {
background: rgba(var(--bs-dark-rgb), 0.95);
}
.detail-box {
padding: .75rem;
border: 1px solid var(--bs-border-color);
border-radius: .375rem;
background-color: rgba(var(--bs-primary-rgb), .04);
}
.detail-label {
font-size: .75rem;
color: var(--bs-secondary-color);
margin-bottom: .25rem;
}
.detail-value {
font-weight: 500;
color: var(--bs-heading-color);
margin-bottom: 0;
}
.custom-plant-popup .leaflet-popup-content-wrapper {
border-radius: 14px;
padding: 0;
overflow: hidden;
background: var(--bs-card-bg);
color: var(--bs-body-color);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
}
.custom-plant-popup .leaflet-popup-content {
margin: 0;
min-width: 250px;
}
.custom-plant-popup .leaflet-popup-tip {
background: var(--bs-card-bg);
}
.plant-popup-card {
overflow: hidden;
}
.plant-popup-header {
padding: 10px 12px;
background: rgba(var(--bs-primary-rgb), 0.14);
border-bottom: 1px solid var(--bs-border-color);
}
.plant-popup-title {
font-size: 0.875rem;
font-weight: 600;
color: var(--bs-heading-color);
margin: 0;
line-height: 1.3;
}
.plant-popup-subtitle {
font-size: 0.75rem;
color: var(--bs-secondary-color);
margin-top: 2px;
}
.plant-popup-body {
padding: 10px 12px 12px;
}
.plant-popup-row {
display: flex;
justify-content: space-between;
gap: 12px;
font-size: 0.75rem;
margin-bottom: 8px;
}
.plant-popup-label {
color: var(--bs-secondary-color);
}
.plant-popup-value {
color: var(--bs-heading-color);
font-weight: 500;
text-align: right;
}
.plant-popup-actions {
display: flex;
gap: 6px;
margin-top: 10px;
}
.plant-popup-actions .btn {
flex: 1;
}
.plant-popup-help {
font-size: 0.7rem;
color: var(--bs-secondary-color);
margin-top: 8px;
line-height: 1.3;
}
</style>
@endpush
@section('content')
@php
$groupName = ($tanaman->komoditas->nama ?? 'Tanaman') . ' - ' . ($tanaman->profile->nama ?? 'Pemilik');
@endphp
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<div class="card-header border-bottom mb-4 d-flex justify-content-between align-items-center">
<div>
<h5 class="card-title mb-0">Edit Kelompok Tanaman</h5>
<small class="text-muted">{{ $groupName }}</small>
</div>
<a href="{{ route('map-tanaman.index') }}" class="btn btn-sm btn-label-secondary">
<i class="ti tabler-arrow-left me-1"></i> Kembali
</a>
</div>
<div class="card-body">
<form id="editTanamanForm" action="{{ route('map-tanaman.update', $tanaman->id) }}" method="POST">
@csrf
@method('PUT')
<div class="row">
<div class="col-lg-5 mb-4">
<h6 class="mb-3 text-primary">
<i class="ti tabler-seeding me-1"></i> Informasi Kelompok
</h6>
<div class="alert alert-info mb-3">
<i class="ti tabler-info-circle me-1"></i>
Data lahan, pemilik, komoditas, jenis, dan varietas tidak diubah dari halaman ini. Perubahan hanya untuk titik tanaman.
</div>
<div class="row g-3 mb-3">
<div class="col-md-6">
<div class="detail-box">
<div class="detail-label">Pemilik/Pengelola</div>
<div class="detail-value">{{ $tanaman->profile->nama ?? '-' }}</div>
</div>
</div>
<div class="col-md-6">
<div class="detail-box">
<div class="detail-label">Lahan</div>
<div class="detail-value">{{ $tanaman->lahan->nama ?? '-' }}</div>
</div>
</div>
<div class="col-md-6">
<div class="detail-box">
<div class="detail-label">Komoditas</div>
<div class="detail-value">{{ $tanaman->komoditas->nama ?? '-' }}</div>
</div>
</div>
<div class="col-md-6">
<div class="detail-box">
<div class="detail-label">Jenis Komoditas</div>
<div class="detail-value">{{ $tanaman->jenisKomoditas->nama ?? '-' }}</div>
</div>
</div>
<div class="col-md-12">
<div class="detail-box">
<div class="detail-label">Varietas</div>
<div class="detail-value">{{ $tanaman->varietas->nama ?? '-' }}</div>
</div>
</div>
</div>
<div class="mb-3 form-control-validation">
<label class="form-label">Deskripsi</label>
<textarea
name="deskripsi"
class="form-control"
rows="3"
placeholder="Catatan tambahan">{{ old('deskripsi', $tanaman->deskripsi ?? '') }}</textarea>
</div>
<hr class="my-4">
<h6 class="mb-3 text-primary">
<i class="ti tabler-current-location me-1"></i> Titik Koordinat Tanaman
</h6>
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<label class="form-label text-success fw-bold mb-0">
Klik map untuk menambahkan titik baru
</label>
<small class="text-muted d-block">
Kondisi dan hapus titik bisa dilakukan dari tabel atau popup map.
</small>
</div>
<div class="btn-group" role="group" aria-label="Marker Mode">
<input type="radio" class="btn-check" name="marker_mode" id="marker-mode-plant" value="plant" checked>
<label class="btn btn-sm btn-outline-primary" for="marker-mode-plant">
<i class="ti tabler-plant-2 me-1"></i> Ikon
</label>
<input type="radio" class="btn-check" name="marker_mode" id="marker-mode-dot" value="dot">
<label class="btn btn-sm btn-outline-primary" for="marker-mode-dot">
<i class="ti tabler-point me-1"></i> Titik
</label>
</div>
</div>
<div class="mb-3 form-control-validation">
<input type="hidden" id="points_validation" name="points_validation">
</div>
<div class="points-table-wrapper border rounded">
<table class="table table-sm table-hover mb-0" id="points-table">
<thead>
<tr>
<th style="width: 50px;">No</th>
<th>Latitude</th>
<th>Longitude</th>
<th style="width: 150px;">Kondisi</th>
<th style="width: 60px;" class="text-center">Aksi</th>
</tr>
</thead>
<tbody>
<tr id="empty-points-row">
<td colspan="5" class="text-center text-muted py-4">
Belum ada titik tanaman. Klik area pada peta.
</td>
</tr>
</tbody>
</table>
</div>
<div id="points-hidden-wrapper"></div>
<div class="mt-4 text-end">
<a href="{{ route('map-tanaman.index') }}" class="btn btn-label-secondary me-2">
Batal
</a>
<button type="submit" class="btn btn-primary data-submit">
Simpan Perubahan
</button>
</div>
</div>
<div class="col-lg-7">
<div class="border rounded h-100 map-wrapper">
<div id="map"></div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
@endsection
@push('scripts')
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
'use strict';
$(document).ready(function () {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': "{{ csrf_token() }}"
}
});
const editForm = document.getElementById('editTanamanForm');
const lahanPath = {{ Illuminate\Support\Js::from($tanaman->lahan->path ?? null) }};
let plantPoints = {{ Illuminate\Support\Js::from(
$points->map(function ($point) {
$kondisi = $point->kondisi_tanaman;
$kondisiValue = is_object($kondisi) && property_exists($kondisi, 'value')
? $kondisi->value
: $kondisi;
return [
'id' => $point->id,
'nama' => $point->nama,
'latitude' => $point->latitude,
'longitude' => $point->longitude,
'kondisi_tanaman' => $kondisiValue,
'_delete' => false,
'_new' => false,
];
})->values()
) }};
const map = L.map('map').setView([-2.5489, 118.0149], 5);
L.tileLayer('https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', {
maxZoom: 22,
subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
attribution: '&copy; Google Maps Satellite'
}).addTo(map);
let currentPolygon = null;
let plantMarkers = [];
function showError(xhr, fallback = 'Terjadi kesalahan sistem.') {
let message = fallback;
if (xhr.responseJSON?.message) {
message = xhr.responseJSON.message;
}
if (xhr.responseJSON?.errors) {
message = Object.values(xhr.responseJSON.errors).flat().join('<br>');
}
Swal.fire({
icon: 'error',
title: 'Gagal!',
html: message,
customClass: {
confirmButton: 'btn btn-primary'
},
buttonsStyling: false
});
}
function escapeHtml(value) {
return String(value ?? '')
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;')
.replace(/'/g, '&#039;');
}
function parsePath(path) {
if (!path) return null;
let parsed = path;
if (typeof path === 'string') {
try {
parsed = JSON.parse(path);
} catch (error) {
console.error('Path bukan JSON valid:', error);
return null;
}
}
if (parsed.type && parsed.coordinates) {
return {
type: 'geojson',
data: parsed
};
}
if (Array.isArray(parsed)) {
return {
type: 'array',
data: parsed
};
}
return null;
}
function renderLahanPolygon(path) {
const parsed = parsePath(path);
if (!parsed) return null;
let layer = null;
if (parsed.type === 'geojson') {
layer = L.geoJSON(parsed.data, {
style: {
color: '#28c76f',
fillColor: '#28c76f',
fillOpacity: 0.35,
weight: 2
}
}).addTo(map);
map.fitBounds(layer.getBounds(), { padding: [30, 30] });
}
if (parsed.type === 'array') {
const latLngs = parsed.data.map(coord => [coord[1], coord[0]]);
layer = L.polygon(latLngs, {
color: '#28c76f',
fillColor: '#28c76f',
fillOpacity: 0.35,
weight: 2
}).addTo(map);
map.fitBounds(layer.getBounds(), { padding: [30, 30] });
}
return layer;
}
function getMarkerMode() {
return $('input[name="marker_mode"]:checked').val() || 'plant';
}
function getPlantIcon(point) {
const kondisi = point.kondisi_tanaman || 'sehat';
return L.divIcon({
className: '',
html: `
<div class="plant-marker-icon ${kondisi}">
<i class="ti tabler-plant-2"></i>
</div>
`,
iconSize: [32, 32],
iconAnchor: [16, 16],
popupAnchor: [0, -16]
});
}
function getDotIcon(point) {
const kondisi = point.kondisi_tanaman || 'sehat';
return L.divIcon({
className: '',
html: `<div class="plant-dot-marker ${kondisi}"></div>`,
iconSize: [11, 11],
iconAnchor: [5.5, 5.5],
popupAnchor: [0, -8]
});
}
function formatKondisiLabel(kondisi) {
const labels = {
sehat: 'Sehat',
sakit: 'Sakit',
mati: 'Mati'
};
return labels[kondisi] || '-';
}
function getActivePoints() {
return plantPoints.filter(point => !point._delete);
}
function buildPlantPopup(point, index, visibleIndex) {
const title = point.nama && point.nama !== 'null'
? point.nama
: `Titik Tanaman ${visibleIndex + 1}`;
return `
<div class="plant-popup-card">
<div class="plant-popup-header">
<p class="plant-popup-title">${escapeHtml(title)}</p>
<div class="plant-popup-subtitle">Titik Tanaman ${visibleIndex + 1}</div>
</div>
<div class="plant-popup-body">
<div class="plant-popup-row">
<span class="plant-popup-label">Latitude</span>
<span class="plant-popup-value">${point.latitude}</span>
</div>
<div class="plant-popup-row">
<span class="plant-popup-label">Longitude</span>
<span class="plant-popup-value">${point.longitude}</span>
</div>
<div class="mb-2">
<label class="form-label mb-1" style="font-size: .75rem;">Kondisi Tanaman</label>
<select class="form-select form-select-sm popup-point-condition" data-index="${index}">
<option value="sehat" ${point.kondisi_tanaman === 'sehat' ? 'selected' : ''}>Sehat</option>
<option value="sakit" ${point.kondisi_tanaman === 'sakit' ? 'selected' : ''}>Sakit</option>
<option value="mati" ${point.kondisi_tanaman === 'mati' ? 'selected' : ''}>Mati</option>
</select>
</div>
<div class="plant-popup-actions">
<button type="button" class="btn btn-sm btn-label-danger popup-remove-point" data-index="${index}">
<i class="ti tabler-trash me-1"></i> Hapus Titik
</button>
</div>
<div class="plant-popup-help">
Perubahan akan tersimpan setelah klik <strong>Simpan Perubahan</strong>.
</div>
</div>
</div>
`;
}
function renderPointsTable() {
const $tbody = $('#points-table tbody');
const $hiddenWrapper = $('#points-hidden-wrapper');
$tbody.empty();
$hiddenWrapper.empty();
const activePoints = getActivePoints();
if (!activePoints.length) {
$tbody.append(`
<tr id="empty-points-row">
<td colspan="5" class="text-center text-muted py-4">
Belum ada titik tanaman. Klik area pada peta.
</td>
</tr>
`);
$('#points_validation').val('');
} else {
$('#points_validation').val('filled');
}
let visibleNumber = 1;
plantPoints.forEach((point, index) => {
const prefix = `points[${index}]`;
$hiddenWrapper.append(`
<input type="hidden" name="${prefix}[id]" value="${point.id || ''}">
<input type="hidden" name="${prefix}[latitude]" value="${point.latitude}">
<input type="hidden" name="${prefix}[longitude]" value="${point.longitude}">
<input type="hidden" name="${prefix}[kondisi_tanaman]" value="${point.kondisi_tanaman}">
<input type="hidden" name="${prefix}[_delete]" value="${point._delete ? 1 : 0}">
`);
if (point._delete) return;
$tbody.append(`
<tr>
<td>${visibleNumber}</td>
<td>${point.latitude}</td>
<td>${point.longitude}</td>
<td>
<select class="form-select form-select-sm point-condition" data-index="${index}">
<option value="sehat" ${point.kondisi_tanaman === 'sehat' ? 'selected' : ''}>Sehat</option>
<option value="sakit" ${point.kondisi_tanaman === 'sakit' ? 'selected' : ''}>Sakit</option>
<option value="mati" ${point.kondisi_tanaman === 'mati' ? 'selected' : ''}>Mati</option>
</select>
</td>
<td class="text-center">
<button type="button" class="btn btn-sm btn-icon btn-label-danger remove-point" data-index="${index}">
<i class="ti tabler-trash"></i>
</button>
</td>
</tr>
`);
visibleNumber++;
});
}
function refreshPlantMarkers() {
plantMarkers.forEach(marker => map.removeLayer(marker));
plantMarkers = [];
const mode = getMarkerMode();
let visibleIndex = 0;
plantPoints.forEach((point, index) => {
if (point._delete) return;
const icon = mode === 'dot'
? getDotIcon(point)
: getPlantIcon(point);
const marker = L.marker([point.latitude, point.longitude], {
icon: icon
}).addTo(map)
.bindPopup(buildPlantPopup(point, index, visibleIndex), {
className: 'custom-plant-popup',
closeButton: true,
maxWidth: 280
});
plantMarkers.push(marker);
visibleIndex++;
});
}
function refreshField(name) {
if (typeof fvEdit !== 'undefined' && name) {
fvEdit.revalidateField(name);
}
}
function refreshAll() {
renderPointsTable();
refreshPlantMarkers();
refreshField('points_validation');
}
currentPolygon = renderLahanPolygon(lahanPath);
renderPointsTable();
refreshPlantMarkers();
const activePoints = getActivePoints();
if (activePoints.length > 0) {
const bounds = L.latLngBounds(
activePoints
.filter(point => point.latitude && point.longitude)
.map(point => [point.latitude, point.longitude])
);
if (bounds.isValid()) {
map.fitBounds(bounds, { padding: [40, 40] });
}
}
map.on('click', function (e) {
const lat = e.latlng.lat.toFixed(8);
const lng = e.latlng.lng.toFixed(8);
plantPoints.push({
id: '',
nama: 'Titik Baru',
latitude: lat,
longitude: lng,
kondisi_tanaman: 'sehat',
_delete: false,
_new: true,
});
refreshAll();
});
$(document).on('change', '.point-condition', function () {
const index = $(this).data('index');
const value = $(this).val();
if (plantPoints[index]) {
plantPoints[index].kondisi_tanaman = value;
}
refreshAll();
});
$(document).on('change', '.popup-point-condition', function () {
const index = $(this).data('index');
const value = $(this).val();
if (plantPoints[index]) {
plantPoints[index].kondisi_tanaman = value;
}
map.closePopup();
refreshAll();
});
$(document).on('click', '.remove-point', function () {
const index = $(this).data('index');
if (!plantPoints[index]) return;
if (plantPoints[index].id) {
plantPoints[index]._delete = true;
} else {
plantPoints.splice(index, 1);
}
refreshAll();
});
$(document).on('click', '.popup-remove-point', function () {
const index = $(this).data('index');
if (!plantPoints[index]) return;
Swal.fire({
title: 'Hapus titik ini?',
text: 'Titik tanaman akan dihapus setelah kamu klik Simpan Perubahan.',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus',
cancelButtonText: 'Batal',
customClass: {
confirmButton: 'btn btn-primary me-3',
cancelButton: 'btn btn-label-secondary'
},
buttonsStyling: false
}).then(function (result) {
if (!result.isConfirmed) return;
if (plantPoints[index].id) {
plantPoints[index]._delete = true;
} else {
plantPoints.splice(index, 1);
}
map.closePopup();
refreshAll();
});
});
$(document).on('change', 'input[name="marker_mode"]', function () {
refreshPlantMarkers();
});
let fvEdit = FormValidation.formValidation(editForm, {
fields: {
points_validation: {
validators: {
callback: {
message: 'Minimal harus ada satu titik tanaman aktif.',
callback: function () {
return getActivePoints().length > 0;
}
}
}
},
},
plugins: {
trigger: new FormValidation.plugins.Trigger(),
bootstrap5: new FormValidation.plugins.Bootstrap5({
eleValidClass: '',
rowSelector: '.form-control-validation'
}),
submitButton: new FormValidation.plugins.SubmitButton(),
}
}).on('core.form.valid', function () {
const formData = $(editForm).serialize();
const $button = $(editForm).find('button[type="submit"]');
$button.prop('disabled', true)
.html('<span class="spinner-border spinner-border-sm me-1"></span>Menyimpan...');
$.ajax({
url: editForm.action,
type: 'POST',
data: formData,
success: function (res) {
Swal.fire({
icon: 'success',
title: 'Berhasil!',
text: res.message || 'Data titik tanaman berhasil diperbarui.',
timer: 1500,
showConfirmButton: false
}).then(() => {
window.location.href = res.redirect || "{{ route('map-tanaman.index') }}";
});
},
error: function (xhr) {
showError(xhr, 'Gagal memperbarui data tanaman.');
},
complete: function () {
$button.prop('disabled', false).html('Simpan Perubahan');
}
});
});
setTimeout(() => map.invalidateSize(), 300);
});
</script>
@endpush
@@ -0,0 +1,230 @@
@extends('layout.main')
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="card">
<div class="card-body border-bottom">
<form method="GET" action="{{ route('tanaman.index') }}" class="d-flex justify-content-between align-items-center row gap-3 gap-md-0">
<div class="col-md-auto">
<div class="d-flex align-items-center">
<label class="me-2 text-nowrap">Show</label>
<select name="per_page" class="form-select form-select-sm w-auto" onchange="this.form.submit()">
@foreach([10, 25, 50, 100] as $limit)
<option value="{{ $limit }}" {{ request('per_page', 10) == $limit ? 'selected' : '' }}>
{{ $limit }}
</option>
@endforeach
</select>
<label class="ms-2 text-nowrap">entries</label>
</div>
</div>
<div class="col-md-auto d-flex gap-2">
<div class="input-group input-group-sm w-auto">
<input type="text" name="search" class="form-control" placeholder="Cari tanaman..." value="{{ request('search') }}">
@if(request('search'))
<a href="{{ route('tanaman.index', ['per_page' => request('per_page', 10)]) }}" class="btn btn-outline-secondary">
<i class="ti tabler-x"></i>
</a>
@endif
<button type="submit" class="btn btn-outline-primary">
<i class="ti tabler-search"></i>
</button>
</div>
<a href="{{ route('map-tanaman.create') }}" class="btn btn-sm btn-primary">
<i class="ti tabler-plus me-1"></i> Tambah
</a>
</div>
</form>
</div>
<div class="table-responsive text-nowrap">
<table class="table table-hover">
<thead class="border-top">
<tr>
<th>No</th>
<th>Kelompok Tanaman</th>
<th>Pemilik</th>
<th>Lahan</th>
<th>Wilayah</th>
<th>Varietas</th>
<th>Jumlah Titik</th>
<th>Kondisi</th>
<th class="text-center">Actions</th>
</tr>
</thead>
<tbody>
@forelse($tanaman as $index => $item)
@php
$states = ['success', 'danger', 'warning', 'info', 'primary', 'secondary'];
$state = $states[$item->id % count($states)];
$groupName = ($item->komoditas->nama ?? 'Tanaman') . ' - ' . ($item->profile->nama ?? 'Pemilik');
@endphp
<tr>
<td>{{ $tanaman->firstItem() + $index }}</td>
<td>
<div class="d-flex align-items-center">
<div class="avatar-wrapper me-3">
<div class="avatar avatar-sm">
<span class="avatar-initial rounded-circle bg-label-{{ $state }}">
{{ strtoupper(substr($item->komoditas->nama ?? 'TN', 0, 2)) }}
</span>
</div>
</div>
<div class="d-flex flex-column">
<span class="fw-medium text-heading">{{ $groupName }}</span>
<small class="text-muted">
{{ $item->jenisKomoditas->nama ?? '-' }}
</small>
</div>
</div>
</td>
<td>{{ $item->profile->nama ?? '-' }}</td>
<td>
<div class="d-flex flex-column">
<span>{{ $item->lahan->nama ?? '-' }}</span>
<small class="text-muted">{{ $item->lahan->uid ?? '-' }}</small>
</div>
</td>
<td>
<small>
{{ $item->lahan->desaKelurahan->nama ?? '-' }}<br>
<span class="text-muted">
{{ $item->lahan->desaKelurahan->kecamatan->nama ?? '-' }}
</span>
</small>
</td>
<td>{{ $item->varietas->nama ?? '-' }}</td>
<td>
<span class="badge bg-label-primary">
{{ $item->total_tanaman }} titik
</span>
</td>
<td>
<div class="d-flex flex-column gap-1">
<span class="badge bg-label-success">
Sehat: {{ $item->total_sehat ?? 0 }}
</span>
<span class="badge bg-label-danger">
Sakit: {{ $item->total_sakit ?? 0 }}
</span>
<span class="badge bg-label-dark">
Mati: {{ $item->total_mati ?? 0 }}
</span>
</div>
</td>
<td>
<div class="d-flex justify-content-center align-items-center">
<a href="{{ route('map-tanaman.show', $item->id) }}"
class="btn btn-icon btn-text-secondary rounded-pill"
title="Lihat">
<i class="ti tabler-eye ti-md"></i>
</a>
<a href="{{ route('map-tanaman.edit', $item->id) }}"
class="btn btn-icon btn-text-secondary rounded-pill"
title="Edit">
<i class="ti tabler-edit ti-md"></i>
</a>
<button
type="button"
class="btn btn-icon btn-text-secondary rounded-pill delete-group"
data-id="{{ $item->id }}"
data-nama="{{ $groupName }}"
title="Hapus">
<i class="ti tabler-trash ti-md"></i>
</button>
</div>
</td>
</tr>
@empty
<tr>
<td colspan="9" class="text-center py-4">
Tidak ada data ditemukan.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
@if($tanaman->hasPages())
<div class="card-footer d-flex justify-content-center justify-content-md-end border-top">
{{ $tanaman->appends(request()->query())->links('pagination::bootstrap-5') }}
</div>
@endif
</div>
</div>
@endsection
@push('scripts')
<script>
'use strict';
$(function () {
$.ajaxSetup({
headers: {
'X-CSRF-TOKEN': "{{ csrf_token() }}"
}
});
$(document).on('click', '.delete-group', function () {
const id = $(this).data('id');
const nama = $(this).data('nama');
Swal.fire({
title: 'Apakah Anda yakin?',
text: `Semua titik tanaman pada kelompok "${nama}" akan dihapus.`,
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal',
customClass: {
confirmButton: 'btn btn-primary me-3',
cancelButton: 'btn btn-label-secondary'
},
buttonsStyling: false
}).then(function (result) {
if (!result.isConfirmed) return;
$.ajax({
url: `/map-tanaman/${id}`,
type: 'DELETE',
success: function (res) {
Swal.fire({
icon: 'success',
title: 'Terhapus!',
text: res.message || 'Data berhasil dihapus.',
showConfirmButton: false,
timer: 1500
}).then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({
icon: 'error',
title: 'Gagal!',
text: xhr.responseJSON?.message || 'Gagal menghapus data.'
});
}
});
});
});
});
</script>
@endpush
@@ -0,0 +1,698 @@
@extends('layout.main')
@push('style')
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<style>
#map {
height: 560px;
width: 100%;
background-color: var(--bs-body-bg);
z-index: 1;
border-radius: 0.375rem;
}
.detail-label {
font-size: 0.8125rem;
color: var(--bs-secondary-color);
margin-bottom: 0.25rem;
}
.detail-value {
font-weight: 500;
color: var(--bs-heading-color);
margin-bottom: 1rem;
word-break: break-word;
}
.detail-section {
padding-bottom: .75rem;
margin-bottom: .75rem;
border-bottom: 1px solid var(--bs-border-color);
}
.detail-section:last-child {
border-bottom: 0;
margin-bottom: 0;
padding-bottom: 0;
}
.map-note {
background-color: rgba(var(--bs-info-rgb), 0.16);
color: var(--bs-info);
border: 1px solid rgba(var(--bs-info-rgb), 0.25);
}
.points-table-wrapper {
max-height: 280px;
overflow-y: auto;
}
.leaflet-popup-content {
color: #2f2b3d;
}
.plant-marker-icon {
width: 32px;
height: 32px;
border-radius: 50%;
background: rgba(var(--bs-success-rgb), 0.95);
border: 2px solid #fff;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.35);
display: flex;
align-items: center;
justify-content: center;
color: #fff;
font-size: 18px;
}
.plant-marker-icon.sakit {
background: rgba(var(--bs-danger-rgb), 0.95);
}
.plant-marker-icon.mati {
background: rgba(var(--bs-dark-rgb), 0.95);
}
.plant-dot-marker {
width: 11px;
height: 11px;
border-radius: 50%;
background: rgba(var(--bs-success-rgb), 0.95);
border: 2px solid #fff;
box-shadow: 0 1px 5px rgba(0, 0, 0, 0.45);
}
.plant-dot-marker.sakit {
background: rgba(var(--bs-danger-rgb), 0.95);
}
.plant-dot-marker.mati {
background: rgba(var(--bs-dark-rgb), 0.95);
}
.custom-plant-popup .leaflet-popup-content-wrapper {
border-radius: 14px;
padding: 0;
overflow: hidden;
background: var(--bs-card-bg);
color: var(--bs-body-color);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
}
.custom-plant-popup .leaflet-popup-content {
margin: 0;
min-width: 220px;
}
.custom-plant-popup .leaflet-popup-tip {
background: var(--bs-card-bg);
}
.plant-popup-card {
overflow: hidden;
}
.plant-popup-header {
padding: 10px 12px;
background: rgba(var(--bs-primary-rgb), 0.14);
border-bottom: 1px solid var(--bs-border-color);
}
.plant-popup-title {
font-size: 0.875rem;
font-weight: 600;
color: var(--bs-heading-color);
margin: 0;
line-height: 1.3;
}
.plant-popup-subtitle {
font-size: 0.75rem;
color: var(--bs-secondary-color);
margin-top: 2px;
}
.plant-popup-body {
padding: 10px 12px 12px;
}
.plant-popup-row {
display: flex;
justify-content: space-between;
gap: 12px;
font-size: 0.75rem;
margin-bottom: 6px;
}
.plant-popup-row:last-child {
margin-bottom: 0;
}
.plant-popup-label {
color: var(--bs-secondary-color);
}
.plant-popup-value {
color: var(--bs-heading-color);
font-weight: 500;
text-align: right;
}
.plant-popup-badge {
display: inline-flex;
align-items: center;
padding: 3px 8px;
border-radius: 999px;
font-size: 0.7rem;
font-weight: 600;
}
.plant-popup-badge.sehat {
background: rgba(var(--bs-success-rgb), 0.16);
color: var(--bs-success);
}
.plant-popup-badge.sakit {
background: rgba(var(--bs-danger-rgb), 0.16);
color: var(--bs-danger);
}
.plant-popup-badge.mati {
background: rgba(var(--bs-dark-rgb), 0.16);
color: var(--bs-dark);
}
</style>
@endpush
@section('content')
@php
$totalTanaman = $points->count();
$totalSehat = $points->filter(function ($point) {
$kondisi = $point->kondisi_tanaman;
$value = is_object($kondisi) && property_exists($kondisi, 'value') ? $kondisi->value : $kondisi;
return $value === 'sehat';
})->count();
$totalSakit = $points->filter(function ($point) {
$kondisi = $point->kondisi_tanaman;
$value = is_object($kondisi) && property_exists($kondisi, 'value') ? $kondisi->value : $kondisi;
return $value === 'sakit';
})->count();
$totalMati = $points->filter(function ($point) {
$kondisi = $point->kondisi_tanaman;
$value = is_object($kondisi) && property_exists($kondisi, 'value') ? $kondisi->value : $kondisi;
return $value === 'mati';
})->count();
$groupName = ($tanaman->komoditas->nama ?? 'Tanaman') . ' - ' . ($tanaman->profile->nama ?? 'Pemilik');
@endphp
<div class="container-xxl flex-grow-1 container-p-y">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h4 class="mb-1">Detail Kelompok Tanaman</h4>
<small class="text-muted">{{ $groupName }}</small>
</div>
<div class="d-flex gap-2">
<a href="{{ route('map-tanaman.index') }}" class="btn btn-label-secondary">
<i class="ti tabler-arrow-left me-1"></i> Kembali
</a>
<a href="{{ route('map-tanaman.edit', $tanaman->id) }}" class="btn btn-primary">
<i class="ti tabler-edit me-1"></i> Edit
</a>
</div>
</div>
<div class="row">
{{-- DETAIL DATA --}}
<div class="col-lg-5 mb-4">
<div class="card mb-4">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">
<i class="ti tabler-seeding me-1"></i> Ringkasan Kelompok
</h5>
</div>
<div class="card-body pt-4">
<div class="detail-section">
<div class="detail-label">Kelompok Tanaman</div>
<div class="detail-value">{{ $groupName }}</div>
</div>
<div class="detail-section">
<div class="detail-label">Jumlah Titik Tanaman</div>
<div class="detail-value">
<span class="badge bg-label-primary">{{ $totalTanaman }} titik</span>
</div>
</div>
<div class="detail-section">
<div class="detail-label">Ringkasan Kondisi</div>
<div class="d-flex flex-wrap gap-2">
<span class="badge bg-label-success">Sehat: {{ $totalSehat }}</span>
<span class="badge bg-label-danger">Sakit: {{ $totalSakit }}</span>
<span class="badge bg-label-dark">Mati: {{ $totalMati }}</span>
</div>
</div>
<div class="detail-section">
<div class="detail-label">Deskripsi</div>
<div class="detail-value">{{ $tanaman->deskripsi ?? '-' }}</div>
</div>
<div class="detail-section">
<div class="detail-label">Metode Pengumpulan</div>
<div class="detail-value">{{ $tanaman->metode_pengumpulan ?? '-' }}</div>
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">
<i class="ti tabler-user-pin me-1"></i> Pemilik & Lahan
</h5>
</div>
<div class="card-body pt-4">
<div class="detail-section">
<div class="detail-label">Pemilik/Pengelola</div>
<div class="detail-value">{{ $tanaman->profile->nama ?? '-' }}</div>
</div>
<div class="detail-section">
<div class="detail-label">Lahan</div>
<div class="detail-value">{{ $tanaman->lahan->nama ?? '-' }}</div>
</div>
<div class="detail-section">
<div class="detail-label">UID Lahan</div>
<div class="detail-value">{{ $tanaman->lahan->uid ?? '-' }}</div>
</div>
</div>
</div>
<div class="card mb-4">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">
<i class="ti tabler-map-pin me-1"></i> Wilayah
</h5>
</div>
<div class="card-body pt-4">
<div class="detail-section">
<div class="detail-label">Provinsi</div>
<div class="detail-value">
{{ $tanaman->lahan->desaKelurahan->kecamatan->kabupatenkota->provinsi->nama ?? '-' }}
</div>
</div>
<div class="detail-section">
<div class="detail-label">Kabupaten/Kota</div>
<div class="detail-value">
{{ $tanaman->lahan->desaKelurahan->kecamatan->kabupatenkota->nama ?? '-' }}
</div>
</div>
<div class="detail-section">
<div class="detail-label">Kecamatan</div>
<div class="detail-value">
{{ $tanaman->lahan->desaKelurahan->kecamatan->nama ?? '-' }}
</div>
</div>
<div class="detail-section">
<div class="detail-label">Desa/Kelurahan</div>
<div class="detail-value">
{{ $tanaman->lahan->desaKelurahan->nama ?? '-' }}
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">
<i class="ti tabler-leaf me-1"></i> Komoditas
</h5>
</div>
<div class="card-body pt-4">
<div class="detail-section">
<div class="detail-label">Komoditas</div>
<div class="detail-value">{{ $tanaman->komoditas->nama ?? '-' }}</div>
</div>
<div class="detail-section">
<div class="detail-label">Jenis Komoditas</div>
<div class="detail-value">{{ $tanaman->jenisKomoditas->nama ?? '-' }}</div>
</div>
<div class="detail-section">
<div class="detail-label">Varietas</div>
<div class="detail-value">{{ $tanaman->varietas->nama ?? '-' }}</div>
</div>
</div>
</div>
</div>
{{-- MAP --}}
<div class="col-lg-7 mb-4">
<div class="card mb-4">
<div class="card-header border-bottom d-flex justify-content-between align-items-center">
<h5 class="card-title mb-0">
<i class="ti tabler-map-pin me-1"></i> Lokasi Semua Titik Tanaman
</h5>
<div class="btn-group" role="group" aria-label="Marker Mode">
<input type="radio" class="btn-check" name="marker_mode" id="marker-mode-plant" value="plant" checked>
<label class="btn btn-sm btn-outline-primary" for="marker-mode-plant">
<i class="ti tabler-plant-2 me-1"></i> Ikon
</label>
<input type="radio" class="btn-check" name="marker_mode" id="marker-mode-dot" value="dot">
<label class="btn btn-sm btn-outline-primary" for="marker-mode-dot">
<i class="ti tabler-point me-1"></i> Titik
</label>
</div>
</div>
<div class="card-body pt-4">
<div id="map"></div>
<div class="alert map-note mt-3 mb-0">
<i class="ti tabler-info-circle me-1"></i>
Area hijau menunjukkan polygon lahan. Marker menunjukkan seluruh titik tanaman dalam kelompok ini.
</div>
</div>
</div>
<div class="card">
<div class="card-header border-bottom">
<h5 class="card-title mb-0">
<i class="ti tabler-list-details me-1"></i> Daftar Titik Tanaman
</h5>
</div>
<div class="card-body pt-4">
<div class="points-table-wrapper border rounded">
<table class="table table-sm table-hover mb-0">
<thead>
<tr>
<th style="width: 50px;">No</th>
<th>Nama</th>
<th>Latitude</th>
<th>Longitude</th>
<th>Kondisi</th>
</tr>
</thead>
<tbody>
@forelse($points as $index => $point)
@php
$kondisi = $point->kondisi_tanaman;
$kondisiValue = is_object($kondisi) && property_exists($kondisi, 'value')
? $kondisi->value
: $kondisi;
$conditionClass = match($kondisiValue) {
'sehat' => 'success',
'sakit' => 'danger',
'mati' => 'dark',
default => 'secondary',
};
@endphp
<tr>
<td>{{ $index + 1 }}</td>
<td>
<div class="fw-medium">{{ $point->nama ?? '-' }}</div>
<small class="text-muted">{{ $point->uid ?? '-' }}</small>
</td>
<td>{{ $point->latitude ?? '-' }}</td>
<td>{{ $point->longitude ?? '-' }}</td>
<td>
<span class="badge bg-label-{{ $conditionClass }}">
{{ $kondisiValue ? ucfirst(str_replace('_', ' ', $kondisiValue)) : '-' }}
</span>
</td>
</tr>
@empty
<tr>
<td colspan="5" class="text-center text-muted py-4">
Belum ada titik tanaman.
</td>
</tr>
@endforelse
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div>
@endsection
@push('scripts')
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<script>
'use strict';
$(document).ready(function () {
const lahanPath = {{ Illuminate\Support\Js::from($tanaman->lahan->path ?? null) }};
const points = {{ Illuminate\Support\Js::from(
$points->map(function ($point) {
$kondisi = $point->kondisi_tanaman;
$kondisiValue = is_object($kondisi) && property_exists($kondisi, 'value')
? $kondisi->value
: $kondisi;
return [
'id' => $point->id,
'uid' => $point->uid,
'nama' => $point->nama,
'latitude' => $point->latitude,
'longitude' => $point->longitude,
'kondisi_tanaman' => $kondisiValue,
];
})->values()
) }};
const map = L.map('map').setView([-2.5489, 118.0149], 5);
L.tileLayer('https://{s}.google.com/vt/lyrs=s,h&x={x}&y={y}&z={z}', {
maxZoom: 22,
subdomains: ['mt0', 'mt1', 'mt2', 'mt3'],
attribution: '&copy; Google Maps Satellite'
}).addTo(map);
let pointMarkers = [];
function parsePath(path) {
if (!path) return null;
let parsed = path;
if (typeof path === 'string') {
try {
parsed = JSON.parse(path);
} catch (error) {
console.error('Path bukan JSON valid:', error);
return null;
}
}
if (parsed.type && parsed.coordinates) {
return {
type: 'geojson',
data: parsed
};
}
if (Array.isArray(parsed)) {
return {
type: 'array',
data: parsed
};
}
return null;
}
function renderLahanPolygon(path) {
const parsed = parsePath(path);
if (!parsed) {
console.warn('Format koordinat lahan tidak valid atau kosong.');
return null;
}
let layer = null;
if (parsed.type === 'geojson') {
layer = L.geoJSON(parsed.data, {
style: {
color: '#28c76f',
fillColor: '#28c76f',
fillOpacity: 0.35,
weight: 2
}
}).addTo(map);
map.fitBounds(layer.getBounds(), { padding: [30, 30] });
}
if (parsed.type === 'array') {
const latLngs = parsed.data.map(coord => [coord[1], coord[0]]);
layer = L.polygon(latLngs, {
color: '#28c76f',
fillColor: '#28c76f',
fillOpacity: 0.35,
weight: 2
}).addTo(map);
map.fitBounds(layer.getBounds(), { padding: [30, 30] });
}
return layer;
}
function getMarkerMode() {
return $('input[name="marker_mode"]:checked').val() || 'plant';
}
function getPlantIcon(point) {
const kondisi = point.kondisi_tanaman || 'sehat';
return L.divIcon({
className: '',
html: `
<div class="plant-marker-icon ${kondisi}">
<i class="ti tabler-plant-2"></i>
</div>
`,
iconSize: [32, 32],
iconAnchor: [16, 16],
popupAnchor: [0, -16]
});
}
function getDotIcon(point) {
const kondisi = point.kondisi_tanaman || 'sehat';
return L.divIcon({
className: '',
html: `<div class="plant-dot-marker ${kondisi}"></div>`,
iconSize: [11, 11],
iconAnchor: [5.5, 5.5],
popupAnchor: [0, -8]
});
}
function formatKondisiLabel(kondisi) {
const labels = {
sehat: 'Sehat',
sakit: 'Sakit',
mati: 'Mati'
};
return labels[kondisi] || '-';
}
function renderPointMarkers() {
pointMarkers.forEach(marker => map.removeLayer(marker));
pointMarkers = [];
const mode = getMarkerMode();
points.forEach((point, index) => {
if (!point.latitude || !point.longitude) return;
const icon = mode === 'dot'
? getDotIcon(point)
: getPlantIcon(point);
const popupContent = `
<div class="plant-popup-card">
<div class="plant-popup-header">
<p class="plant-popup-title">
${point.nama || 'Titik Tanaman ' + (index + 1)}
</p>
<div class="plant-popup-subtitle">
Titik Tanaman ${index + 1}
</div>
</div>
<div class="plant-popup-body">
<div class="plant-popup-row">
<span class="plant-popup-label">Latitude</span>
<span class="plant-popup-value">${point.latitude}</span>
</div>
<div class="plant-popup-row">
<span class="plant-popup-label">Longitude</span>
<span class="plant-popup-value">${point.longitude}</span>
</div>
<div class="plant-popup-row">
<span class="plant-popup-label">Kondisi</span>
<span class="plant-popup-value">
<span class="plant-popup-badge ${point.kondisi_tanaman || 'sehat'}">
${formatKondisiLabel(point.kondisi_tanaman)}
</span>
</span>
</div>
</div>
</div>
`;
const marker = L.marker([point.latitude, point.longitude], {
icon: icon
}).addTo(map)
.bindPopup(popupContent, {
className: 'custom-plant-popup',
closeButton: true,
maxWidth: 260
});
pointMarkers.push(marker);
});
}
const lahanLayer = renderLahanPolygon(lahanPath);
renderPointMarkers();
if (points.length > 0) {
const bounds = L.latLngBounds(
points
.filter(point => point.latitude && point.longitude)
.map(point => [point.latitude, point.longitude])
);
if (bounds.isValid()) {
map.fitBounds(bounds, { padding: [40, 40] });
}
} else if (lahanLayer) {
map.fitBounds(lahanLayer.getBounds(), { padding: [30, 30] });
}
$(document).on('change', 'input[name="marker_mode"]', function () {
renderPointMarkers();
});
setTimeout(function () {
map.invalidateSize();
}, 300);
});
</script>
@endpush
@@ -12,10 +12,12 @@
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="add-new-aktivitas pt-0" id="addNewAktivitasForm" action="{{ route('framework.transformasi.aktivitas.store', [$framework->id, ':transformasiId']) }}" method="POST">
<form class="pt-0" id="addNewAktivitasForm" action="{{ route('framework.transformasi.aktivitas.store', [$framework->id, ':transformasiId']) }}" method="POST">
@csrf
<input type="hidden" id="add-aktivitas-transformasi-id" name="transformasi_id"/>
<input type="hidden" id="add-aktivitas-jenis" name="jenis_aktivitas" value="aktivitas"/>
<input type="hidden" id="add-aktivitas-parent_id" name="parent_id"/>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-aktivitas-kode">Kode<span class="text-danger">*</span></label>
@@ -25,13 +27,6 @@
<label class="form-label" for="add-aktivitas-nama">Aktivitas<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-aktivitas-nama" name="nama" placeholder="Cth: Aktivitas" />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-aktivitas-jenis">Jenis Aktivitas<span class="text-danger">*</span></label>
<select class="form-select" id="add-aktivitas-jenis" name="jenis_aktivitas">
<option value="tujuan">Tujuan Jangka Pendek</option>
<option value="aktivitas">Aktivitas</option>
</select>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-aktivitas-indikator">Indikator<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-aktivitas-indikator" name="indikator" placeholder="Cth: Indikator" />
@@ -54,7 +49,15 @@
$(".add-aktivitas").click(function () {
const id = $(this).data('id');
console.log('Add aktivitas, transformasi id: ' + id);
const parentId = $(this).data('parent-id');
$('#add-aktivitas-transformasi-id').val(id);
console.log('Add aktivitas, parent id: ' + parentId);
$('#add-aktivitas-parent_id').val(parentId);
const addForm = $('#addNewAktivitasForm');
addForm.validate().resetForm();
addForm.find('.is-invalid').removeClass('is-invalid');
offcanvasAddAktivitas.show();
});
$('#addNewAktivitasForm').validate({
@@ -0,0 +1,106 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasAddTujuan"
aria-labelledby="offcanvasAddTujuanLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasAddTujuanLabel" class="offcanvas-title">Tambah Tujuan</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="pt-0" id="addNewTujuanForm" action="{{ route('framework.transformasi.aktivitas.store', [$framework->id, ':transformasiId']) }}" method="POST">
@csrf
<input type="hidden" id="add-tujuan-transformasi-id" name="transformasi_id"/>
<input type="hidden" id="add-tujuan-jenis" name="jenis_aktivitas" value="tujuan"/>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-tujuan-kode">Kode<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-tujuan-kode" name="kode" placeholder="Cth: AK1" />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-tujuan-nama">Tujuan Jangka Pendek<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-tujuan-nama" name="nama" placeholder="Cth: Tujuan Jangka Pendek" />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-tujuan-indikator">Indikator<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-tujuan-indikator" name="indikator" placeholder="Cth: Indikator" />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-tujuan-deskripsi">Deskripsi<span class="text-danger">*</span></label>
<textarea class="form-control" id="add-tujuan-deskripsi" name="deskripsi" placeholder="Cth: Deskripsi"></textarea>
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasAddTujuan = new bootstrap.Offcanvas(document.getElementById('offcanvasAddTujuan'));
$(".add-tujuan").click(function () {
const id = $(this).data('id');
console.log('Add tujuan, transformasi id: ' + id);
$('#add-aktivitas-transformasi-id').val(id);
const addForm = $('#addNewTujuanForm');
addForm.validate().resetForm();
addForm.find('.is-invalid').removeClass('is-invalid');
// $.get("{{ route('framework.transformasi.show', [$framework->id, ':id']) }}".replace(':id', id), function (res) {
// if (res.status === 'success') {
// for(let i = 0; i < res.data.aktivitas.length; i++) {
// $('#add-aktivitas-parent_id').append(`<option value="${res.data.aktivitas[i].id}">${res.data.aktivitas[i].nama}</option>`);
// }
// }
// });
offcanvasAddTujuan.show();
});
$('#addNewTujuanForm').validate({
rules: {
kode: { required: true },
nama: { required: true }
},
messages: {
kode: { required: "Kode tujuan wajib diisi" },
nama: { required: "Tujuan wajib diisi" }
},
submitHandler: function (form) {
$.ajax({
url: $(form).attr('action').replace(':transformasiId', $('#add-aktivitas-transformasi-id').val()),
type: 'POST',
data: $(form).serialize(),
success: function () {
offcanvasAddTujuan.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -18,6 +18,8 @@
<input type="hidden" id="update-aktivitas-transformasi-id" name="transformasi_id"/>
<input type="hidden" id="update-aktivitas-id" name="id"/>
<input type="hidden" id="update-aktivitas-jenis" name="jenis_aktivitas" value="aktivitas"/>
<input type="hidden" id="update-aktivitas-parent_id" name="parent_id"/>
<div class="mb-6 form-control-validation">
<label class="form-label" for="update-aktivitas-kode">Kode<span class="text-danger">*</span></label>
@@ -27,13 +29,6 @@
<label class="form-label" for="update-aktivitas-nama">Aktivitas<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="update-aktivitas-nama" name="nama" placeholder="Cth: Aktivitas" />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="update-aktivitas-jenis">Jenis Aktivitas<span class="text-danger">*</span></label>
<select class="form-select" id="update-aktivitas-jenis" name="jenis_aktivitas">
<option value="tujuan">Tujuan Jangka Pendek</option>
<option value="aktivitas">Aktivitas</option>
</select>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="update-aktivitas-indikator">Indikator<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="update-aktivitas-indikator" name="indikator" placeholder="Cth: Indikator" />
@@ -72,6 +67,7 @@
$('#update-aktivitas-jenis').val(res.data.jenis_aktivitas);
$('#update-aktivitas-indikator').val(res.data.indikator);
$('#update-aktivitas-deskripsi').val(res.data.deskripsi);
$('#update-aktivitas-parent_id').val(res.data.parent_id);
offcanvasUpdateAktivitas.show();
}
});
@@ -0,0 +1,112 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasUpdateTujuan"
aria-labelledby="offcanvasUpdateTujuanLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasUpdateTujuanLabel" class="offcanvas-title">Update Tujuan</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="pt-0" id="updateTujuanForm" action="{{ route('framework.transformasi.aktivitas.update', [$framework->id, ':transformasiId', ':id']) }}" method="POST">
@csrf
@method('PUT')
<input type="hidden" id="update-tujuan-transformasi-id" name="transformasi_id"/>
<input type="hidden" id="update-tujuan-id" name="id"/>
<input type="hidden" id="update-tujuan-jenis" name="jenis_aktivitas" value="tujuan"/>
<div class="mb-6 form-control-validation">
<label class="form-label" for="update-tujuan-kode">Kode<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="update-tujuan-kode" name="kode" placeholder="Cth: AK1" />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="update-tujuan-nama">Aktivitas<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="update-tujuan-nama" name="nama" placeholder="Cth: Aktivitas" />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="update-tujuan-indikator">Indikator<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="update-tujuan-indikator" name="indikator" placeholder="Cth: Indikator" />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="update-tujuan-deskripsi">Deskripsi<span class="text-danger">*</span></label>
<textarea class="form-control" id="update-tujuan-deskripsi" name="deskripsi" placeholder="Cth: Deskripsi"></textarea>
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasUpdateTujuan = new bootstrap.Offcanvas(document.getElementById('offcanvasUpdateTujuan'));
$(".edit-tujuan").click(function () {
const id = $(this).data('id');
const transformasiId = $(this).data('transformasi-id');
console.log('Update aktivitas, aktivitas id: ' + id + ', transformasi id: ' + transformasiId);
$('#update-tujuan-id').val(id);
$('#update-tujuan-transformasi-id').val(transformasiId);
const editForm = $('#updateTujuanForm');
editForm.validate().resetForm();
editForm.find('.is-invalid').removeClass('is-invalid');
$.get("{{ route('framework.transformasi.aktivitas.show', [$framework->id, ':transformasiId', ':id']) }}"
.replace(':transformasiId', transformasiId).replace(':id', id), function (res) {
if (res.status === 'success') {
$('#update-tujuan-kode').val(res.data.kode);
$('#update-tujuan-nama').val(res.data.nama);
$('#update-tujuan-jenis').val(res.data.jenis_aktivitas);
$('#update-tujuan-indikator').val(res.data.indikator);
$('#update-tujuan-deskripsi').val(res.data.deskripsi);
offcanvasUpdateTujuan.show();
}
});
});
$('#updateTujuanForm').validate({
rules: {
kode: { required: true },
nama: { required: true }
},
messages: {
kode: { required: "Kode tujuan wajib diisi" },
nama: { required: "Tujuan wajib diisi" }
},
submitHandler: function (form) {
$.ajax({
url: $(form).attr('action').replace(':transformasiId', $('#update-tujuan-transformasi-id').val()).replace(':id', $('#update-tujuan-id').val()),
type: 'POST',
data: $(form).serialize(),
success: function () {
offcanvasUpdateTujuan.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -0,0 +1,74 @@
@forelse($tujuan->aktivitas as $aktivitas)
@forelse($aktivitas->target as $targetAktivitas)
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }} (ID: {{ $transformasi->id }})</span></td>
<td><span class="{{ $aktivitas->jenis_aktivitas == 'tujuan' ? 'text-heading' : 'text-muted' }}">{{ $aktivitas->kode }} {{ $aktivitas->nama }} (ID: {{ $aktivitas->id }})</span></td>
<td><span class="text-muted">{{ $aktivitas->deskripsi }}</span></td>
<td><span class="text-muted">{{ $aktivitas->indikator }}</span></td>
<td><span class="text-muted">{{ $targetAktivitas->variabel }} (ID: {{ $targetAktivitas->id }}, Tipe: {{ $targetAktivitas->tipe }}, Realiasasi: {{ $targetAktivitas->tipe_realisasi }})</span></td>
<td>
@if($targetAktivitas->tipe === 'integer')
<span class="text-muted">{{ $targetAktivitas->target_integer }}</span>
@elseif($targetAktivitas->tipe === 'float')
<span class="text-muted">{{ $targetAktivitas->target_float }}</span>
@elseif($targetAktivitas->tipe === 'boolean')
<span class="text-muted">{{ $targetAktivitas->target_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
</td>
<td>
@if($targetAktivitas->tipe === 'integer')
<span class="text-muted">{{ $targetAktivitas->baseline_integer }}</span>
@elseif($targetAktivitas->tipe === 'float')
<span class="text-muted">{{ $targetAktivitas->baseline_float }}</span>
@elseif($targetAktivitas->tipe === 'boolean')
<span class="text-muted">{{ $targetAktivitas->baseline_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
</td>
<td>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow" data-bs-toggle="dropdown">
<i class="icon-base ti tabler-dots-vertical"></i>
</button>
<div class="dropdown-menu">
<!-- aktivitas -->
<a class="dropdown-item add-aktivitas" href="javascript:void(0);" data-id="{{ $transformasi->id }}" data-parent-id="{{ $tujuan->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Aktivitas</a>
<a class="dropdown-item edit-aktivitas" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-tujuan-id="{{ $tujuan->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Aktivitas</a>
<a class="dropdown-item delete-aktivitas" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}" data-nama="{{ $aktivitas->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Aktivitas</a>
<hr/>
<a class="dropdown-item add-target" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Target</a>
<a class="dropdown-item edit-target" href="javascript:void(0);" data-id="{{ $targetAktivitas->id }}" data-aktivitas-id="{{ $targetAktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Target</a>
<a class="dropdown-item delete-target" href="javascript:void(0);" data-id="{{ $targetAktivitas->id }}" data-aktivitas-id="{{ $targetAktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}" data-nama="{{ $targetAktivitas->variabel }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Target</a>
</div>
</div>
</td>
</tr>
@empty
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }} (ID: {{ $transformasi->id }})</span></td>
<td><span class="{{ $aktivitas->jenis_aktivitas == 'tujuan' ? 'text-heading' : 'text-muted' }}">{{ $aktivitas->kode }} {{ $aktivitas->nama }} (ID: {{ $aktivitas->id }})</span></td>
<td><span class="text-muted">{{ $aktivitas->deskripsi }}</span></td>
<td><span class="text-muted">{{ $aktivitas->indikator }}</span></td>
<td></td>
<td></td>
<td></td>
<td>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow" data-bs-toggle="dropdown">
<i class="icon-base ti tabler-dots-vertical"></i>
</button>
<div class="dropdown-menu">
<!-- aktivitas -->
<a class="dropdown-item add-aktivitas" href="javascript:void(0);" data-id="{{ $transformasi->id }}" data-parent-id="{{ $tujuan->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Aktivitas</a>
<a class="dropdown-item edit-aktivitas" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-tujuan-id="{{ $tujuan->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Aktivitas</a>
<a class="dropdown-item delete-aktivitas" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}" data-nama="{{ $aktivitas->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Aktivitas</a>
<hr/>
<a class="dropdown-item add-target" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Target</a>
</div>
</div>
</td>
</tr>
@endforelse
<!-- endforelse target -->
@empty
@endforelse
<!-- endforelse tujuan -->
@@ -0,0 +1,109 @@
@forelse($transformasi->tujuan as $tujuan)
@forelse($tujuan->target as $target)
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }} (ID: {{ $transformasi->id }})</span></td>
<td><span class="{{ $tujuan->jenis_aktivitas == 'tujuan' ? 'text-heading' : 'text-muted' }}">{{ $tujuan->kode }} {{ $tujuan->nama }} (ID: {{ $tujuan->id }})</span></td>
<td><span class="text-muted">{{ $tujuan->deskripsi }}</span></td>
<td><span class="text-muted">{{ $tujuan->indikator }}</span></td>
<td><span class="text-muted">{{ $target->variabel }} (ID: {{ $target->id }}, Tipe: {{ $target->tipe }}, Realiasasi: {{ $target->tipe_realisasi }})</span></td>
<td>
@if($target->tipe === 'integer')
<span class="text-muted">{{ $target->target_integer }}</span>
@elseif($target->tipe === 'float')
<span class="text-muted">{{ $target->target_float }}</span>
@elseif($target->tipe === 'boolean')
<span class="text-muted">{{ $target->target_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
</td>
<td>
@if($target->tipe === 'integer')
<span class="text-muted">{{ $target->baseline_integer }}</span>
@elseif($target->tipe === 'float')
<span class="text-muted">{{ $target->baseline_float }}</span>
@elseif($target->tipe === 'boolean')
<span class="text-muted">{{ $target->baseline_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
</td>
<td>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow" data-bs-toggle="dropdown">
<i class="icon-base ti tabler-dots-vertical"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item edit-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Transformasi</a>
<a class="dropdown-item delete-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}" data-nama="{{ $transformasi->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Transformasi</a>
<hr/>
<!-- tujuan -->
<a class="dropdown-item add-tujuan" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Tujuan</a>
<a class="dropdown-item edit-tujuan" href="javascript:void(0);" data-id="{{ $tujuan->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Tujuan</a>
<a class="dropdown-item delete-tujuan" href="javascript:void(0);" data-id="{{ $tujuan->id }}" data-transformasi-id="{{ $transformasi->id }}" data-nama="{{ $tujuan->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Tujuan</a>
<hr/>
<!-- aktivitas -->
<a class="dropdown-item add-aktivitas" href="javascript:void(0);" data-id="{{ $transformasi->id }}" data-parent-id="{{ $tujuan->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Aktivitas</a>
<hr/>
<a class="dropdown-item add-target" href="javascript:void(0);" data-id="{{ $tujuan->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Target</a>
<a class="dropdown-item edit-target" href="javascript:void(0);" data-id="{{ $target->id }}" data-aktivitas-id="{{ $tujuan->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Target</a>
<a class="dropdown-item delete-target" href="javascript:void(0);" data-id="{{ $target->id }}" data-aktivitas-id="{{ $tujuan->id }}" data-transformasi-id="{{ $transformasi->id }}" data-nama="{{ $target->variabel }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Target</a>
</div>
</div>
</td>
</tr>
@empty
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }} (ID: {{ $transformasi->id }})</span></td>
<td><span class="{{ $tujuan->jenis_aktivitas == 'tujuan' ? 'text-heading' : 'text-muted' }}">{{ $tujuan->kode }} {{ $tujuan->nama }} (ID: {{ $tujuan->id }})</span></td>
<td><span class="text-muted">{{ $tujuan->deskripsi }}</span></td>
<td><span class="text-muted">{{ $tujuan->indikator }}</span></td>
<td></td>
<td></td>
<td></td>
<td>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow" data-bs-toggle="dropdown">
<i class="icon-base ti tabler-dots-vertical"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item edit-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Transformasi</a>
<a class="dropdown-item delete-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}" data-nama="{{ $transformasi->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Transformasi</a>
<hr/>
<!-- tujuan -->
<a class="dropdown-item add-tujuan" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Tujuan</a>
<a class="dropdown-item edit-tujuan" href="javascript:void(0);" data-id="{{ $tujuan->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Tujuan</a>
<a class="dropdown-item delete-tujuan" href="javascript:void(0);" data-id="{{ $tujuan->id }}" data-transformasi-id="{{ $transformasi->id }}" data-nama="{{ $tujuan->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Tujuan</a>
<hr/>
<!-- aktivitas -->
<a class="dropdown-item add-aktivitas" href="javascript:void(0);" data-id="{{ $transformasi->id }}" data-parent-id="{{ $tujuan->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Aktivitas</a>
<hr/>
<a class="dropdown-item add-target" href="javascript:void(0);" data-id="{{ $tujuan->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Target</a>
</div>
</div>
</td>
</tr>
@endforelse
<!-- endforelse target -->
@include('program.framework.view-aktivitas')
@empty
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }}</span></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow" data-bs-toggle="dropdown">
<i class="icon-base ti tabler-dots-vertical"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item edit-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Transformasi</a>
<a class="dropdown-item delete-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}" data-nama="{{ $transformasi->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Transformasi</a>
<hr/>
<a class="dropdown-item add-tujuan" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Tujuan</a>
</div>
</div>
</td>
</tr>
@endforelse
<!-- endforelse tujuan -->
@@ -57,106 +57,7 @@
</thead>
<tbody>
@forelse($framework->transformasi as $transformasi)
@forelse($transformasi->aktivitas as $aktivitas)
@forelse($aktivitas->target as $target)
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }}</span></td>
<td><span class="{{ $aktivitas->jenis_aktivitas == 'tujuan' ? 'text-heading' : 'text-muted' }}">{{ $aktivitas->nama }}</span></td>
<td><span class="text-muted">{{ $aktivitas->deskripsi }}</span></td>
<td><span class="text-muted">{{ $aktivitas->indikator }}</span></td>
<td><span class="text-muted">{{ $target->variabel }}</span></td>
<td>
@if($target->tipe === 'integer')
<span class="text-muted">{{ $target->target_integer }}</span>
@elseif($target->tipe === 'float')
<span class="text-muted">{{ $target->target_float }}</span>
@elseif($target->tipe === 'boolean')
<span class="text-muted">{{ $target->target_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
</td>
<td>
@if($target->tipe === 'integer')
<span class="text-muted">{{ $target->baseline_integer }}</span>
@elseif($target->tipe === 'float')
<span class="text-muted">{{ $target->baseline_float }}</span>
@elseif($target->tipe === 'boolean')
<span class="text-muted">{{ $target->baseline_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
</td>
<td>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow" data-bs-toggle="dropdown">
<i class="icon-base ti tabler-dots-vertical"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item edit-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Transformasi</a>
<a class="dropdown-item delete-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}" data-nama="{{ $transformasi->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Transformasi</a>
<hr/>
<a class="dropdown-item add-aktivitas" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Tujuan/Aktivitas</a>
<a class="dropdown-item edit-aktivitas" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Tujuan/Aktivitas</a>
<a class="dropdown-item delete-aktivitas" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}" data-nama="{{ $aktivitas->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Tujuan/Aktivitas</a>
<hr/>
<a class="dropdown-item add-target" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Target</a>
<a class="dropdown-item edit-target" href="javascript:void(0);" data-id="{{ $target->id }}" data-aktivitas-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Target</a>
<a class="dropdown-item delete-target" href="javascript:void(0);" data-id="{{ $target->id }}" data-aktivitas-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}" data-nama="{{ $target->variabel }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Target</a>
</div>
</div>
</td>
</tr>
@empty
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }}</span></td>
<td><span class="{{ $aktivitas->jenis_aktivitas == 'tujuan' ? 'text-heading' : 'text-muted' }}">{{ $aktivitas->nama }}</span></td>
<td><span class="text-muted">{{ $aktivitas->deskripsi }}</span></td>
<td><span class="text-muted">{{ $aktivitas->indikator }}</span></td>
<td></td>
<td></td>
<td></td>
<td>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow" data-bs-toggle="dropdown">
<i class="icon-base ti tabler-dots-vertical"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item edit-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Transformasi</a>
<a class="dropdown-item delete-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}" data-nama="{{ $transformasi->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Transformasi</a>
<hr/>
<a class="dropdown-item add-aktivitas" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Tujuan/Aktivitas</a>
<a class="dropdown-item edit-aktivitas" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Tujuan/Aktivitas</a>
<a class="dropdown-item delete-aktivitas" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}" data-nama="{{ $aktivitas->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Tujuan/Aktivitas</a>
<hr/>
<a class="dropdown-item add-target" href="javascript:void(0);" data-id="{{ $aktivitas->id }}" data-transformasi-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Target</a>
</div>
</div>
</td>
</tr>
@endforelse
<!-- endforelse target -->
@empty
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }}</span></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td>
<div class="dropdown">
<button type="button" class="btn p-0 dropdown-toggle hide-arrow" data-bs-toggle="dropdown">
<i class="icon-base ti tabler-dots-vertical"></i>
</button>
<div class="dropdown-menu">
<a class="dropdown-item edit-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-edit me-1"></i> Edit Transformasi</a>
<a class="dropdown-item delete-transformasi" href="javascript:void(0);" data-id="{{ $transformasi->id }}" data-nama="{{ $transformasi->nama }}"><i class="icon-base ti tabler-trash me-1"></i> Hapus Transformasi</a>
<hr/>
<a class="dropdown-item add-aktivitas" href="javascript:void(0);" data-id="{{ $transformasi->id }}"><i class="icon-base ti tabler-plus me-1"></i> Tambah Tujuan/Aktivitas</a>
</div>
</div>
</td>
</tr>
@endforelse
<!-- endforelse aktivitas -->
@include('program.framework.view-tujuan')
@empty
<tr>
<td colspan="4" class="text-center py-4">Tidak ada data ditemukan.</td>
@@ -178,9 +79,12 @@
</div>
@include('program.transformasi.add')
@include('program.transformasi.edit')
@include('program.aktivitas.add')
@include('program.aktivitas.edit')
@include('program.aktivitas.add-tujuan')
@include('program.aktivitas.add-aktivitas')
@include('program.aktivitas.edit-tujuan')
@include('program.aktivitas.edit-aktivitas')
@include('program.target.add')
@include('program.target.edit')
</div>
@endsection
@@ -0,0 +1,166 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasAddRealisasi"
aria-labelledby="offcanvasAddRealisasiLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasAddRealisasiLabel" class="offcanvas-title">Perbarui Capaian Realisasi</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="t-0" id="addNewRealisasiForm" action="{{ route('realisasi.store') }}" method="POST">
@csrf
<input type="hidden" id="add-target-transformasi-id" name="transformasi_id"/>
<input type="hidden" id="add-target-aktivitas-id" name="aktivitas_id"/>
<input type="hidden" id="add-target-id" name="target_id"/>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-target-variabel">Variabel Target<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-target-variabel" name="variabel" placeholder="Cth: AK1" readonly />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label form-check-label" for="add-target-target">Target<span class="text-danger">*</span></label>
<input type="number" step="1" min="0" class="form-control" style="display: none;" id="add-target-target-integer" name="target_integer" placeholder="Cth: 100" readonly />
<input type="number" step="any" min="0" class="form-control" style="display: none;" id="add-target-target-float" name="target_float" placeholder="Cth: 100.23" readonly />
<input type="text" class="form-control" style="display: none;" id="add-target-target-boolean" name="target_text" placeholder="Cth: 100.23" readonly />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label form-check-label" for="add-target-baseline">Baseline<span class="text-danger">*</span></label>
<input type="number" step="1" min="0" class="form-control" style="display: none;" id="add-target-baseline-integer" name="baseline_integer" placeholder="Cth: 100" readonly />
<input type="number" step="any" min="0" class="form-control" style="display: none;" id="add-target-baseline-float" name="baseline_float" placeholder="Cth: 100.23" readonly />
<input type="text" class="form-control" style="display: none;" id="add-target-baseline-boolean" name="baseline_text" placeholder="Cth: 100.23" readonly />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label form-check-label" for="add-target-realisasi">Capaian Realisasi<span class="text-danger">*</span></label>
<input type="number" step="1" min="0" class="form-control" style="display: none;" id="add-target-realisasi-integer" name="realisasi_integer" placeholder="Cth: 100" />
<input type="number" step="any" min="0" class="form-control" style="display: none;" id="add-target-realisasi-float" name="realisasi_float" placeholder="Cth: 100.23" />
<div class="form-check form-check-inline" id="add-target-realisasi-boolean-true" style="display: none;">
<input type="radio" name="realisasi_boolean" value="1" />
<label class="form-check-label" for="add-target-realisasi-boolean-true">Ya</label>
</div>
<div class="form-check form-check-inline" id="add-target-realisasi-boolean-false" style="display: none;">
<input type="radio" name="realisasi_boolean" value="0" />
<label class="form-check-label" for="add-target-realisasi-boolean-false">Tidak</label>
</div>
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasAddRealisasi = new bootstrap.Offcanvas(document.getElementById('offcanvasAddRealisasi'));
$(".add-realisasi").click(function () {
const targetId = $(this).data('target-id');
const aktivitasId = $(this).data('aktivitas-id');
const transformasiId = $(this).data('transformasi-id');
console.log('Add target, target id: ' + targetId);
$('#add-target-aktivitas-id').val(aktivitasId);
$('#add-target-transformasi-id').val(transformasiId);
$('#add-target-id').val(targetId);
$.get("{{ route('framework.transformasi.aktivitas.target.show', [$framework->id, ':transformasiId', ':aktivitasId', ':id']) }}"
.replace(':aktivitasId', aktivitasId)
.replace(':transformasiId', transformasiId)
.replace(':id', targetId), function (res) {
if (res.status === 'success') {
$('#add-target-variabel').val(res.data.variabel);
$('#add-target-target-integer').val(res.data.target_integer);
$('#add-target-target-float').val(res.data.target_float);
$('#add-target-target-boolean').val(res.data.target_boolean ? 'Ya' : 'Tidak');
$('#add-target-baseline-integer').val(res.data.baseline_integer);
$('#add-target-baseline-float').val(res.data.baseline_float);
$('#add-target-baseline-boolean').val(res.data.baseline_boolean ? 'Ya' : 'Tidak');
if (res.data.latest_realisasi != null) {
$('#add-target-realisasi-integer').val(res.data.latest_realisasi.realisasi_integer);
$('#add-target-realisasi-float').val(res.data.latest_realisasi.realisasi_float);
$('#add-target-realisasi-boolean-true').prop('checked', res.data.latest_realisasi.realisasi_boolean);
$('#add-target-realisasi-boolean-false').prop('checked', !res.data.latest_realisasi.realisasi_boolean);
} else {
$('#add-target-realisasi-integer').val(res.data.baseline_integer);
$('#add-target-realisasi-float').val(res.data.baseline_float);
$('#add-target-realisasi-boolean-true').prop('checked', res.data.baseline_boolean);
$('#add-target-realisasi-boolean-false').prop('checked', !res.data.baseline_boolean);
}
$('#add-target-target-integer, #add-target-target-float, #add-target-target-boolean')
.hide().prop('disabled', true);
$('#add-target-baseline-integer, #add-target-baseline-float, #add-target-baseline-boolean')
.hide().prop('disabled', true);
$('#add-target-realisasi-integer, #add-target-realisasi-float, #add-target-realisasi-boolean-true, #add-target-realisasi-boolean-false')
.hide().prop('disabled', true);
if (res.data.tipe === 'integer') {
$('#add-target-target-integer').show().prop('disabled', false);
$('#add-target-baseline-integer').show().prop('disabled', false);
$('#add-target-realisasi-integer').show().prop('disabled', false);
} else if (res.data.tipe === 'float') {
$('#add-target-target-float').show().prop('disabled', false);
$('#add-target-baseline-float').show().prop('disabled', false);
$('#add-target-realisasi-float').show().prop('disabled', false);
} else if (res.data.tipe === 'boolean') {
$('#add-target-target-boolean').show().prop('disabled', false);
$('#add-target-baseline-boolean').show().prop('disabled', false);
$('#add-target-realisasi-boolean-true, #add-target-realisasi-boolean-false').show().prop('disabled', false);
}
offcanvasAddRealisasi.show();
}
});
});
$('#addNewRealisasiForm').validate({
rules: {
realisasi_integer: { required: true, number: true, min: 0 },
realisasi_float: { required: true, number: true, min: 0 },
realisasi_boolean: { required: true }
},
messages: {
realisasi_integer: { required: "Realisasi integer wajib diisi", number: "Harus berupa angka", min: "Harus berupa angka positif" },
realisasi_float: { required: "Realisasi float wajib diisi", number: "Harus berupa angka", min: "Harus berupa angka positif" },
realisasi_boolean: { required: "Realisasi boolean wajib diisi" }
},
submitHandler: function (form) {
$.ajax({
url: $(form).attr('action'),
type: 'POST',
data: $(form).serialize(),
success: function () {
offcanvasAddRealisasi.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -0,0 +1,168 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasEditRealisasi"
aria-labelledby="offcanvasEditRealisasiLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasEditRealisasiLabel" class="offcanvas-title">Ubah Capaian Realisasi</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="t-0" id="editRealisasiForm" action="{{ route('realisasi.update', ':id') }}" method="POST">
@csrf
@method('PUT')
<input type="hidden" id="edit-target-transformasi-id" name="transformasi_id"/>
<input type="hidden" id="edit-target-aktivitas-id" name="aktivitas_id"/>
<input type="hidden" id="edit-target-id" name="target_id"/>
<input type="hidden" id="edit-target-realisasi-id" name="id"/>
<div class="mb-6 form-control-validation">
<label class="form-label" for="edit-target-variabel">Variabel Target<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="edit-target-variabel" name="variabel" placeholder="Cth: AK1" readonly />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label form-check-label" for="edit-target-target">Target<span class="text-danger">*</span></label>
<input type="number" step="1" min="0" class="form-control" id="edit-target-target-integer" name="target_integer" placeholder="Cth: 100" readonly />
<input type="number" step="any" min="0" class="form-control" style="display: none;" id="edit-target-target-float" name="target_float" placeholder="Cth: 100.23" readonly />
<div class="form-check form-check-inline" id="edit-target-target-boolean-true" style="display: none;">
<input type="radio" name="target_boolean" value="1" readonly />
<label class="form-check-label" for="edit-target-target-boolean-true">Ya</label>
</div>
<div class="form-check form-check-inline" id="edit-target-target-boolean-false" style="display: none;">
<input type="radio" name="target_boolean" value="0" readonly />
<label class="form-check-label" for="edit-target-target-boolean-false">Tidak</label>
</div>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label form-check-label" for="edit-target-baseline">Baseline<span class="text-danger">*</span></label>
<input type="number" step="1" min="0" class="form-control" id="edit-target-baseline-integer" name="baseline_integer" placeholder="Cth: 100" readonly />
<input type="number" step="any" min="0" class="form-control" style="display: none;" id="edit-target-baseline-float" name="baseline_float" placeholder="Cth: 100.23" readonly />
<div class="form-check form-check-inline" id="edit-target-baseline-boolean-true" style="display: none;">
<input type="radio" name="baseline_boolean" value="1" readonly />
<label class="form-check-label" for="edit-target-baseline-boolean-true">Ya</label>
</div>
<div class="form-check form-check-inline" id="edit-target-baseline-boolean-false" style="display: none;">
<input type="radio" name="baseline_boolean" value="0" readonly />
<label class="form-check-label" for="edit-target-baseline-boolean-false">Tidak</label>
</div>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label form-check-label" for="edit-target-realisasi">Capaian Realisasi<span class="text-danger">*</span></label>
<input type="number" step="1" min="0" class="form-control" id="edit-target-realisasi-integer" name="realisasi_integer" placeholder="Cth: 100" />
<input type="number" step="any" min="0" class="form-control" style="display: none;" id="edit-target-realisasi-float" name="realisasi_float" placeholder="Cth: 100.23" />
<div class="form-check form-check-inline" id="edit-target-realisasi-boolean-true" style="display: none;">
<input type="radio" name="realisasi_boolean" value="1" />
<label class="form-check-label" for="edit-target-realisasi-boolean-true">Ya</label>
</div>
<div class="form-check form-check-inline" id="edit-target-realisasi-boolean-false" style="display: none;">
<input type="radio" name="realisasi_boolean" value="0" />
<label class="form-check-label" for="edit-target-realisasi-boolean-false">Tidak</label>
</div>
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasEditRealisasi = new bootstrap.Offcanvas(document.getElementById('offcanvasEditRealisasi'));
$(".edit-realisasi").click(function () {
const targetId = $(this).data('target-id');
const aktivitasId = $(this).data('aktivitas-id');
const transformasiId = $(this).data('transformasi-id');
const id = $(this).data('id');
console.log('Edit target, target id: ' + targetId);
$('#edit-target-aktivitas-id').val(aktivitasId);
$('#edit-target-transformasi-id').val(transformasiId);
$('#edit-target-id').val(targetId);
$('#edit-target-realisasi-id').val(id);
$.get("{{ route('realisasi.show', ':id') }}"
.replace(':id', id), function (res) {
if (res.status === 'success') {
$('#edit-target-variabel').val(res.data.target.variabel);
$('#edit-target-target-integer').val(res.data.target.target_integer);
$('#edit-target-target-float').val(res.data.target.target_float);
$('#edit-target-target-boolean-true').prop('checked', res.data.target.target_boolean);
$('#edit-target-target-boolean-false').prop('checked', !res.data.target.target_boolean);
$('#edit-target-baseline-integer').val(res.data.target.baseline_integer);
$('#edit-target-baseline-float').val(res.data.target.baseline_float);
$('#edit-target-baseline-boolean-true').prop('checked', res.data.target.baseline_boolean);
$('#edit-target-baseline-boolean-false').prop('checked', !res.data.target.baseline_boolean);
$('#edit-target-realisasi-integer').val(res.data.realisasi_integer);
$('#edit-target-realisasi-float').val(res.data.realisasi_float);
$('#edit-target-realisasi-boolean-true').prop('checked', res.data.realisasi_boolean);
$('#edit-target-realisasi-boolean-false').prop('checked', !res.data.realisasi_boolean);
if (res.data.target.tipe === 'integer') {
$('#edit-target-target-integer').show().prop('disabled', false);
$('#edit-target-baseline-integer').show().prop('disabled', false);
$('#edit-target-realisasi-integer').show().prop('disabled', false);
} else if (res.data.target.tipe === 'float') {
$('#edit-target-target-float').show().prop('disabled', false);
$('#edit-target-baseline-float').show().prop('disabled', false);
$('#edit-target-realisasi-float').show().prop('disabled', false);
} else if (res.data.target.tipe === 'boolean') {
$('#edit-target-target-boolean-true, #edit-target-target-boolean-false').show().prop('disabled', false);
$('#edit-target-baseline-boolean-true, #edit-target-baseline-boolean-false').show().prop('disabled', false);
$('#edit-target-realisasi-boolean-true, #edit-target-realisasi-boolean-false').show().prop('disabled', false);
}
offcanvasEditRealisasi.show();
}
});
});
$('#editRealisasiForm').validate({
rules: {
realisasi_integer: { required: true, number: true, min: 0 },
realisasi_float: { required: true, number: true, min: 0 },
realisasi_boolean: { required: true }
},
messages: {
realisasi_integer: { required: "Realisasi integer wajib diisi", number: "Harus berupa angka", min: "Harus berupa angka positif" },
realisasi_float: { required: "Realisasi float wajib diisi", number: "Harus berupa angka", min: "Harus berupa angka positif" },
realisasi_boolean: { required: "Realisasi boolean wajib diisi" }
},
submitHandler: function (form) {
$.ajax({
url: $(form).attr('action').replace(':id', $('#edit-target-realisasi-id').val()),
type: 'PUT',
data: $(form).serialize(),
success: function () {
offcanvasEditRealisasi.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -0,0 +1,126 @@
@forelse($tujuan->aktivitas as $aktivitas)
@forelse($aktivitas->target as $targetAktivitas)
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }} (ID: {{ $transformasi->id }})</span></td>
<td><span class="{{ $aktivitas->jenis_aktivitas == 'tujuan' ? 'text-heading' : 'text-muted' }}">{{ $aktivitas->kode }} {{ $aktivitas->nama }} (ID: {{ $aktivitas->id }})</span></td>
<td><span class="text-muted">{{ $aktivitas->deskripsi }}</span></td>
<td><span class="text-muted">{{ $aktivitas->indikator }}</span></td>
<td><span class="text-muted">{{ $targetAktivitas->variabel }} (ID: {{ $targetAktivitas->id }}, Tipe: {{ $targetAktivitas->tipe }}, Realiasasi: {{ $targetAktivitas->tipe_realisasi }})</span></td>
<td>
@if($targetAktivitas->tipe === 'integer')
<span class="text-muted">{{ $targetAktivitas->target_integer }}</span>
@elseif($targetAktivitas->tipe === 'float')
<span class="text-muted">{{ $targetAktivitas->target_float }}</span>
@elseif($targetAktivitas->tipe === 'boolean')
<span class="text-muted">{{ $targetAktivitas->target_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
</td>
<td>
@if($targetAktivitas->tipe === 'integer')
<span class="text-muted">{{ $targetAktivitas->baseline_integer }}</span>
@elseif($targetAktivitas->tipe === 'float')
<span class="text-muted">{{ $targetAktivitas->baseline_float }}</span>
@elseif($targetAktivitas->tipe === 'boolean')
<span class="text-muted">{{ $targetAktivitas->baseline_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
</td>
<td>
@if($targetAktivitas->latestRealisasi)
@if($targetAktivitas->tipe === 'integer')
<span class="text-muted">{{ $targetAktivitas->latestRealisasi->realisasi_integer }}</span>
<span class="text-muted {{ $targetAktivitas->latestRealisasi->realisasi_integer / $targetAktivitas->target_integer <= 0.5 ? 'text-danger' : 'text-success' }}">
({{ number_format(($targetAktivitas->latestRealisasi->realisasi_integer / $targetAktivitas->target_integer) * 100, 2) }}%)
</span>
@elseif($targetAktivitas->tipe === 'float')
<span class="text-muted">{{ $targetAktivitas->latestRealisasi->realisasi_float }}</span>
<span class="text-muted {{ $targetAktivitas->latestRealisasi->realisasi_float / $targetAktivitas->target_float <= 0.5 ? 'text-danger' : 'text-success' }}">
({{ number_format(($targetAktivitas->latestRealisasi->realisasi_float / $targetAktivitas->target_float) * 100, 2) }}%)
</span>
@elseif($targetAktivitas->tipe === 'boolean')
<span class="text-muted">{{ $targetAktivitas->latestRealisasi->realisasi_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
@if($targetAktivitas->latestRealisasi && $targetAktivitas->latestRealisasi->verifikasi->count() > 0)
<span class="badge badge-center rounded-pill bg-label-success ms-2"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Terverifikasi">
<i class="ti tabler-check ti-md"></i>
</span>
@else
<span class="badge badge-center rounded-pill bg-label-danger ms-2"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Belum Terverifikasi">
<i class="ti tabler-exclamation-mark ti-md"></i>
</span>
@endif
@else
-
@endif
</td>
<td>
<div class="d-flex align-items-center">
<a href="javascript:;" class="btn btn-icon btn-text-success rounded-pill add-realisasi"
data-framework-id="{{ $framework->id }}"
data-transformasi-id="{{ $transformasi->id }}"
data-aktivitas-id="{{ $aktivitas->id }}"
data-target-id="{{ $targetAktivitas->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Perbarui Realisasi">
<i class="ti tabler-chart-dots ti-md"></i>
</a>
@if($targetAktivitas->latestRealisasi)
<a href="javascript:;" class="btn btn-icon btn-text-success rounded-pill edit-realisasi"
data-framework-id="{{ $framework->id }}"
data-transformasi-id="{{ $transformasi->id }}"
data-aktivitas-id="{{ $aktivitas->id }}"
data-target-id="{{ $targetAktivitas->id }}"
data-id="{{ $targetAktivitas->latestRealisasi->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Edit Realisasi">
<i class="ti tabler-edit-circle ti-md"></i>
</a>
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill history-realisasi"
data-framework-id="{{ $framework->id }}"
data-transformasi-id="{{ $transformasi->id }}"
data-aktivitas-id="{{ $aktivitas->id }}"
data-target-id="{{ $targetAktivitas->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Riwayat Realisasi">
<i class="ti tabler-logs ti-md"></i>
</a>
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill add-verifikasi"
data-framework-id="{{ $framework->id }}"
data-transformasi-id="{{ $transformasi->id }}"
data-aktivitas-id="{{ $aktivitas->id }}"
data-target-id="{{ $targetAktivitas->id }}"
data-realisasi-id="{{ $targetAktivitas->latestRealisasi->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Tambah Dokumen Verifikasi">
<i class="ti tabler-file-plus ti-md"></i>
</a>
@if($targetAktivitas->latestRealisasi && $targetAktivitas->latestRealisasi->verifikasi->count() > 0)
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill list-verifikasi"
data-framework-id="{{ $framework->id }}"
data-transformasi-id="{{ $transformasi->id }}"
data-aktivitas-id="{{ $aktivitas->id }}"
data-target-id="{{ $targetAktivitas->id }}"
data-realisasi-id="{{ $targetAktivitas->latestRealisasi->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Daftar Dokumen Verifikasi">
<i class="ti tabler-files ti-md"></i>
</a>
@endif
@endif
</div>
</td>
</tr>
@empty
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }} (ID: {{ $transformasi->id }})</span></td>
<td><span class="{{ $aktivitas->jenis_aktivitas == 'tujuan' ? 'text-heading' : 'text-muted' }}">{{ $aktivitas->kode }} {{ $aktivitas->nama }} (ID: {{ $aktivitas->id }})</span></td>
<td><span class="text-muted">{{ $aktivitas->deskripsi }}</span></td>
<td><span class="text-muted">{{ $aktivitas->indikator }}</span></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
@endforelse
<!-- endforelse target -->
@empty
@endforelse
<!-- endforelse tujuan -->
@@ -0,0 +1,137 @@
@forelse($transformasi->tujuan as $tujuan)
@forelse($tujuan->target as $target)
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }} (ID: {{ $transformasi->id }})</span></td>
<td><span class="{{ $tujuan->jenis_aktivitas == 'tujuan' ? 'text-heading' : 'text-muted' }}">{{ $tujuan->kode }} {{ $tujuan->nama }} (ID: {{ $tujuan->id }})</span></td>
<td><span class="text-muted">{{ $tujuan->deskripsi }}</span></td>
<td><span class="text-muted">{{ $tujuan->indikator }}</span></td>
<td><span class="text-muted">{{ $target->variabel }} (ID: {{ $target->id }}, Tipe: {{ $target->tipe }}, Realiasasi: {{ $target->tipe_realisasi }})</span></td>
<td>
@if($target->tipe === 'integer')
<span class="text-muted">{{ $target->target_integer }}</span>
@elseif($target->tipe === 'float')
<span class="text-muted">{{ $target->target_float }}</span>
@elseif($target->tipe === 'boolean')
<span class="text-muted">{{ $target->target_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
</td>
<td>
@if($target->tipe === 'integer')
<span class="text-muted">{{ $target->baseline_integer }}</span>
@elseif($target->tipe === 'float')
<span class="text-muted">{{ $target->baseline_float }}</span>
@elseif($target->tipe === 'boolean')
<span class="text-muted">{{ $target->baseline_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
</td>
<td>
@if($target->latestRealisasi)
@if($target->tipe === 'integer')
<span class="text-muted">{{ $target->latestRealisasi->realisasi_integer }}</span>
<span class="text-muted {{ $target->latestRealisasi->realisasi_integer / $target->target_integer <= 0.5 ? 'text-danger' : 'text-success' }}">
({{ number_format(($target->latestRealisasi->realisasi_integer / $target->target_integer) * 100, 2) }}%)
</span>
@elseif($target->tipe === 'float')
<span class="text-muted">{{ $target->latestRealisasi->realisasi_float }}</span>
<span class="text-muted {{ $target->latestRealisasi->realisasi_float / $target->target_float <= 0.5 ? 'text-danger' : 'text-success' }}">
({{ number_format(($target->latestRealisasi->realisasi_float / $target->target_float) * 100, 2) }}%)
</span>
@elseif($target->tipe === 'boolean')
<span class="text-muted">{{ $target->latestRealisasi->realisasi_boolean ? 'Ya' : 'Tidak' }}</span>
@endif
@if($target->latestRealisasi && $target->latestRealisasi->verifikasi->count() > 0)
<span class="badge badge-center rounded-pill bg-label-success ms-2"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Terverifikasi">
<i class="ti tabler-check ti-md"></i>
</span>
@else
<span class="badge badge-center rounded-pill bg-label-danger ms-2"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Belum Terverifikasi">
<i class="ti tabler-exclamation-mark ti-md"></i>
</span>
@endif
@else
-
@endif
</td>
<td>
<div class="d-flex align-items-center">
<a href="javascript:;" class="btn btn-icon btn-text-success rounded-pill add-realisasi"
data-framework-id="{{ $framework->id }}"
data-transformasi-id="{{ $transformasi->id }}"
data-aktivitas-id="{{ $tujuan->id }}"
data-target-id="{{ $target->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Perbarui Realisasi">
<i class="ti tabler-chart-dots ti-md"></i>
</a>
@if($target->latestRealisasi)
<a href="javascript:;" class="btn btn-icon btn-text-success rounded-pill edit-realisasi"
data-framework-id="{{ $framework->id }}"
data-transformasi-id="{{ $transformasi->id }}"
data-aktivitas-id="{{ $tujuan->id }}"
data-target-id="{{ $target->id }}"
data-id="{{ $target->latestRealisasi->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Edit Realisasi">
<i class="ti tabler-edit-circle ti-md"></i>
</a>
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill history-realisasi"
data-framework-id="{{ $framework->id }}"
data-transformasi-id="{{ $transformasi->id }}"
data-aktivitas-id="{{ $tujuan->id }}"
data-target-id="{{ $target->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Riwayat Realisasi">
<i class="ti tabler-logs ti-md"></i>
</a>
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill add-verifikasi"
data-framework-id="{{ $framework->id }}"
data-transformasi-id="{{ $transformasi->id }}"
data-aktivitas-id="{{ $tujuan->id }}"
data-target-id="{{ $target->id }}"
data-realisasi-id="{{ $target->latestRealisasi->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Tambah Dokumen Verifikasi">
<i class="ti tabler-file-plus ti-md"></i>
</a>
@if($target->latestRealisasi && $target->latestRealisasi->verifikasi->count() > 0)
<a href="javascript:;" class="btn btn-icon btn-text-secondary rounded-pill list-verifikasi"
data-framework-id="{{ $framework->id }}"
data-transformasi-id="{{ $transformasi->id }}"
data-aktivitas-id="{{ $tujuan->id }}"
data-target-id="{{ $target->id }}"
data-realisasi-id="{{ $target->latestRealisasi->id }}"
data-bs-toggle="tooltip" data-bs-placement="top" data-bs-original-title="Daftar Dokumen Verifikasi">
<i class="ti tabler-files ti-md"></i>
</a>
@endif
@endif
</div>
</td>
</tr>
@empty
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }} (ID: {{ $transformasi->id }})</span></td>
<td><span class="{{ $tujuan->jenis_aktivitas == 'tujuan' ? 'text-heading' : 'text-muted' }}">{{ $tujuan->kode }} {{ $tujuan->nama }} (ID: {{ $tujuan->id }})</span></td>
<td><span class="text-muted">{{ $tujuan->deskripsi }}</span></td>
<td><span class="text-muted">{{ $tujuan->indikator }}</span></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
@endforelse
<!-- endforelse target -->
@include('program.realisasi.index-aktivitas')
@empty
<tr>
<td><span class="text-heading">{{ $transformasi->kode }} {{ $transformasi->nama }}</span></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
</tr>
@endforelse
<!-- endforelse tujuan -->
@@ -0,0 +1,178 @@
@extends('layout.main')
@push('styles')
<link rel="stylesheet"
href="https://cdn.datatables.net/rowgroup/1.4.1/css/rowGroup.dataTables.min.css">
@endpush
@section('content')
<div class="container-xxl flex-grow-1 container-p-y">
<div class="row g-6 mb-6">
<div class="col-sm-6 col-xl-4">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span class="text-heading">Capaian Realisasi</span>
<div class="d-flex align-items-center my-1">
<h4 class="mb-0 me-2 {{ $ringkasanCapaianRealisasi->persentase >= 80 ? 'text-success' : ($ringkasanIsiRealisasi->persentase >= 50 ? 'text-warning' : 'text-danger') }}">
{{ $ringkasanCapaianRealisasi->persentase ?? 0 }}%
</h4>
</div>
<small class="mb-0">
{{ $ringkasanCapaianRealisasi->capaian_80 ?? 0 }} Capaian &ge; 80%,
{{ $ringkasanCapaianRealisasi->capaian_50_80 ?? 0 }} Capaian 50% - 80%,
{{ $ringkasanCapaianRealisasi->capaian_50 ?? 0 }} Capaian < 50%</small>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-success">
<i class="icon-base ti tabler-gauge icon-26px"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-4">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span class="text-heading">Keterisian Data Capaian Realisasi</span>
<div class="d-flex align-items-center my-1">
<h4 class="mb-0 me-2 {{ $ringkasanIsiRealisasi->persentase >= 80 ? 'text-success' : ($ringkasanIsiRealisasi->persentase >= 50 ? 'text-warning' : 'text-danger') }}">
{{ $ringkasanIsiRealisasi->persentase ?? 0 }}%
</h4>
</div>
<small class="mb-0">{{ $ringkasanIsiRealisasi->count_realisasi ?? 0 }} Data Terisi / {{ $ringkasanIsiRealisasi->count_target ?? 0 }} Data Target</small>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-primary">
<i class="icon-base ti tabler-chart-dots icon-26px"></i>
</span>
</div>
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-4">
<div class="card">
<div class="card-body">
<div class="d-flex align-items-start justify-content-between">
<div class="content-left">
<span class="text-heading">Kelengkapan Dokumen Verifikasi</span>
<div class="d-flex align-items-center my-1">
<h4 class="mb-0 me-2 {{ $ringkasanVerifikasi->persentase >= 80 ? 'text-success' : ($ringkasanVerifikasi->persentase >= 50 ? 'text-warning' : 'text-danger') }}">
{{ $ringkasanVerifikasi->persentase ?? 0 }}%
</h4>
</div>
<small class="mb-0">{{ $ringkasanVerifikasi->count_unverifikasi ?? 0 }} Realisasi Belum Dilengkapi Dokumen</small>
</div>
<div class="avatar">
<span class="avatar-initial rounded bg-label-secondary">
<i class="icon-base ti tabler-files icon-26px"></i>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="card">
<div class="card-header border-bottom mb-4">
<h5 class="card-title mb-0">Realiasi Capaian</h5>
</div>
<div class="card-body">
<form id="viewFrameworkForm">
@csrf
<h6 class="mb-3">Informasi Kerangka Kerja</h6>
<div class="row">
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="update-framework-nama">Nama Kerangka Kerja<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="update" name="nama" placeholder="Cth: Tabel Pertanian" value="{{ old('nama', $framework->nama) }}" readonly/>
</div>
<div class="col-md-3 mb-4 form-control-validation">
<label class="form-label" for="update-framework-tahun">Tahun Implementasi<span class="text-danger">*</span></label>
<input type="number" step="1" min="2026" class="form-control" id="update-framework-tahun" name="tahun" placeholder="Cth: 2023" value="{{ old('tahun', $framework->tahun) ?? now()->year }}" readonly/>
</div>
<div class="col-md-6 mb-4 form-control-validation">
<label class="form-label" for="update-framework-program">Untuk Program<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="update-framework-tahun" name="tahun" placeholder="Cth: 2023" value="{{ old('program_id', $framework->program->nama) }}" readonly/>
</div>
</div>
<h6 class="mb-3">Informasi Rencana Kerja dan Realisasi</h6>
<hr class="my-4 mx-n4" />
<div class="row mb-4">
<div class="col-md-12 mb-4 form-control-validation">
<div class="table-responsive text-nowrap mb-4">
<table class="table table-hover" id="realisasiTable">
<thead class="border-top">
<tr>
<th>Transformasi</th>
<th>Tujuan Jangka Pendek/Aktivitas</th>
<th>Deskripsi</th>
<th>Indikator</th>
<th>Variabel</th>
<th>Target</th>
<th>Baseline</th>
<th>Capaian Realisasi</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@forelse($framework->transformasi as $transformasi)
@include('program.realisasi.index-tujuan')
@empty
<tr>
<td colspan="4" class="text-center py-4">Tidak ada data ditemukan.</td>
</tr>
@endforelse
<!-- endforelse tranformasi -->
</tbody>
</table>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
@include('program.realisasi.add')
@include('program.realisasi.list')
@include('program.realisasi.edit')
@include('program.verifikasi.add')
@include('program.verifikasi.list')
@endsection
@push('scripts')
<script src="https://cdn.datatables.net/rowgroup/1.4.1/js/dataTables.rowGroup.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/datatables-rowsgroup@2.0.0/dataTables.rowsGroup.js"></script>
<script>
'use strict';
$(document).ready(function () {
$('#realisasiTable').DataTable({
"paging": true,
"lengthChange": false,
"searching": true,
"ordering": false,
"info": true,
"autoWidth": true,
"scrollX": true,
"rowsGroup": [0]
});
});
</script>
@endpush
@@ -0,0 +1,83 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasListRealisasi"
aria-labelledby="offcanvasListRealisasiLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasListRealisasiLabel" class="offcanvas-title">Riwayat Capaian Realisasi</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="t-0" id="historyRealisasiForm">
<input type="hidden" id="list-target-transformasi-id" name="transformasi_id"/>
<input type="hidden" id="list-target-aktivitas-id" name="aktivitas_id"/>
<input type="hidden" id="list-target-id" name="target_id"/>
<div class="mb-6 form-control-validation">
<label class="form-label" for="list-target-variabel">Variabel Target<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="list-target-variabel" name="variabel" placeholder="Cth: AK1" readonly />
</div>
<table class="table table-hover">
<thead>
<tr>
<th>Nilai Realisasi</th>
<th>Update</th>
</tr>
</thead>
<tbody id="historyRealisasiTableBody">
</tbody>
</table>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Tutup</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasListRealisasi = new bootstrap.Offcanvas(document.getElementById('offcanvasListRealisasi'));
$(".history-realisasi").click(function () {
const targetId = $(this).data('target-id');
const aktivitasId = $(this).data('aktivitas-id');
const transformasiId = $(this).data('transformasi-id');
console.log('Add target, target id: ' + targetId);
$('#list-target-aktivitas-id').val(aktivitasId);
$('#list-target-transformasi-id').val(transformasiId);
$('#list-target-id').val(targetId);
$.get("{{ route('framework.transformasi.aktivitas.target.show', [$framework->id, ':transformasiId', ':aktivitasId', ':id']) }}"
.replace(':aktivitasId', aktivitasId)
.replace(':transformasiId', transformasiId)
.replace(':id', targetId), function (res) {
if (res.status === 'success') {
$('#list-target-variabel').val(res.data.variabel);
let rows = '';
res.data.realisasi.forEach(function (realisasi) {
let realisasiValue = '';
if (res.data.tipe === 'integer') {
realisasiValue = realisasi.realisasi_integer;
} else if (res.data.tipe === 'float') {
realisasiValue = realisasi.realisasi_float;
} else if (res.data.tipe === 'boolean') {
realisasiValue = realisasi.realisasi_boolean ? 'Ya' : 'Tidak';
}
rows += '<tr><td>' + realisasiValue + '</td><td>' + new Date(realisasi.updated_at).toLocaleString() + '</td></tr>';
});
$('#historyRealisasiTableBody').html(rows);
offcanvasListRealisasi.show();
}
});
});
});
</script>
@endpush
+19 -1
View File
@@ -56,6 +56,17 @@
<label class="form-check-label" for="add-target-baseline-boolean-false">Tidak</label>
</div>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-target-tipe-realisasi">Tipe Perhitungan Nilai Realisasi<span class="text-danger">*</span></label>
<select class="form-select" id="add-target-tipe-realisasi" name="tipe_realisasi">
<option value="manual">Input Manual</option>
<option value="referensi">Dihitung Otomatis</option>
</select>
</div>
<div class="mb-6 form-control-validation" id="add-target-value-query-container" style="display: none;">
<label class="form-label" for="add-target-value-query">Query Perhitungan Nilai Realisasi</label>
<textarea class="form-control" id="add-target-value-query" name="value_query" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
@@ -68,7 +79,7 @@
$(document).ready(function() {
const offcanvasAddTarget = new bootstrap.Offcanvas(document.getElementById('offcanvasAddTarget'));
$('#add-target-tipe').select2({
$('#add-target-tipe, #add-target-tipe-realisasi').select2({
dropdownParent: $('#offcanvasAddTarget'),
width: '100%',
});
@@ -91,6 +102,13 @@
$('#add-target-baseline-boolean-true, #add-target-baseline-boolean-false').show().prop('disabled', false);
}
});
$('#add-target-tipe-realisasi').change(function() {
const tipe = $(this).val();
$('#add-target-value-query-container').hide().prop('disabled', true);
if (tipe === 'referensi') {
$('#add-target-value-query-container').show().prop('disabled', false);
}
});
$(".add-target").click(function () {
const id = $(this).data('id');
@@ -0,0 +1,196 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasUpdateTarget"
aria-labelledby="offcanvasUpdateTargetLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasUpdateTargetLabel" class="offcanvas-title">Edit Target</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="pt-0" id="updateTargetForm" action="{{ route('framework.transformasi.aktivitas.target.update', [$framework->id, ':transformasiId', ':aktivitasId', ':id']) }}" method="POST">
@csrf
@method('PUT')
<input type="hidden" id="update-target-transformasi-id" name="transformasi_id"/>
<input type="hidden" id="update-target-aktivitas-id" name="aktivitas_id"/>
<input type="hidden" id="update-target-id" name="id"/>
<div class="mb-6 form-control-validation">
<label class="form-label" for="update-target-variabel">Variabel Target<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="update-target-variabel" name="variabel" placeholder="Cth: AK1" />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="update-target-tipe">Tipe Data Target<span class="text-danger">*</span></label>
<select class="form-select" id="update-target-tipe" name="tipe">
<option value="integer">Integer</option>
<option value="float">Float</option>
<option value="boolean">Boolean</option>
</select>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label form-check-label" for="update-target-target">Target<span class="text-danger">*</span></label>
<input type="number" step="1" min="0" class="form-control" id="update-target-target-integer" name="target_integer" placeholder="Cth: 100" />
<input type="number" step="any" min="0" class="form-control" style="display: none;" id="update-target-target-float" name="target_float" placeholder="Cth: 100.23" />
<div class="form-check form-check-inline" id="update-target-target-boolean-true" style="display: none;">
<input type="radio" name="target_boolean" value="1" />
<label class="form-check-label" for="update-target-target-boolean-true">Ya</label>
</div>
<div class="form-check form-check-inline" id="update-target-target-boolean-false" style="display: none;">
<input type="radio" name="target_boolean" value="0" />
<label class="form-check-label" for="update-target-target-boolean-false">Tidak</label>
</div>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label form-check-label" for="update-target-baseline">Baseline<span class="text-danger">*</span></label>
<input type="number" step="1" min="0" class="form-control" id="update-target-baseline-integer" name="baseline_integer" placeholder="Cth: 100" />
<input type="number" step="any" min="0" class="form-control" style="display: none;" id="update-target-baseline-float" name="baseline_float" placeholder="Cth: 100.23" />
<div class="form-check form-check-inline" id="update-target-baseline-boolean-true" style="display: none;">
<input type="radio" name="baseline_boolean" value="1" />
<label class="form-check-label" for="update-target-baseline-boolean-true">Ya</label>
</div>
<div class="form-check form-check-inline" id="update-target-baseline-boolean-false" style="display: none;">
<input type="radio" name="baseline_boolean" value="0" />
<label class="form-check-label" for="update-target-baseline-boolean-false">Tidak</label>
</div>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="update-target-tipe-realisasi">Tipe Perhitungan Nilai Realisasi<span class="text-danger">*</span></label>
<select class="form-select" id="update-target-tipe-realisasi" name="tipe_realisasi">
<option value="manual">Input Manual</option>
<option value="referensi">Dihitung Otomatis</option>
</select>
</div>
<div class="mb-6 form-control-validation" id="update-target-value-query-container" style="display: none;">
<label class="form-label" for="update-target-value-query">Query Perhitungan Nilai Realisasi</label>
<textarea class="form-control" id="update-target-value-query" name="value_query" rows="3"></textarea>
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasUpdateTarget = new bootstrap.Offcanvas(document.getElementById('offcanvasUpdateTarget'));
$('#update-target-tipe, #update-target-tipe-realisasi').select2({
dropdownParent: $('#offcanvasUpdateTarget'),
width: '100%',
});
$(".edit-target").click(function () {
const id = $(this).data('id');
const transformasiId = $(this).data('transformasi-id');
const aktivitasId = $(this).data('aktivitas-id');
console.log('Add target, aktivitas id: ' + id);
$('#update-target-aktivitas-id').val(aktivitasId);
$('#update-target-transformasi-id').val(transformasiId);
$('#update-target-id').val(id);
$.get("{{ route('framework.transformasi.aktivitas.target.show', [$framework->id, ':transformasiId', ':aktivitasId', ':id']) }}"
.replace(':aktivitasId', aktivitasId)
.replace(':transformasiId', transformasiId)
.replace(':id', id), function (res) {
if (res.status === 'success') {
$('#update-target-variabel').val(res.data.variabel);
$('#update-target-tipe').val(res.data.tipe).trigger('change');
$('#update-target-target-integer').val(res.data.target_integer);
$('#update-target-target-float').val(res.data.target_float);
$('#update-target-target-boolean-true').prop('checked', res.data.target_boolean);
$('#update-target-target-boolean-false').prop('checked', !res.data.target_boolean);
$('#update-target-baseline-integer').val(res.data.baseline_integer);
$('#update-target-baseline-float').val(res.data.baseline_float);
$('#update-target-baseline-boolean-true').prop('checked', res.data.baseline_boolean);
$('#update-target-baseline-boolean-false').prop('checked', !res.data.baseline_boolean);
$('#update-target-tipe-realisasi').val(res.data.tipe_realisasi).trigger('change');
$('#update-target-value-query').val(res.data.value_query);
offcanvasUpdateTarget.show();
}
});
});
$('#update-target-tipe').change(function() {
const tipe = $(this).val();
$('#update-target-target-integer, #update-target-target-float, #update-target-target-boolean-true, #update-target-target-boolean-false').hide().prop('disabled', true);
if (tipe === 'integer') {
$('#update-target-target-integer').show().prop('disabled', false);
} else if (tipe === 'float') {
$('#update-target-target-float').show().prop('disabled', false);
} else if (tipe === 'boolean') {
$('#update-target-target-boolean-true, #update-target-target-boolean-false').show().prop('disabled', false);
}
$('#update-target-baseline-integer, #update-target-baseline-float, #update-target-baseline-boolean-true, #update-target-baseline-boolean-false').hide().prop('disabled', true);
if (tipe === 'integer') {
$('#update-target-baseline-integer').show().prop('disabled', false);
} else if (tipe === 'float') {
$('#update-target-baseline-float').show().prop('disabled', false);
} else if (tipe === 'boolean') {
$('#update-target-baseline-boolean-true, #update-target-baseline-boolean-false').show().prop('disabled', false);
}
});
$('#update-target-tipe-realisasi').change(function() {
const tipe = $(this).val();
$('#update-target-value-query-container').hide().prop('disabled', true);
if (tipe === 'referensi') {
$('#update-target-value-query-container').show().prop('disabled', false);
}
});
$('#updateTargetForm').validate({
rules: {
variabel: { required: true },
tipe: { required: true },
target_integer: { required: true, number: true, min: 0 },
target_float: { required: true, number: true, min: 0 },
target_boolean: { required: true }
},
messages: {
variabel: { required: "Variabel target wajib diisi" },
tipe: { required: "Tipe data target wajib diisi" },
target_integer: { required: "Target integer wajib diisi", number: "Harus berupa angka", min: "Harus berupa angka positif" },
target_float: { required: "Target float wajib diisi", number: "Harus berupa angka", min: "Harus berupa angka positif" },
target_boolean: { required: "Target boolean wajib diisi" }
},
submitHandler: function (form) {
$.ajax({
url: $(form).attr('action')
.replace(':aktivitasId', $('#update-target-aktivitas-id').val())
.replace(':transformasiId', $('#update-target-transformasi-id').val())
.replace(':id', $('#update-target-id').val()),
type: 'PUT',
data: $(form).serialize(),
success: function () {
offcanvasUpdateTarget.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -0,0 +1,176 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasAddVerifikasi"
aria-labelledby="offcanvasAddVerifikasiLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasAddVerifikasiLabel" class="offcanvas-title">Tambah Verifikasi</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="t-0" id="addNewVerifikasiForm" action="{{ route('verifikasi.store') }}" method="POST">
@csrf
<input type="hidden" id="add-verifikasi-transformasi-id" name="transformasi_id"/>
<input type="hidden" id="add-verifikasi-aktivitas-id" name="aktivitas_id"/>
<input type="hidden" id="add-verifikasi-target-id" name="target_id"/>
<input type="hidden" id="add-verifikasi-realisasi-id" name="realisasi_id"/>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-verifikasi-variabel">Variabel Target<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-verifikasi-variabel" name="variabel" placeholder="Cth: AK1" readonly />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label form-check-label" for="add-verifikasi-target">Target<span class="text-danger">*</span></label>
<input type="number" step="1" min="0" class="form-control" id="add-verifikasi-target-integer" name="target_integer" placeholder="Cth: 100" readonly />
<input type="number" step="any" min="0" class="form-control" style="display: none;" id="add-verifikasi-target-float" name="target_float" placeholder="Cth: 100.23" readonly />
<div class="form-check form-check-inline" id="add-verifikasi-target-boolean-true" style="display: none;">
<input type="radio" name="target_boolean" value="1" readonly />
<label class="form-check-label" for="add-verifikasi-target-boolean-true">Ya</label>
</div>
<div class="form-check form-check-inline" id="add-verifikasi-target-boolean-false" style="display: none;">
<input type="radio" name="target_boolean" value="0" readonly />
<label class="form-check-label" for="add-verifikasi-target-boolean-false">Tidak</label>
</div>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label form-check-label" for="add-verifikasi-baseline">Baseline<span class="text-danger">*</span></label>
<input type="number" step="1" min="0" class="form-control" id="add-verifikasi-baseline-integer" name="baseline_integer" placeholder="Cth: 100" readonly />
<input type="number" step="any" min="0" class="form-control" style="display: none;" id="add-verifikasi-baseline-float" name="baseline_float" placeholder="Cth: 100.23" readonly />
<div class="form-check form-check-inline" id="add-verifikasi-baseline-boolean-true" style="display: none;">
<input type="radio" name="baseline_boolean" value="1" readonly />
<label class="form-check-label" for="add-verifikasi-baseline-boolean-true">Ya</label>
</div>
<div class="form-check form-check-inline" id="add-verifikasi-baseline-boolean-false" style="display: none;">
<input type="radio" name="baseline_boolean" value="0" readonly />
<label class="form-check-label" for="add-verifikasi-baseline-boolean-false">Tidak</label>
</div>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label form-check-label" for="add-verifikasi-realisasi">Capaian Realisasi<span class="text-danger">*</span></label>
<input type="number" step="1" min="0" class="form-control" id="add-verifikasi-realisasi-integer" name="realisasi_integer" placeholder="Cth: 100" readonly />
<input type="number" step="any" min="0" class="form-control" style="display: none;" id="add-verifikasi-realisasi-float" name="realisasi_float" placeholder="Cth: 100.23" readonly />
<div class="form-check form-check-inline" id="add-verifikasi-realisasi-boolean-true" style="display: none;">
<input type="radio" name="realisasi_boolean" value="1" readonly />
<label class="form-check-label" for="add-verifikasi-realisasi-boolean-true">Ya</label>
</div>
<div class="form-check form-check-inline" id="add-verifikasi-realisasi-boolean-false" style="display: none;">
<input type="radio" name="realisasi_boolean" value="0" readonly />
<label class="form-check-label" for="add-verifikasi-realisasi-boolean-false">Tidak</label>
</div>
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-verifikasi-dokumen">Dokumen Verifikasi<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-verifikasi-dokumen" name="dokumen" placeholder="Cth: AK1" />
</div>
<div class="mb-6 form-control-validation">
<label class="form-label" for="add-verifikasi-link-dokumen">Link Dokumen<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="add-verifikasi-link-dokumen" name="link_dokumen" placeholder="Cth: https://example.com/dokumen" />
</div>
<button type="submit" class="btn btn-success me-3 data-submit">Simpan</button>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Batal</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasAddVerifikasi = new bootstrap.Offcanvas(document.getElementById('offcanvasAddVerifikasi'));
$(".add-verifikasi").click(function () {
const targetId = $(this).data('target-id');
const aktivitasId = $(this).data('aktivitas-id');
const transformasiId = $(this).data('transformasi-id');
const realisasiId = $(this).data('realisasi-id');
console.log('Add target, target id: ' + targetId);
$('#add-verifikasi-aktivitas-id').val(aktivitasId);
$('#add-verifikasi-transformasi-id').val(transformasiId);
$('#add-verifikasi-target-id').val(targetId);
$('#add-verifikasi-realisasi-id').val(realisasiId);
$.get("{{ route('realisasi.show', ':id') }}"
.replace(':id', realisasiId), function (res) {
if (res.status === 'success') {
$('#add-verifikasi-variabel').val(res.data.target.variabel);
$('#add-verifikasi-target-integer').val(res.data.target.target_integer);
$('#add-verifikasi-target-float').val(res.data.target.target_float);
$('#add-verifikasi-target-boolean-true').prop('checked', res.data.target.target_boolean);
$('#add-verifikasi-target-boolean-false').prop('checked', !res.data.target.target_boolean);
$('#add-verifikasi-baseline-integer').val(res.data.target.baseline_integer);
$('#add-verifikasi-baseline-float').val(res.data.target.baseline_float);
$('#add-verifikasi-baseline-boolean-true').prop('checked', res.data.target.baseline_boolean);
$('#add-verifikasi-baseline-boolean-false').prop('checked', !res.data.target.baseline_boolean);
$('#add-verifikasi-realisasi-integer').val(res.data.realisasi_integer);
$('#add-verifikasi-realisasi-float').val(res.data.realisasi_float);
$('#add-verifikasi-realisasi-boolean-true').prop('checked', res.data.realisasi_boolean);
$('#add-verifikasi-realisasi-boolean-false').prop('checked', !res.data.realisasi_boolean);
if (res.data.target.tipe === 'integer') {
$('#add-verifikasi-target-integer').show().prop('disabled', false);
$('#add-verifikasi-baseline-integer').show().prop('disabled', false);
$('#add-verifikasi-realisasi-integer').show().prop('disabled', false);
} else if (res.data.target.tipe === 'float') {
$('#add-verifikasi-target-float').show().prop('disabled', false);
$('#add-verifikasi-baseline-float').show().prop('disabled', false);
$('#add-verifikasi-realisasi-float').show().prop('disabled', false);
} else if (res.data.target.tipe === 'boolean') {
$('#add-verifikasi-target-boolean-true, #add-verifikasi-target-boolean-false').show().prop('disabled', false);
$('#add-verifikasi-baseline-boolean-true, #add-verifikasi-baseline-boolean-false').show().prop('disabled', false);
$('#add-verifikasi-realisasi-boolean-true, #add-verifikasi-realisasi-boolean-false').show().prop('disabled', false);
}
offcanvasAddVerifikasi.show();
}
});
});
$('#addNewVerifikasiForm').validate({
rules: {
realisasi_integer: { required: true, number: true, min: 0 },
realisasi_float: { required: true, number: true, min: 0 },
realisasi_boolean: { required: true }
},
messages: {
realisasi_integer: { required: "Realisasi integer wajib diisi", number: "Harus berupa angka", min: "Harus berupa angka positif" },
realisasi_float: { required: "Realisasi float wajib diisi", number: "Harus berupa angka", min: "Harus berupa angka positif" },
realisasi_boolean: { required: "Realisasi boolean wajib diisi" }
},
submitHandler: function (form) {
$.ajax({
url: $(form).attr('action'),
type: 'POST',
data: $(form).serialize(),
success: function () {
offcanvasAddVerifikasi.hide();
form.reset();
Swal.fire({ icon: 'success', title: 'Berhasil!', showConfirmButton: false, timer: 1500 })
.then(() => window.location.reload());
},
error: function (xhr) {
console.error(xhr);
Swal.fire({ icon: 'error', title: 'Gagal!', text: xhr.responseJSON?.message || 'Error' });
}
});
},
errorElement: 'div',
errorPlacement: function (error, element) {
error.addClass('invalid-feedback');
element.closest('.mb-6, .form-control-validation').append(error);
},
highlight: function (element) {
$(element).addClass('is-invalid');
},
unhighlight: function (element) {
$(element).removeClass('is-invalid');
}
});
});
</script>
@endpush
@@ -0,0 +1,117 @@
<div
class="offcanvas offcanvas-end"
tabindex="-1"
id="offcanvasListVerifikasi"
aria-labelledby="offcanvasListVerifikasiLabel">
<div class="offcanvas-header border-bottom">
<h5 id="offcanvasListVerifikasiLabel" class="offcanvas-title">Daftar Dokumen Verifikasi</h5>
<button
type="button"
class="btn-close text-reset"
data-bs-dismiss="offcanvas"
aria-label="Tutup"></button>
</div>
<div class="offcanvas-body mx-0 flex-grow-0 p-6 h-100">
<form class="t-0" id="historyVerifikasiForm">
<input type="hidden" id="list-verifikasi-transformasi-id" name="transformasi_id"/>
<input type="hidden" id="list-verifikasi-aktivitas-id" name="aktivitas_id"/>
<input type="hidden" id="list-verifikasi-target-id" name="target_id"/>
<input type="hidden" id="list-verifikasi-realisasi-id" name="realisasi_id"/>
<div class="mb-6 form-control-validation">
<label class="form-label" for="list-verifikasi-variabel">Variabel Target<span class="text-danger">*</span></label>
<input type="text" class="form-control" id="list-verifikasi-variabel" name="variabel" placeholder="Cth: AK1" readonly />
</div>
<table class="table table-hover" id="historyVerifikasiTable">
<thead>
<tr>
<th>Dokumen</th>
<th>Aksi</th>
</tr>
</thead>
<tbody id="historyVerifikasiTableBody">
</tbody>
</table>
<button type="reset" class="btn btn-label-danger" data-bs-dismiss="offcanvas">Tutup</button>
</form>
</div>
</div>
@push('scripts')
<script>
$(document).ready(function() {
const offcanvasListVerifikasi = new bootstrap.Offcanvas(document.getElementById('offcanvasListVerifikasi'));
$(".list-verifikasi").click(function () {
const targetId = $(this).data('target-id');
const aktivitasId = $(this).data('aktivitas-id');
const transformasiId = $(this).data('transformasi-id');
const realisasiId = $(this).data('realisasi-id');
$('#list-verifikasi-aktivitas-id').val(aktivitasId);
$('#list-verifikasi-transformasi-id').val(transformasiId);
$('#list-verifikasi-target-id').val(targetId);
$('#list-verifikasi-realisasi-id').val(realisasiId);
$.get("{{ route('realisasi.show', ':id') }}"
.replace(':id', realisasiId), function (res) {
if (res.status === 'success') {
$('#list-verifikasi-variabel').val(res.data.target.variabel);
let rows = '';
if (res.data.tipe === 'integer') {
realisasiValue = res.data.realisasi_integer;
} else if (res.data.tipe === 'float') {
realisasiValue = res.data.realisasi_float;
} else if (res.data.tipe === 'boolean') {
realisasiValue = res.data.realisasi_boolean ? 'Ya' : 'Tidak';
}
res.data.verifikasi.forEach(function (verifikasi) {
rows += '<tr><td>' + verifikasi.dokumen + '</td><td>'
+ '<a href="' + verifikasi.link_dokumen + '" target="_blank" class="btn btn-icon btn-text-secondary rounded-pill">'
+ '<i class="ti tabler-file ti-md"></i></a>'
+ '<a href="javascript:;" class="btn btn-icon btn-text-danger rounded-pill delete-verifikasi" data-id="' + verifikasi.id + '" data-nama="' + verifikasi.dokumen + '">'
+ '<i class="ti tabler-trash ti-md"></i></a>'
+ '</td></tr>';
});
$('#historyVerifikasiTableBody').html(rows);
offcanvasListVerifikasi.show();
}
});
$('#historyVerifikasiTable').on('click', '.delete-verifikasi', function () {
const id = $(this).data('id');
const nama = $(this).data('nama');
Swal.fire({
title: 'Hapus?',
text: `Dokumen Verifikasi: ${nama} akan dihapus!`,
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, Hapus!',
customClass: { confirmButton: 'btn btn-danger me-3', cancelButton: 'btn btn-label-secondary' },
buttonsStyling: false
}).then(function (result) {
if (result.value) {
$.ajax({
url: "{{ route('verifikasi.destroy', ':id') }}".replace(':id', id),
type: 'POST',
data: {
_token: '{{ csrf_token() }}',
_method: 'DELETE'
},
success: function () {
Swal.fire({ icon: 'success', title: 'Terhapus!', showConfirmButton: false, timer: 1500 })
.then(() => location.reload());
}
});
}
});
});
});
});
</script>
@endpush