dashboard produksi & ganti warna chart
This commit is contained in:
@@ -8,7 +8,7 @@ enum FrekuensiPemangkasanPendamping: string
|
||||
case TIDAK_RUTIN = 'Memangkas, tapi tidak rutin dan tidak tahu tekniknya';
|
||||
case SATU_KALI_TAHUN = 'Memangkas 1 kali/tahun, tahu jenis tekniknya';
|
||||
case DUA_KALI_TAHUN = 'Memangkas 2 kali/tahun, tahu jenis tekniknya';
|
||||
case LEBIH_DUA_KALI_TAHUN = 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat dan pohon penaung juga dipangkas rutin';
|
||||
case LEBIH_DUA_KALI_TAHUN = 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat, dan pohon penaung juga dipangkas rutin';
|
||||
|
||||
public function label(): string
|
||||
{
|
||||
@@ -17,7 +17,7 @@ enum FrekuensiPemangkasanPendamping: string
|
||||
self::TIDAK_RUTIN => 'Memangkas, tapi tidak rutin dan tidak tahu tekniknya',
|
||||
self::SATU_KALI_TAHUN => 'Memangkas 1 kali/tahun, tahu jenis tekniknya',
|
||||
self::DUA_KALI_TAHUN => 'Memangkas 2 kali/tahun, tahu jenis tekniknya',
|
||||
self::LEBIH_DUA_KALI_TAHUN => 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat dan pohon penaung juga dipangkas rutin',
|
||||
self::LEBIH_DUA_KALI_TAHUN => 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat, dan pohon penaung juga dipangkas rutin',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@ enum FrekuensiPemangkasanUtama: int
|
||||
self::TIDAK_RUTIN => 'Memangkas, tapi tidak rutin dan tidak tahu tekniknya',
|
||||
self::SATU_KALI_TAHUN => 'Memangkas 1 kali/tahun, tahu jenis tekniknya',
|
||||
self::DUA_KALI_TAHUN => 'Memangkas 2 kali/tahun, tahu jenis tekniknya',
|
||||
self::LEBIH_DUA_KALI_TAHUN => 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat dan pohon penaung juga dipangkas rutin',
|
||||
self::LEBIH_DUA_KALI_TAHUN => 'Memangkas lebih dari 2 kali/tahun dengan teknik yang tepat, dan pohon penaung juga dipangkas rutin',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -16,8 +16,8 @@ enum MetodePenangananHamaPenyakit: int
|
||||
self::TIDAK_ADA => 'Tidak ada penanganan',
|
||||
self::CARA_TRADISIONAL => 'Cara tradisional (doa/ritual)',
|
||||
self::PESTISIDA_SANITASI => 'Pestisida atau sanitasi',
|
||||
self::KOMBINASI => 'Kombinasi pestisada dan sanitasi',
|
||||
self::PHT => 'Pengendalian hama terpadu(PHT)',
|
||||
self::KOMBINASI => 'Kombinasi pestisida dan sanitasi',
|
||||
self::PHT => 'Pengendalian hama terpadu (PHT)',
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -5,8 +5,13 @@ namespace App\Http\Controllers\Web\Dashboard;
|
||||
use App\Enums\KeparahanSerangan;
|
||||
use App\Enums\KondisiTanaman;
|
||||
use App\Enums\PenyakitTanaman;
|
||||
use App\Enums\PraktikBudidaya\FrekuensiPemangkasanUtama;
|
||||
use App\Enums\PraktikBudidaya\FrekuensiSanitasiLahan;
|
||||
use App\Enums\PraktikBudidaya\MetodePenangananHamaPenyakit;
|
||||
use App\Enums\PraktikBudidaya\PemberianPupuk;
|
||||
use App\Enums\SeranganHama;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\Pertanian\Komoditas;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
@@ -67,6 +72,7 @@ class SummaryController extends Controller
|
||||
->get([
|
||||
'id',
|
||||
'lahan_id',
|
||||
'komoditas_id',
|
||||
'kondisi_tanaman',
|
||||
'umur_tanaman',
|
||||
'serangan_hama',
|
||||
@@ -87,49 +93,189 @@ class SummaryController extends Controller
|
||||
|
||||
$landMetrics = $this->karakteristikLandMetrics($request);
|
||||
$totalLuasLahan = $landMetrics['total_luas_lahan'];
|
||||
$petaniCount = $landMetrics['total_petani'];
|
||||
$avgLuasPerPetani = $landMetrics['ratarata_luas_lahan'];
|
||||
$jumlahTanaman = $conditionSource->whereNotNull('kondisi_tanaman')->count();
|
||||
$productivityCards = $this->productivityKpiCards($lahanIds);
|
||||
$praktikBudidaya = $this->filteredPraktikBudidaya($request);
|
||||
|
||||
$kpiCards = [
|
||||
[
|
||||
'value' => number_format($totalLuasLahan, 2, ',', '.'),
|
||||
'unit' => 'Ha',
|
||||
'unit' => 'ha',
|
||||
'label' => 'Total Luas Lahan',
|
||||
'description' => 'Luas keseluruhan lahan terfilter',
|
||||
'description' => 'Seluruh lahan petani',
|
||||
'icon' => 'tabler-chart-area-line',
|
||||
'variant' => 'success',
|
||||
],
|
||||
[
|
||||
'value' => number_format($avgLuasPerPetani, 2, ',', '.'),
|
||||
'unit' => 'Ha/petani',
|
||||
'label' => 'Rata-Rata Luas Lahan per Petani',
|
||||
'description' => number_format($petaniCount, 0, ',', '.').' petani terfilter',
|
||||
'unit' => 'ha',
|
||||
'label' => 'Rata-rata Luas Lahan',
|
||||
'description' => 'Rata-rata per petani',
|
||||
'icon' => 'tabler-ruler-measure',
|
||||
'variant' => 'danger',
|
||||
],
|
||||
[
|
||||
'value' => number_format($jumlahTanaman, 0, ',', '.'),
|
||||
'unit' => 'tanaman',
|
||||
'label' => 'Jumlah Tanaman',
|
||||
'description' => 'Total tanaman pada chart kondisi tanaman',
|
||||
'icon' => 'tabler-plant-2',
|
||||
'variant' => 'secondary',
|
||||
],
|
||||
];
|
||||
$kpiCards = array_merge($kpiCards, $productivityCards);
|
||||
|
||||
return [
|
||||
'kpiCards' => $kpiCards,
|
||||
'chartData' => [
|
||||
'umurLahan' => $this->ageBucketChart($lahan, 'umur_lahan'),
|
||||
'umurTanaman' => $this->ageBucketChart($healthSource, 'umur_tanaman'),
|
||||
'jumlahTanamanPerKomoditas' => $this->plantCountByCommodityChart($tanaman, $lahan),
|
||||
'kondisiTanaman' => $this->attributeChart($conditionSource, 'kondisi_tanaman', fn ($value) => $this->conditionLabel($value)),
|
||||
'keparahanSerangan' => $this->attributeChart($healthSource, 'keparahan_serangan', fn ($value) => $this->severityLabel($value)),
|
||||
'gejalaSerangan' => $this->symptomChart($healthSource),
|
||||
'intensitasPemangkasan' => $this->enumOptionChart($praktikBudidaya, 'frekuensi_pemangkasan_utama', FrekuensiPemangkasanUtama::class),
|
||||
'intensitasPemupukan' => $this->enumOptionChart($praktikBudidaya, 'pemberian_pupuk', PemberianPupuk::class),
|
||||
'metodePenangananHamaPenyakit' => $this->enumOptionChart($praktikBudidaya, 'metode_penanganan_opt', MetodePenangananHamaPenyakit::class),
|
||||
'intensitasPembersihanLahan' => $this->enumOptionChart($praktikBudidaya, 'frekuensi_sanitasi_lahan', FrekuensiSanitasiLahan::class),
|
||||
'sumberAirKebun' => $this->arrayOptionChart(
|
||||
$praktikBudidaya,
|
||||
'sumber_air',
|
||||
['Sungai/mata air', 'Sumur', 'Mengandalkan hujan saja', 'Lainnya'],
|
||||
fn ($value) => in_array($value, ['Sungai/mata air', 'Sumur', 'Mengandalkan hujan saja'], true) ? $value : 'Lainnya',
|
||||
),
|
||||
'kebiasaanPraktikBaik' => $this->arrayOptionChart(
|
||||
$praktikBudidaya,
|
||||
'kebiasaan_konservasi_tanah',
|
||||
[
|
||||
'Membuat pematang/tanggul tanah di lahan miring supaya tanah tidak longsor ke bawah',
|
||||
'Menumpuk daun kering/sisa tanaman di sekitar pohon',
|
||||
'Menanam tanaman lain di sela-sela komoditas utama untuk menutupi tanah',
|
||||
'Membuat lubang atau parit kecil untuk menampung air hujan',
|
||||
'Tidak ada yang seperti itu di kebun saya',
|
||||
],
|
||||
fn ($value) => $value === 'Membuat pematang atau tanggul tanah di lahan miring supaya tanah tidak longsor ke bawah'
|
||||
? 'Membuat pematang/tanggul tanah di lahan miring supaya tanah tidak longsor ke bawah'
|
||||
: $value,
|
||||
),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
private function filteredPraktikBudidaya(Request $request)
|
||||
{
|
||||
$petaniIds = $this->filteredPetaniIdsQuery($request);
|
||||
|
||||
return DB::table('praktik_budidaya')
|
||||
->whereNull('praktik_budidaya.deleted_at')
|
||||
->whereIn('praktik_budidaya.petani_id', $petaniIds)
|
||||
->get([
|
||||
'frekuensi_pemangkasan_utama',
|
||||
'pemberian_pupuk',
|
||||
'metode_penanganan_opt',
|
||||
'frekuensi_sanitasi_lahan',
|
||||
'sumber_air',
|
||||
'kebiasaan_konservasi_tanah',
|
||||
]);
|
||||
}
|
||||
|
||||
private function plantCountByCommodityChart($tanaman, $lahan): array
|
||||
{
|
||||
$lahanKomoditas = $lahan
|
||||
->mapWithKeys(fn ($row) => [(int) $row->id => $row->komoditas_nama ?: 'Belum ditentukan']);
|
||||
|
||||
$komoditasNames = DB::table('master_komoditas')
|
||||
->whereNull('deleted_at')
|
||||
->pluck('nama', 'id');
|
||||
|
||||
$counts = $tanaman
|
||||
->map(function ($row) use ($komoditasNames, $lahanKomoditas) {
|
||||
if ($row->komoditas_id && $komoditasNames->has($row->komoditas_id)) {
|
||||
return $komoditasNames[$row->komoditas_id];
|
||||
}
|
||||
|
||||
return $lahanKomoditas[(int) $row->lahan_id] ?? 'Belum ditentukan';
|
||||
})
|
||||
->filter()
|
||||
->countBy()
|
||||
->sortDesc();
|
||||
|
||||
return [
|
||||
'labels' => $counts->keys()->values()->all(),
|
||||
'series' => $counts->values()->map(fn ($value) => (int) $value)->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function productivityKpiCards($lahanIds): array
|
||||
{
|
||||
$komoditas = Komoditas::query()
|
||||
->where('is_utama', true)
|
||||
->orderBy('nama')
|
||||
->limit(4)
|
||||
->get(['id', 'nama']);
|
||||
|
||||
$produksiRows = DB::table('produksi')
|
||||
->whereNull('produksi.deleted_at')
|
||||
->leftJoin('map_lahan as lahan_produksi', 'lahan_produksi.id', '=', 'produksi.lahan_id')
|
||||
->when($lahanIds->isNotEmpty(), fn ($query) => $query->whereIn('produksi.lahan_id', $lahanIds))
|
||||
->when($lahanIds->isEmpty(), fn ($query) => $query->whereRaw('1 = 0'))
|
||||
->selectRaw('COALESCE(produksi.komoditas_id, lahan_produksi.komoditas_id) as komoditas_id')
|
||||
->addSelect([
|
||||
'produksi.jumlah_panen',
|
||||
'produksi.satuan',
|
||||
'produksi.luas_panen',
|
||||
'produksi.produktivitas',
|
||||
])
|
||||
->get()
|
||||
->groupBy('komoditas_id');
|
||||
|
||||
$variants = ['primary', 'warning', 'info', 'secondary'];
|
||||
$icon = 'tabler-chart-bar';
|
||||
|
||||
return $komoditas
|
||||
->values()
|
||||
->map(function ($item, int $index) use ($produksiRows, $variants, $icon) {
|
||||
$rows = $produksiRows->get($item->id, collect());
|
||||
$average = $this->averageProductivity($rows);
|
||||
|
||||
return [
|
||||
'value' => number_format($average, 0, ',', '.'),
|
||||
'unit' => 'kg/ha',
|
||||
'label' => 'Produktivitas Rata-rata '.$item->nama,
|
||||
'description' => 'Rata-rata produktivitas per komoditas',
|
||||
'icon' => $icon,
|
||||
'variant' => $variants[$index % count($variants)],
|
||||
];
|
||||
})
|
||||
->all();
|
||||
}
|
||||
|
||||
private function averageProductivity($rows): float
|
||||
{
|
||||
if ($rows->isEmpty()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$storedAverage = $rows
|
||||
->pluck('produktivitas')
|
||||
->filter(fn ($value) => (float) $value > 0)
|
||||
->avg();
|
||||
|
||||
if ($storedAverage > 0) {
|
||||
return round((float) $storedAverage, 2);
|
||||
}
|
||||
|
||||
$totalProduksiKg = $rows->sum(fn ($row) => $this->toKilograms(
|
||||
(float) ($row->jumlah_panen ?? 0),
|
||||
(string) ($row->satuan ?? 'kg')
|
||||
));
|
||||
$totalLuasPanen = $rows->sum(fn ($row) => (float) ($row->luas_panen ?? 0));
|
||||
|
||||
return $totalLuasPanen > 0
|
||||
? round($totalProduksiKg / $totalLuasPanen, 2)
|
||||
: 0;
|
||||
}
|
||||
|
||||
private function toKilograms(float $value, string $unit): float
|
||||
{
|
||||
return match (strtolower($unit)) {
|
||||
'ton' => $value * 1000,
|
||||
'kuintal', 'kwintal' => $value * 100,
|
||||
default => $value,
|
||||
};
|
||||
}
|
||||
|
||||
private function filteredLahanQuery(Request $request)
|
||||
{
|
||||
return DB::table('map_lahan')
|
||||
@@ -271,6 +417,48 @@ class SummaryController extends Controller
|
||||
];
|
||||
}
|
||||
|
||||
private function enumOptionChart($rows, string $field, string $enumClass): array
|
||||
{
|
||||
$options = collect($enumClass::options())
|
||||
->mapWithKeys(fn ($option) => [(string) $option['value'] => $option['label']]);
|
||||
|
||||
$counts = $rows
|
||||
->pluck($field)
|
||||
->filter(fn ($value) => $value !== null && $value !== '')
|
||||
->map(fn ($value) => (string) $value)
|
||||
->countBy();
|
||||
|
||||
return [
|
||||
'labels' => $options->values()->all(),
|
||||
'series' => $options->keys()->map(fn ($value) => (int) ($counts[$value] ?? 0))->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function arrayOptionChart($rows, string $field, array $labels, ?callable $normalizer = null): array
|
||||
{
|
||||
$counts = collect($labels)->mapWithKeys(fn ($label) => [$label => 0]);
|
||||
|
||||
foreach ($rows as $row) {
|
||||
foreach ($this->arrayValues($row->{$field} ?? null) as $value) {
|
||||
$label = $normalizer ? $normalizer($value) : $value;
|
||||
if ($label === null || $label === '') {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!$counts->has($label)) {
|
||||
$counts[$label] = 0;
|
||||
}
|
||||
|
||||
$counts[$label] = (int) $counts[$label] + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'labels' => $counts->keys()->values()->all(),
|
||||
'series' => $counts->values()->map(fn ($value) => (int) $value)->values()->all(),
|
||||
];
|
||||
}
|
||||
|
||||
private function symptomChart($rows): array
|
||||
{
|
||||
$counts = collect();
|
||||
|
||||
@@ -489,34 +489,34 @@ class SurveyPetaniController extends Controller
|
||||
{
|
||||
return [
|
||||
[
|
||||
'color' => '#28a745',
|
||||
'bg' => 'rgba(40,167,69,.15)',
|
||||
'tx' => '#28a745',
|
||||
'color' => '#6FD68F',
|
||||
'bg' => 'rgba(111,214,143,.24)',
|
||||
'tx' => '#6FD68F',
|
||||
],
|
||||
[
|
||||
'color' => '#007bff',
|
||||
'bg' => 'rgba(0,123,255,.15)',
|
||||
'tx' => '#007bff',
|
||||
'color' => '#5FC7D6',
|
||||
'bg' => 'rgba(95,199,214,.24)',
|
||||
'tx' => '#5FC7D6',
|
||||
],
|
||||
[
|
||||
'color' => '#fd7e14',
|
||||
'bg' => 'rgba(253,126,20,.15)',
|
||||
'tx' => '#fd7e14',
|
||||
'color' => '#FFB46F',
|
||||
'bg' => 'rgba(255,180,111,.26)',
|
||||
'tx' => '#FFB46F',
|
||||
],
|
||||
[
|
||||
'color' => '#dc3545',
|
||||
'bg' => 'rgba(220,53,69,.15)',
|
||||
'tx' => '#dc3545',
|
||||
'color' => '#F47C86',
|
||||
'bg' => 'rgba(244,124,134,.26)',
|
||||
'tx' => '#F47C86',
|
||||
],
|
||||
[
|
||||
'color' => '#6f42c1',
|
||||
'bg' => 'rgba(111,66,193,.15)',
|
||||
'tx' => '#6f42c1',
|
||||
'color' => '#9D8EF0',
|
||||
'bg' => 'rgba(157,142,240,.26)',
|
||||
'tx' => '#9D8EF0',
|
||||
],
|
||||
[
|
||||
'color' => '#20c997',
|
||||
'bg' => 'rgba(32,201,151,.15)',
|
||||
'tx' => '#20c997',
|
||||
'color' => '#65D8BD',
|
||||
'bg' => 'rgba(101,216,189,.26)',
|
||||
'tx' => '#65D8BD',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
@@ -37,6 +37,46 @@ window.config = {
|
||||
danger: window.Helpers.getCssVar('danger-bg-subtle'),
|
||||
dark: window.Helpers.getCssVar('dark-bg-subtle')
|
||||
},
|
||||
chartColors: {
|
||||
series: ['#9D8EF0', '#6FD68F', '#FFB46F', '#5FC7D6', '#F47C86', '#9EA6B0', '#65D8BD', '#63C7E0'],
|
||||
semantic: {
|
||||
primary: '#9D8EF0',
|
||||
secondary: '#9EA6B0',
|
||||
success: '#6FD68F',
|
||||
info: '#5FC7D6',
|
||||
warning: '#FFB46F',
|
||||
danger: '#F47C86',
|
||||
purple: '#9D8EF0',
|
||||
teal: '#65D8BD',
|
||||
pink: '#F47C86',
|
||||
gray: '#9EA6B0'
|
||||
},
|
||||
text: {
|
||||
primary: '#7367F0',
|
||||
secondary: '#808390',
|
||||
success: '#28C76F',
|
||||
info: '#00BAD1',
|
||||
warning: '#FF9F43',
|
||||
danger: '#FF4C51',
|
||||
purple: '#7367F0',
|
||||
teal: '#20C997',
|
||||
pink: '#FF4C51',
|
||||
gray: '#808390'
|
||||
},
|
||||
categories: {
|
||||
coffee: '#6FD68F',
|
||||
vanilla: '#FFB46F',
|
||||
cacao: '#9D8EF0',
|
||||
candlenut: '#9EA6B0',
|
||||
shade: '#65D8BD',
|
||||
policyPurple: '#9D8EF0',
|
||||
policyPink: '#F47C86',
|
||||
marketGreen: '#6FD68F',
|
||||
marketRed: '#F47C86',
|
||||
marketBlue: '#5FC7D6'
|
||||
},
|
||||
grid: '#ECF0F6'
|
||||
},
|
||||
fontFamily: window.Helpers.getCssVar('font-family-base'),
|
||||
enableMenuLocalStorage: true // Enable menu state with local storage support
|
||||
};
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
cardColor = config.colors.cardColor;
|
||||
labelColor = config.colors.textMuted;
|
||||
headingColor = config.colors.headingColor;
|
||||
const pastelChart = config.chartColors || {};
|
||||
const pastelSemantic = pastelChart.semantic || {};
|
||||
const pastelSeries = pastelChart.series || ['#9D8EF0', '#6FD68F', '#FFB46F', '#5FC7D6', '#F47C86', '#9EA6B0', '#65D8BD', '#63C7E0'];
|
||||
|
||||
// swiper loop and autoplay
|
||||
// --------------------------------------------------------------------
|
||||
@@ -48,16 +51,10 @@
|
||||
grid: {
|
||||
show: false
|
||||
},
|
||||
colors: [config.colors.success],
|
||||
colors: [pastelSemantic.success || '#6FD68F'],
|
||||
fill: {
|
||||
type: 'gradient',
|
||||
gradient: {
|
||||
shadeIntensity: 1,
|
||||
opacityFrom: 0.4,
|
||||
gradientToColors: [config.colors.cardColor],
|
||||
opacityTo: 0.1,
|
||||
stops: [0, 100]
|
||||
}
|
||||
type: 'solid',
|
||||
opacity: 0.22
|
||||
},
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
@@ -151,13 +148,13 @@
|
||||
}
|
||||
},
|
||||
colors: [
|
||||
config.colors_label.primary,
|
||||
config.colors_label.primary,
|
||||
config.colors_label.primary,
|
||||
config.colors_label.primary,
|
||||
config.colors.primary,
|
||||
config.colors_label.primary,
|
||||
config.colors_label.primary
|
||||
pastelSemantic.secondary || '#9EA6B0',
|
||||
pastelSemantic.secondary || '#9EA6B0',
|
||||
pastelSemantic.secondary || '#9EA6B0',
|
||||
pastelSemantic.secondary || '#9EA6B0',
|
||||
pastelSemantic.primary || '#9D8EF0',
|
||||
pastelSemantic.secondary || '#9EA6B0',
|
||||
pastelSemantic.secondary || '#9EA6B0'
|
||||
],
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
@@ -262,18 +259,10 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
colors: [config.colors.primary],
|
||||
colors: [pastelSemantic.primary || '#9D8EF0'],
|
||||
fill: {
|
||||
type: 'gradient',
|
||||
gradient: {
|
||||
shade: 'dark',
|
||||
shadeIntensity: 0.5,
|
||||
gradientToColors: [config.colors.primary],
|
||||
inverseColors: true,
|
||||
opacityFrom: 1,
|
||||
opacityTo: 0.6,
|
||||
stops: [30, 70, 100]
|
||||
}
|
||||
type: 'solid',
|
||||
opacity: 0.9
|
||||
},
|
||||
stroke: {
|
||||
dashArray: 10
|
||||
@@ -356,7 +345,10 @@
|
||||
}
|
||||
},
|
||||
|
||||
colors: [config.colors.primary, config.colors.secondary],
|
||||
colors: [
|
||||
pastelSeries[0] || '#9D8EF0',
|
||||
pastelSeries[1] || '#6FD68F'
|
||||
],
|
||||
dataLabels: {
|
||||
enabled: false
|
||||
},
|
||||
@@ -369,7 +361,10 @@
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
colors: [config.colors.primary, config.colors.secondary],
|
||||
colors: [
|
||||
pastelSeries[0] || '#9D8EF0',
|
||||
pastelSeries[1] || '#6FD68F'
|
||||
],
|
||||
fill: {
|
||||
opacity: 1
|
||||
},
|
||||
|
||||
@@ -238,6 +238,10 @@
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const pastelChart = config.chartColors || {};
|
||||
const pastelSemantic = pastelChart.semantic || {};
|
||||
const pastelCategory = pastelChart.categories || {};
|
||||
const gridColor = pastelChart.grid || '#E8EDF5';
|
||||
|
||||
// 1. Inisialisasi Icons
|
||||
if(typeof lucide !== 'undefined') {
|
||||
@@ -255,7 +259,7 @@
|
||||
{
|
||||
label: 'Desa Mengadopsi (Realisasi)',
|
||||
data: [0, 3, 8, 15], // Kumulatif: 3 Desa Inti + 12 Baru
|
||||
backgroundColor: '#8b5cf6', // Purple-500
|
||||
backgroundColor: pastelCategory.policyPurple || '#9D8EF0',
|
||||
borderRadius: 4,
|
||||
order: 2
|
||||
},
|
||||
@@ -263,12 +267,12 @@
|
||||
label: 'Target Perluasan ToC',
|
||||
type: 'line',
|
||||
data: [3, 5, 10, 15],
|
||||
borderColor: '#d946ef', // Fuchsia-500
|
||||
borderColor: pastelCategory.policyPink || '#F47C86',
|
||||
backgroundColor: 'transparent',
|
||||
borderWidth: 3,
|
||||
borderDash: [5, 5],
|
||||
tension: 0.3,
|
||||
pointBackgroundColor: '#d946ef',
|
||||
pointBackgroundColor: pastelCategory.policyPink || '#F47C86',
|
||||
order: 1
|
||||
}
|
||||
]
|
||||
@@ -280,7 +284,7 @@
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: { color: '#f3f4f6' },
|
||||
grid: { color: gridColor },
|
||||
ticks: { stepSize: 5 }
|
||||
},
|
||||
x: { grid: { display: false } }
|
||||
@@ -309,10 +313,10 @@
|
||||
datasets: [{
|
||||
data: [4, 2, 1, 1],
|
||||
backgroundColor: [
|
||||
'#f59e0b', // Amber-500
|
||||
'#10b981', // Emerald-500
|
||||
'#3b82f6', // Blue-500
|
||||
'#8b5cf6' // Purple-500
|
||||
pastelSemantic.warning || '#FFB46F',
|
||||
pastelSemantic.success || '#6FD68F',
|
||||
pastelSemantic.info || '#5FC7D6',
|
||||
pastelCategory.policyPurple || '#9D8EF0'
|
||||
],
|
||||
borderWidth: 2,
|
||||
borderColor: '#ffffff',
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
transition: transform .2s ease;
|
||||
}
|
||||
.tk-stat:hover { transform: translateX(4px); }
|
||||
.tk-stat.green { border-left-color: #28a745; }
|
||||
.tk-stat.blue { border-left-color: #007bff; }
|
||||
.tk-stat.amber { border-left-color: #fd7e14; }
|
||||
.tk-stat.green { border-left-color: #6FD68F; }
|
||||
.tk-stat.blue { border-left-color: #5FC7D6; }
|
||||
.tk-stat.amber { border-left-color: #FFB46F; }
|
||||
|
||||
.tk-stat-lbl {
|
||||
font-size: 10px; text-transform: uppercase;
|
||||
@@ -88,38 +88,38 @@
|
||||
'new_month' => 12,
|
||||
|
||||
'enumerators' => [
|
||||
['name'=>'Enumerator Ngada 1','done'=>72,'target'=>80,'color'=>'#28a745','bg'=>'rgba(40,167,69,.15)','tx'=>'#28a745'],
|
||||
['name'=>'Enumerator Ngada 2','done'=>58,'target'=>80,'color'=>'#007bff','bg'=>'rgba(0,123,255,.15)','tx'=>'#007bff'],
|
||||
['name'=>'Enumerator Ngada 3','done'=>48,'target'=>80,'color'=>'#fd7e14','bg'=>'rgba(253,126,20,.15)','tx'=>'#fd7e14'],
|
||||
['name'=>'Enumerator Ngada 4','done'=>36,'target'=>80,'color'=>'#dc3545','bg'=>'rgba(220,53,69,.15)','tx'=>'#dc3545'],
|
||||
['name'=>'Enumerator Ngada 1','done'=>72,'target'=>80,'color'=>'#6FD68F','bg'=>'rgba(111,214,143,.24)','tx'=>'#6FD68F'],
|
||||
['name'=>'Enumerator Ngada 2','done'=>58,'target'=>80,'color'=>'#5FC7D6','bg'=>'rgba(95,199,214,.24)','tx'=>'#5FC7D6'],
|
||||
['name'=>'Enumerator Ngada 3','done'=>48,'target'=>80,'color'=>'#FFB46F','bg'=>'rgba(255,180,111,.26)','tx'=>'#FFB46F'],
|
||||
['name'=>'Enumerator Ngada 4','done'=>36,'target'=>80,'color'=>'#F47C86','bg'=>'rgba(244,124,134,.26)','tx'=>'#F47C86'],
|
||||
],
|
||||
|
||||
'recent' => [
|
||||
['code'=>'TKD-2026-0178','name'=>'Markus Bela', 'init'=>'MB','desa'=>'Wangka', 'date'=>'24 Jun 2026','pct'=>100,'status'=>'completed', 'bg'=>'rgba(40,167,69,.15)','tx'=>'#28a745'],
|
||||
['code'=>'TKD-2026-0177','name'=>'Theresia Nuka','init'=>'TN','desa'=>'Langa', 'date'=>'23 Jun 2026','pct'=>67, 'status'=>'in_progress','bg'=>'rgba(0,123,255,.15)','tx'=>'#007bff'],
|
||||
['code'=>'TKD-2026-0176','name'=>'Yosef Meo', 'init'=>'YM','desa'=>'Bobo', 'date'=>'22 Jun 2026','pct'=>100,'status'=>'completed', 'bg'=>'rgba(40,167,69,.15)','tx'=>'#28a745'],
|
||||
['code'=>'TKD-2026-0175','name'=>'Maria Seda', 'init'=>'MS','desa'=>'Tana Wolo','date'=>'21 Jun 2026','pct'=>22, 'status'=>'draft', 'bg'=>'rgba(253,126,20,.15)','tx'=>'#fd7e14'],
|
||||
['code'=>'TKD-2026-0174','name'=>'Rafael Bolo', 'init'=>'RB','desa'=>'Wangka', 'date'=>'20 Jun 2026','pct'=>11, 'status'=>'draft', 'bg'=>'rgba(220,53,69,.15)','tx'=>'#dc3545'],
|
||||
['code'=>'TKD-2026-0178','name'=>'Markus Bela', 'init'=>'MB','desa'=>'Wangka', 'date'=>'24 Jun 2026','pct'=>100,'status'=>'completed', 'bg'=>'rgba(111,214,143,.24)','tx'=>'#6FD68F'],
|
||||
['code'=>'TKD-2026-0177','name'=>'Theresia Nuka','init'=>'TN','desa'=>'Langa', 'date'=>'23 Jun 2026','pct'=>67, 'status'=>'in_progress','bg'=>'rgba(95,199,214,.24)','tx'=>'#5FC7D6'],
|
||||
['code'=>'TKD-2026-0176','name'=>'Yosef Meo', 'init'=>'YM','desa'=>'Bobo', 'date'=>'22 Jun 2026','pct'=>100,'status'=>'completed', 'bg'=>'rgba(111,214,143,.24)','tx'=>'#6FD68F'],
|
||||
['code'=>'TKD-2026-0175','name'=>'Maria Seda', 'init'=>'MS','desa'=>'Tana Wolo','date'=>'21 Jun 2026','pct'=>22, 'status'=>'draft', 'bg'=>'rgba(255,180,111,.26)','tx'=>'#FFB46F'],
|
||||
['code'=>'TKD-2026-0174','name'=>'Rafael Bolo', 'init'=>'RB','desa'=>'Wangka', 'date'=>'20 Jun 2026','pct'=>11, 'status'=>'draft', 'bg'=>'rgba(244,124,134,.26)','tx'=>'#F47C86'],
|
||||
],
|
||||
|
||||
'komoditas' => [
|
||||
['label'=>'Kopi', 'total'=>148,'pct'=>60,'color'=>'#28a745','arc'=>174],
|
||||
['label'=>'Kakao', 'total'=>82, 'pct'=>33,'color'=>'#007bff','arc'=>96],
|
||||
['label'=>'Vanili','total'=>33, 'pct'=>13,'color'=>'#fd7e14','arc'=>38],
|
||||
['label'=>'Kemiri','total'=>22, 'pct'=>9, 'color'=>'#6f42c1','arc'=>26],
|
||||
['label'=>'Kopi', 'total'=>148,'pct'=>60,'color'=>'#6FD68F','arc'=>174],
|
||||
['label'=>'Kakao', 'total'=>82, 'pct'=>33,'color'=>'#9D8EF0','arc'=>96],
|
||||
['label'=>'Vanili','total'=>33, 'pct'=>13,'color'=>'#FFB46F','arc'=>38],
|
||||
['label'=>'Kemiri','total'=>22, 'pct'=>9, 'color'=>'#9EA6B0','arc'=>26],
|
||||
],
|
||||
|
||||
'kecamatan' => [
|
||||
['name'=>'Bajawa','total'=>89,'pct'=>78,'color'=>'#28a745'],
|
||||
['name'=>'Soa', 'total'=>68,'pct'=>59,'color'=>'#007bff'],
|
||||
['name'=>'Golewa','total'=>57,'pct'=>50,'color'=>'#fd7e14'],
|
||||
['name'=>'Bajawa','total'=>89,'pct'=>78,'color'=>'#6FD68F'],
|
||||
['name'=>'Soa', 'total'=>68,'pct'=>59,'color'=>'#5FC7D6'],
|
||||
['name'=>'Golewa','total'=>57,'pct'=>50,'color'=>'#FFB46F'],
|
||||
],
|
||||
|
||||
'pending' => [
|
||||
['name'=>'Theresia Nuka','section'=>'D4-K3','pct'=>67,'bg'=>'rgba(0,123,255,.15)','tx'=>'#007bff'],
|
||||
['name'=>'Maria Seda', 'section'=>'D2-K2','pct'=>22,'bg'=>'rgba(253,126,20,.15)','tx'=>'#fd7e14'],
|
||||
['name'=>'Rafael Bolo', 'section'=>'D2-K1','pct'=>11,'bg'=>'rgba(220,53,69,.15)', 'tx'=>'#dc3545'],
|
||||
['name'=>'Antonius Wea', 'section'=>'D2-K1','pct'=>0, 'bg'=>'rgba(220,53,69,.15)', 'tx'=>'#dc3545'],
|
||||
['name'=>'Theresia Nuka','section'=>'D4-K3','pct'=>67,'bg'=>'rgba(95,199,214,.24)','tx'=>'#5FC7D6'],
|
||||
['name'=>'Maria Seda', 'section'=>'D2-K2','pct'=>22,'bg'=>'rgba(255,180,111,.26)','tx'=>'#FFB46F'],
|
||||
['name'=>'Rafael Bolo', 'section'=>'D2-K1','pct'=>11,'bg'=>'rgba(244,124,134,.26)', 'tx'=>'#F47C86'],
|
||||
['name'=>'Antonius Wea', 'section'=>'D2-K1','pct'=>0, 'bg'=>'rgba(244,124,134,.26)', 'tx'=>'#F47C86'],
|
||||
],
|
||||
'pending_more' => 32,
|
||||
];
|
||||
@@ -258,10 +258,10 @@
|
||||
@foreach($d['recent'] as $s)
|
||||
@php
|
||||
$bc = match(true) {
|
||||
$s['pct'] === 100 => '#28a745',
|
||||
$s['pct'] >= 50 => '#007bff',
|
||||
$s['pct'] >= 20 => '#fd7e14',
|
||||
default => '#dc3545',
|
||||
$s['pct'] === 100 => '#6FD68F',
|
||||
$s['pct'] >= 50 => '#5FC7D6',
|
||||
$s['pct'] >= 20 => '#FFB46F',
|
||||
default => '#F47C86',
|
||||
};
|
||||
@endphp
|
||||
<tr>
|
||||
|
||||
@@ -208,6 +208,10 @@
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const pastelChart = config.chartColors || {};
|
||||
const pastelSemantic = pastelChart.semantic || {};
|
||||
const pastelCategory = pastelChart.categories || {};
|
||||
const gridColor = pastelChart.grid || '#E8EDF5';
|
||||
|
||||
// 1. Inisialisasi Icons
|
||||
if(typeof lucide !== 'undefined') {
|
||||
@@ -225,7 +229,7 @@
|
||||
{
|
||||
label: 'Mentah/Gelondong (Rp)',
|
||||
data: [35000, 200000, 25000, 15000],
|
||||
backgroundColor: '#9ca3af', // Gray
|
||||
backgroundColor: pastelSemantic.gray || '#9EA6B0',
|
||||
borderRadius: { topLeft: 4, topRight: 4, bottomLeft: 0, bottomRight: 0 },
|
||||
barPercentage: 0.6,
|
||||
categoryPercentage: 0.8
|
||||
@@ -233,7 +237,7 @@
|
||||
{
|
||||
label: 'Produk Olahan (Rp)',
|
||||
data: [120000, 1500000, 80000, 45000],
|
||||
backgroundColor: '#d97706', // Amber-600
|
||||
backgroundColor: pastelSemantic.warning || '#FFB46F',
|
||||
borderRadius: { topLeft: 4, topRight: 4, bottomLeft: 0, bottomRight: 0 },
|
||||
barPercentage: 0.6,
|
||||
categoryPercentage: 0.8
|
||||
@@ -246,7 +250,7 @@
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: true,
|
||||
grid: { color: '#f3f4f6' },
|
||||
grid: { color: gridColor },
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
if (value >= 1000000) return 'Rp ' + (value/1000000) + ' Jt';
|
||||
@@ -282,8 +286,8 @@
|
||||
{
|
||||
label: 'Kopi Bubuk/Roasted',
|
||||
data: [12, 15, 18, 25, 30, 45],
|
||||
borderColor: '#6F4E37',
|
||||
backgroundColor: 'rgba(111, 78, 55, 0.1)',
|
||||
borderColor: pastelCategory.coffee || '#6FD68F',
|
||||
backgroundColor: 'rgba(104, 193, 124, 0.16)',
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
borderWidth: 2
|
||||
@@ -291,7 +295,7 @@
|
||||
{
|
||||
label: 'Kakao Fermentasi',
|
||||
data: [5, 8, 12, 15, 22, 28],
|
||||
borderColor: '#D2691E',
|
||||
borderColor: pastelCategory.cacao || '#9D8EF0',
|
||||
backgroundColor: 'transparent',
|
||||
tension: 0.4,
|
||||
borderWidth: 2
|
||||
@@ -305,7 +309,7 @@
|
||||
scales: {
|
||||
y: {
|
||||
title: { display: true, text: 'Volume (Ton)' },
|
||||
grid: { color: '#f3f4f6' }
|
||||
grid: { color: gridColor }
|
||||
},
|
||||
x: { grid: { display: false } }
|
||||
},
|
||||
|
||||
@@ -60,8 +60,8 @@
|
||||
}
|
||||
|
||||
.indicator-filter-bar .form-select:focus {
|
||||
border-color: rgba(40, 167, 69, .55);
|
||||
box-shadow: 0 0 0 .2rem rgba(40, 167, 69, .12);
|
||||
border-color: rgba(111, 214, 143, .65);
|
||||
box-shadow: 0 0 0 .2rem rgba(111, 214, 143, .20);
|
||||
}
|
||||
|
||||
.indicator-filter-bar.is-loading .form-select,
|
||||
@@ -190,7 +190,7 @@
|
||||
|
||||
.indicator-commodity-chip {
|
||||
align-items: center;
|
||||
background: linear-gradient(180deg, rgba(40, 199, 111, .08), rgba(40, 199, 111, .02));
|
||||
background: rgba(111, 214, 143, .20);
|
||||
border: 1px solid var(--bs-border-color);
|
||||
border-radius: var(--bs-border-radius);
|
||||
display: flex;
|
||||
@@ -201,7 +201,7 @@
|
||||
|
||||
.indicator-commodity-icon {
|
||||
align-items: center;
|
||||
background-color: rgba(40, 199, 111, .16);
|
||||
background-color: rgba(111, 214, 143, .24);
|
||||
border-radius: var(--bs-border-radius);
|
||||
color: var(--bs-success);
|
||||
display: inline-flex;
|
||||
@@ -296,11 +296,11 @@
|
||||
}
|
||||
|
||||
.bg-success-light {
|
||||
background-color: rgba(40, 167, 69, .1);
|
||||
background-color: rgba(104, 193, 124, .20);
|
||||
}
|
||||
|
||||
.bg-primary-light {
|
||||
background-color: rgba(0, 123, 255, .1);
|
||||
background-color: rgba(95, 199, 214, .20);
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
@@ -1658,7 +1658,7 @@
|
||||
const parentDesa = (levels.desa || []).find(item => String(item.id) === String(parentId));
|
||||
const desaLayer = parentDesa
|
||||
? makeLayerFromPolygon(parentDesa.polygon, {
|
||||
color: '#dc3545',
|
||||
color: '#F47C86',
|
||||
dashArray: '5, 8',
|
||||
fillOpacity: 0,
|
||||
interactive: false,
|
||||
@@ -1673,8 +1673,8 @@
|
||||
|
||||
dataToRender.forEach(function (item) {
|
||||
const layer = makeLayerFromPolygon(item.polygon, nextLevel === 'lahan'
|
||||
? { color: '#28a745', fillColor: '#28a745', fillOpacity: .6, weight: 2 }
|
||||
: { color: '#f39c12', fillColor: '#f1c40f', fillOpacity: .3, weight: 2 });
|
||||
? { color: '#6FD68F', fillColor: '#6FD68F', fillOpacity: .6, weight: 2 }
|
||||
: { color: '#FFB46F', fillColor: '#FFB46F', fillOpacity: .3, weight: 2 });
|
||||
|
||||
if (!layer) {
|
||||
return;
|
||||
@@ -1889,20 +1889,23 @@
|
||||
}
|
||||
|
||||
const appConfig = typeof config !== 'undefined' ? config : {};
|
||||
const pastelChart = appConfig.chartColors || {};
|
||||
const pastelSeries = pastelChart.series || ['#9D8EF0', '#6FD68F', '#FFB46F', '#5FC7D6', '#F47C86', '#9EA6B0', '#65D8BD', '#63C7E0'];
|
||||
const pastelSemantic = pastelChart.semantic || {};
|
||||
const headingColor = appConfig.colors?.headingColor || '#5d596c';
|
||||
const legendColor = appConfig.colors?.bodyColor || '#6f6b7d';
|
||||
const fontFamily = appConfig.fontFamily || 'Public Sans';
|
||||
const chartColors = {
|
||||
primary: appConfig.colors?.primary || '#7367f0',
|
||||
success: appConfig.colors?.success || '#28c76f',
|
||||
warning: appConfig.colors?.warning || '#ff9f43',
|
||||
info: appConfig.colors?.info || '#00cfe8',
|
||||
secondary: appConfig.colors?.secondary || '#a8aaae',
|
||||
labelPrimary: appConfig.colors_label?.primary || 'rgba(115, 103, 240, 0.16)',
|
||||
labelSuccess: appConfig.colors_label?.success || 'rgba(40, 199, 111, 0.16)',
|
||||
labelWarning: appConfig.colors_label?.warning || 'rgba(255, 159, 67, 0.16)',
|
||||
labelInfo: appConfig.colors_label?.info || 'rgba(0, 207, 232, 0.16)',
|
||||
labelSecondary: appConfig.colors_label?.secondary || 'rgba(168, 170, 174, 0.16)',
|
||||
primary: pastelSemantic.primary || '#9D8EF0',
|
||||
success: pastelSemantic.success || '#6FD68F',
|
||||
warning: pastelSemantic.warning || '#FFB46F',
|
||||
info: pastelSemantic.info || '#5FC7D6',
|
||||
secondary: pastelSemantic.secondary || '#9EA6B0',
|
||||
labelPrimary: 'rgba(111, 214, 143, 0.22)',
|
||||
labelSuccess: 'rgba(111, 214, 143, 0.24)',
|
||||
labelWarning: 'rgba(255, 180, 111, 0.22)',
|
||||
labelInfo: 'rgba(95, 199, 214, 0.20)',
|
||||
labelSecondary: 'rgba(179, 181, 188, 0.22)',
|
||||
body: appConfig.colors?.bodyColor || '#6f6b7d',
|
||||
muted: appConfig.colors?.textMuted || '#a5a3ae',
|
||||
heading: appConfig.colors?.headingColor || '#5d596c',
|
||||
@@ -1910,12 +1913,7 @@
|
||||
card: appConfig.colors?.cardColor || '#fff',
|
||||
};
|
||||
const surveyPalette = [
|
||||
'#28a745',
|
||||
'#007bff',
|
||||
'#fd7e14',
|
||||
'#dc3545',
|
||||
'#6f42c1',
|
||||
'#20c997',
|
||||
...pastelSeries,
|
||||
chartColors.primary,
|
||||
chartColors.info,
|
||||
chartColors.warning,
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
.chart-group-header-toggle {
|
||||
cursor: pointer;
|
||||
background-color: rgba(var(--bs-primary-rgb, 115, 103, 240), 0.08);
|
||||
border-inline-start: 4px solid var(--bs-primary, #7367f0);
|
||||
border-inline-start: 4px solid var(--bs-primary, #9D8EF0);
|
||||
box-shadow: none;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
@@ -649,18 +649,18 @@
|
||||
<div class="col-12">
|
||||
<div class="card chart-group-header-toggle" role="button" tabindex="0"
|
||||
data-bs-toggle="collapse" data-bs-target="#demografi-charts"
|
||||
aria-expanded="true" aria-controls="demografi-charts" aria-label="Sembunyikan chart">
|
||||
aria-expanded="false" aria-controls="demografi-charts" aria-label="Tampilkan chart">
|
||||
<div class="card-body d-flex justify-content-between align-items-center gap-3">
|
||||
<div>
|
||||
<h5>Profil Demografi</h5>
|
||||
<p class="text-body-secondary mb-0">Komposisi petani berdasarkan gender, kelompok umur, dan pendidikan.</p>
|
||||
</div>
|
||||
<span class="btn btn-icon btn-label-secondary btn-sm chart-group-icon-toggle" aria-hidden="true">
|
||||
<i class="icon-base ti tabler-chevron-up"></i>
|
||||
<i class="icon-base ti tabler-chevron-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse show chart-group-panel" id="demografi-charts">
|
||||
<div class="collapse chart-group-panel" id="demografi-charts">
|
||||
<div class="row g-6 pt-4">
|
||||
<div class="col-12 col-md-6 col-xl-4">
|
||||
<div class="card h-100">
|
||||
@@ -729,18 +729,18 @@
|
||||
<div class="col-12">
|
||||
<div class="card chart-group-header-toggle" role="button" tabindex="0"
|
||||
data-bs-toggle="collapse" data-bs-target="#rumah-tangga-charts"
|
||||
aria-expanded="true" aria-controls="rumah-tangga-charts" aria-label="Sembunyikan chart">
|
||||
aria-expanded="false" aria-controls="rumah-tangga-charts" aria-label="Tampilkan chart">
|
||||
<div class="card-body d-flex justify-content-between align-items-center gap-3">
|
||||
<div>
|
||||
<h5>Sosial Ekonomi</h5>
|
||||
<p class="text-body-secondary mb-0">Tanggungan keluarga, penghasilan usaha tani, dan keanggotaan kelompok tani.</p>
|
||||
</div>
|
||||
<span class="btn btn-icon btn-label-secondary btn-sm chart-group-icon-toggle" aria-hidden="true">
|
||||
<i class="icon-base ti tabler-chevron-up"></i>
|
||||
<i class="icon-base ti tabler-chevron-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse show chart-group-panel" id="rumah-tangga-charts">
|
||||
<div class="collapse chart-group-panel" id="rumah-tangga-charts">
|
||||
<div class="row g-6 pt-4">
|
||||
<div class="col-12 col-md-6 col-xl-4">
|
||||
<div class="card h-100">
|
||||
@@ -816,18 +816,18 @@
|
||||
<div class="col-12">
|
||||
<div class="card chart-group-header-toggle" role="button" tabindex="0"
|
||||
data-bs-toggle="collapse" data-bs-target="#usaha-tani-charts"
|
||||
aria-expanded="true" aria-controls="usaha-tani-charts" aria-label="Sembunyikan chart">
|
||||
aria-expanded="false" aria-controls="usaha-tani-charts" aria-label="Tampilkan chart">
|
||||
<div class="card-body d-flex justify-content-between align-items-center gap-3">
|
||||
<div>
|
||||
<h5>Profil Lahan</h5>
|
||||
<p class="text-body-secondary mb-0">Karakteristik lahan usaha tani berdasarkan komoditas utama, luas lahan garapan, dan status kepemilikan lahan.</p>
|
||||
</div>
|
||||
<span class="btn btn-icon btn-label-secondary btn-sm chart-group-icon-toggle" aria-hidden="true">
|
||||
<i class="icon-base ti tabler-chevron-up"></i>
|
||||
<i class="icon-base ti tabler-chevron-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse show chart-group-panel" id="usaha-tani-charts">
|
||||
<div class="collapse chart-group-panel" id="usaha-tani-charts">
|
||||
<div class="row g-6 pt-4">
|
||||
<div class="col-12 col-md-6 col-xl-4">
|
||||
<div class="card h-100">
|
||||
@@ -1096,18 +1096,18 @@
|
||||
<div class="py-4">
|
||||
<div class="card chart-group-header-toggle" role="button" tabindex="0"
|
||||
data-bs-toggle="collapse" data-bs-target="#indeks-segmentasi-charts"
|
||||
aria-expanded="true" aria-controls="indeks-segmentasi-charts" aria-label="Sembunyikan chart">
|
||||
aria-expanded="false" aria-controls="indeks-segmentasi-charts" aria-label="Tampilkan chart">
|
||||
<div class="card-body d-flex justify-content-between align-items-center gap-3">
|
||||
<div>
|
||||
<h5>Indeks Petani</h5>
|
||||
<p class="text-body-secondary mb-0">Komposisi petani menurut tingkat kesiapan adopsi, secara keseluruhan dan pada setiap komoditas.</p>
|
||||
</div>
|
||||
<span class="btn btn-icon btn-label-secondary btn-sm chart-group-icon-toggle" aria-hidden="true">
|
||||
<i class="icon-base ti tabler-chevron-up"></i>
|
||||
<i class="icon-base ti tabler-chevron-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse show chart-group-panel" id="indeks-segmentasi-charts">
|
||||
<div class="collapse chart-group-panel" id="indeks-segmentasi-charts">
|
||||
<div class="row g-6 py-4">
|
||||
<div class="col-lg-6 col-sm-12">
|
||||
<div class="card card-border-shadow-primary h-100">
|
||||
@@ -1169,18 +1169,18 @@
|
||||
<div class="py-4">
|
||||
<div class="card chart-group-header-toggle" role="button" tabindex="0"
|
||||
data-bs-toggle="collapse" data-bs-target="#indeks-kesiapan-charts"
|
||||
aria-expanded="true" aria-controls="indeks-kesiapan-charts" aria-label="Sembunyikan chart">
|
||||
aria-expanded="false" aria-controls="indeks-kesiapan-charts" aria-label="Tampilkan chart">
|
||||
<div class="card-body d-flex justify-content-between align-items-center gap-3">
|
||||
<div>
|
||||
<h5>Dimensi dan Komponen Kesiapan Adopsi</h5>
|
||||
<p class="text-body-secondary mb-0">Perbandingan skor dimensi dan komponen kesiapan adopsi petani.</p>
|
||||
</div>
|
||||
<span class="btn btn-icon btn-label-secondary btn-sm chart-group-icon-toggle" aria-hidden="true">
|
||||
<i class="icon-base ti tabler-chevron-up"></i>
|
||||
<i class="icon-base ti tabler-chevron-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse show chart-group-panel" id="indeks-kesiapan-charts">
|
||||
<div class="collapse chart-group-panel" id="indeks-kesiapan-charts">
|
||||
<div class="row g-6 py-4">
|
||||
<div class="col-lg-6 col-sm-12">
|
||||
<div class="card card-border-shadow-info h-100">
|
||||
@@ -1219,18 +1219,18 @@
|
||||
<div class="py-4">
|
||||
<div class="card chart-group-header-toggle" role="button" tabindex="0"
|
||||
data-bs-toggle="collapse" data-bs-target="#indeks-wilayah-charts"
|
||||
aria-expanded="true" aria-controls="indeks-wilayah-charts" aria-label="Sembunyikan chart">
|
||||
aria-expanded="false" aria-controls="indeks-wilayah-charts" aria-label="Tampilkan chart">
|
||||
<div class="card-body d-flex justify-content-between align-items-center gap-3">
|
||||
<div>
|
||||
<h5>Sebaran wilayah petani</h5>
|
||||
<p class="text-body-secondary mb-0">Peta dan komposisi segmen kesiapan adopsi petani per desa/kelurahan.</p>
|
||||
</div>
|
||||
<span class="btn btn-icon btn-label-secondary btn-sm chart-group-icon-toggle" aria-hidden="true">
|
||||
<i class="icon-base ti tabler-chevron-up"></i>
|
||||
<i class="icon-base ti tabler-chevron-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="collapse show chart-group-panel" id="indeks-wilayah-charts">
|
||||
<div class="collapse chart-group-panel" id="indeks-wilayah-charts">
|
||||
<div class="row g-6 py-4">
|
||||
<div class="col-lg-12 col-sm-12">
|
||||
<div class="card card-border-shadow-danger h-100">
|
||||
@@ -1366,6 +1366,32 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const headingColor = config.colors.headingColor;
|
||||
const legendColor = config.colors.bodyColor;
|
||||
const fontFamily = config.fontFamily;
|
||||
const pastelChart = config.chartColors || {};
|
||||
const pastelSeries = pastelChart.series || ['#9D8EF0', '#6FD68F', '#FFB46F', '#5FC7D6', '#F47C86', '#9EA6B0', '#65D8BD', '#63C7E0'];
|
||||
const pastelSemantic = pastelChart.semantic || {};
|
||||
const pastelText = pastelChart.text || {};
|
||||
|
||||
function hexToRgba(hex, alpha) {
|
||||
const value = String(hex || '').replace('#', '');
|
||||
if (!/^[0-9a-fA-F]{6}$/.test(value)) {
|
||||
return hex;
|
||||
}
|
||||
|
||||
const red = parseInt(value.slice(0, 2), 16);
|
||||
const green = parseInt(value.slice(2, 4), 16);
|
||||
const blue = parseInt(value.slice(4, 6), 16);
|
||||
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
||||
}
|
||||
|
||||
function softBadgeStyle(backgroundColor, textColor) {
|
||||
const borderColor = hexToRgba(backgroundColor, 0.36);
|
||||
return [
|
||||
`background-color:${hexToRgba(backgroundColor, 0.22)} !important`,
|
||||
`border:1px solid ${borderColor}`,
|
||||
`color:${textColor} !important`,
|
||||
'font-weight:600'
|
||||
].join(';');
|
||||
}
|
||||
|
||||
const farmerDataUrl = @json(route('dashboard.demografi-petani.data'));
|
||||
const farmerProfileUrl = @json(url('/dashboard/profil/petani'));
|
||||
@@ -1500,8 +1526,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
tbody.innerHTML = rows.map((petani, index) => {
|
||||
const rowNumber = ((result.pagination.current_page - 1) * result.pagination.per_page) + index + 1;
|
||||
const genderBadge = Number(petani.jenis_kelamin) === 1
|
||||
? `<span class="badge bg-label-info">Laki-laki</span>`
|
||||
: `<span class="badge" style="background-color:#ffe8f1 !important; color:#ff5598 !important;">Perempuan</span>`;
|
||||
? `<span class="badge" style="${softBadgeStyle(pastelSemantic.info || '#5FC7D6', pastelText.info || '#00BAD1')}">Laki-laki</span>`
|
||||
: `<span class="badge" style="${softBadgeStyle(pastelSemantic.danger || '#F47C86', pastelText.danger || '#FF4C51')}">Perempuan</span>`;
|
||||
const umur = (petani.umur !== null && petani.umur !== undefined) ? `${petani.umur} tahun` : '-';
|
||||
|
||||
return `
|
||||
@@ -1706,7 +1732,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
},
|
||||
labels: ['Laki-laki', 'Perempuan'],
|
||||
series: [lakiLaki, perempuan],
|
||||
colors: [config.colors.info, '#ff5598'],
|
||||
colors: [pastelSemantic.info || '#5FC7D6', pastelSemantic.pink || '#F47C86'],
|
||||
stroke: { width: 0 },
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
@@ -1815,12 +1841,11 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
data: ageGroupData
|
||||
}
|
||||
],
|
||||
colors: ['#c7c2f9', '#9d94f4', '#7367f0', '#5048a8', '#2e2960'],
|
||||
colors: [pastelSemantic.primary || '#9D8EF0'],
|
||||
plotOptions: {
|
||||
bar: {
|
||||
borderRadius: 4,
|
||||
columnWidth: '45%',
|
||||
distributed: true,
|
||||
dataLabels: { position: 'top' }
|
||||
}
|
||||
},
|
||||
@@ -1875,7 +1900,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const label = w.globals.labels[dataPointIndex];
|
||||
const percentage = totalAgeGroup > 0 ? (Number(value) / totalAgeGroup) * 100 : 0;
|
||||
return `
|
||||
<div style="background:${w.config.colors[dataPointIndex % w.config.colors.length]}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
<div style="background:${w.config.colors[dataPointIndex % w.config.colors.length]}; color:${headingColor}; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
${label} tahun: <strong>${value} petani (${percentage.toFixed(1)}%)</strong>
|
||||
</div>
|
||||
`;
|
||||
@@ -1918,10 +1943,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
},
|
||||
series: [{ name: 'Petani', data: educationData }],
|
||||
colors: ['#e9f9f0', '#a9e8c5', '#68d79a', '#28c76f', '#1c8b4d', '#104f2c'],
|
||||
colors: [pastelSemantic.success || '#6FD68F'],
|
||||
plotOptions: {
|
||||
bar: {
|
||||
distributed: true,
|
||||
horizontal: true,
|
||||
borderRadius: 4,
|
||||
barHeight: '55%',
|
||||
@@ -1975,7 +1999,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const label = w.globals.labels[dataPointIndex];
|
||||
const percentage = totalPendidikan > 0 ? ((value / totalPendidikan) * 100).toFixed(1) : '0.0';
|
||||
return `
|
||||
<div style="background:${w.config.colors[dataPointIndex % w.config.colors.length]}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
<div style="background:${w.config.colors[dataPointIndex % w.config.colors.length]}; color:${headingColor}; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
${label}: <strong>${value} petani (${percentage}%)</strong>
|
||||
</div>
|
||||
`;
|
||||
@@ -2017,10 +2041,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
},
|
||||
series: [{ name: 'Petani', data: dependentData }],
|
||||
colors: ['#ffb7b9', '#ff4c51', '#b23538', '#661e20'],
|
||||
colors: [pastelSemantic.danger || '#F47C86'],
|
||||
plotOptions: {
|
||||
bar: {
|
||||
distributed: true,
|
||||
horizontal: true,
|
||||
borderRadius: 4,
|
||||
barHeight: '50%',
|
||||
@@ -2074,7 +2097,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const label = w.globals.labels[dataPointIndex];
|
||||
const percentage = totalDependentsFarmers > 0 ? ((value / totalDependentsFarmers) * 100).toFixed(1) : '0.0';
|
||||
return `
|
||||
<div style="background:${w.config.colors[dataPointIndex % w.config.colors.length]}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
<div style="background:${w.config.colors[dataPointIndex % w.config.colors.length]}; color:${headingColor}; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
${label}: <strong>${value} petani (${percentage}%)</strong>
|
||||
</div>
|
||||
`;
|
||||
@@ -2116,7 +2139,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
},
|
||||
labels: ['Terdaftar', 'Tidak Terdaftar', 'Belum Terisi'],
|
||||
series: farmerGroupData,
|
||||
colors: [config.colors.success, config.colors.warning, config.colors.secondary],
|
||||
colors: [pastelSemantic.success || '#6FD68F', pastelSemantic.warning || '#FFB46F', pastelSemantic.secondary || '#9EA6B0'],
|
||||
stroke: { width: 0 },
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
@@ -2215,12 +2238,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
labels: commodityLabels,
|
||||
series: commoditySeries.map(Number),
|
||||
colors: [
|
||||
config.colors.primary,
|
||||
config.colors.warning,
|
||||
config.colors.success,
|
||||
config.colors.info,
|
||||
config.colors.danger,
|
||||
config.colors.secondary
|
||||
pastelSemantic.primary || '#9D8EF0',
|
||||
pastelSemantic.warning || '#FFB46F',
|
||||
pastelSemantic.success || '#6FD68F',
|
||||
pastelSemantic.info || '#5FC7D6',
|
||||
pastelSemantic.danger || '#F47C86',
|
||||
pastelSemantic.secondary || '#9EA6B0'
|
||||
],
|
||||
stroke: { width: 0 },
|
||||
dataLabels: {
|
||||
@@ -2270,7 +2293,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const percentage = totalCommodityFarmers > 0 ? ((value / totalCommodityFarmers) * 100).toFixed(1) : '0.0';
|
||||
const color = w.globals.colors[seriesIndex];
|
||||
return `
|
||||
<div style="background:${color}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
<div style="background:${color}; color:${headingColor}; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
${label}: <strong>${value} petani (${percentage}%)</strong>
|
||||
</div>
|
||||
`;
|
||||
@@ -2335,10 +2358,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
},
|
||||
series: [{ name: 'Petani', data: landAreaData }],
|
||||
colors: ['#ffbb7b', '#ff9f43', '#b26f2e', '#663f1a'],
|
||||
colors: [pastelSemantic.warning || '#FFB46F'],
|
||||
plotOptions: {
|
||||
bar: {
|
||||
distributed: true,
|
||||
horizontal: true,
|
||||
borderRadius: 4,
|
||||
barHeight: '50%',
|
||||
@@ -2392,7 +2414,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const label = w.globals.labels[dataPointIndex];
|
||||
const percentage = totalLandFarmers > 0 ? ((value / totalLandFarmers) * 100).toFixed(1) : '0.0';
|
||||
return `
|
||||
<div style="background:${w.config.colors[dataPointIndex % w.config.colors.length]}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
<div style="background:${w.config.colors[dataPointIndex % w.config.colors.length]}; color:${headingColor}; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
${label}: <strong>${value} petani (${percentage}%)</strong>
|
||||
</div>
|
||||
`;
|
||||
@@ -2431,10 +2453,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
labels: landOwnershipStatusLabels,
|
||||
series: landOwnershipStatusSeries,
|
||||
colors: [
|
||||
config.colors.success,
|
||||
config.colors.warning,
|
||||
config.colors.info,
|
||||
config.colors.secondary
|
||||
pastelSemantic.success || '#6FD68F',
|
||||
pastelSemantic.warning || '#FFB46F',
|
||||
pastelSemantic.info || '#5FC7D6',
|
||||
pastelSemantic.secondary || '#9EA6B0'
|
||||
],
|
||||
stroke: { width: 0 },
|
||||
dataLabels: {
|
||||
@@ -2538,10 +2560,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
},
|
||||
series: [{ name: 'Petani', data: incomeData }],
|
||||
colors: ['#99e3ec', '#4ccede', '#00bad1', '#008292', '#004a53'],
|
||||
colors: [pastelSemantic.info || '#5FC7D6'],
|
||||
plotOptions: {
|
||||
bar: {
|
||||
distributed: true,
|
||||
horizontal: true,
|
||||
borderRadius: 4,
|
||||
barHeight: '50%',
|
||||
@@ -2595,7 +2616,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
const label = w.globals.labels[dataPointIndex];
|
||||
const percentage = totalIncomeFarmers > 0 ? ((value / totalIncomeFarmers) * 100).toFixed(1) : '0.0';
|
||||
return `
|
||||
<div style="background:${w.config.colors[dataPointIndex % w.config.colors.length]}; color:#fff; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
<div style="background:${w.config.colors[dataPointIndex % w.config.colors.length]}; color:${headingColor}; padding:8px 12px; border-radius:5px; font-size:13px; font-weight:500; font-family:${fontFamily}; box-shadow:0 4px 12px rgba(0,0,0,.15);">
|
||||
${label}: <strong>${value} petani (${percentage}%)</strong>
|
||||
</div>
|
||||
`;
|
||||
@@ -2832,6 +2853,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
const segments = @json($segments);
|
||||
const segmentPalette = segments.map(function (segment, index) {
|
||||
return config.chartColors?.semantic?.[segment.color] || config.chartColors?.series?.[index % config.chartColors.series.length] || '#9EA6B0';
|
||||
});
|
||||
const segmentTextColors = config.chartColors?.text || {};
|
||||
|
||||
// Chart Segmentasi Petani
|
||||
var chartSegmentasiPetani;
|
||||
@@ -2871,7 +2896,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}
|
||||
},
|
||||
labels: @json(collect($segments)->pluck('name')->values()),
|
||||
colors: @json($colors),
|
||||
colors: segmentPalette,
|
||||
plotOptions: {
|
||||
pie: {
|
||||
borderRadius: 12,
|
||||
@@ -2925,16 +2950,17 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
const total = response.series[index] ?? 0;
|
||||
const persen = response.persentase[index] ?? 0;
|
||||
const segmentColor = segmentTextColors[segment.color] || config.colors[segment.color] || segmentPalette[index] || '#808390';
|
||||
|
||||
html += `
|
||||
<tr>
|
||||
<td>
|
||||
<div class="d-flex justify-content-start align-items-center gap-2">
|
||||
<div class="flex-shrink-0">
|
||||
<i class="icon-base ti ${segment.icon} icon-lg text-${segment.color}"></i>
|
||||
<i class="icon-base ti ${segment.icon} icon-lg" style="color:${segmentColor};"></i>
|
||||
</div>
|
||||
|
||||
<span class="text-${segment.color}">
|
||||
<span style="color:${segmentColor};">
|
||||
${segment.name}
|
||||
</span>
|
||||
</div>
|
||||
@@ -2971,9 +2997,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
</script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
const segmentPalette = (@json($segments)).map(function (segment, index) {
|
||||
return config.chartColors?.semantic?.[segment.color] || config.chartColors?.series?.[index % config.chartColors.series.length] || '#9EA6B0';
|
||||
});
|
||||
var optionsSegmentasiPetaniByKomoditas = {
|
||||
series: [],
|
||||
colors: @json($colors),
|
||||
colors: segmentPalette,
|
||||
|
||||
chart: {
|
||||
type: 'bar',
|
||||
@@ -3157,7 +3186,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
plotOptions: { radar: { polygons: { strokeColors: borderColor, connectorColors: borderColor } } },
|
||||
stroke: { width: 2 },
|
||||
legend: { show: false },
|
||||
colors: [config.colors.primary],
|
||||
colors: [config.chartColors?.semantic?.primary || '#9D8EF0'],
|
||||
fill: { opacity: 0.12 },
|
||||
markers: {
|
||||
size: 5, strokeWidth: 2, strokeColors: config.colors.cardColor, hover: { sizeOffset: 2 },
|
||||
@@ -3208,7 +3237,14 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
</script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
var palette = [config.colors.success, config.colors.primary, config.colors.warning, config.colors.info, config.colors.danger, config.colors.secondary];
|
||||
var palette = [
|
||||
config.chartColors?.semantic?.success || '#6FD68F',
|
||||
config.chartColors?.semantic?.primary || '#9D8EF0',
|
||||
config.chartColors?.semantic?.warning || '#FFB46F',
|
||||
config.chartColors?.semantic?.info || '#5FC7D6',
|
||||
config.chartColors?.semantic?.danger || '#F47C86',
|
||||
config.chartColors?.semantic?.secondary || '#9EA6B0'
|
||||
];
|
||||
[
|
||||
{ id: 'dimensi', key: 'radarDimensi', url: "{{ route('dashboard.indeks-petani.dimensi') }}", title: "Dimensi Kesiapan Adopsi", labels: ["Sistem & Praktik Budidaya","Sosial Ekonomi Rumah Tangga","Rantai Nilai & Akses Pasar"], categories: [["Sistem & Praktik","Budidaya"],["Sosial Ekonomi","Rumah Tangga"],["Rantai Nilai &","Akses Pasar"]] },
|
||||
{ id: 'komponen', key: 'radarKomponen', url: "{{ route('dashboard.indeks-petani.komponen') }}", title: "Komponen Kesiapan Adopsi", labels: ["Motivasi & Kapasitas Adopsi","Kondisi Ekonomi Rumah Tangga","Praktik Budidaya & GAP","Keterlibatan Kelembagaan","Akses & Kebebasan Keuangan","Rantai Nilai & Akses Pasar"], categories: [["Motivasi & Kapasitas","Adopsi"],["Kondisi Ekonomi","Rumah Tangga"],["Praktik Budidaya","& GAP"],["Keterlibatan","Kelembagaan"],["Akses & Kebebasan","Keuangan"],["Rantai Nilai &","Akses Pasar"]] }
|
||||
@@ -3282,7 +3318,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
const warnaSegmen = {
|
||||
@foreach($segments as $segment)
|
||||
"{{ $segment['name'] }}": "var(--bs-{{ $segment['color'] }})",
|
||||
"{{ $segment['name'] }}": config.chartColors?.semantic?.["{{ $segment['color'] }}"] || '#9EA6B0',
|
||||
@endforeach
|
||||
};
|
||||
|
||||
@@ -3340,7 +3376,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
color: '#666',
|
||||
weight: 1,
|
||||
fillColor:
|
||||
warnaSegmen[desa.segmentasi?.segmen] || '#ccc',
|
||||
warnaSegmen[desa.segmentasi?.segmen] || '#9EA6B0',
|
||||
fillOpacity: 0.7
|
||||
});
|
||||
|
||||
@@ -3389,6 +3425,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
</script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
const segmentTextColors = config.chartColors?.text || {};
|
||||
var tableDesa = $('#table-desa').DataTable({
|
||||
"paging": true,
|
||||
"pageLength": 5,
|
||||
@@ -3429,7 +3466,8 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
var count = Number(data && data.count) || 0;
|
||||
if (type !== 'display') return count;
|
||||
var percentage = Number(data && data.percentage || 0).toLocaleString('id-ID', { maximumFractionDigits: 2 });
|
||||
return '<span class="fw-semibold text-' + (data && data.color || 'secondary') + '">' + count.toLocaleString('id-ID') +
|
||||
var segmentColor = segmentTextColors[data && data.color || 'secondary'] || '#808390';
|
||||
return '<span class="fw-semibold" style="color:' + segmentColor + ';">' + count.toLocaleString('id-ID') +
|
||||
'</span> <span class="text-body-secondary small">(' + percentage + '%)</span>';
|
||||
}
|
||||
};
|
||||
@@ -3439,6 +3477,36 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
</script>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
const indexSegmentColorKeys = {
|
||||
@foreach($segments as $segment)
|
||||
"{{ $segment['name'] }}": "{{ $segment['color'] }}",
|
||||
@endforeach
|
||||
};
|
||||
const indexSegmentColors = {
|
||||
@foreach($segments as $segment)
|
||||
"{{ $segment['name'] }}": config.chartColors?.semantic?.["{{ $segment['color'] }}"] || '#9EA6B0',
|
||||
@endforeach
|
||||
};
|
||||
const indexSegmentTextColors = config.chartColors?.text || {};
|
||||
function indexHexToRgba(hex, alpha) {
|
||||
const value = String(hex || '').replace('#', '');
|
||||
if (!/^[0-9a-fA-F]{6}$/.test(value)) {
|
||||
return hex;
|
||||
}
|
||||
|
||||
const red = parseInt(value.slice(0, 2), 16);
|
||||
const green = parseInt(value.slice(2, 4), 16);
|
||||
const blue = parseInt(value.slice(4, 6), 16);
|
||||
return `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
||||
}
|
||||
function indexSegmentBadgeStyle(backgroundColor, textColor) {
|
||||
return [
|
||||
`background-color:${indexHexToRgba(backgroundColor, 0.22)} !important`,
|
||||
`border:1px solid ${indexHexToRgba(backgroundColor, 0.36)}`,
|
||||
`color:${textColor} !important`,
|
||||
'font-weight:600'
|
||||
].join(';');
|
||||
}
|
||||
var tablePetani = $('#table-petani').DataTable({
|
||||
dom: 'rt<"card-footer d-flex flex-wrap justify-content-between align-items-center gap-3"ip>',
|
||||
pagingType: 'full_numbers',
|
||||
@@ -3540,9 +3608,12 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
if (!data.skor || !data.segmen) {
|
||||
return '';
|
||||
}
|
||||
const segmentKey = indexSegmentColorKeys[data.segmen] || data.color || 'secondary';
|
||||
const segmentColor = indexSegmentColors[data.segmen] || config.chartColors?.semantic?.[segmentKey] || '#9EA6B0';
|
||||
const segmentTextColor = indexSegmentTextColors[segmentKey] || '#808390';
|
||||
|
||||
return `
|
||||
<span class="badge bg-${data.color ?? 'secondary'}">
|
||||
<span class="badge" style="${indexSegmentBadgeStyle(segmentColor, segmentTextColor)}">
|
||||
${data.segmen}
|
||||
</span>
|
||||
`;
|
||||
|
||||
@@ -212,6 +212,9 @@
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const pastelChart = config.chartColors || {};
|
||||
const pastelCategory = pastelChart.categories || {};
|
||||
const gridColor = pastelChart.grid || '#E8EDF5';
|
||||
|
||||
// 1. Inisialisasi Icons
|
||||
if(typeof lucide !== 'undefined') {
|
||||
@@ -230,8 +233,8 @@
|
||||
label: 'Total Produksi Lahan (Ton)',
|
||||
type: 'line',
|
||||
data: [100, 110, 125, 145],
|
||||
borderColor: '#10b981', // Emerald-500
|
||||
backgroundColor: '#10b981',
|
||||
borderColor: pastelCategory.marketGreen || '#6FD68F',
|
||||
backgroundColor: pastelCategory.marketGreen || '#6FD68F',
|
||||
borderWidth: 3,
|
||||
tension: 0.3,
|
||||
fill: false
|
||||
@@ -239,14 +242,14 @@
|
||||
{
|
||||
label: 'Tengkulak / Pasar Bebas',
|
||||
data: [80, 65, 40, 25],
|
||||
backgroundColor: '#ef4444', // Red-500
|
||||
backgroundColor: pastelCategory.marketRed || '#F47C86',
|
||||
stack: 'Stack 0',
|
||||
borderRadius: { topLeft: 0, topRight: 0, bottomLeft: 4, bottomRight: 4 }
|
||||
},
|
||||
{
|
||||
label: 'Kelembagaan (Koperasi/BUMDes)',
|
||||
data: [20, 45, 85, 120],
|
||||
backgroundColor: '#3b82f6', // Blue-500
|
||||
backgroundColor: pastelCategory.marketBlue || '#5FC7D6',
|
||||
stack: 'Stack 0',
|
||||
borderRadius: { topLeft: 4, topRight: 4, bottomLeft: 0, bottomRight: 0 }
|
||||
}
|
||||
@@ -260,7 +263,7 @@
|
||||
y: {
|
||||
stacked: true,
|
||||
beginAtZero: true,
|
||||
grid: { color: '#f3f4f6' }
|
||||
grid: { color: gridColor }
|
||||
},
|
||||
x: {
|
||||
stacked: true,
|
||||
@@ -292,20 +295,20 @@
|
||||
{
|
||||
label: 'Kopi & Kakao',
|
||||
data: [150, 280, 320, 500, 650, 850],
|
||||
borderColor: '#6F4E37',
|
||||
borderColor: pastelCategory.coffee || '#6FD68F',
|
||||
backgroundColor: 'transparent',
|
||||
tension: 0.4,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#6F4E37'
|
||||
pointBackgroundColor: pastelCategory.coffee || '#6FD68F'
|
||||
},
|
||||
{
|
||||
label: 'Vanili & Kemiri',
|
||||
data: [50, 120, 210, 350, 420, 600],
|
||||
borderColor: '#eab308',
|
||||
borderColor: pastelCategory.vanilla || '#FFB46F',
|
||||
backgroundColor: 'transparent',
|
||||
tension: 0.4,
|
||||
borderWidth: 2,
|
||||
pointBackgroundColor: '#eab308'
|
||||
pointBackgroundColor: pastelCategory.vanilla || '#FFB46F'
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -316,7 +319,7 @@
|
||||
scales: {
|
||||
y: {
|
||||
title: { display: false },
|
||||
grid: { color: '#f3f4f6' },
|
||||
grid: { color: gridColor },
|
||||
ticks: {
|
||||
callback: function(value) {
|
||||
return 'Rp ' + value + ' Jt';
|
||||
|
||||
@@ -493,6 +493,7 @@ $segments = [
|
||||
legendColor = config.colors.bodyColor;
|
||||
borderColor = config.colors.borderColor;
|
||||
headingColor = config.colors.headingColor;
|
||||
const pastelSemantic = config.chartColors?.semantic || {};
|
||||
|
||||
var radarDimensi = new ApexCharts($("#chart-dimensi")[0],
|
||||
options = {
|
||||
@@ -542,7 +543,7 @@ $segments = [
|
||||
highlightDataSeries: false
|
||||
}
|
||||
},
|
||||
colors: [config.colors.primary, config.colors.info],
|
||||
colors: [pastelSemantic.primary || '#9D8EF0', pastelSemantic.info || '#5FC7D6'],
|
||||
fill: {
|
||||
opacity: 0.85
|
||||
},
|
||||
@@ -640,7 +641,7 @@ $segments = [
|
||||
highlightDataSeries: false
|
||||
}
|
||||
},
|
||||
colors: [config.colors.primary, config.colors.info],
|
||||
colors: [pastelSemantic.primary || '#9D8EF0', pastelSemantic.info || '#5FC7D6'],
|
||||
fill: {
|
||||
opacity: 0.85
|
||||
},
|
||||
|
||||
@@ -2,21 +2,85 @@
|
||||
|
||||
@push('style')
|
||||
<style>
|
||||
.chart-group-header-toggle {
|
||||
cursor: pointer;
|
||||
background-color: rgba(var(--bs-primary-rgb, 115, 103, 240), 0.08);
|
||||
border-inline-start: 4px solid var(--bs-primary, #9D8EF0);
|
||||
box-shadow: none;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
|
||||
.chart-group-header-toggle:hover {
|
||||
background-color: rgba(var(--bs-primary-rgb, 115, 103, 240), 0.13);
|
||||
}
|
||||
|
||||
.chart-group-header-toggle .card-body {
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.chart-group-header-toggle h5 {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.chart-group-header-toggle p {
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.chart-group-header-toggle:focus-visible {
|
||||
outline: 2px solid var(--bs-primary);
|
||||
outline-offset: 3px;
|
||||
}
|
||||
|
||||
.chart-group-icon-toggle {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.chart-group-icon-toggle .icon-base {
|
||||
font-size: 24px;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.chart-group-panel.show,
|
||||
.chart-group-panel.collapsing {
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.produksi-kpi-value {
|
||||
font-size: 1.35rem;
|
||||
font-weight: 800;
|
||||
line-height: 1.1;
|
||||
color: var(--bs-heading-color);
|
||||
font-size: 1.6rem;
|
||||
font-weight: 600;
|
||||
line-height: 1.2;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.produksi-kpi-label {
|
||||
font-size: .82rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: .2rem;
|
||||
color: var(--bs-heading-color);
|
||||
font-size: 0.9375rem;
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.produksi-kpi-desc {
|
||||
font-size: .78rem;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.produksi-chart-card .card-title h5 {
|
||||
color: var(--bs-heading-color);
|
||||
font-size: 1.125rem;
|
||||
font-weight: 500;
|
||||
line-height: 1.35;
|
||||
letter-spacing: 0;
|
||||
}
|
||||
|
||||
.produksi-chart-card .card-title small {
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.produksi-chart-sm {
|
||||
@@ -31,6 +95,52 @@
|
||||
min-height: 240px;
|
||||
}
|
||||
|
||||
.produksi-praktik-chart {
|
||||
min-height: 360px;
|
||||
}
|
||||
|
||||
.produksi-pie-canvas {
|
||||
min-height: 190px;
|
||||
}
|
||||
|
||||
.produksi-pie-legend {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: .5rem;
|
||||
margin-top: .75rem;
|
||||
}
|
||||
|
||||
.produksi-pie-legend-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: .5rem;
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.4;
|
||||
color: var(--bs-body-color);
|
||||
}
|
||||
|
||||
.produksi-pie-legend-marker {
|
||||
width: .65rem;
|
||||
height: .65rem;
|
||||
border-radius: 999px;
|
||||
flex: 0 0 .65rem;
|
||||
margin-top: .24rem;
|
||||
}
|
||||
|
||||
.produksi-pie-legend-label {
|
||||
min-width: 0;
|
||||
flex: 1 1 auto;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
|
||||
.produksi-pie-legend-value {
|
||||
flex: 0 0 auto;
|
||||
color: var(--bs-heading-color);
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.produksi-empty {
|
||||
min-height: 220px;
|
||||
display: flex;
|
||||
@@ -142,7 +252,7 @@
|
||||
<div class="row g-4 mb-4">
|
||||
@foreach ($kpiCards as $card)
|
||||
<div class="col-xl-4 col-md-4">
|
||||
<div class="card card-border-shadow-{{ $card['variant'] }} h-100">
|
||||
<div class="card card-border-shadow-{{ $card['variant'] }} h-100 produksi-kpi-card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center gap-4">
|
||||
<div class="avatar avatar-lg">
|
||||
@@ -159,9 +269,9 @@
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<p class="mb-0 mt-1">{{ $card['label'] }}</p>
|
||||
<p class="mb-0 mt-1 produksi-kpi-label">{{ $card['label'] }}</p>
|
||||
|
||||
<small class="text-body-secondary">{{ $card['description'] }}</small>
|
||||
<small class="text-body-secondary produksi-kpi-desc">{{ $card['description'] }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -170,34 +280,30 @@
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-start gap-2 mb-3">
|
||||
<div class="card chart-group-header-toggle mb-4" role="button" tabindex="0"
|
||||
data-bs-toggle="collapse" data-bs-target="#produksi-charts"
|
||||
aria-expanded="false" aria-controls="produksi-charts" aria-label="Tampilkan chart">
|
||||
<div class="card-body d-flex justify-content-between align-items-center gap-3">
|
||||
<div>
|
||||
<h5 class="mb-1">Kondisi Kebun</h5>
|
||||
<p class="text-body-secondary mb-0">Ringkasan umur lahan, umur tanaman, kondisi tanaman, gejala terlihat, dan tingkat keparahan serangan.</p>
|
||||
<h5>Lahan dan Tanaman</h5>
|
||||
<p class="text-body-secondary mb-0">Distribusi umur tanaman, jumlah tanaman per komoditas, kondisi tanaman, gejala serangan, dan tingkat keparahan serangan.</p>
|
||||
</div>
|
||||
<span class="btn btn-icon btn-label-secondary btn-sm chart-group-icon-toggle" aria-hidden="true">
|
||||
<i class="icon-base ti tabler-chevron-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
<button type="button" class="btn btn-label-secondary btn-sm chart-group-toggle" data-bs-toggle="collapse" data-bs-target="#produksi-charts" aria-expanded="true" aria-controls="produksi-charts">Sembunyikan Chart</button>
|
||||
</div>
|
||||
|
||||
<div class="collapse show chart-group-panel" id="produksi-charts">
|
||||
<div class="collapse chart-group-panel" id="produksi-charts">
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0 fw-bold">Umur Lahan</h6>
|
||||
<small class="text-body-secondary">Kelompok umur lahan terdata</small>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-umur-lahan" class="produksi-chart-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0 fw-bold">Umur Tanaman</h6>
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Umur Tanaman</h5>
|
||||
<small class="text-body-secondary">Kelompok umur tanaman saat ini</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-umur-tanaman" class="produksi-chart-sm"></div>
|
||||
</div>
|
||||
@@ -205,11 +311,27 @@
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0 fw-bold">Kondisi Tanaman</h6>
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Jumlah Tanaman Per Komoditas</h5>
|
||||
<small class="text-body-secondary">Jumlah tanaman terdata pada setiap komoditas</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-jumlah-tanaman-komoditas" class="produksi-chart-sm"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Kondisi Tanaman</h5>
|
||||
<small class="text-body-secondary">Distribusi kondisi baik, sedang, buruk, atau mati</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-kondisi-tanaman" class="produksi-chart"></div>
|
||||
</div>
|
||||
@@ -219,11 +341,13 @@
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-xl-8 col-lg-8">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0 fw-bold">Gejala Serangan yang Terlihat</h6>
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Gejala Serangan yang Terlihat</h5>
|
||||
<small class="text-body-secondary">Gabungan data hama, penyakit, dan gejala otomatis</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-gejala-serangan" class="produksi-chart-gejala"></div>
|
||||
</div>
|
||||
@@ -231,11 +355,13 @@
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-lg-4">
|
||||
<div class="card h-100">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0 fw-bold">Keparahan Serangan</h6>
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Keparahan Serangan</h5>
|
||||
<small class="text-body-secondary">Ringan, sedang, dan parah</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-keparahan-serangan" class="produksi-chart"></div>
|
||||
</div>
|
||||
@@ -243,6 +369,111 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card chart-group-header-toggle mb-4" role="button" tabindex="0"
|
||||
data-bs-toggle="collapse" data-bs-target="#praktik-budidaya-charts"
|
||||
aria-expanded="false" aria-controls="praktik-budidaya-charts" aria-label="Tampilkan chart">
|
||||
<div class="card-body d-flex justify-content-between align-items-center gap-3">
|
||||
<div>
|
||||
<h5>Praktik Budidaya</h5>
|
||||
<p class="text-body-secondary mb-0">Distribusi intensitas pemangkasan, pemupukan, penanganan hama dan penyakit, pembersihan lahan, sumber air, dan kebiasaan praktik baik.</p>
|
||||
</div>
|
||||
<span class="btn btn-icon btn-label-secondary btn-sm chart-group-icon-toggle" aria-hidden="true">
|
||||
<i class="icon-base ti tabler-chevron-down"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collapse chart-group-panel" id="praktik-budidaya-charts">
|
||||
<div class="row g-4 mb-4">
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Intensitas Pemangkasan</h5>
|
||||
<small class="text-body-secondary">Frekuensi dan ketepatan teknik pemangkasan</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-intensitas-pemangkasan" class="produksi-praktik-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Intensitas Pemupukan</h5>
|
||||
<small class="text-body-secondary">Kesesuaian jenis, dosis, dan waktu pemupukan</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-intensitas-pemupukan" class="produksi-praktik-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Metode Penanganan Hama dan Penyakit</h5>
|
||||
<small class="text-body-secondary">Cara penanganan serangan yang dilakukan petani</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-metode-penanganan-hama-penyakit" class="produksi-praktik-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row g-4">
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Intensitas Pembersihan Lahan</h5>
|
||||
<small class="text-body-secondary">Kebiasaan sanitasi dan pembersihan kebun</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-intensitas-pembersihan-lahan" class="produksi-praktik-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Sumber Air untuk Kebun</h5>
|
||||
<small class="text-body-secondary">Sumber air yang digunakan di kebun</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-sumber-air-kebun" class="produksi-praktik-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-xl-4 col-lg-6">
|
||||
<div class="card h-100 produksi-chart-card">
|
||||
<div class="card-header d-flex align-items-start justify-content-between gap-2">
|
||||
<div class="card-title mb-0">
|
||||
<h5 class="m-0 me-2">Kebiasaan Praktik Baik</h5>
|
||||
<small class="text-body-secondary">Praktik konservasi sederhana yang dilakukan petani</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chart-kebiasaan-praktik-baik" class="produksi-praktik-chart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@@ -251,9 +482,14 @@
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
const chartData = @json($chartData);
|
||||
const wilayah = @json($wilayah);
|
||||
const chartColors = ['#28a745', '#007bff', '#fd7e14', '#dc3545', '#6f42c1', '#20c997', '#6c757d', '#17a2b8'];
|
||||
const labelColor = '#6c757d';
|
||||
const borderColor = 'rgba(128, 128, 128, .18)';
|
||||
const headingColor = config.colors.headingColor;
|
||||
const legendColor = config.colors.bodyColor;
|
||||
const fontFamily = config.fontFamily;
|
||||
const pastelChart = config.chartColors || {};
|
||||
const pastelSemantic = pastelChart.semantic || {};
|
||||
const chartColors = pastelChart.series || ['#9D8EF0', '#6FD68F', '#FFB46F', '#5FC7D6', '#F47C86', '#9EA6B0', '#65D8BD', '#63C7E0'];
|
||||
const labelColor = legendColor;
|
||||
const borderColor = pastelChart.grid || 'rgba(128, 128, 128, .18)';
|
||||
|
||||
function hasData(data) {
|
||||
return data && Array.isArray(data.series) && data.series.some(function (value) {
|
||||
@@ -285,7 +521,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}, 0);
|
||||
|
||||
new ApexCharts(element, {
|
||||
chart: { type: 'donut', height: 265, toolbar: { show: false } },
|
||||
chart: { type: 'donut', height: 265, fontFamily: fontFamily, toolbar: { show: false } },
|
||||
labels: data.labels,
|
||||
series: data.series.map(Number),
|
||||
colors: chartColors,
|
||||
@@ -296,10 +532,19 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
formatter: function (value) {
|
||||
return value.toFixed(0) + '%';
|
||||
},
|
||||
style: {
|
||||
fontSize: '13px',
|
||||
fontFamily: fontFamily,
|
||||
fontWeight: 600,
|
||||
colors: ['#fff'],
|
||||
},
|
||||
},
|
||||
legend: {
|
||||
position: 'bottom',
|
||||
labels: { colors: labelColor },
|
||||
fontSize: '13px',
|
||||
fontFamily: fontFamily,
|
||||
fontWeight: 400,
|
||||
formatter: function (seriesName, options) {
|
||||
const value = Number(options.w.globals.series[options.seriesIndex] || 0);
|
||||
const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : '0.0';
|
||||
@@ -307,6 +552,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'dark',
|
||||
y: {
|
||||
formatter: function (value) {
|
||||
const percentage = total > 0 ? ((Number(value) / total) * 100).toFixed(1) : '0.0';
|
||||
@@ -334,6 +580,76 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}).render();
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? '')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function renderPieWithLegend(selector, data, options = {}) {
|
||||
const element = document.querySelector(selector);
|
||||
if (!element) return;
|
||||
if (!hasData(data)) return emptyChart(selector);
|
||||
|
||||
const series = data.series.map(Number);
|
||||
const total = series.reduce(function (sum, value) {
|
||||
return sum + Number(value || 0);
|
||||
}, 0);
|
||||
const colors = options.colors || chartColors;
|
||||
|
||||
element.innerHTML = '<div class="produksi-pie-canvas"></div><div class="produksi-pie-legend"></div>';
|
||||
const canvas = element.querySelector('.produksi-pie-canvas');
|
||||
const legend = element.querySelector('.produksi-pie-legend');
|
||||
|
||||
legend.innerHTML = data.labels.map(function (label, index) {
|
||||
const value = Number(series[index] || 0);
|
||||
const percentage = total > 0 ? ((value / total) * 100).toFixed(1) : '0.0';
|
||||
const color = colors[index % colors.length];
|
||||
|
||||
return `
|
||||
<div class="produksi-pie-legend-item">
|
||||
<span class="produksi-pie-legend-marker" style="background-color: ${escapeHtml(color)}"></span>
|
||||
<span class="produksi-pie-legend-label">${escapeHtml(label)}</span>
|
||||
<span class="produksi-pie-legend-value">${formatNumber(value)} (${percentage}%)</span>
|
||||
</div>
|
||||
`;
|
||||
}).join('');
|
||||
|
||||
new ApexCharts(canvas, {
|
||||
chart: { type: 'pie', height: options.height || 205, fontFamily: fontFamily, toolbar: { show: false } },
|
||||
labels: data.labels,
|
||||
series: series,
|
||||
colors: colors,
|
||||
stroke: { width: 0 },
|
||||
legend: { show: false },
|
||||
dataLabels: {
|
||||
enabled: true,
|
||||
formatter: function (value) {
|
||||
return value >= 8 ? value.toFixed(0) + '%' : '';
|
||||
},
|
||||
style: {
|
||||
fontSize: '11px',
|
||||
fontFamily: fontFamily,
|
||||
fontWeight: 600,
|
||||
colors: ['#fff'],
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'dark',
|
||||
y: {
|
||||
formatter: function (value) {
|
||||
const percentage = total > 0 ? ((Number(value) / total) * 100).toFixed(1) : '0.0';
|
||||
return `${formatNumber(value)} ${options.unit || 'data'} (${percentage}%)`;
|
||||
},
|
||||
},
|
||||
},
|
||||
plotOptions: { pie: { expandOnClick: true } },
|
||||
}).render();
|
||||
}
|
||||
|
||||
function renderBar(selector, data, options = {}) {
|
||||
const element = document.querySelector(selector);
|
||||
if (!element) return;
|
||||
@@ -345,11 +661,11 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
}, 0);
|
||||
|
||||
new ApexCharts(element, {
|
||||
chart: { type: 'bar', height: options.height || 285, toolbar: { show: false } },
|
||||
chart: { type: 'bar', height: options.height || 285, fontFamily: fontFamily, toolbar: { show: false } },
|
||||
series: [{ name: options.name || 'Jumlah', data: data.series.map(Number) }],
|
||||
xaxis: {
|
||||
categories: data.labels,
|
||||
labels: { style: { colors: labelColor } },
|
||||
labels: { style: { colors: labelColor, fontSize: '12px', fontFamily: fontFamily } },
|
||||
axisBorder: { color: borderColor },
|
||||
axisTicks: { color: borderColor },
|
||||
},
|
||||
@@ -357,10 +673,10 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
labels: {
|
||||
minWidth: options.yLabelWidth || 0,
|
||||
maxWidth: options.yLabelWidth || 160,
|
||||
style: { colors: labelColor },
|
||||
style: { colors: labelColor, fontSize: '12px', fontFamily: fontFamily },
|
||||
},
|
||||
},
|
||||
colors: [options.color || '#28a745'],
|
||||
colors: [options.color || pastelSemantic.success || '#6FD68F'],
|
||||
plotOptions: {
|
||||
bar: {
|
||||
horizontal: horizontal,
|
||||
@@ -377,8 +693,9 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
offsetY: horizontal ? 0 : -20,
|
||||
style: {
|
||||
fontSize: '12px',
|
||||
fontFamily: fontFamily,
|
||||
fontWeight: 500,
|
||||
colors: [labelColor],
|
||||
colors: [headingColor],
|
||||
},
|
||||
formatter: function (value) {
|
||||
const percentage = total > 0 ? ((Number(value) / total) * 100).toFixed(1) : '0.0';
|
||||
@@ -394,6 +711,7 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
theme: 'dark',
|
||||
y: {
|
||||
formatter: function (value) {
|
||||
const percentage = total > 0 ? ((Number(value) / total) * 100).toFixed(1) : '0.0';
|
||||
@@ -439,11 +757,17 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
element.innerHTML = '<div class="produksi-empty">ApexCharts belum dimuat.</div>';
|
||||
});
|
||||
} else {
|
||||
renderBar('#chart-umur-lahan', chartData.umurLahan, { color: '#007bff', unit: 'lahan' });
|
||||
renderBar('#chart-umur-tanaman', chartData.umurTanaman, { color: '#fd7e14', unit: 'data' });
|
||||
renderBar('#chart-umur-tanaman', chartData.umurTanaman, { color: pastelSemantic.warning || '#FFB46F', unit: 'data' });
|
||||
renderBar('#chart-jumlah-tanaman-komoditas', chartData.jumlahTanamanPerKomoditas, { color: pastelSemantic.success || '#6FD68F', unit: 'tanaman' });
|
||||
renderDonut('#chart-kondisi-tanaman', chartData.kondisiTanaman);
|
||||
renderBar('#chart-gejala-serangan', chartData.gejalaSerangan, { horizontal: true, height: 250, color: '#dc3545', unit: 'data', yLabelWidth: 210, rightPadding: 115 });
|
||||
renderBar('#chart-gejala-serangan', chartData.gejalaSerangan, { horizontal: true, height: 250, color: pastelSemantic.danger || '#F47C86', unit: 'data', yLabelWidth: 210, rightPadding: 115 });
|
||||
renderDonut('#chart-keparahan-serangan', chartData.keparahanSerangan);
|
||||
renderPieWithLegend('#chart-intensitas-pemangkasan', chartData.intensitasPemangkasan, { unit: 'petani' });
|
||||
renderPieWithLegend('#chart-intensitas-pemupukan', chartData.intensitasPemupukan, { unit: 'petani' });
|
||||
renderPieWithLegend('#chart-metode-penanganan-hama-penyakit', chartData.metodePenangananHamaPenyakit, { unit: 'petani' });
|
||||
renderPieWithLegend('#chart-intensitas-pembersihan-lahan', chartData.intensitasPembersihanLahan, { unit: 'petani' });
|
||||
renderPieWithLegend('#chart-sumber-air-kebun', chartData.sumberAirKebun, { unit: 'petani' });
|
||||
renderPieWithLegend('#chart-kebiasaan-praktik-baik', chartData.kebiasaanPraktikBaik, { unit: 'petani' });
|
||||
}
|
||||
|
||||
$('#filter-provinsi, #filter-kabkota, #filter-kecamatan, #filter-desa, #filter-komoditas, #filter-segmen').select2({ width: '100%' });
|
||||
@@ -493,16 +817,35 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
window.location.href = @json(route('dashboard.produktivitas'));
|
||||
});
|
||||
|
||||
document.querySelectorAll('.chart-group-header-toggle').forEach(function (header) {
|
||||
header.addEventListener('keydown', function (event) {
|
||||
if (event.target !== header || !['Enter', ' '].includes(event.key)) return;
|
||||
event.preventDefault();
|
||||
header.click();
|
||||
});
|
||||
});
|
||||
|
||||
document.querySelectorAll('.chart-group-panel').forEach(function (panel) {
|
||||
const button = document.querySelector('[aria-controls="' + panel.id + '"]');
|
||||
panel.addEventListener('hidden.bs.collapse', function (event) {
|
||||
if (event.target !== panel || !button) return;
|
||||
button.textContent = 'Tampilkan Chart';
|
||||
const control = document.querySelector('.chart-group-header-toggle[aria-controls="' + panel.id + '"]');
|
||||
if (!control) return;
|
||||
const icon = control.querySelector('.chart-group-icon-toggle i');
|
||||
const syncState = function () {
|
||||
const expanded = panel.classList.contains('show');
|
||||
const label = expanded ? 'Sembunyikan chart' : 'Tampilkan chart';
|
||||
control.setAttribute('aria-expanded', String(expanded));
|
||||
control.setAttribute('aria-label', label);
|
||||
control.title = label;
|
||||
icon.classList.toggle('tabler-chevron-up', expanded);
|
||||
icon.classList.toggle('tabler-chevron-down', !expanded);
|
||||
};
|
||||
|
||||
syncState();
|
||||
['hidden.bs.collapse', 'shown.bs.collapse'].forEach(function (eventName) {
|
||||
panel.addEventListener(eventName, function (event) {
|
||||
if (event.target !== panel) return;
|
||||
syncState();
|
||||
if (eventName === 'shown.bs.collapse') window.dispatchEvent(new Event('resize'));
|
||||
});
|
||||
panel.addEventListener('shown.bs.collapse', function (event) {
|
||||
if (event.target !== panel || !button) return;
|
||||
button.textContent = 'Sembunyikan Chart';
|
||||
window.dispatchEvent(new Event('resize'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -144,10 +144,10 @@
|
||||
<div class="absolute bottom-3 left-3 bg-white/95 p-3 rounded-lg text-xs border border-gray-200 shadow-sm backdrop-blur-sm">
|
||||
<div class="font-bold text-gray-700 mb-2">Legenda Tanaman:</div>
|
||||
<div class="grid grid-cols-2 gap-x-4 gap-y-2">
|
||||
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#6F4E37] rounded-full shadow-sm"></div> Kopi</div>
|
||||
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#eab308] rounded-full shadow-sm"></div> Vanili</div>
|
||||
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#D2691E] rounded-full shadow-sm"></div> Kakao</div>
|
||||
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#9ca3af] rounded-full shadow-sm"></div> Kemiri</div>
|
||||
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#6FD68F] rounded-full shadow-sm"></div> Kopi</div>
|
||||
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#FFB46F] rounded-full shadow-sm"></div> Vanili</div>
|
||||
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#9D8EF0] rounded-full shadow-sm"></div> Kakao</div>
|
||||
<div class="flex items-center gap-2"><div class="w-3 h-3 bg-[#9EA6B0] rounded-full shadow-sm"></div> Kemiri</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -253,6 +253,9 @@
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const pastelChart = config.chartColors || {};
|
||||
const pastelCategory = pastelChart.categories || {};
|
||||
const gridColor = pastelChart.grid || '#E8EDF5';
|
||||
|
||||
// 1. Inisialisasi Icons
|
||||
if(typeof lucide !== 'undefined') {
|
||||
@@ -263,19 +266,19 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
const spatialData = [
|
||||
{
|
||||
id: 'L-001', owner: 'Yosef Neta', familySize: 5, area: '1.2 Ha', village: 'Radabata',
|
||||
polygon: "15,25 75,15 90,85 20,70", fill: "rgba(34, 197, 94, 0.15)", stroke: "#16a34a",
|
||||
polygon: "15,25 75,15 90,85 20,70", fill: "rgba(111, 214, 143, 0.24)", stroke: "#6FD68F",
|
||||
plants: [
|
||||
{ id: 'P-01', type: 'Kopi', cx: 35, cy: 35, planted: 'Mei 2021', status: 'Produktif', intervensi: 'Pemupukan Organik (Okt 2023)', panen: '45 kg/musim', color: '#6F4E37' },
|
||||
{ id: 'P-02', type: 'Vanili', cx: 65, cy: 45, planted: 'Jan 2022', status: 'Berbunga', intervensi: 'Pelatihan Polinasi (Feb 2024)', panen: 'Belum panen', color: '#eab308' },
|
||||
{ id: 'P-03', type: 'Kopi', cx: 50, cy: 65, planted: 'Mei 2021', status: 'Produktif', intervensi: 'Pemangkasan (Nov 2023)', panen: '40 kg/musim', color: '#6F4E37' },
|
||||
{ id: 'P-01', type: 'Kopi', cx: 35, cy: 35, planted: 'Mei 2021', status: 'Produktif', intervensi: 'Pemupukan Organik (Okt 2023)', panen: '45 kg/musim', color: pastelCategory.coffee || '#6FD68F' },
|
||||
{ id: 'P-02', type: 'Vanili', cx: 65, cy: 45, planted: 'Jan 2022', status: 'Berbunga', intervensi: 'Pelatihan Polinasi (Feb 2024)', panen: 'Belum panen', color: pastelCategory.vanilla || '#FFB46F' },
|
||||
{ id: 'P-03', type: 'Kopi', cx: 50, cy: 65, planted: 'Mei 2021', status: 'Produktif', intervensi: 'Pemangkasan (Nov 2023)', panen: '40 kg/musim', color: pastelCategory.coffee || '#6FD68F' },
|
||||
]
|
||||
},
|
||||
{
|
||||
id: 'L-002', owner: 'Maria Goreti', familySize: 3, area: '0.8 Ha', village: 'Ubedolumolo',
|
||||
polygon: "110,40 170,15 190,65 125,85", fill: "rgba(59, 130, 246, 0.15)", stroke: "#2563eb",
|
||||
polygon: "110,40 170,15 190,65 125,85", fill: "rgba(95, 199, 214, 0.20)", stroke: "#5FC7D6",
|
||||
plants: [
|
||||
{ id: 'P-04', type: 'Kemiri', cx: 140, cy: 45, planted: 'Agu 2018', status: 'Produktif', intervensi: 'Pembersihan Gulma', panen: '120 kg/musim', color: '#9ca3af' },
|
||||
{ id: 'P-05', type: 'Kakao', cx: 165, cy: 55, planted: 'Okt 2020', status: 'Terserang Hama', intervensi: 'Penyemprotan Bio-pestisida (Jan 2024)', panen: '20 kg/musim', color: '#D2691E' },
|
||||
{ id: 'P-04', type: 'Kemiri', cx: 140, cy: 45, planted: 'Agu 2018', status: 'Produktif', intervensi: 'Pembersihan Gulma', panen: '120 kg/musim', color: pastelCategory.candlenut || '#9EA6B0' },
|
||||
{ id: 'P-05', type: 'Kakao', cx: 165, cy: 55, planted: 'Okt 2020', status: 'Terserang Hama', intervensi: 'Penyemprotan Bio-pestisida (Jan 2024)', panen: '20 kg/musim', color: pastelCategory.cacao || '#9D8EF0' },
|
||||
]
|
||||
}
|
||||
];
|
||||
@@ -440,24 +443,24 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
{
|
||||
label: 'Kopi',
|
||||
data: [0.5, 0.7, 1.1, 1.5],
|
||||
borderColor: '#6F4E37',
|
||||
backgroundColor: '#6F4E37',
|
||||
borderColor: pastelCategory.coffee || '#6FD68F',
|
||||
backgroundColor: pastelCategory.coffee || '#6FD68F',
|
||||
tension: 0.3,
|
||||
borderWidth: 2
|
||||
},
|
||||
{
|
||||
label: 'Vanili',
|
||||
data: [0.1, 0.15, 0.3, 0.5],
|
||||
borderColor: '#eab308',
|
||||
backgroundColor: '#eab308',
|
||||
borderColor: pastelCategory.vanilla || '#FFB46F',
|
||||
backgroundColor: pastelCategory.vanilla || '#FFB46F',
|
||||
tension: 0.3,
|
||||
borderWidth: 2
|
||||
},
|
||||
{
|
||||
label: 'Kakao',
|
||||
data: [0.4, 0.5, 0.8, 1.2],
|
||||
borderColor: '#D2691E',
|
||||
backgroundColor: '#D2691E',
|
||||
borderColor: pastelCategory.cacao || '#9D8EF0',
|
||||
backgroundColor: pastelCategory.cacao || '#9D8EF0',
|
||||
tension: 0.3,
|
||||
borderWidth: 2
|
||||
}
|
||||
@@ -467,7 +470,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
responsive: true, maintainAspectRatio: false,
|
||||
interaction: { mode: 'index', intersect: false },
|
||||
scales: {
|
||||
y: { title: { display: true, text: 'Ton per Hektar (Ton/Ha)' }, grid: { color: '#f3f4f6' } },
|
||||
y: { title: { display: true, text: 'Ton per Hektar (Ton/Ha)' }, grid: { color: gridColor } },
|
||||
x: { grid: { display: false } }
|
||||
},
|
||||
plugins: {
|
||||
@@ -489,11 +492,11 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
label: 'Jumlah Bibit Didistribusikan',
|
||||
data: [15000, 5200, 8000, 7000, 10000],
|
||||
backgroundColor: [
|
||||
'rgba(111, 78, 55, 0.8)', // Kopi
|
||||
'rgba(234, 179, 8, 0.8)', // Vanili
|
||||
'rgba(210, 105, 30, 0.8)', // Kakao
|
||||
'rgba(156, 163, 175, 0.8)', // Kemiri
|
||||
'rgba(34, 197, 94, 0.8)' // Pelindung (Hijau)
|
||||
'rgba(111, 214, 143, 0.86)', // Kopi
|
||||
'rgba(255, 180, 111, 0.86)', // Vanili
|
||||
'rgba(157, 142, 240, 0.86)', // Kakao
|
||||
'rgba(179, 181, 188, 0.86)', // Kemiri
|
||||
'rgba(101, 216, 189, 0.86)' // Pelindung
|
||||
],
|
||||
borderRadius: 4
|
||||
}
|
||||
@@ -502,7 +505,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
||||
options: {
|
||||
responsive: true, maintainAspectRatio: false,
|
||||
scales: {
|
||||
y: { beginAtZero: true, grid: { color: '#f3f4f6' } },
|
||||
y: { beginAtZero: true, grid: { color: gridColor } },
|
||||
x: { grid: { display: false } }
|
||||
},
|
||||
plugins: {
|
||||
|
||||
@@ -219,6 +219,10 @@
|
||||
@push('scripts')
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const pastelChart = config.chartColors || {};
|
||||
const pastelSemantic = pastelChart.semantic || {};
|
||||
const pastelCategory = pastelChart.categories || {};
|
||||
const gridColor = pastelChart.grid || '#E8EDF5';
|
||||
|
||||
// Inisialisasi Icons (Khusus Mockup, Vuexy mungkin pakai library icon sendiri seperti Tabler/FontAwesome)
|
||||
if(typeof lucide !== 'undefined') {
|
||||
@@ -236,7 +240,7 @@
|
||||
{
|
||||
label: 'Baseline (Tanpa Intervensi)',
|
||||
data: [12, 12.5, 13, 13.5],
|
||||
borderColor: '#9ca3af',
|
||||
borderColor: pastelSemantic.gray || '#9EA6B0',
|
||||
backgroundColor: 'transparent',
|
||||
borderDash: [5, 5],
|
||||
fill: false,
|
||||
@@ -245,11 +249,11 @@
|
||||
{
|
||||
label: 'Pendapatan Aktual',
|
||||
data: [12, 15, 22, 31],
|
||||
borderColor: '#10b981',
|
||||
backgroundColor: 'rgba(16, 185, 129, 0.15)', // Light green fill
|
||||
borderColor: pastelCategory.marketGreen || '#6FD68F',
|
||||
backgroundColor: 'rgba(111, 214, 143, 0.20)',
|
||||
fill: true,
|
||||
tension: 0.4,
|
||||
pointBackgroundColor: '#10b981',
|
||||
pointBackgroundColor: pastelCategory.marketGreen || '#6FD68F',
|
||||
pointBorderWidth: 2,
|
||||
pointRadius: 4
|
||||
}
|
||||
@@ -265,7 +269,7 @@
|
||||
scales: {
|
||||
y: {
|
||||
beginAtZero: false,
|
||||
grid: { color: '#f3f4f6' },
|
||||
grid: { color: gridColor },
|
||||
ticks: { callback: function(val) { return 'Rp ' + val + 'M'; } }
|
||||
},
|
||||
x: {
|
||||
@@ -296,7 +300,12 @@
|
||||
labels: ['Kopi', 'Vanili', 'Kakao', 'Kemiri'],
|
||||
datasets: [{
|
||||
data: [45, 25, 15, 15],
|
||||
backgroundColor: ['#6F4E37', '#eab308', '#D2691E', '#9ca3af'],
|
||||
backgroundColor: [
|
||||
pastelCategory.coffee || '#6FD68F',
|
||||
pastelCategory.vanilla || '#FFB46F',
|
||||
pastelCategory.cacao || '#9D8EF0',
|
||||
pastelCategory.candlenut || '#9EA6B0'
|
||||
],
|
||||
borderWidth: 2,
|
||||
borderColor: '#ffffff',
|
||||
hoverOffset: 4
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
box-shadow: 0 .25rem 1rem rgba(34, 48, 62, .08);
|
||||
}
|
||||
|
||||
.tk-stat.green { border-left-color: #28a745; }
|
||||
.tk-stat.blue { border-left-color: #007bff; }
|
||||
.tk-stat.amber { border-left-color: #fd7e14; }
|
||||
.tk-stat.green { border-left-color: #6FD68F; }
|
||||
.tk-stat.blue { border-left-color: #5FC7D6; }
|
||||
.tk-stat.amber { border-left-color: #FFB46F; }
|
||||
|
||||
.tk-stat-lbl {
|
||||
font-size: 10px;
|
||||
@@ -444,10 +444,10 @@
|
||||
@forelse ($progressPetani as $petani)
|
||||
@php
|
||||
$barColor = match (true) {
|
||||
$petani['percent'] === 100 => '#28a745',
|
||||
$petani['percent'] >= 50 => '#007bff',
|
||||
$petani['percent'] >= 20 => '#fd7e14',
|
||||
default => '#dc3545',
|
||||
$petani['percent'] === 100 => '#6FD68F',
|
||||
$petani['percent'] >= 50 => '#5FC7D6',
|
||||
$petani['percent'] >= 20 => '#FFB46F',
|
||||
default => '#F47C86',
|
||||
};
|
||||
|
||||
$initial = collect(explode(' ', trim($petani['name'])))
|
||||
@@ -465,8 +465,8 @@
|
||||
style="
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
background: rgba(40,167,69,.15);
|
||||
color: #28a745;
|
||||
background: rgba(111,214,143,.24);
|
||||
color: #6FD68F;
|
||||
"
|
||||
>
|
||||
{{ $initial ?: 'PT' }}
|
||||
|
||||
@@ -1054,12 +1054,13 @@ document.addEventListener('DOMContentLoaded', function () {
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
const primaryColor = config.colors.primary;
|
||||
const secondaryColor = config.colors.secondary;
|
||||
const successColor = config.colors.success;
|
||||
const infoColor = config.colors.info;
|
||||
const warningColor = config.colors.warning;
|
||||
const dangerColor = config.colors.danger;
|
||||
const pastelSemantic = config.chartColors?.semantic || {};
|
||||
const primaryColor = pastelSemantic.primary || '#9D8EF0';
|
||||
const secondaryColor = pastelSemantic.secondary || '#9EA6B0';
|
||||
const successColor = pastelSemantic.success || '#6FD68F';
|
||||
const infoColor = pastelSemantic.info || '#5FC7D6';
|
||||
const warningColor = pastelSemantic.warning || '#FFB46F';
|
||||
const dangerColor = pastelSemantic.danger || '#F47C86';
|
||||
|
||||
const bodyColor = config.colors.bodyColor;
|
||||
const borderColor = config.colors.borderColor;
|
||||
|
||||
Reference in New Issue
Block a user