Update workshop
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum JenisWorkshop: string
|
||||
{
|
||||
case LOKAKARYA = 'lokakarya';
|
||||
case PELATIHAN = 'pelatihan';
|
||||
case SOSIALISASI = 'sosialisasi';
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App\Enums;
|
||||
|
||||
enum StatusWorkshop: string
|
||||
{
|
||||
case DRAFT = 'draft';
|
||||
case AKTIF = 'aktif';
|
||||
case TUNDA = 'tunda';
|
||||
case BATAL = 'batal';
|
||||
case SELESAI = 'selesai';
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Program;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class PelatihanController extends Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Web\Program;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Http\Request;
|
||||
use App\Models\Program\Workshop;
|
||||
use App\Models\Program\SesiWorkshop;
|
||||
use App\Models\Program\PesertaWorkshop;
|
||||
use App\Models\Program\Aktivitas;
|
||||
use App\Enums\StatusWorkshop;
|
||||
|
||||
class WorkshopController extends Controller
|
||||
{
|
||||
public function index(Request $request)
|
||||
{
|
||||
$title = 'Workshop';
|
||||
|
||||
$summary = ([
|
||||
'kegiatan' => Workshop::count(),
|
||||
'lokakarya' => Workshop::where('jenis', 'lokakarya')->count(),
|
||||
'pelatihan' => Workshop::where('jenis', 'pelatihan')->count(),
|
||||
'sosialisasi' => Workshop::where('jenis', 'sosialisasi')->count(),
|
||||
'sesi' => SesiWorkshop::count(),
|
||||
'peserta' => PesertaWorkshop::count(),
|
||||
]);
|
||||
|
||||
$query = Workshop::query();
|
||||
|
||||
if ($request->filled('search')) {
|
||||
$search = $request->search;
|
||||
$query->where(function($q) use ($search) {
|
||||
$q->where('judul', 'ilike', "%{$search}%")
|
||||
->orWhere('deskripsi', 'ilike', "%{$search}%");
|
||||
});
|
||||
}
|
||||
|
||||
if ($request->filled('jenis')) {
|
||||
$query->where('jenis', $request->jenis);
|
||||
}
|
||||
|
||||
if ($request->filled('status')) {
|
||||
$query->where('status', $request->status);
|
||||
}
|
||||
|
||||
if ($request->filled('tanggal_awal') && $request->filled('tanggal_akhir')) {
|
||||
$query->whereBetween('tanggal_awal', [$request->tanggal_awal, $request->tanggal_akhir]);
|
||||
} elseif ($request->filled('tanggal_awal')) {
|
||||
$query->where('tanggal_awal', '>=', $request->tanggal_awal);
|
||||
} elseif ($request->filled('tanggal_akhir')) {
|
||||
$query->where('tanggal_akhir', '<=', $request->tanggal_akhir);
|
||||
}
|
||||
|
||||
$perPage = $request->input('per_page', 10);
|
||||
$workshops = $query->latest()->paginate($perPage);
|
||||
|
||||
return view('program.workshop.index', compact('workshops', 'summary', 'title'));
|
||||
}
|
||||
|
||||
public function create()
|
||||
{
|
||||
$programs = Aktivitas::latest()->get();
|
||||
return view('program.workshop.add', compact('programs'));
|
||||
}
|
||||
|
||||
public function store(Request $request)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'program_aktivitas_id' => 'nullable|integer',
|
||||
'judul' => 'required|string|max:255',
|
||||
'jenis' => 'required|string',
|
||||
'tanggal_awal' => 'required|date',
|
||||
'tanggal_akhir' => 'required|date|after_or_equal:tanggal_awal',
|
||||
'status' => 'required|string',
|
||||
'deskripsi' => 'nullable|string',
|
||||
], [
|
||||
'program_aktivitas_id.integer' => 'Program aktivitas harus berupa angka.',
|
||||
|
||||
'judul.required' => 'Judul wajib diisi.',
|
||||
'judul.string' => 'Judul harus berupa teks.',
|
||||
'judul.max' => 'Judul maksimal 255 karakter.',
|
||||
|
||||
'jenis.required' => 'Jenis wajib diisi.',
|
||||
'jenis.string' => 'Jenis harus berupa teks.',
|
||||
|
||||
'tanggal_awal.required' => 'Tanggal awal wajib diisi.',
|
||||
'tanggal_awal.date' => 'Tanggal awal harus berupa tanggal yang valid.',
|
||||
|
||||
'tanggal_akhir.required' => 'Tanggal akhir wajib diisi.',
|
||||
'tanggal_akhir.date' => 'Tanggal akhir harus berupa tanggal yang valid.',
|
||||
'tanggal_akhir.after_or_equal' => 'Tanggal akhir harus setelah atau sama dengan tanggal awal.',
|
||||
|
||||
'status.required' => 'Status wajib diisi.',
|
||||
'status.string' => 'Status harus berupa teks.',
|
||||
|
||||
'deskripsi.string' => 'Deskripsi harus berupa teks.',
|
||||
]);
|
||||
|
||||
$workshop = Workshop::create([
|
||||
'judul' => $request->judul,
|
||||
'jenis' => $request->jenis,
|
||||
'tanggal_awal' => $request->tanggal_awal,
|
||||
'tanggal_akhir' => $request->tanggal_akhir,
|
||||
'status' => $request->status,
|
||||
'deskripsi' => $request->deskripsi,
|
||||
'program_aktivitas_id' => $request->program_aktivitas_id ?? null,
|
||||
'created_by' => auth()->user()->id,
|
||||
'updated_by' => auth()->user()->id,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Workshop berhasil disimpan sebagai Draft.'
|
||||
]);
|
||||
}
|
||||
|
||||
public function edit($id)
|
||||
{
|
||||
$workshop = Workshop::findOrFail($id);
|
||||
$programs = Aktivitas::latest()->get();
|
||||
|
||||
return view('program.workshop.edit', compact('workshop', 'programs'));
|
||||
}
|
||||
|
||||
public function update(Request $request, $id)
|
||||
{
|
||||
$validated = $request->validate([
|
||||
'program_aktivitas_id' => 'nullable|integer',
|
||||
'judul' => 'required|string|max:255',
|
||||
'jenis' => 'required|string',
|
||||
'tanggal_awal' => 'required|date',
|
||||
'tanggal_akhir' => 'required|date|after_or_equal:tanggal_awal',
|
||||
'status' => 'required|string',
|
||||
'deskripsi' => 'nullable|string',
|
||||
], [
|
||||
'program_aktivitas_id.integer' => 'Program aktivitas harus berupa angka.',
|
||||
|
||||
'judul.required' => 'Judul wajib diisi.',
|
||||
'judul.string' => 'Judul harus berupa teks.',
|
||||
'judul.max' => 'Judul maksimal 255 karakter.',
|
||||
|
||||
'jenis.required' => 'Jenis wajib diisi.',
|
||||
'jenis.string' => 'Jenis harus berupa teks.',
|
||||
|
||||
'tanggal_awal.required' => 'Tanggal awal wajib diisi.',
|
||||
'tanggal_awal.date' => 'Tanggal awal harus berupa tanggal yang valid.',
|
||||
|
||||
'tanggal_akhir.required' => 'Tanggal akhir wajib diisi.',
|
||||
'tanggal_akhir.date' => 'Tanggal akhir harus berupa tanggal yang valid.',
|
||||
'tanggal_akhir.after_or_equal' => 'Tanggal akhir harus setelah atau sama dengan tanggal awal.',
|
||||
|
||||
'status.required' => 'Status wajib diisi.',
|
||||
'status.string' => 'Status harus berupa teks.',
|
||||
|
||||
'deskripsi.string' => 'Deskripsi harus berupa teks.',
|
||||
]);
|
||||
|
||||
$workshop = Workshop::findOrFail($id);
|
||||
|
||||
$workshop->update([
|
||||
'judul' => $request->judul,
|
||||
'jenis' => $request->jenis,
|
||||
'tanggal_awal' => $request->tanggal_awal,
|
||||
'tanggal_akhir' => $request->tanggal_akhir,
|
||||
'status' => $request->status,
|
||||
'deskripsi' => $request->deskripsi,
|
||||
'program_aktivitas_id' => $request->program_aktivitas_id ?? null,
|
||||
'updated_by' => auth()->user()->id,
|
||||
]);
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Perubahan workshop berhasil disimpan.'
|
||||
]);
|
||||
}
|
||||
|
||||
public function destroy($id)
|
||||
{
|
||||
$workshop = Workshop::findOrFail($id);
|
||||
$workshop->update(['deleted_by' => auth()->user()->id]);
|
||||
|
||||
$workshop->delete();
|
||||
|
||||
return response()->json([
|
||||
'status' => 'success',
|
||||
'message' => 'Workshop berhasil dihapus.'
|
||||
]);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Program;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Models\Profile;
|
||||
use App\Models\Program\SesiWorkshop;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class PesertaWorkshop extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $table = 'peserta_workshop';
|
||||
|
||||
protected $fillable = [
|
||||
'sesi_workshop_id',
|
||||
'profil_id',
|
||||
'status',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
public function profil()
|
||||
{
|
||||
return $this->belongsTo(Profile::class, 'profil_id');
|
||||
}
|
||||
|
||||
public function sesiWorkshop()
|
||||
{
|
||||
return $this->belongsTo(SesiWorkshop::class, 'sesi_workshop_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Program;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Models\Program\Workshop;
|
||||
use App\Models\Program\PesertaWorkshop;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class SesiWorkshop extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $table = 'sesi_workshop';
|
||||
|
||||
protected $fillable = [
|
||||
'workshop_id',
|
||||
'judul',
|
||||
'deskripsi',
|
||||
'tanggal_awal',
|
||||
'tanggal_akhir',
|
||||
'status',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
public function workshop()
|
||||
{
|
||||
return $this->belongsTo(Workshop::class, 'workshop_id');
|
||||
}
|
||||
|
||||
public function pesertaWorkshop()
|
||||
{
|
||||
return $this->hasMany(PesertaWorkshop::class, 'sesi_workshop_id');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models\Program;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use App\Models\Program\Aktivitas;
|
||||
use App\Models\Program\SesiWorkshop;
|
||||
use App\Enums\StatusWorkshop;
|
||||
use App\Enums\JenisWorkshop;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class Workshop extends Model
|
||||
{
|
||||
use HasFactory, SoftDeletes;
|
||||
|
||||
protected $table = 'workshop';
|
||||
|
||||
protected $casts = [
|
||||
'status' => StatusWorkshop::class,
|
||||
'jenis' => JenisWorkshop::class,
|
||||
];
|
||||
|
||||
protected $fillable = [
|
||||
'program_aktivitas_id',
|
||||
'judul',
|
||||
'deskripsi',
|
||||
'jenis',
|
||||
'tanggal_awal',
|
||||
'tanggal_akhir',
|
||||
'status',
|
||||
'created_by',
|
||||
'updated_by',
|
||||
'deleted_by',
|
||||
];
|
||||
|
||||
public function program()
|
||||
{
|
||||
return $this->belongsTo(Aktivitas::class, 'program_aktivitas_id');
|
||||
}
|
||||
public function sesiWorkshop()
|
||||
{
|
||||
return $this->hasMany(SesiWorkshop::class, 'workshop_id');
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ return new class extends Migration
|
||||
$table->id();
|
||||
$table->string('kode', 2)->unique();
|
||||
$table->string('nama', 100);
|
||||
$table->string('path', 10000);
|
||||
$table->jsonb('path')->nullable();
|
||||
$table->string('iso', 100);
|
||||
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
@@ -28,7 +28,7 @@ return new class extends Migration
|
||||
$table->id();
|
||||
$table->string('kode', 4)->unique();
|
||||
$table->string('nama', 100);
|
||||
$table->string('path', 10000);
|
||||
$table->jsonb('path')->nullable();
|
||||
$table->foreignId('provinsi_id')->constrained('master_provinsi')->onDelete('set null');
|
||||
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
@@ -41,7 +41,7 @@ return new class extends Migration
|
||||
$table->id();
|
||||
$table->string('kode', 6)->unique();
|
||||
$table->string('nama', 100);
|
||||
$table->string('path', 10000);
|
||||
$table->jsonb('path')->nullable();
|
||||
$table->foreignId('kabupaten_kota_id')->constrained('master_kabupaten_kota')->onDelete('set null');
|
||||
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
@@ -54,7 +54,7 @@ return new class extends Migration
|
||||
$table->id();
|
||||
$table->string('kode', 10)->unique();
|
||||
$table->string('nama', 100);
|
||||
$table->string('path', 10000);
|
||||
$table->jsonb('path')->nullable();
|
||||
$table->foreignId('kecamatan_id')->constrained('master_kecamatan')->onDelete('set null');
|
||||
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
return new class extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
Schema::create('workshop', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->foreignId('program_aktivitas_id')->nullable()->constrained('program_aktivitas')->nullOnDelete();
|
||||
$table->string('judul');
|
||||
$table->text('deskripsi')->nullable();
|
||||
$table->string('jenis')->nullable();
|
||||
$table->date('tanggal_awal')->nullable();
|
||||
$table->date('tanggal_akhir')->nullable();
|
||||
$table->string('status')->nullable();
|
||||
|
||||
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
Schema::create('sesi_workshop', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->foreignId('workshop_id')->nullable()->constrained('workshop')->nullOnDelete();
|
||||
$table->string('judul');
|
||||
$table->text('deskripsi')->nullable();
|
||||
$table->foreignId('desa_kelurahan_id')->nullable()->constrained('master_desa_kelurahan');
|
||||
$table->string('lokasi')->nullable();
|
||||
$table->timestamp('tanggal_awal')->nullable();
|
||||
$table->timestamp('tanggal_akhir')->nullable();
|
||||
$table->string('status')->nullable();
|
||||
|
||||
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
|
||||
Schema::create('peserta_workshop', function (Blueprint $table) {
|
||||
$table->id();
|
||||
|
||||
$table->foreignId('sesi_workshop_id')->nullable()->constrained('sesi_workshop')->nullOnDelete();
|
||||
$table->foreignId('profile_id')->nullable()->constrained('profiles')->nullOnDelete();
|
||||
$table->string('status')->nullable();
|
||||
|
||||
$table->foreignId('created_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->foreignId('updated_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
$table->foreignId('deleted_by')->nullable()->constrained('users')->onDelete('set null');
|
||||
|
||||
$table->timestamps();
|
||||
$table->softDeletes();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
Schema::dropIfExists('peserta_workshop');
|
||||
Schema::dropIfExists('sesi_workshop');
|
||||
Schema::dropIfExists('workshop');
|
||||
}
|
||||
};
|
||||
@@ -338,7 +338,6 @@
|
||||
@push('scripts')
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script>
|
||||
// 1. Setup Base Map (Gunakan HTTPS untuk menghindari error mixed-content)
|
||||
var satellite = L.tileLayer('https://mt1.google.com/vt/lyrs=s&x={x}&y={y}&z={z}', { maxZoom: 22 });
|
||||
var topo = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png');
|
||||
var parcels = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png');
|
||||
@@ -346,7 +345,6 @@
|
||||
var map = L.map('map', { center: [-0.789, 113.921], zoom: 5, layers: [satellite], zoomControl: false });
|
||||
L.control.zoom({ position: 'topleft' }).addTo(map);
|
||||
|
||||
// Map Switcher Custom
|
||||
function setMapLayer(type) {
|
||||
map.removeLayer(satellite); map.removeLayer(topo); map.removeLayer(parcels);
|
||||
if(type === 'satellite') satellite.addTo(map);
|
||||
@@ -369,23 +367,16 @@
|
||||
var mapHistory = [];
|
||||
var btnBack = document.getElementById('btnBackMap');
|
||||
|
||||
// ==========================================
|
||||
// FUNGSI PINTAR: DETEKSI LATITUDE/LONGITUDE
|
||||
// ==========================================
|
||||
function ensureLatLon(coords) {
|
||||
if (typeof coords[0] === 'number') {
|
||||
// Indonesia: Longitude = ~95 s/d ~141 | Latitude = ~-11 s/d ~6
|
||||
// Jika angka pertama melebihi 90, itu PASTI Longitude. Kita balik agar menjadi [Lat, Lon]
|
||||
if (Math.abs(coords[0]) > 90) {
|
||||
return [coords[1], coords[0]];
|
||||
}
|
||||
// Jika angka pertama kecil, berarti sudah benar [Lat, Lon], biarkan saja
|
||||
return [coords[0], coords[1]];
|
||||
}
|
||||
return coords.map(ensureLatLon);
|
||||
}
|
||||
|
||||
// 3. Fungsi Render Peta Dinamis
|
||||
function renderLevel(level, parentId = null, fallbackBounds = null) {
|
||||
var dataToRender = dataLevels[level];
|
||||
|
||||
@@ -403,11 +394,7 @@
|
||||
|
||||
currentLayerGroup.clearLayers();
|
||||
|
||||
// =========================================================
|
||||
// FITUR BARU: GAMBAR BATAS DESA SAAT MELIHAT LAHAN
|
||||
// =========================================================
|
||||
if (level === 'lahan' && parentId !== null) {
|
||||
// Cari data desa yang sedang diklik
|
||||
var parentDesa = dataLevels['desa'].find(d => d.id == parentId);
|
||||
|
||||
if (parentDesa && parentDesa.polygon) {
|
||||
@@ -415,13 +402,12 @@
|
||||
var desaGeojson = typeof parentDesa.polygon === 'string' ? JSON.parse(parentDesa.polygon) : parentDesa.polygon;
|
||||
var desaLayer;
|
||||
|
||||
// Style untuk batas Desa (Garis putus-putus merah, isian transparan)
|
||||
var desaStyle = {
|
||||
color: '#dc3545', // Merah
|
||||
color: '#dc3545',
|
||||
weight: 2,
|
||||
dashArray: '5, 8', // Garis putus-putus
|
||||
fillColor: 'none', // Tidak ada warna isian
|
||||
interactive: false // Tidak bisa diklik agar tidak menutupi klik lahan
|
||||
dashArray: '5, 8',
|
||||
fillColor: 'none',
|
||||
interactive: false
|
||||
};
|
||||
|
||||
if (desaGeojson !== null && typeof desaGeojson === 'object' && desaGeojson.type) {
|
||||
@@ -439,7 +425,6 @@
|
||||
desaLayer = L.polygon(safeCoordsDesa, desaStyle);
|
||||
}
|
||||
|
||||
// Tambahkan layer desa ke peta
|
||||
if (desaLayer) {
|
||||
currentLayerGroup.addLayer(desaLayer);
|
||||
}
|
||||
@@ -448,9 +433,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
// =========================================================
|
||||
|
||||
// Render data utama (Lahan / Wilayah Administratif)
|
||||
dataToRender.forEach(function(item) {
|
||||
if (!item.polygon) return;
|
||||
try {
|
||||
@@ -478,19 +461,15 @@
|
||||
|
||||
if (layer) {
|
||||
if (level === 'lahan') {
|
||||
// 1. Handling Khusus untuk Komoditas/Tanaman
|
||||
var listTanaman = item.tanaman && item.tanaman.length > 0 ? item.tanaman.join(', ') : null;
|
||||
|
||||
// Teks & Style conditional untuk komoditas kosong
|
||||
var komoditasText = listTanaman
|
||||
? listTanaman
|
||||
: '<span class="text-danger-light fw-medium small">Belum terdaftar</span>';
|
||||
|
||||
// Ikon conditional (ikon tanaman jika ada, ikon off jika kosong)
|
||||
var cropIcon = listTanaman ? 'ti tabler-plant' : 'ti tabler-leaf-off';
|
||||
var cropIconColor = listTanaman ? 'text-success' : 'text-danger';
|
||||
|
||||
// 2. Tampilan Baru Popup Lahan (Modern Card)
|
||||
var popupContent = `
|
||||
<div class="modern-lahan-popup">
|
||||
<div class="p-3 bg-white rounded-top-12 header-farmer">
|
||||
@@ -538,16 +517,13 @@
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Pengaturan Popup Leaflet
|
||||
layer.bindPopup(popupContent, {
|
||||
maxWidth: 300, // Membatasi lebar agar tetap rapi
|
||||
closeButton: true, // Menampilkan tombol close pro
|
||||
offset: [0, 0], // Penyesuaian jarak tip
|
||||
className: 'leaflet-modern-popup-wrapper' // Kelas CSS kustom (Langkah 2)
|
||||
maxWidth: 300,
|
||||
closeButton: true,
|
||||
offset: [0, 0],
|
||||
className: 'leaflet-modern-popup-wrapper'
|
||||
});
|
||||
} else {
|
||||
// FORMAT TOOLTIP BARU: Struktur Card Modern
|
||||
// Menggunakan template literal (backticks ``) agar rapi
|
||||
var tooltipContent = `
|
||||
<div class="modern-map-card">
|
||||
<div class="card-header-region">
|
||||
@@ -585,15 +561,13 @@
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Pengaturan Tooltip Leaflet
|
||||
layer.bindTooltip(tooltipContent, {
|
||||
sticky: true, // Ikut kursor
|
||||
direction: 'top', // Muncul di atas kursor
|
||||
className: 'leaflet-modern-tooltip', // Kelas CSS kustom (Langkah 2)
|
||||
offset: [0, -10] // Jarak dari kursor agar tidak menutupi poligon
|
||||
sticky: true,
|
||||
direction: 'top',
|
||||
className: 'leaflet-modern-tooltip',
|
||||
offset: [0, -10]
|
||||
});
|
||||
|
||||
// Logika Klik (Hapalan dari sebelumnya - jangan diubah)
|
||||
layer.on('click', function(e) {
|
||||
L.DomEvent.stopPropagation(e);
|
||||
var nextLevel = level === 'provinsi' ? 'kabupaten'
|
||||
@@ -623,7 +597,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
// Aksi Tombol Kembali
|
||||
if (btnBack) {
|
||||
btnBack.addEventListener('click', function() {
|
||||
var prevState = mapHistory.pop();
|
||||
@@ -636,7 +609,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
// Pastikan ukuran peta sesuai dengan container saat dimuat
|
||||
setTimeout(() => {
|
||||
map.invalidateSize();
|
||||
// Render Awal: Provinsi
|
||||
|
||||
@@ -55,6 +55,8 @@
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/flatpickr/dist/flatpickr.min.css">
|
||||
|
||||
|
||||
@stack('style')
|
||||
<script src="{{ asset('assets/vendor/js/helpers.js') }}"></script>
|
||||
|
||||
@@ -82,6 +82,10 @@
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/flatpickr"></script>
|
||||
|
||||
<script src="https://npmcdn.com/flatpickr/dist/l10n/id.js"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$('#logout').on('click', function(e) {
|
||||
@@ -90,11 +94,11 @@
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<!-- <script src="https://cdn.tailwindcss.com"></script> -->
|
||||
|
||||
<!-- Dark Mode Shifting -->
|
||||
|
||||
<style type="text/tailwindcss">
|
||||
<!-- <style type="text/tailwindcss">
|
||||
@layer utilities {
|
||||
|
||||
.teks-header-judul {
|
||||
@@ -106,7 +110,7 @@
|
||||
@apply text-gray-500 dark:text-gray-300;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style> -->
|
||||
@stack('scripts')
|
||||
</body>
|
||||
</html>
|
||||
@@ -170,7 +170,7 @@
|
||||
</ul>
|
||||
<ul class="menu-sub">
|
||||
<li class="menu-item">
|
||||
<a href="#" class="menu-link">
|
||||
<a href="{{ route('workshop.index') }}" class="menu-link">
|
||||
<div data-i18n="Workshop & Lokakarya">Workshop & Lokakarya</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
@extends('layout.main')
|
||||
|
||||
@section('content')
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center pt-4 pb-3">
|
||||
<h5 class="mb-0">Tambah Workshop Baru</h5>
|
||||
<span class="badge bg-label-secondary">Status: Draft</span>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('workshop.store') }}" method="POST" id="formAddWorkshop">
|
||||
@csrf
|
||||
|
||||
<input type="hidden" name="status" value="draft">
|
||||
|
||||
<div class="card-body pt-4">
|
||||
<div class="mb-2">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<i class="ti tabler-settings me-2 text-primary"></i>
|
||||
<h6 class="fw-medium mb-0 text-uppercase">Konfigurasi & Penjadwalan</h6>
|
||||
</div>
|
||||
<div class="row g-4 align-items-end">
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="program_id">Pilih Program Induk</label>
|
||||
<select class="form-select @error('program_id') is-invalid @enderror" id="program_id" name="program_id">
|
||||
<option value="">-- Tidak Terikat Program --</option>
|
||||
@foreach($programs as $program)
|
||||
<option value="{{ $program->id }}" {{ old('program_id') == $program->id ? 'selected' : '' }}>
|
||||
{{ $program->nama_program ?? $program->judul }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('program_id') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="jenis">Jenis Program <span class="text-danger">*</span></label>
|
||||
<select class="form-select @error('jenis') is-invalid @enderror" id="jenis" name="jenis" required>
|
||||
<option value="" disabled selected>-- Pilih Jenis Program --</option>
|
||||
@foreach(App\Enums\JenisWorkshop::cases() as $jenisEnum)
|
||||
<option value="{{ $jenisEnum->value }}" {{ old('jenis') == $jenisEnum->value ? 'selected' : '' }}>
|
||||
{{ ucfirst($jenisEnum->value) }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('jenis') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="tanggal_awal">Mulai Kegiatan <span class="text-danger">*</span></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ti tabler-calendar"></i></span>
|
||||
<input type="text" class="form-control flatpickr-date" id="tanggal_awal" name="tanggal_awal" placeholder="DD/MM/YYYY" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="tanggal_akhir">Selesai Kegiatan <span class="text-danger">*</span></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ti tabler-calendar"></i></span>
|
||||
<input type="text" class="form-control flatpickr-date" id="tanggal_akhir" name="tanggal_akhir" placeholder="DD/MM/YYYY" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<i class="ti tabler-info-circle me-2 text-primary"></i>
|
||||
<h6 class="fw-medium mb-0 text-uppercase">Informasi Dasar</h6>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="judul">Judul Workshop / Kegiatan <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="judul" name="judul" placeholder="Masukkan judul workshop..." value="{{ old('judul') }}" required>
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="deskripsi">Deskripsi Singkat</label>
|
||||
<textarea class="form-control" id="deskripsi" name="deskripsi" rows="3" placeholder="Jelaskan tujuan atau ringkasan kegiatan..."></textarea>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 mt-4">
|
||||
<div class="alert alert-label-info d-flex align-items-center border-0 mb-0" role="alert">
|
||||
<span class="alert-icon me-2">
|
||||
<i class="ti ti-info-circle ti-xs"></i>
|
||||
</span>
|
||||
<div>
|
||||
Kegiatan baru akan disimpan sebagai <strong>Draft</strong>. Anda dapat mengaktifkannya nanti melalui halaman kelola.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer border-top pt-4 pb-4 d-flex justify-content-end gap-3 bg-lighter">
|
||||
<a href="{{ route('workshop.index') }}" class="btn btn-label-secondary">Kembali ke Daftar</a>
|
||||
<button type="submit" class="btn btn-primary px-4">
|
||||
<i class="ti tabler-device-floppy me-1"></i> Simpan Workshop
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
// 1. Inisialisasi Flatpickr
|
||||
const dateInputs = document.querySelectorAll('.flatpickr-date');
|
||||
if (dateInputs.length) {
|
||||
flatpickr(dateInputs, {
|
||||
locale: "id",
|
||||
altInput: true,
|
||||
altFormat: "l, d/m/Y",
|
||||
dateFormat: "Y-m-d",
|
||||
allowInput: true
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Submit Form menggunakan AJAX (Fetch API) persis seperti halaman Lahan
|
||||
const formAddWorkshop = document.getElementById('formAddWorkshop');
|
||||
|
||||
if(formAddWorkshop) {
|
||||
formAddWorkshop.addEventListener('submit', function(e) {
|
||||
e.preventDefault(); // Mencegah reload halaman
|
||||
|
||||
let form = this;
|
||||
let formData = new FormData(form);
|
||||
let submitBtn = form.querySelector('button[type="submit"]');
|
||||
let originalBtnText = submitBtn.innerHTML;
|
||||
|
||||
// Disable tombol dan ubah teks jadi Menyimpan...
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span> Menyimpan...';
|
||||
|
||||
fetch(form.action, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Accept': 'application/json' // Meminta respons JSON dari Laravel
|
||||
}
|
||||
})
|
||||
.then(async response => {
|
||||
// Karena Laravel mengembalikan 422 jika validasi gagal, kita tangkap statusnya
|
||||
const isJson = response.headers.get('content-type')?.includes('application/json');
|
||||
const data = isJson ? await response.json() : null;
|
||||
|
||||
if (!response.ok) {
|
||||
const error = (data && data.message) || response.statusText;
|
||||
return Promise.reject({ status: response.status, data: data, message: error });
|
||||
}
|
||||
return data;
|
||||
})
|
||||
.then(data => {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = originalBtnText;
|
||||
|
||||
if (data.status === 'success') {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil!',
|
||||
text: data.message,
|
||||
showConfirmButton: false,
|
||||
timer: 2000,
|
||||
customClass: { confirmButton: 'btn btn-primary' },
|
||||
buttonsStyling: false
|
||||
}).then(() => {
|
||||
// Redirect ke index setelah sukses
|
||||
window.location.href = "{{ route('workshop.index') }}";
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
// Kembalikan tombol ke semula
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = originalBtnText;
|
||||
|
||||
let errorMsg = 'Terjadi kesalahan saat menyimpan data.';
|
||||
|
||||
// Jika error validasi Laravel (Status 422)
|
||||
if (error.status === 422 && error.data && error.data.errors) {
|
||||
errorMsg = Object.values(error.data.errors).map(err => err.join(', ')).join('<br>');
|
||||
} else if (error.data && error.data.message) {
|
||||
errorMsg = error.data.message;
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Data Tidak Valid',
|
||||
html: errorMsg,
|
||||
customClass: { confirmButton: 'btn btn-primary' },
|
||||
buttonsStyling: false
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@@ -0,0 +1,220 @@
|
||||
@extends('layout.main')
|
||||
|
||||
@section('content')
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header border-bottom d-flex justify-content-between align-items-center pt-4 pb-3">
|
||||
<h5 class="mb-0">Edit Workshop</h5>
|
||||
@php
|
||||
$badgeColor = match($workshop->status->value) {
|
||||
'aktif' => 'bg-label-success',
|
||||
'tunda' => 'bg-label-warning',
|
||||
'batal' => 'bg-label-danger',
|
||||
'draft' => 'bg-label-secondary',
|
||||
'selesai' => 'bg-label-primary',
|
||||
default => 'bg-label-secondary'
|
||||
};
|
||||
@endphp
|
||||
<span class="badge {{ $badgeColor }}">Status: {{ ucfirst($workshop->status->value) }}</span>
|
||||
</div>
|
||||
|
||||
<form action="{{ route('workshop.update', $workshop->id) }}" method="POST" id="formEditWorkshop">
|
||||
@csrf
|
||||
@method('PUT') <div class="card-body pt-4">
|
||||
<div class="mb-2">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<i class="ti tabler-settings me-2 text-primary"></i>
|
||||
<h6 class="fw-medium mb-0 text-uppercase">Konfigurasi & Penjadwalan</h6>
|
||||
</div>
|
||||
<div class="row g-4 align-items-end">
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="program_id">Pilih Program Induk</label>
|
||||
<select class="form-select @error('program_id') is-invalid @enderror" id="program_id" name="program_id">
|
||||
<option value="">-- Tidak Terikat Program --</option>
|
||||
@foreach($programs as $program)
|
||||
<option value="{{ $program->id }}" {{ old('program_id', $workshop->program_id) == $program->id ? 'selected' : '' }}>
|
||||
{{ $program->nama_program ?? $program->judul }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('program_id') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="jenis">Jenis Program <span class="text-danger">*</span></label>
|
||||
<select class="form-select @error('jenis') is-invalid @enderror" id="jenis" name="jenis" required>
|
||||
<option value="" disabled>-- Pilih Jenis Program --</option>
|
||||
@foreach(App\Enums\JenisWorkshop::cases() as $jenisEnum)
|
||||
<option value="{{ $jenisEnum->value }}" {{ old('jenis', $workshop->jenis->value) == $jenisEnum->value ? 'selected' : '' }}>
|
||||
{{ ucfirst($jenisEnum->value) }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('jenis') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="tanggal_awal">Mulai Kegiatan <span class="text-danger">*</span></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ti tabler-calendar"></i></span>
|
||||
<input type="text" class="form-control flatpickr-date" id="tanggal_awal" name="tanggal_awal" placeholder="DD/MM/YYYY" value="{{ old('tanggal_awal', $workshop->tanggal_awal) }}" required>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<label class="form-label" for="tanggal_akhir">Selesai Kegiatan <span class="text-danger">*</span></label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text"><i class="ti tabler-calendar"></i></span>
|
||||
<input type="text" class="form-control flatpickr-date" id="tanggal_akhir" name="tanggal_akhir" placeholder="DD/MM/YYYY" value="{{ old('tanggal_akhir', $workshop->tanggal_akhir) }}" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="my-4">
|
||||
|
||||
<div class="mb-4">
|
||||
<div class="d-flex align-items-center mb-3">
|
||||
<i class="ti tabler-info-circle me-2 text-primary"></i>
|
||||
<h6 class="fw-medium mb-0 text-uppercase">Informasi Dasar & Status</h6>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-8">
|
||||
<label class="form-label" for="judul">Judul Workshop / Kegiatan <span class="text-danger">*</span></label>
|
||||
<input type="text" class="form-control" id="judul" name="judul" placeholder="Masukkan judul workshop..." value="{{ old('judul', $workshop->judul) }}" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<label class="form-label" for="status">Status Saat Ini <span class="text-danger">*</span></label>
|
||||
<select class="form-select @error('status') is-invalid @enderror" id="status" name="status" required>
|
||||
@foreach(App\Enums\StatusWorkshop::cases() as $statusEnum)
|
||||
<option value="{{ $statusEnum->value }}" {{ old('status', $workshop->status->value) == $statusEnum->value ? 'selected' : '' }}>
|
||||
{{ ucfirst($statusEnum->value) }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
@error('status') <div class="invalid-feedback">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="col-12">
|
||||
<label class="form-label" for="deskripsi">Deskripsi Singkat</label>
|
||||
<textarea class="form-control" id="deskripsi" name="deskripsi" rows="3" placeholder="Jelaskan tujuan atau ringkasan kegiatan...">{{ old('deskripsi', $workshop->deskripsi) }}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer border-top pt-4 pb-4 d-flex justify-content-end gap-3 bg-lighter">
|
||||
<a href="{{ route('workshop.index') }}" class="btn btn-label-secondary">Batal</a>
|
||||
<button type="submit" class="btn btn-primary px-4">
|
||||
<i class="ti tabler-device-floppy me-1"></i> Simpan Perubahan
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function () {
|
||||
|
||||
// 1. Inisialisasi Flatpickr
|
||||
const dateInputs = document.querySelectorAll('.flatpickr-date');
|
||||
|
||||
if (dateInputs.length) {
|
||||
flatpickr(dateInputs, {
|
||||
locale: "id",
|
||||
altInput: true,
|
||||
altFormat: "l, d/m/Y",
|
||||
dateFormat: "Y-m-d",
|
||||
allowInput: true
|
||||
});
|
||||
}
|
||||
|
||||
// 2. Submit Form menggunakan AJAX (Fetch API) persis seperti solusi Add Workshop
|
||||
const formEditWorkshop = document.getElementById('formEditWorkshop');
|
||||
|
||||
if(formEditWorkshop) {
|
||||
formEditWorkshop.addEventListener('submit', function(e) {
|
||||
e.preventDefault(); // Mencegah reload halaman
|
||||
|
||||
let form = this;
|
||||
let formData = new FormData(form);
|
||||
let submitBtn = form.querySelector('button[type="submit"]');
|
||||
let originalBtnText = submitBtn.innerHTML;
|
||||
|
||||
// Disable tombol dan ubah teks jadi Menyimpan...
|
||||
submitBtn.disabled = true;
|
||||
submitBtn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span> Menyimpan...';
|
||||
|
||||
fetch(form.action, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Accept': 'application/json' // Meminta respons JSON dari Laravel
|
||||
}
|
||||
})
|
||||
.then(async response => {
|
||||
// Karena Laravel mengembalikan 422 jika validasi gagal, kita tangkap statusnya
|
||||
const isJson = response.headers.get('content-type')?.includes('application/json');
|
||||
const data = isJson ? await response.json() : null;
|
||||
|
||||
if (!response.ok) {
|
||||
const error = (data && data.message) || response.statusText;
|
||||
return Promise.reject({ status: response.status, data: data, message: error });
|
||||
}
|
||||
return data;
|
||||
})
|
||||
.then(data => {
|
||||
// Kembalikan tombol ke semula
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = originalBtnText;
|
||||
|
||||
if (data.status === 'success') {
|
||||
// Munculkan SweetAlert persis seperti di menu User
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Berhasil!',
|
||||
text: data.message,
|
||||
showConfirmButton: false,
|
||||
timer: 2000,
|
||||
customClass: { confirmButton: 'btn btn-primary' },
|
||||
buttonsStyling: false
|
||||
}).then(() => {
|
||||
// Redirect ke index setelah sukses
|
||||
window.location.href = "{{ route('workshop.index') }}";
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
submitBtn.disabled = false;
|
||||
submitBtn.innerHTML = originalBtnText;
|
||||
|
||||
let errorMsg = 'Terjadi kesalahan saat menyimpan data.';
|
||||
|
||||
if (error.status === 422 && error.data && error.data.errors) {
|
||||
errorMsg = Object.values(error.data.errors).map(err => err.join(', ')).join('<br>');
|
||||
} else if (error.data && error.data.message) {
|
||||
errorMsg = error.data.message;
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Data Tidak Valid',
|
||||
html: errorMsg,
|
||||
customClass: { confirmButton: 'btn btn-primary' },
|
||||
buttonsStyling: false
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@@ -0,0 +1,369 @@
|
||||
@extends('layout.main')
|
||||
|
||||
@section('content')
|
||||
<div class="container-xxl flex-grow-1 container-p-y">
|
||||
|
||||
<div class="row g-6 mb-6">
|
||||
<div class="col-sm-6 col-xl-2">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div class="content-left">
|
||||
<span class="text-heading">Total Kegiatan</span>
|
||||
<div class="d-flex align-items-center my-1">
|
||||
<h4 class="mb-0 me-2">{{ number_format($summary['kegiatan'] ?? 0) }}</h4>
|
||||
</div>
|
||||
<small class="mb-0">Kegiatan</small>
|
||||
</div>
|
||||
<div class="avatar">
|
||||
<span class="avatar-initial rounded bg-label-primary">
|
||||
<i class="icon-base ti tabler-briefcase icon-26px"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xl-2">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div class="content-left">
|
||||
<span class="text-heading">Total Lokakarya</span>
|
||||
<div class="d-flex align-items-center my-1">
|
||||
<h4 class="mb-0 me-2">{{ number_format($summary['lokakarya'] ?? 0) }}</h4>
|
||||
</div>
|
||||
<small class="mb-0">Lokakarya</small>
|
||||
</div>
|
||||
<div class="avatar">
|
||||
<span class="avatar-initial rounded bg-label-primary">
|
||||
<i class="icon-base ti tabler-brand-pnpm icon-26px"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xl-2">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div class="content-left">
|
||||
<span class="text-heading">Total Pelatihan</span>
|
||||
<div class="d-flex align-items-center my-1">
|
||||
<h4 class="mb-0 me-2">{{ number_format($summary['pelatihan'] ?? 0) }}</h4>
|
||||
</div>
|
||||
<small class="mb-0">Pelatihan</small>
|
||||
</div>
|
||||
<div class="avatar">
|
||||
<span class="avatar-initial rounded bg-label-primary">
|
||||
<i class="icon-base ti tabler-artboard icon-26px"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xl-2">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div class="content-left">
|
||||
<span class="text-heading">Total Sosialisasi</span>
|
||||
<div class="d-flex align-items-center my-1">
|
||||
<h4 class="mb-0 me-2">{{ number_format($summary['sosialisasi'] ?? 0) }}</h4>
|
||||
</div>
|
||||
<small class="mb-0">Sosialisasi</small>
|
||||
</div>
|
||||
<div class="avatar">
|
||||
<span class="avatar-initial rounded bg-label-primary">
|
||||
<i class="icon-base ti tabler-notification icon-26px"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xl-2">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div class="content-left">
|
||||
<span class="text-heading">Total sesi</span>
|
||||
<div class="d-flex align-items-center my-1">
|
||||
<h4 class="mb-0 me-2">{{ number_format($summary['sesi'] ?? 0) }}</h4>
|
||||
</div>
|
||||
<small class="mb-0">Sesi</small>
|
||||
</div>
|
||||
<div class="avatar">
|
||||
<span class="avatar-initial rounded bg-label-primary">
|
||||
<i class="icon-base ti tabler-section icon-26px"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6 col-xl-2">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-start justify-content-between">
|
||||
<div class="content-left">
|
||||
<span class="text-heading">Peserta</span>
|
||||
<div class="d-flex align-items-center my-1">
|
||||
<h4 class="mb-0 me-2">{{ number_format($summary['peserta'] ?? 0) }}</h4>
|
||||
</div>
|
||||
<small class="mb-0">Peserta</small>
|
||||
</div>
|
||||
<div class="avatar">
|
||||
<span class="avatar-initial rounded bg-label-primary">
|
||||
<i class="icon-base ti tabler-calendar-user icon-26px"></i>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form method="GET" action="{{ route('workshop.index') }}" id="formFilterData"></form>
|
||||
|
||||
<div class="card mb-4">
|
||||
|
||||
<div class="card-header border-bottom d-flex flex-column flex-md-row justify-content-between align-items-center pt-4 pb-3">
|
||||
<h5 class="card-title mb-2 mb-md-0">Data Workshop</h5>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-label-secondary" data-bs-toggle="collapse" data-bs-target="#collapseFilter" aria-expanded="{{ request()->anyFilled(['jenis', 'status', 'tanggal_awal', 'tanggal_akhir']) ? 'true' : 'false' }}">
|
||||
<i class="ti tabler-filter me-1"></i> Filter
|
||||
</button>
|
||||
<a href="{{ route('workshop.create') }}" class="btn btn-primary">
|
||||
<i class="ti tabler-plus me-1"></i> Tambah Workshop
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collapse {{ request()->anyFilled(['jenis', 'status', 'tanggal_awal', 'tanggal_akhir']) ? 'show' : '' }}" id="collapseFilter">
|
||||
<div class="card-body border-bottom bg-lighter pt-4 pb-4">
|
||||
<div class="row g-3 align-items-end">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Jenis Program</label>
|
||||
<select name="jenis" form="formFilterData" class="form-select form-select-sm">
|
||||
<option value="">Semua Jenis</option>
|
||||
@foreach(App\Enums\JenisWorkshop::cases() as $jenis)
|
||||
<option value="{{ $jenis->value }}" {{ request('jenis') == $jenis->value ? 'selected' : '' }}>{{ ucfirst($jenis->value) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Status</label>
|
||||
<select name="status" form="formFilterData" class="form-select form-select-sm">
|
||||
<option value="">Semua Status</option>
|
||||
@foreach(App\Enums\StatusWorkshop::cases() as $status)
|
||||
<option value="{{ $status->value }}" {{ request('status') == $status->value ? 'selected' : '' }}>{{ ucfirst($status->value) }}</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Tanggal Awal</label>
|
||||
<input type="text" name="tanggal_awal" form="formFilterData" class="form-control form-control-sm flatpickr-date" placeholder="DD/MM/YYYY" value="{{ request('tanggal_awal') }}">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Tanggal Akhir</label>
|
||||
<input type="text" name="tanggal_akhir" form="formFilterData" class="form-control form-control-sm flatpickr-date" placeholder="DD/MM/YYYY" value="{{ request('tanggal_akhir') }}">
|
||||
</div>
|
||||
<div class="col-md-2 d-flex gap-2">
|
||||
<button type="submit" form="formFilterData" class="btn btn-sm btn-primary w-100">Terapkan</button>
|
||||
@if(request()->anyFilled(['jenis', 'status', 'tanggal_awal', 'tanggal_akhir', 'search']))
|
||||
<a href="{{ route('workshop.index') }}" class="btn btn-sm btn-label-danger" title="Reset Filter"><i class="ti tabler-refresh"></i></a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body border-bottom pt-3 pb-3">
|
||||
<div class="d-flex flex-column flex-md-row justify-content-between align-items-center gap-3">
|
||||
<div class="d-flex align-items-center">
|
||||
<label class="me-2 text-nowrap text-muted">Show</label>
|
||||
<select name="per_page" form="formFilterData" class="form-select form-select-sm w-auto" onchange="document.getElementById('formFilterData').submit()">
|
||||
<option value="10" {{ request('per_page') == 10 ? 'selected' : '' }}>10</option>
|
||||
<option value="25" {{ request('per_page') == 25 ? 'selected' : '' }}>25</option>
|
||||
<option value="50" {{ request('per_page') == 50 ? 'selected' : '' }}>50</option>
|
||||
</select>
|
||||
<label class="ms-2 text-nowrap text-muted">entries</label>
|
||||
</div>
|
||||
|
||||
<div class="input-group input-group-sm w-auto" style="min-width: 250px;">
|
||||
<input type="text" name="search" form="formFilterData" class="form-control" placeholder="Cari judul workshop..." value="{{ request('search') }}">
|
||||
<button type="submit" form="formFilterData" class="btn btn-primary"><i class="ti tabler-search"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive text-nowrap">
|
||||
<table class="table table-hover">
|
||||
<thead class="border-top">
|
||||
<tr>
|
||||
<th>No</th>
|
||||
<th>Informasi Workshop</th>
|
||||
<th>Jenis</th>
|
||||
<th>Jadwal Pelaksanaan</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($workshops as $index => $item)
|
||||
<tr>
|
||||
<td>{{ $workshops->firstItem() + $index }}</td>
|
||||
<td>
|
||||
<div class="d-flex justify-content-start align-items-center">
|
||||
<div class="avatar-wrapper me-3">
|
||||
<div class="avatar avatar-sm">
|
||||
<span class="avatar-initial rounded-circle bg-label-primary">{{ strtoupper(substr($item->judul, 0, 2)) }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="fw-medium text-heading">{{ $item->judul }}</span>
|
||||
<small class="text-muted text-truncate" style="max-width: 250px;">{{ $item->deskripsi ?? 'Tidak ada deskripsi' }}</small>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td><span class="badge bg-label-info">{{ ucfirst($item->jenis->value) }}</span></td>
|
||||
<td>
|
||||
<div class="d-flex flex-column">
|
||||
<span class="text-heading"><i class="ti tabler-calendar-event me-1 ti-xs"></i> {{ \Carbon\Carbon::parse($item->tanggal_awal)->format('d M Y') }}</span>
|
||||
<small class="text-muted">s/d {{ \Carbon\Carbon::parse($item->tanggal_akhir)->format('d M Y') }}</small>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
@php
|
||||
$color = match($item->status->value) {
|
||||
'aktif' => 'bg-label-success',
|
||||
'tunda' => 'bg-label-warning',
|
||||
'batal' => 'bg-label-danger',
|
||||
'draft' => 'bg-label-secondary',
|
||||
'selesai' => 'bg-label-primary',
|
||||
default => 'bg-label-secondary'
|
||||
};
|
||||
@endphp
|
||||
<span class="badge {{ $color }}">{{ ucfirst($item->status->value) }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div class="d-flex align-items-center">
|
||||
<a href="{{ route('workshop.edit', $item->id) }}" class="btn btn-icon btn-text-secondary rounded-pill" title="Edit">
|
||||
<i class="ti tabler-edit ti-md"></i>
|
||||
</a>
|
||||
<form action="{{ route('workshop.destroy', $item->id) }}" method="POST" class="d-inline form-delete" data-name="{{ $item->judul }}">
|
||||
@csrf @method('DELETE')
|
||||
<button type="button" class="btn btn-icon btn-text-secondary rounded-pill btn-delete" title="Delete">
|
||||
<i class="ti tabler-trash ti-md"></i>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr><td colspan="6" class="text-center py-4">Data tidak ditemukan.</td></tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="card-footer d-flex justify-content-center justify-content-md-end border-top pt-4 pb-4">
|
||||
{{ $workshops->appends(request()->query())->links('pagination::bootstrap-5') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@push('scripts')
|
||||
<script>
|
||||
'use strict';
|
||||
|
||||
$(document).ready(function () {
|
||||
|
||||
const dateInputs = document.querySelectorAll('.flatpickr-date');
|
||||
if (dateInputs.length > 0) {
|
||||
flatpickr(dateInputs, {
|
||||
locale: "id",
|
||||
altInput: true,
|
||||
altFormat: "d/m/Y",
|
||||
dateFormat: "Y-m-d",
|
||||
allowInput: true,
|
||||
});
|
||||
}
|
||||
|
||||
@if(session('success'))
|
||||
const Toast = Swal.mixin({
|
||||
toast: true,
|
||||
position: 'top-end',
|
||||
showConfirmButton: false,
|
||||
timer: 3000,
|
||||
timerProgressBar: true,
|
||||
});
|
||||
Toast.fire({ icon: 'success', title: "{{ session('success') }}" });
|
||||
@endif
|
||||
|
||||
$(document).on('click', '.btn-delete', function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const form = $(this).closest('.form-delete');
|
||||
const name = form.data('name');
|
||||
const url = form.attr('action');
|
||||
const formData = new FormData(form[0]);
|
||||
|
||||
Swal.fire({
|
||||
title: 'Hapus Workshop?',
|
||||
text: `Apakah kamu yakin ingin menghapus "${name}"? Data yang terhapus tidak dapat dikembalikan.`,
|
||||
icon: 'warning',
|
||||
showCancelButton: true,
|
||||
confirmButtonText: 'Ya, hapus!',
|
||||
cancelButtonText: 'Batal',
|
||||
customClass: {
|
||||
confirmButton: 'btn btn-danger me-3',
|
||||
cancelButton: 'btn btn-label-secondary'
|
||||
},
|
||||
buttonsStyling: false
|
||||
}).then(function (result) {
|
||||
if (result.value) {
|
||||
fetch(url, {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
headers: {
|
||||
'X-Requested-With': 'XMLHttpRequest',
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.status === 'success') {
|
||||
Swal.fire({
|
||||
icon: 'success',
|
||||
title: 'Terhapus!',
|
||||
text: data.message,
|
||||
showConfirmButton: false,
|
||||
timer: 2000,
|
||||
customClass: { confirmButton: 'btn btn-primary' },
|
||||
buttonsStyling: false
|
||||
}).then(() => {
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Gagal!',
|
||||
text: 'Terjadi kesalahan saat menghapus data.',
|
||||
customClass: { confirmButton: 'btn btn-primary' },
|
||||
buttonsStyling: false
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endpush
|
||||
@@ -104,6 +104,7 @@ Route::middleware('auth')
|
||||
Route::resource('program', App\Http\Controllers\Web\ProgramController::class);
|
||||
Route::resource('program.komoditas', App\Http\Controllers\Web\ProgramKomoditasController::class)->only(['store', 'update', 'destroy']);
|
||||
Route::resource('program.lokasi', App\Http\Controllers\Web\ProgramLokasiController::class)->only(['store', 'update', 'destroy']);
|
||||
Route::resource('workshop', App\Http\Controllers\Web\Program\WorkshopController::class);
|
||||
|
||||
// Basis Data Ekosistem
|
||||
Route::resource('group', App\Http\Controllers\Web\Ekosistem\GroupBasisDataController::class);
|
||||
|
||||
Reference in New Issue
Block a user