MIF_E31230910_MP-HRIS-WEB/resources/views/jadwal/create.blade.php

264 lines
12 KiB
PHP

@extends('layouts.app')
@section('title', 'Generate Jadwal Kerja')
@section('content')
<div class="space-y-6">
<x-page-header title="Generate Jadwal Kerja" subtitle="Buat jadwal massal untuk beberapa pegawai sekaligus.">
<x-back-button href="{{ route('jadwal.index') }}" />
</x-page-header>
<div class="bg-white rounded-xl shadow-sm border border-slate-200 p-6">
<form action="{{ route('jadwal.store') }}" method="POST">
@csrf
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-6">
<x-date-input label="Tanggal Mulai" name="tanggal_mulai"
value="{{ old('tanggal_mulai', now()->format('Y-m-d')) }}" required />
<x-date-input label="Tanggal Selesai" name="tanggal_selesai"
value="{{ old('tanggal_selesai', now()->addDays(6)->format('Y-m-d')) }}" required />
<x-select label="Shift" name="id_shift" id="id_shift" class="!mb-0" required>
<option value="">-- Pilih Shift --</option>
@foreach($shifts as $shift)
<option value="{{ $shift->id_shift }}" {{ old('id_shift') == $shift->id_shift ? 'selected' : '' }}>
{{ $shift->nama_shift }}
({{ \Carbon\Carbon::parse($shift->jam_mulai)->format('H:i') }} -
{{ \Carbon\Carbon::parse($shift->jam_selesai)->format('H:i') }})
</option>
@endforeach
</x-select>
</div>
<div class="mb-6">
<div class="flex flex-col md:flex-row justify-between md:items-center gap-4 mb-4">
<label class="block text-sm font-semibold text-slate-700">Pilih Pegawai</label>
<div class="flex gap-2">
<x-filter-select id="filter-kantor" class="py-1.5 text-xs">
<option value="">Semua Kantor</option>
@foreach($kantor as $k)
<option value="{{ $k->id_kantor }}">{{ $k->nama_kantor }}</option>
@endforeach
</x-filter-select>
<x-filter-select id="filter-divisi" class="py-1.5 text-xs">
<option value="">Semua Divisi</option>
@foreach($divisi as $d)
<option value="{{ $d->id_divisi }}">{{ $d->nama_divisi }}</option>
@endforeach
</x-filter-select>
</div>
</div>
<div class="flex items-center gap-4 mb-4">
<label class="flex items-center gap-2 text-sm text-primary font-bold cursor-pointer hover:bg-blue-50 px-3 py-1.5 rounded-lg border border-transparent hover:border-blue-100 transition-all active:scale-95">
<x-checkbox id="selectAll" class="pointer-events-none" />
<span>Pilih Semua (yang tampil)</span>
</label>
<span class="text-xs text-slate-400 bg-slate-100 px-2 py-1 rounded-full">(<span id="selectedCount" class="font-bold text-slate-700">0</span> pegawai dipilih)</span>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-3 max-h-96 overflow-y-auto border border-slate-200 rounded-lg p-4"
id="pegawai-container">
@foreach($pegawai as $p)
<label
class="flex items-center gap-3 p-3 bg-slate-50 rounded-lg hover:bg-slate-100 transition cursor-pointer pegawai-item"
data-kantor="{{ $p->id_kantor }}" data-divisi="{{ $p->id_divisi }}">
<x-checkbox name="user_ids[]" value="{{ $p->id }}" class="user-checkbox" />
<div>
<div class="font-medium text-sm text-slate-800">{{ $p->nama_lengkap }}</div>
<div class="text-xs text-slate-500">
{{ $p->kantor->nama_kantor ?? '-' }} {{ $p->jabatan->nama_jabatan ?? '-' }}
</div>
</div>
</label>
@endforeach
</div>
<div id="empty-state" class="hidden text-center py-8 text-slate-400 text-sm">
Tidak ada pegawai yang sesuai filter.
</div>
@error('user_ids')
<p class="mt-2 text-xs text-red-500">{{ $message }}</p>
@enderror
</div>
<div class="flex justify-end gap-3 pt-4 border-t border-slate-100">
<a href="{{ route('jadwal.index') }}"
class="px-4 py-2 text-sm border rounded-xl hover:bg-slate-50">Batal</a>
<button type="submit"
class="px-6 py-2 text-sm bg-primary text-white rounded-xl hover:bg-primary/90 font-medium">
Generate Jadwal
</button>
</div>
</form>
</div>
</div>
@endsection
@section('script')
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script>
(function() {
const inputMulai = document.querySelector('input[name="tanggal_mulai"]');
const inputSelesai = document.querySelector('input[name="tanggal_selesai"]');
if (inputMulai && inputSelesai) {
inputMulai.addEventListener('change', function() {
if (this.value) {
inputSelesai.min = this.value;
if (inputSelesai.value && inputSelesai.value < this.value) {
inputSelesai.value = this.value;
}
}
});
inputSelesai.addEventListener('change', function() {
if (this.value) {
inputMulai.max = this.value;
if (inputMulai.value && inputMulai.value > this.value) {
inputMulai.value = this.value;
}
}
});
if (inputMulai.value) inputSelesai.min = inputMulai.value;
if (inputSelesai.value) inputMulai.max = inputSelesai.value;
}
})();
(function() {
const formObj = document.querySelector('form');
if (formObj) {
formObj.addEventListener('submit', function (e) {
e.preventDefault();
let form = this;
let selectedUserIds = [];
document.querySelectorAll('.user-checkbox:checked').forEach((cb) => {
selectedUserIds.push(cb.value);
});
let payload = {
_token: "{{ csrf_token() }}",
user_ids: selectedUserIds,
tanggal_mulai: document.querySelector('input[name="tanggal_mulai"]').value,
tanggal_selesai: document.querySelector('input[name="tanggal_selesai"]').value
};
if (payload.user_ids.length === 0 || !payload.tanggal_mulai || !payload.tanggal_selesai) {
Swal.fire('Peringatan', 'Mohon lengkapi data form dan pilih pegawai.', 'warning');
return;
}
Swal.fire({
title: 'Memeriksa Jadwal...',
text: 'Sedang mengecek bentrok penggunaan poin',
allowOutsideClick: false,
didOpen: () => { Swal.showLoading() }
});
fetch("{{ route('jadwal.check-conflicts') }}", {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-CSRF-TOKEN": "{{ csrf_token() }}"
},
body: JSON.stringify(payload)
})
.then(response => response.json())
.then(data => {
if (data.has_conflict) {
Swal.fire({
title: '⚠️ Konflik Data Poin!',
html: data.message,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Ubah & Batalkan Poin',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
form.submit();
}
});
} else {
form.submit();
}
})
.catch(error => {
console.error('Error:', error);
Swal.fire('Error', 'Gagal melakukan pengecekan data.', 'error');
});
});
}
const selectAllCheckbox = document.getElementById('selectAll');
const pegawaiContainer = document.getElementById('pegawai-container');
const emptyState = document.getElementById('empty-state');
const selectedCountEl = document.getElementById('selectedCount');
const filterKantor = document.getElementById('filter-kantor');
const filterDivisi = document.getElementById('filter-divisi');
if (!selectAllCheckbox) return;
let userCheckboxes = document.querySelectorAll('.user-checkbox');
let pegawaiItems = document.querySelectorAll('.pegawai-item');
function updateCount() {
const count = document.querySelectorAll('.user-checkbox:checked').length;
selectedCountEl.textContent = count;
}
function filterPegawai() {
const kantorId = filterKantor.value;
const divisiId = filterDivisi.value;
let visibleCount = 0;
pegawaiItems.forEach(item => {
const itemKantor = item.getAttribute('data-kantor');
const itemDivisi = item.getAttribute('data-divisi');
let show = true;
if (kantorId && itemKantor !== kantorId) show = false;
if (divisiId && itemDivisi !== divisiId) show = false;
if (show) {
item.classList.remove('hidden');
visibleCount++;
} else {
item.classList.add('hidden');
}
});
if (visibleCount === 0) {
pegawaiContainer.classList.add('hidden');
emptyState.classList.remove('hidden');
} else {
pegawaiContainer.classList.remove('hidden');
emptyState.classList.add('hidden');
}
}
selectAllCheckbox.addEventListener('change', function () {
const visibleCheckboxes = document.querySelectorAll('.pegawai-item:not(.hidden) .user-checkbox');
visibleCheckboxes.forEach(cb => cb.checked = this.checked);
updateCount();
});
if(filterKantor) filterKantor.addEventListener('change', filterPegawai);
if(filterDivisi) filterDivisi.addEventListener('change', filterPegawai);
if(pegawaiContainer) {
pegawaiContainer.addEventListener('change', function (e) {
if (e.target.classList.contains('user-checkbox')) {
updateCount();
}
});
}
})();
</script>
@endsection