809 lines
46 KiB
PHP
809 lines
46 KiB
PHP
<x-app-layout>
|
|
|
|
@push('styles')
|
|
<link rel="stylesheet" href="{{ asset('css/tugas.css') }}">
|
|
@endpush
|
|
<div class="pug-wrap">
|
|
<div class="pug-inner">
|
|
|
|
{{-- ── PAGE HEADER ── --}}
|
|
<div style="display:flex; align-items:center; justify-content:space-between; margin-bottom:24px;">
|
|
<div style="display:flex; align-items:center; gap:12px;">
|
|
<div class="pug-icon-wrap">
|
|
<i class="fas fa-tasks"></i>
|
|
</div>
|
|
<div>
|
|
<div class="pug-page-title">Data Penugasan</div>
|
|
<div class="pug-page-sub">Manajemen penugasan teknisi lapangan</div>
|
|
</div>
|
|
</div>
|
|
<button onclick="openCreateModal()" class="pug-btn-primary">
|
|
<i class="fas fa-plus"></i> Tambah Penugasan
|
|
</button>
|
|
</div>
|
|
|
|
|
|
<div class="pug-wrap">
|
|
<div class="pug-inner">
|
|
|
|
@if(session('success'))
|
|
<div class="pug-alert pug-alert-ok" role="alert">
|
|
<i class="fas fa-check-circle"></i>
|
|
<span>{{ session('success') }}</span>
|
|
</div>
|
|
@endif
|
|
@if(session('error'))
|
|
<div class="pug-alert pug-alert-err" role="alert">
|
|
<i class="fas fa-exclamation-circle"></i>
|
|
<span>{{ session('error') }}</span>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- ── ABANDONED TASKS ALERT ── --}}
|
|
@if(isset($abandonedTasks) && $abandonedTasks->count() > 0)
|
|
<div class="alert alert-danger" style="background: #fef2f2; border: 1px solid #fecaca; border-radius: 12px; padding: 16px 20px; margin-bottom: 24px; display: flex; align-items: flex-start; gap: 15px;">
|
|
<div style="color: #ef4444; font-size: 24px; margin-top: 2px;">
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
</div>
|
|
<div style="flex: 1;">
|
|
<h4 style="color: #991b1b; font-size: 15px; font-weight: 700; margin: 0 0 5px 0;">PERHATIAN: Ada Personel Tim yang Sakit/Izin Hari Ini!</h4>
|
|
<p style="color: #7f1d1d; font-size: 13px; margin: 0 0 10px 0;">
|
|
Terdapat <strong>{{ $abandonedTasks->count() }} Penugasan</strong> (Belum Mulai / Dalam Proses) yang anggotanya hari ini berstatus <strong>Sakit / Izin</strong>. Segera cek dan sesuaikan formasi tim berikut:
|
|
</p>
|
|
<div style="display: flex; flex-wrap: wrap; gap: 10px;">
|
|
@foreach($abandonedTasks as $task)
|
|
<div style="background: #fff; border: 1px solid #fca5a5; padding: 8px 12px; border-radius: 8px; font-size: 12px; display: flex; flex-direction: column; gap: 4px;">
|
|
<span style="font-weight: 700; color: #991b1b;"><i class="fas fa-clipboard-list"></i> {{ \Illuminate\Support\Str::limit($task->jenis_pekerjaan, 20) }}</span>
|
|
<span style="color: #64748b;"><i class="fas fa-map-marker-alt"></i> {{ \Illuminate\Support\Str::limit($task->alamat_lokasi, 25) }}</span>
|
|
<button onclick="editPenugasan({{ $task->id_penugasan }})" style="margin-top: 4px; background: #ef4444; color: white; border: none; padding: 4px 8px; border-radius: 4px; cursor: pointer; font-size: 11px; font-weight: 600;">
|
|
<i class="fas fa-exchange-alt"></i> Sesuaikan Tim
|
|
</button>
|
|
</div>
|
|
@endforeach
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@endif
|
|
|
|
<!-- Stats -->
|
|
<div class="pug-stats">
|
|
<div class="pug-stat pug-stat-total" onclick="setStatus('')" style="cursor: pointer;" title="Klik untuk melihat semua">
|
|
<div class="pug-stat-icon"><i class="fas fa-clipboard-list"></i></div>
|
|
<div class="pug-stat-label">Total</div>
|
|
<div class="pug-stat-val">{{ $totalPenugasan ?? 0 }}</div>
|
|
<div style="font-size:11px;color:var(--t3);">Semua penugasan</div>
|
|
</div>
|
|
<div class="pug-stat pug-stat-belum" onclick="setStatus('belum_mulai')" style="cursor: pointer;" title="Klik untuk filter: Belum Mulai">
|
|
<div class="pug-stat-icon"><i class="fas fa-clock"></i></div>
|
|
<div class="pug-stat-label">Belum Mulai</div>
|
|
<div class="pug-stat-val">{{ $belumMulai ?? 0 }}</div>
|
|
<div style="font-size:11px;color:var(--t3);">Menunggu eksekusi</div>
|
|
</div>
|
|
<div class="pug-stat pug-stat-proses" onclick="setStatus('dalam_proses')" style="cursor: pointer;" title="Klik untuk filter: Dalam Proses">
|
|
<div class="pug-stat-icon"><i class="fas fa-spinner"></i></div>
|
|
<div class="pug-stat-label">Dalam Proses</div>
|
|
<div class="pug-stat-val">{{ $dalamProses ?? 0 }}</div>
|
|
<div style="font-size:11px;color:var(--t3);">Sedang dikerjakan</div>
|
|
</div>
|
|
<div class="pug-stat pug-stat-selesai" onclick="setStatus('selesai')" style="cursor: pointer;" title="Klik untuk filter: Selesai">
|
|
<div class="pug-stat-icon"><i class="fas fa-check-circle"></i></div>
|
|
<div class="pug-stat-label">Selesai</div>
|
|
<div class="pug-stat-val">{{ $selesai ?? 0 }}</div>
|
|
<div style="font-size:11px;color:var(--t3);">Pekerjaan rampung</div>
|
|
</div>
|
|
<div class="pug-stat pug-stat-batal" onclick="setStatus('dibatalkan')" style="cursor: pointer;" title="Klik untuk filter: Dibatalkan">
|
|
<div class="pug-stat-icon"><i class="fas fa-times-circle"></i></div>
|
|
<div class="pug-stat-label">Dibatalkan</div>
|
|
<div class="pug-stat-val">{{ $dibatalkan ?? 0 }}</div>
|
|
<div style="font-size:11px;color:var(--t3);">Penugasan dibatalkan</div>
|
|
</div>
|
|
<div class="pug-stat pug-stat-garansi" onclick="setStatus('garansi_aktif')" style="cursor: pointer;" title="Filter Pekerjaan Selesai (Garansi)">
|
|
<div class="pug-stat-icon"><i class="fas fa-shield-alt"></i></div>
|
|
<div class="pug-stat-label">Garansi Aktif</div>
|
|
<div class="pug-stat-val">{{ $garansiAktif ?? 0 }}</div>
|
|
<div style="font-size:11px;color:var(--t3);">Dalam periode garansi</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Content layout -->
|
|
<div class="pug-main-content">
|
|
|
|
<!-- Toolbar Filter -->
|
|
<div class="pug-toolbar">
|
|
<form method="GET" action="{{ route('pekerjaan.penugasan.index') }}" id="filterForm" class="pug-toolbar-form">
|
|
|
|
<!-- Date Range -->
|
|
<div class="pug-toolbar-item" style="flex: 0 0 auto; min-width: 340px;">
|
|
<label class="pug-field-label">Rentang Tanggal</label>
|
|
<div class="d-flex align-items-center gap-2">
|
|
<input type="date" name="start_date" class="pug-input" value="{{ request('start_date') }}" onchange="this.form.submit()" style="width: 145px; padding: 10px 10px;">
|
|
<span style="font-size: 10px; color: var(--pug-t3); font-weight: 800;">S/D</span>
|
|
<input type="date" name="end_date" class="pug-input" value="{{ request('end_date') }}" onchange="this.form.submit()" style="width: 145px; padding: 10px 10px;">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search Technician (Direct Select like Monitoring) -->
|
|
<div class="pug-toolbar-item" style="flex: 1; min-width: 240px;">
|
|
<label class="pug-field-label">Pilih Teknisi (Cari Langsung)</label>
|
|
<div class="pug-input-icon">
|
|
<i class="fas fa-user-tie"></i>
|
|
<select name="id_teknisi" class="pug-input" onchange="this.form.submit()">
|
|
<option value="">Semua Teknisi</option>
|
|
@foreach($teknisis as $t)
|
|
<option value="{{ $t->id_teknisi }}" {{ request('id_teknisi') == $t->id_teknisi ? 'selected' : '' }}>{{ $t->nama }}</option>
|
|
@endforeach
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Hidden Status (digunakan oleh filter card di atas) -->
|
|
<input type="hidden" name="status" id="hiddenStatus" value="{{ request('status') }}">
|
|
|
|
<!-- Buttons -->
|
|
<div class="pug-toolbar-item item-btn d-flex align-items-end gap-2" style="flex: 0 0 auto;">
|
|
<button type="button" class="pug-reset-btn" onclick="window.location.href='{{ route('pekerjaan.penugasan.index') }}'" style="height: 44px; white-space: nowrap;">
|
|
<i class="fas fa-redo"></i> Reset Filter
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Table -->
|
|
<div class="pug-panel">
|
|
<div class="pug-panel-head">
|
|
<div class="pug-panel-head-left">
|
|
<i class="fas fa-list-ul" style="color:var(--green);margin-right:8px;"></i>
|
|
Daftar Penugasan
|
|
</div>
|
|
<div class="pug-panel-head-right">
|
|
{{ isset($penugasan) ? $penugasan->total() : 0 }} data ditemukan
|
|
</div>
|
|
</div>
|
|
|
|
<div style="overflow-x:auto;">
|
|
<table class="pug-table">
|
|
<thead>
|
|
<tr>
|
|
<th>#</th>
|
|
<th>Teknisi</th>
|
|
<th>Surat</th>
|
|
<th>Tanggal</th>
|
|
<th>Status Pekerjaan</th>
|
|
<th>Aksi</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
@forelse($penugasan ?? [] as $index => $item)
|
|
<tr>
|
|
<td>
|
|
<span class="pug-rownum">{{ str_pad($penugasan->firstItem() + $index, 2, '0', STR_PAD_LEFT) }}</span>
|
|
</td>
|
|
<td>
|
|
<div style="display:flex; flex-wrap:wrap; gap:4px; max-width:280px;">
|
|
@foreach($item->timTeknisi as $tt)
|
|
<span class="pug-tech-badge">
|
|
<i class="fas fa-user"></i> {{ $tt->teknisi->nama ?? 'N/A' }}
|
|
</span>
|
|
@endforeach
|
|
</div>
|
|
</td>
|
|
<td>
|
|
@if($item->foto_surat)
|
|
<img src="{{ asset('storage/'.$item->foto_surat) }}" class="pug-thumb"
|
|
alt="Surat" onclick="previewFoto('{{ asset('storage/'.$item->foto_surat) }}')">
|
|
@else
|
|
<span style="color:var(--t3);font-size:11px;">N/A</span>
|
|
@endif
|
|
</td>
|
|
<td>
|
|
<div class="pug-date">{{ \Carbon\Carbon::parse($item->tanggal_diberikan)->format('d/m/Y') }}</div>
|
|
</td>
|
|
|
|
|
|
<td>
|
|
@php
|
|
$bMap = [
|
|
'belum_mulai' => ['class'=>'pug-badge-muted', 'icon'=>'fa-clock', 'label'=>'Belum Mulai'],
|
|
'dalam_proses'=> ['class'=>'pug-badge-violet', 'icon'=>'fa-spinner', 'label'=>'Dalam Proses'],
|
|
'selesai' => ['class'=>'pug-badge-green', 'icon'=>'fa-check-circle', 'label'=>'Selesai'],
|
|
'dibatalkan' => ['class'=>'pug-badge-rose', 'icon'=>'fa-times-circle', 'label'=>'Dibatalkan'],
|
|
];
|
|
$bCfg = $bMap[$item->status_pekerjaan] ?? $bMap['belum_mulai'];
|
|
@endphp
|
|
<span class="pug-badge {{ $bCfg['class'] }}">
|
|
<i class="fas {{ $bCfg['icon'] }}"></i> {{ $bCfg['label'] }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<div class="pug-actions">
|
|
<button class="pug-action-btn pug-action-view" onclick="viewDetail({{ $item->id_penugasan }})" title="Lihat Detail">
|
|
<i class="fas fa-eye"></i>
|
|
</button>
|
|
<button class="pug-action-btn pug-action-edit" onclick="editPenugasan({{ $item->id_penugasan }})" title="Edit Info Tugas">
|
|
<i class="fas fa-pen"></i>
|
|
</button>
|
|
<button class="pug-action-btn pug-action-del" onclick="deletePenugasan({{ $item->id_penugasan }})" title="Hapus">
|
|
<i class="fas fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@empty
|
|
<tr>
|
|
<td colspan="7">
|
|
<div class="pug-empty">
|
|
<div class="pug-empty-icon"><i class="fas fa-inbox"></i></div>
|
|
<div class="pug-empty-title">Belum ada penugasan</div>
|
|
<div class="pug-empty-sub">Klik "Tambah Penugasan" untuk membuat penugasan baru</div>
|
|
</div>
|
|
</td>
|
|
</tr>
|
|
@endforelse
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
@if(isset($penugasan) && $penugasan->hasPages())
|
|
<div class="pug-pag">{{ $penugasan->links() }}</div>
|
|
@endif
|
|
</div>
|
|
|
|
</div><!-- /content-layout -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Modal Tambah/Edit ── -->
|
|
<div id="createModal" class="pug-overlay">
|
|
<div class="pug-modal pug-modal-sm">
|
|
<div class="pug-modal-header">
|
|
<div class="pug-modal-title"><i class="fas fa-user-plus"></i><span id="modalTitle">Tambah Penugasan Baru</span></div>
|
|
<button class="pug-close-btn" onclick="closeModal('createModal')"><i class="fas fa-times"></i></button>
|
|
</div>
|
|
|
|
<div class="pug-info-box">
|
|
<i class="fas fa-info-circle"></i>
|
|
<p><strong>Metode Foto:</strong> Silakan unggah foto daftar tugas/surat tugas dari kantor. Teknisi akan melihat foto ini di aplikasi mereka.</p>
|
|
</div>
|
|
|
|
<form id="penugasanForm" method="POST" action="{{ route('pekerjaan.penugasan.store') }}" enctype="multipart/form-data">
|
|
@csrf
|
|
<input type="hidden" id="formMethod" name="_method" value="POST">
|
|
<input type="hidden" id="penugasanId" name="id">
|
|
|
|
<div class="pug-form-grid">
|
|
<!-- Teknisi -->
|
|
<div class="pug-form-field pug-form-full">
|
|
<label class="pug-form-label"><i class="fas fa-user-tie"></i> Pilih Tim Teknisi <span class="req">*</span></label>
|
|
|
|
<div class="pug-tag-select">
|
|
<div class="pug-tag-input-wrapper" id="tagWrapper">
|
|
<div id="selectedTags" class="pug-tags-container"></div>
|
|
<input type="text" id="techSearch" placeholder="Ketik nama teknisi..." class="pug-tag-input">
|
|
</div>
|
|
|
|
<div class="pug-tag-dropdown" id="techDropdown">
|
|
@foreach($teknisiList ?? [] as $teknisi)
|
|
<div class="pug-tag-option"
|
|
data-id="{{ $teknisi->id_teknisi }}"
|
|
data-name="{{ $teknisi->nama }}"
|
|
data-sub="{{ $teknisi->spesialisasi }}">
|
|
<strong>{{ $teknisi->nama }}</strong>
|
|
<span>{{ $teknisi->spesialisasi }}</span>
|
|
</div>
|
|
@endforeach
|
|
<div class="pug-tag-no-results">Tidak ada hasil</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Hidden inputs for actual form submission -->
|
|
<div id="hiddenTechInputs"></div>
|
|
</div>
|
|
|
|
<!-- Tanggal -->
|
|
<div class="pug-form-field">
|
|
<label class="pug-form-label"><i class="fas fa-calendar-alt"></i> Tanggal <span class="req">*</span></label>
|
|
<input type="date" name="tanggal_diberikan" id="tanggal_diberikan" required class="pug-text-input">
|
|
</div>
|
|
|
|
<!-- Jenis Pekerjaan -->
|
|
<div class="pug-form-field">
|
|
<label class="pug-form-label"><i class="fas fa-tools"></i> Jenis Pekerjaan <span class="req">*</span></label>
|
|
<select name="jenis_pekerjaan" id="jenis_pekerjaan_modal" required class="pug-select">
|
|
<option value="">Pilih Jenis Pekerjaan</option>
|
|
<option value="sr">SR (Sambungan Rumah)</option>
|
|
<option value="pengembangan_jaringan_pipa">Pengembangan Jaringan Pipa</option>
|
|
<option value="pengangkatan">Pengangkatan</option>
|
|
<option value="pemasangan_gate_valve">Pemasangan Gate Valve</option>
|
|
<option value="perbaikan_jaringan_pipa">Perbaikan Jaringan Pipa</option>
|
|
<option value="pengecatan_pipa_besi">Pengecatan Pipa Besi</option>
|
|
<option value="penyempurnaan_jaringan_pipa">Penyempurnaan Jaringan Pipa</option>
|
|
</select>
|
|
</div>
|
|
|
|
<!-- No Sambungan -->
|
|
<div class="pug-form-field">
|
|
<label class="pug-form-label"><i class="fas fa-hashtag"></i> No. Sambungan <span class="opt">(opsional)</span></label>
|
|
<input type="text" name="no_sambungan" id="no_sambungan" class="pug-text-input" placeholder="Contoh: 0032 atau -">
|
|
</div>
|
|
|
|
<!-- Nama Pelanggan -->
|
|
<div class="pug-form-field pug-form-full">
|
|
<label class="pug-form-label"><i class="fas fa-user"></i> Nama Pelanggan <span class="opt">(opsional)</span></label>
|
|
<input type="text" name="nama_pelanggan" id="nama_pelanggan" class="pug-text-input" placeholder="Contoh: Bpk. Slamet atau Nama Gedung">
|
|
</div>
|
|
|
|
<!-- Alamat -->
|
|
<div class="pug-form-field pug-form-full">
|
|
<label class="pug-form-label"><i class="fas fa-map-marked-alt"></i> Alamat Lengkap <span class="req">*</span></label>
|
|
<input type="text" name="alamat_lokasi" id="alamat_lokasi" required class="pug-text-input" placeholder="Contoh: Jl. Merdeka No. 123, Desa Sukamaju">
|
|
</div>
|
|
|
|
<!-- Foto Surat -->
|
|
<div class="pug-form-field pug-form-full">
|
|
<label class="pug-form-label"><i class="fas fa-camera"></i> Unggah Foto Denah / Surat <span class="opt">(opsional)</span></label>
|
|
<div class="pug-upload-zone" onclick="document.getElementById('foto_surat').click()">
|
|
<input type="file" name="foto_surat" id="foto_surat" accept="image/*" style="display:none" onchange="previewSelectedFile(this)">
|
|
<div id="filePlaceholder">
|
|
<i class="fas fa-cloud-upload-alt"></i>
|
|
<p>Klik untuk unggah denah lokasi atau surat tugas</p>
|
|
<span>Format: JPG, PNG, WEBP (Boleh dikosongkan)</span>
|
|
</div>
|
|
<img id="filePreview" class="pug-preview-img" src="" style="display:none;">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Catatan Admin -->
|
|
<div class="pug-form-field pug-form-full">
|
|
<label class="pug-form-label"><i class="fas fa-sticky-note"></i> Instruksi Tambahan <span class="opt">(opsional)</span></label>
|
|
<textarea name="catatan_admin" id="catatan_admin" class="pug-text-input" rows="2" placeholder="Contoh: Pipa bocor di bawah pohon mangga, dst..."></textarea>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="pug-modal-footer">
|
|
<button type="button" class="pug-btn-cancel" onclick="closeModal('createModal')">
|
|
<i class="fas fa-times"></i> Batal
|
|
</button>
|
|
<button type="submit" class="pug-btn-submit">
|
|
<i class="fas fa-paper-plane"></i> Kirim Penugasan ke Teknisi
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Modal Detail ── -->
|
|
<div id="detailModal" class="pug-overlay">
|
|
<div class="pug-modal pug-modal-lg">
|
|
<div class="pug-modal-header">
|
|
<div class="pug-modal-title"><i class="fas fa-info-circle"></i> Detail Penugasan</div>
|
|
<button class="pug-close-btn" onclick="closeModal('detailModal')"><i class="fas fa-times"></i></button>
|
|
</div>
|
|
<div id="detailContent"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- ── Preview Foto ── -->
|
|
<div id="previewModal" class="pug-preview-overlay" onclick="closeModal('previewModal')">
|
|
<img id="previewImg" src="" alt="Preview">
|
|
<button class="pug-preview-x" onclick="closeModal('previewModal')"><i class="fas fa-times"></i></button>
|
|
</div>
|
|
|
|
<script>
|
|
const appUrl = "{{ url('/') }}";
|
|
document.addEventListener('DOMContentLoaded', function () {
|
|
document.getElementById('tanggal_diberikan').value = new Date().toISOString().split('T')[0];
|
|
|
|
let t;
|
|
const searchInput = document.getElementById('searchInput');
|
|
if (searchInput) {
|
|
searchInput.addEventListener('input', function () {
|
|
clearTimeout(t);
|
|
t = setTimeout(() => document.getElementById('filterForm').submit(), 600);
|
|
});
|
|
}
|
|
|
|
document.getElementById('tanggal_diberikan').addEventListener('change', function () {
|
|
updateTechDropdown(this.value);
|
|
});
|
|
|
|
setTimeout(() => {
|
|
document.querySelectorAll('[role="alert"]').forEach(el => {
|
|
el.style.transition = 'opacity .5s'; el.style.opacity = '0';
|
|
setTimeout(() => el.style.display = 'none', 500);
|
|
});
|
|
}, 5000);
|
|
});
|
|
|
|
function setStatus(val) {
|
|
document.getElementById('hiddenStatus').value = val;
|
|
document.getElementById('filterForm').submit();
|
|
}
|
|
|
|
function openCreateModal() {
|
|
document.getElementById('modalTitle').textContent = 'Tambah Penugasan';
|
|
document.getElementById('penugasanForm').action = "{{ route('pekerjaan.penugasan.store') }}";
|
|
document.getElementById('formMethod').value = 'POST';
|
|
document.getElementById('penugasanForm').reset();
|
|
|
|
// Reset preview
|
|
document.getElementById('filePreview').style.display = 'none';
|
|
document.getElementById('filePlaceholder').style.display = 'block';
|
|
|
|
const todayDate = new Date().toISOString().split('T')[0];
|
|
document.getElementById('tanggal_diberikan').value = todayDate;
|
|
updateTechDropdown(todayDate);
|
|
|
|
document.getElementById('createModal').classList.add('show');
|
|
}
|
|
|
|
function previewSelectedFile(input) {
|
|
const preview = document.getElementById('filePreview');
|
|
const placeholder = document.getElementById('filePlaceholder');
|
|
if (input.files && input.files[0]) {
|
|
const reader = new FileReader();
|
|
reader.onload = function(e) {
|
|
preview.src = e.target.result;
|
|
preview.style.display = 'block';
|
|
placeholder.style.display = 'none';
|
|
}
|
|
reader.readAsDataURL(input.files[0]);
|
|
}
|
|
}
|
|
|
|
function closeModal(id) {
|
|
const el = document.getElementById(id);
|
|
el.classList.remove('show');
|
|
if (id === 'previewModal') el.style.display = 'none'; // fallback for preview overlay
|
|
}
|
|
|
|
function previewFoto(url) {
|
|
document.getElementById('previewImg').src = url;
|
|
document.getElementById('previewModal').style.display = 'flex';
|
|
document.getElementById('previewModal').classList.add('show');
|
|
}
|
|
|
|
function viewDetail(id) {
|
|
fetch(`${appUrl}/pekerjaan/penugasan/${id}`)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (!data.success) return alert('Gagal memuat detail');
|
|
const d = data.data;
|
|
const formatRp = v => 'Rp ' + parseInt(v||0).toLocaleString('id-ID');
|
|
const formatDate = v => v ? new Date(v).toLocaleDateString('id-ID',{day:'2-digit',month:'long',year:'numeric'}) : '';
|
|
|
|
let html = `
|
|
<div class="pug-detail-section">
|
|
<div class="pug-detail-section-title"><i class="fas fa-users" style="color:var(--cyan);"></i> Tim Teknisi Lapangan</div>
|
|
<div style="display:grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap:10px; margin-top:10px;">
|
|
${d.tim_teknisi.map(tt => `
|
|
<div style="display:flex; align-items:center; gap:8px; background:var(--bg); padding:8px 12px; border-radius:8px; border:1px solid var(--line);">
|
|
<div class="pug-av" style="width:28px;height:28px;font-size:11px;flex-shrink:0;">${(tt.teknisi?.nama||'NA').substring(0,2).toUpperCase()}</div>
|
|
<div style="overflow:hidden;">
|
|
<div style="font-size:12px; font-weight:600; color:var(--t1); white-space:nowrap; overflow:hidden; text-overflow:ellipsis;">${tt.teknisi?.nama || 'N/A'}</div>
|
|
<div style="font-size:10px; color:var(--t3);">${tt.teknisi?.spesialisasi || '—'}</div>
|
|
</div>
|
|
</div>
|
|
`).join('')}
|
|
</div>
|
|
|
|
<div style="margin-top:20px; padding-top:16px; border-top:1px solid var(--line);">
|
|
<div style="color:var(--t3); font-size:11px; font-weight:700; text-transform:uppercase; margin-bottom:12px;">Informasi Lokasi & Pelanggan</div>
|
|
<div style="display:grid; grid-template-columns:1fr 1fr; gap:16px;">
|
|
<div>
|
|
<div class="pug-detail-item-label"><i class="fas fa-user" style="margin-right:4px;"></i> Nama Pelanggan</div>
|
|
<div class="pug-detail-item-val">${d.nama_pelanggan || ''}</div>
|
|
</div>
|
|
<div>
|
|
<div class="pug-detail-item-label"><i class="fas fa-hashtag" style="margin-right:4px;"></i> No. Sambungan</div>
|
|
<div class="pug-detail-item-val">${d.no_sambungan || ''}</div>
|
|
</div>
|
|
</div>
|
|
<div style="margin-top:12px;">
|
|
<div class="pug-detail-item-label"><i class="fas fa-map-marker-alt" style="margin-right:4px;"></i> Alamat Lokasi</div>
|
|
<div class="pug-detail-item-val" style="color:var(--pug-green);">${d.alamat_lokasi || ''}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
${d.foto_surat_url ? `
|
|
<div style="margin-bottom:14px;">
|
|
<div class="pug-detail-item-label" style="margin-bottom:8px;"><i class="fas fa-camera" style="color:var(--green);margin-right:5px;"></i>Denah / Surat Tugas</div>
|
|
<img src="${d.foto_surat_url}" style="width:100%;max-height:260px;object-fit:contain;border-radius:10px;border:1px solid var(--line2);cursor:pointer;" onclick="previewFoto('${d.foto_surat_url}')">
|
|
<div style="font-size:11px;color:var(--t3);text-align:center;margin-top:5px;">Klik untuk perbesar</div>
|
|
</div>` : ''}
|
|
|
|
${d.catatan_admin ? `
|
|
<div style="background:var(--amber-dim);border:1px solid rgba(255,181,71,.2);border-radius:12px;padding:14px 16px;margin-bottom:14px;">
|
|
<div style="color:var(--amber);font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.1em;margin-bottom:7px;">
|
|
<i class="fas fa-map-marker-alt" style="margin-right:5px;"></i>Instruksi Mandor
|
|
</div>
|
|
<div style="color:var(--t1);font-size:13.5px;font-weight:600;line-height:1.5;">${d.catatan_admin}</div>
|
|
</div>` : ''}`;
|
|
|
|
if (d.items && d.items.length > 0) {
|
|
html += `
|
|
<div class="pug-detail-section">
|
|
<div class="pug-detail-section-title"><i class="fas fa-tools" style="color:var(--violet);"></i> Rincian Pekerjaan (${d.items.length} Item)</div>
|
|
<table class="pug-table" style="margin-bottom:14px; font-size:12px;">
|
|
<thead>
|
|
<tr style="background:var(--line);">
|
|
<th style="padding:8px;">Jenis Pekerjaan</th>
|
|
<th style="padding:8px;">Detail</th>
|
|
<th style="padding:8px; text-align:right;">Subtotal</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
${d.items.map(it => `
|
|
<tr>
|
|
<td style="padding:8px;">
|
|
<strong>${it.label_jenis_pekerjaan || it.jenis_pekerjaan}</strong>
|
|
</td>
|
|
<td style="padding:8px; color:var(--t3);">
|
|
${it.dimensi_pipa ? `Dim: ${it.dimensi_pipa}<br>` : ''}
|
|
${it.jarak_meter ? `Jarak: ${it.jarak_meter}m<br>` : ''}
|
|
${it.jumlah_unit ? `Unit: ${it.jumlah_unit}<br>` : ''}
|
|
${it.jumlah_titik ? `Titik: ${it.jumlah_titik}<br>` : ''}
|
|
${it.jenis_pengangkatan ? `Tipe: ${it.jenis_pengangkatan}<br>` : ''}
|
|
${it.pakai_pipa_besi !== null ? `Pipa Besi: ${it.pakai_pipa_besi ? 'Ya' : 'Tidak'}` : ''}
|
|
</td>
|
|
<td style="padding:8px; text-align:right; font-weight:700; color:var(--green);">
|
|
${formatRp(it.total_nilai_pekerjaan)}
|
|
</td>
|
|
</tr>
|
|
`).join('')}
|
|
</tbody>
|
|
<tfoot>
|
|
<tr style="background:var(--pug-green-l);">
|
|
<td colspan="2" style="padding:8px; text-align:right; font-weight:700;">TOTAL NILAI</td>
|
|
<td style="padding:8px; text-align:right; font-weight:800; color:var(--green); font-size:14px;">${formatRp(d.total_nilai_pekerjaan)}</td>
|
|
</tr>
|
|
</tfoot>
|
|
</table>
|
|
|
|
<div style="font-size:11px; color:var(--t3); margin-bottom:5px;">Status Pekerjaan: <strong>${(d.status_pekerjaan||'').replace(/_/g,' ').toUpperCase()}</strong></div>
|
|
${d.detail_pekerjaan?`<div style="margin-top:14px;padding-top:14px;border-top:1px solid var(--line);"><div class="pug-detail-item-label" style="margin-bottom:7px;">Catatan Teknisi</div><div style="color:var(--t1);font-size:13px;background:var(--bg);padding:12px;border-radius:9px;border:1px solid var(--line);line-height:1.6;">${d.detail_pekerjaan}</div></div>`:''}
|
|
</div>`;
|
|
|
|
if (d.tanggal_garansi_mulai) {
|
|
html += `<div class="pug-garansi-box">
|
|
<i class="fas fa-shield-alt"></i>
|
|
<div>
|
|
<div style="color:var(--violet);font-size:12px;font-weight:700;margin-bottom:3px;">Garansi Aktif</div>
|
|
<div style="color:var(--t2);font-size:12px;">${new Date(d.tanggal_garansi_mulai).toLocaleDateString('id-ID')} s/d ${new Date(d.tanggal_garansi_selesai).toLocaleDateString('id-ID')}</div>
|
|
</div>
|
|
</div>`;
|
|
}
|
|
|
|
if (d.foto_sebelum_url || d.foto_sesudah_url) {
|
|
html += `
|
|
<div class="pug-detail-section" style="background:var(--bg); border:1px dashed var(--line);">
|
|
<div class="pug-detail-section-title"><i class="fas fa-camera" style="color:var(--green);"></i> Dokumentasi Pekerjaan</div>
|
|
<div style="display:grid; grid-template-columns:1fr 1fr; gap:12px; margin-top:10px;">
|
|
${d.foto_sebelum_url ? `
|
|
<div class="pug-photo-container">
|
|
<div class="pug-photo-label"><i class="fas fa-history"></i> Sebelum Pekerjaan</div>
|
|
<img src="${d.foto_sebelum_url}" class="pug-photo-item" onclick="window.open('${d.foto_sebelum_url}', '_blank')">
|
|
</div>
|
|
` : `
|
|
<div class="pug-photo-empty">Belum ada foto sebelum</div>
|
|
`}
|
|
|
|
${d.foto_sesudah_url ? `
|
|
<div class="pug-photo-container">
|
|
<div class="pug-photo-label"><i class="fas fa-check-double"></i> Sesudah Pekerjaan</div>
|
|
<img src="${d.foto_sesudah_url}" class="pug-photo-item" onclick="window.open('${d.foto_sesudah_url}', '_blank')">
|
|
</div>
|
|
` : `
|
|
<div class="pug-photo-empty">Belum ada foto sesudah</div>
|
|
`}
|
|
</div>
|
|
</div>`;
|
|
}
|
|
} else {
|
|
html += `<div class="pug-waiting-box">
|
|
<i class="fas fa-hourglass-half"></i>
|
|
<div style="color:var(--t2);font-size:13px;"><strong style="color:var(--amber);">Menunggu:</strong> Teknisi belum mengisi detail pekerjaan via aplikasi mobile.</div>
|
|
</div>`;
|
|
}
|
|
|
|
document.getElementById('detailContent').innerHTML = html;
|
|
document.getElementById('detailModal').classList.add('show');
|
|
})
|
|
.catch(() => alert('Gagal memuat detail penugasan'));
|
|
}
|
|
|
|
function editPenugasan(id) {
|
|
fetch(`${appUrl}/pekerjaan/penugasan/${id}/edit`)
|
|
.then(r => r.json())
|
|
.then(data => {
|
|
if (!data.success) return alert(data.message || 'Gagal memuat data');
|
|
const item = data.data;
|
|
document.getElementById('modalTitle').textContent = 'Edit Penugasan';
|
|
document.getElementById('penugasanForm').action = `/pekerjaan/penugasan/${id}`;
|
|
document.getElementById('formMethod').value = 'PUT';
|
|
document.getElementById('penugasanId').value = id;
|
|
|
|
// Handle Tag Selection for Team
|
|
clearTags();
|
|
item.tim_teknisi.forEach(tt => {
|
|
addTag(tt.id_teknisi, tt.teknisi.nama);
|
|
});
|
|
|
|
const targetDate = item.tanggal_diberikan || '';
|
|
document.getElementById('tanggal_diberikan').value = targetDate;
|
|
updateTechDropdown(targetDate);
|
|
|
|
document.getElementById('catatan_admin').value = item.catatan_admin || '';
|
|
document.getElementById('alamat_lokasi').value = item.alamat_lokasi || '';
|
|
document.getElementById('nama_pelanggan').value = item.nama_pelanggan || '';
|
|
document.getElementById('no_sambungan').value = item.no_sambungan || '';
|
|
document.getElementById('jenis_pekerjaan_modal').value = item.jenis_pekerjaan || '';
|
|
|
|
// Show existing image preview if available
|
|
const preview = document.getElementById('filePreview');
|
|
const placeholder = document.getElementById('filePlaceholder');
|
|
if (item.foto_surat_url) {
|
|
preview.src = item.foto_surat_url;
|
|
preview.style.display = 'block';
|
|
placeholder.style.display = 'none';
|
|
} else {
|
|
preview.style.display = 'none';
|
|
placeholder.style.display = 'block';
|
|
}
|
|
|
|
document.getElementById('createModal').classList.add('show');
|
|
})
|
|
.catch(() => alert('Gagal memuat data penugasan'));
|
|
}
|
|
|
|
function deletePenugasan(id) {
|
|
if (!confirm('Apakah Anda yakin ingin menghapus penugasan ini?')) return;
|
|
fetch(`${appUrl}/pekerjaan/penugasan/${id}`, {
|
|
method: 'DELETE',
|
|
headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Accept': 'application/json' }
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => { data.success ? (alert(data.message), location.reload()) : alert('Gagal: '+(data.message||'Terjadi kesalahan')); })
|
|
.catch(() => alert('Terjadi kesalahan saat menghapus'));
|
|
}
|
|
|
|
document.getElementById('penugasanForm').addEventListener('submit', function (e) {
|
|
if (document.getElementById('formMethod').value === 'PUT') {
|
|
e.preventDefault();
|
|
const id = document.getElementById('penugasanId').value;
|
|
const formData = new FormData(this);
|
|
fetch(`${appUrl}/pekerjaan/penugasan/${id}`, {
|
|
method: 'POST',
|
|
headers: { 'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').getAttribute('content'), 'Accept': 'application/json' },
|
|
body: formData
|
|
})
|
|
.then(r => r.json())
|
|
.then(data => { data.success ? (alert(data.message), location.reload()) : alert('Gagal: '+(data.message||'Terjadi kesalahan')); })
|
|
.catch(() => alert('Terjadi kesalahan saat menyimpan'));
|
|
}
|
|
});
|
|
|
|
function resetFilter() { window.location.href = "{{ route('pekerjaan.penugasan.index') }}"; }
|
|
|
|
window.addEventListener('click', e => {
|
|
['createModal','detailModal'].forEach(id => {
|
|
const el = document.getElementById(id);
|
|
if (e.target === el) closeModal(id);
|
|
});
|
|
if (e.target === document.getElementById('previewModal')) closeModal('previewModal');
|
|
});
|
|
|
|
// ── Logic Searchable Tag Multi-Select ──
|
|
const techSearch = document.getElementById('techSearch');
|
|
const techDropdown = document.getElementById('techDropdown');
|
|
const selectedTags = document.getElementById('selectedTags');
|
|
const hiddenInputs = document.getElementById('hiddenTechInputs');
|
|
let selectedIds = new Set();
|
|
|
|
function addTag(id, name) {
|
|
if (selectedIds.has(id.toString())) return;
|
|
selectedIds.add(id.toString());
|
|
|
|
// Create Tag UI
|
|
const tag = document.createElement('div');
|
|
tag.className = 'pug-tag';
|
|
tag.innerHTML = `${name} <i class="fas fa-times" onclick="removeTag('${id}', this)"></i>`;
|
|
selectedTags.appendChild(tag);
|
|
|
|
// Create Hidden Input
|
|
const input = document.createElement('input');
|
|
input.type = 'hidden';
|
|
input.name = 'id_teknisi[]';
|
|
input.value = id;
|
|
input.id = `input-tech-${id}`;
|
|
hiddenInputs.appendChild(input);
|
|
|
|
techSearch.value = '';
|
|
techDropdown.classList.remove('show');
|
|
}
|
|
|
|
window.removeTag = function(id, el) {
|
|
selectedIds.delete(id.toString());
|
|
el.parentElement.remove();
|
|
const input = document.getElementById(`input-tech-${id}`);
|
|
if (input) input.remove();
|
|
};
|
|
|
|
function clearTags() {
|
|
selectedIds.clear();
|
|
selectedTags.innerHTML = '';
|
|
hiddenInputs.innerHTML = '';
|
|
}
|
|
|
|
techSearch.addEventListener('focus', () => {
|
|
techDropdown.classList.add('show');
|
|
});
|
|
|
|
techSearch.addEventListener('input', (e) => {
|
|
const q = e.target.value.toLowerCase();
|
|
let hasResults = false;
|
|
document.querySelectorAll('.pug-tag-option').forEach(opt => {
|
|
const match = opt.dataset.name.toLowerCase().includes(q);
|
|
opt.style.display = match ? 'block' : 'none';
|
|
if (match) hasResults = true;
|
|
});
|
|
document.querySelector('.pug-tag-no-results').style.display = hasResults ? 'none' : 'block';
|
|
techDropdown.classList.add('show');
|
|
});
|
|
|
|
document.querySelectorAll('.pug-tag-option').forEach(opt => {
|
|
opt.addEventListener('click', function() {
|
|
addTag(this.dataset.id, this.dataset.name);
|
|
});
|
|
});
|
|
|
|
function updateTechDropdown(date) {
|
|
const dropdown = document.getElementById('techDropdown');
|
|
dropdown.innerHTML = '<div style="padding: 12px; text-align: center; color: var(--pug-t3); font-size: 11.5px;"><i class="fas fa-spinner fa-spin" style="color: #10b981; margin-right: 5px;"></i> Memuat teknisi...</div>';
|
|
|
|
fetch(`${appUrl}/pekerjaan/penugasan/get-teknisi?date=${date}`)
|
|
.then(r => r.json())
|
|
.then(res => {
|
|
if (res.success) {
|
|
let html = '';
|
|
if (res.data.length === 0) {
|
|
html = '<div style="padding: 12px; text-align: center; color: #ef4444; font-size: 11.5px; font-weight: 600;"><i class="fas fa-exclamation-circle"></i> Tidak ada teknisi yang Hadir hari ini!</div>';
|
|
} else {
|
|
res.data.forEach(t => {
|
|
html += `
|
|
<div class="pug-tag-option"
|
|
data-id="${t.id_teknisi}"
|
|
data-name="${t.nama}"
|
|
data-sub="${t.spesialisasi || 'Teknisi'}">
|
|
<strong>${t.nama}</strong>
|
|
<span>${t.spesialisasi || 'Teknisi'}</span>
|
|
</div>
|
|
`;
|
|
});
|
|
html += '<div class="pug-tag-no-results" style="display:none;">Tidak ada hasil</div>';
|
|
}
|
|
dropdown.innerHTML = html;
|
|
|
|
// Re-bind click event to newly created elements
|
|
document.querySelectorAll('#techDropdown .pug-tag-option').forEach(opt => {
|
|
opt.addEventListener('click', function() {
|
|
addTag(this.dataset.id, this.dataset.name);
|
|
});
|
|
});
|
|
} else {
|
|
dropdown.innerHTML = '<div style="padding: 12px; text-align: center; color: #ef4444; font-size: 11.5px;"><i class="fas fa-times-circle"></i> Gagal memuat data</div>';
|
|
}
|
|
})
|
|
.catch(() => {
|
|
dropdown.innerHTML = '<div style="padding: 12px; text-align: center; color: #ef4444; font-size: 11.5px;"><i class="fas fa-exclamation-triangle"></i> Terjadi kesalahan koneksi</div>';
|
|
});
|
|
}
|
|
|
|
// Close dropdown on outside click
|
|
document.addEventListener('click', (e) => {
|
|
if (!e.target.closest('.pug-tag-select')) {
|
|
techDropdown.classList.remove('show');
|
|
}
|
|
});
|
|
</script>
|
|
</x-app-layout> |