Initial commit

This commit is contained in:
root
2026-05-26 08:07:45 +00:00
commit a234e55e64
4263 changed files with 855927 additions and 0 deletions
+215
View File
@@ -0,0 +1,215 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5 class="mb-0">Chart of Account (COA)</h5>
<button class="btn btn-warning btn-add">Tambah Akun</button>
</div>
<div class="table-responsive">
<table id="tableAccounts" class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th width="5%">No</th>
<th>Kode</th>
<th>Nama</th>
<th>Tipe</th>
<th>Posisi</th>
<th>Kategori</th>
<th width="15%">Aksi</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalAccount">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-4">
<div class="modal-header bg-warning text-white">
<h5 class="modal-title">Tambah Akun</h5>
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="inputId">
<label class="fw-semibold">Kode Akun</label>
<input type="text" class="form-control" id="kode_akun">
<label class="fw-semibold mt-2">Nama Akun</label>
<input type="text" class="form-control" id="nama_akun">
<label class="fw-semibold mt-2">Tipe</label>
<select class="form-control" id="tipe">
<option value="asset">Asset</option>
<option value="liability">Liability</option>
<option value="equity">Equity</option>
<option value="revenue">Revenue</option>
<option value="expense">Expense</option>
</select>
<label class="fw-semibold mt-2">Parent</label>
<select class="form-control" id="parent_id">
<option value="">-- Tanpa Parent --</option>
<?php foreach($parent_accounts as $p): ?>
<option value="<?= $p->id ?>">
<?= $p->kode_akun ?> - <?= $p->nama_akun ?>
</option>
<?php endforeach; ?>
</select>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button class="btn btn-warning" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
let table = $('#tableAccounts').DataTable({
processing: true,
serverSide: true,
responsive: true,
autoWidth: false,
order: [],
ajax: {
url: "<?= base_url('accounts/get_data'); ?>",
type: "POST"
},
columns: [
{ data: 0, orderable:false },
{ data: 1 },
{ data: 2 },
{ data: 3 },
{ data: 4 },
{ data: 5 },
{ data: 6, orderable:false }
],
language: {
processing: `<div class="text-center"><div class="spinner-border text-warning"></div></div>`
}
});
// ================= RESET FORM
function resetForm(){
$('#inputId').val('');
$('#kode_akun').val('');
$('#nama_akun').val('');
$('#tipe').val('asset');
$('#parent_id').val('');
$('#modalAccount .modal-title').text('Tambah Akun');
}
// ================= OPEN MODAL
$('.btn-add').click(function(){
resetForm();
$('#btnSimpan').data('action','add');
$('#modalAccount').modal('show');
});
// ================= SAVE / UPDATE
$('#btnSimpan').click(function(){
let action = $(this).data('action');
let data = {
id: $('#inputId').val(),
kode_akun: $('#kode_akun').val().trim(),
nama_akun: $('#nama_akun').val().trim(),
tipe: $('#tipe').val(),
parent_id: $('#parent_id').val()
};
if(!data.kode_akun || !data.nama_akun){
Swal.fire('Warning','Kode & Nama wajib diisi','warning');
return;
}
let url = action === 'add'
? "<?= base_url('accounts/save'); ?>"
: "<?= base_url('accounts/update'); ?>";
$.ajax({
url: url,
type: 'POST',
data: data,
dataType: 'json',
success: function(res){
if(res.status){
$('#modalAccount').modal('hide');
table.ajax.reload(null,false);
Swal.fire('Sukses', res.message, 'success');
} else {
Swal.fire('Error', res.message, 'error');
}
},
error: function(){
Swal.fire('Error','Terjadi kesalahan server','error');
}
});
});
// ================= EDIT
$(document).on('click','.btn-edit',function(){
let id = $(this).data('id');
$.get("<?= base_url('accounts/detail/'); ?>" + id, function(res){
$('#inputId').val(res.id);
$('#kode_akun').val(res.kode_akun);
$('#nama_akun').val(res.nama_akun);
$('#tipe').val(res.tipe);
$('#parent_id').val(res.parent_id);
$('#modalAccount .modal-title').text('Edit Akun');
$('#btnSimpan').data('action','edit');
$('#modalAccount').modal('show');
},'json');
});
// ================= DELETE
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
Swal.fire({
title: 'Yakin hapus?',
text: 'Data tidak bisa dikembalikan!',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus'
}).then((result)=>{
if(result.isConfirmed){
$.get("<?= base_url('accounts/delete/'); ?>" + id, function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire('Sukses', res.message, 'success');
} else {
Swal.fire('Error', res.message, 'error');
}
},'json');
}
});
});
});
</script>
+527
View File
@@ -0,0 +1,527 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5>Data Asset</h5>
<div class="text-end">
<button class="btn btn-add bg-success btn-from-stock mr-2">Dari Gudang</button>
<button class="btn btn-add bg-warning btn-add-asset">Tambah Asset</button>
</div>
</div>
<div class="table-responsive">
<table id="tableAsset" class="table modern-table w-100">
<thead>
<tr>
<th>No</th>
<th>Tanggal</th>
<th>Kode</th>
<th>Nama</th>
<th>Lokasi Asset</th>
<th>Nilai</th>
<th>Masa</th>
<th>Susut/Bulan</th>
<th>Create</th>
<th>Aksi</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL TAMBAH ASSET -->
<div class="modal fade" id="modalAsset">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5 id="modalTitleAsset">Tambah Asset</h5>
</div>
<div class="modal-body">
<input type="hidden" id="id_asset">
<label class="mt-2">Akun Asset (Debit)</label>
<select id="account_asset" class="form-control"></select>
<label class="mt-2">Akun Kas (Kredit)</label>
<select id="account_kas" class="form-control"></select>
<label class="mt-2">Lokasi Aset</label>
<select id="lokasi_asset" class="form-control"></select>
<label>Nama Asset</label>
<input type="text" id="nama_asset" class="form-control">
<label class="mt-2">Keterangan</label>
<input type="text" id="keterangan" class="form-control">
<label class="mt-2">Harga Per Unit</label>
<input type="text" id="harga" class="form-control format-rupiah">
<label class="mt-2">Tanggal</label>
<input type="date" id="tanggal" class="form-control">
<hr>
<label>Masa Manfaat (bulan)</label>
<input type="number" id="masa" class="form-control">
<label>Nilai Residu</label>
<input type="text" id="residu" class="form-control format-rupiah">
<label>Penyusutan / bulan</label>
<input type="text" id="susut" class="form-control format-rupiah" readonly>
</div>
<div class="modal-footer">
<button class="btn btn-warning" id="btnSave">Simpan</button>
</div>
</div>
</div>
</div>
<!-- MODAL EDIT ASSET -->
<div class="modal fade" id="modalEditAsset">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-primary text-white">
<h5>Edit Asset</h5>
</div>
<div class="modal-body">
<input type="hidden" id="edit_id">
<label>Nama Asset</label>
<input type="text" id="edit_nama" class="form-control">
<label class="mt-2">Lokasi Aset</label>
<select id="edit_lokasi_asset" class="form-control"></select>
<label class="mt-2">Keterangan</label>
<input type="text" id="edit_keterangan" class="form-control">
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="btnUpdate">Update</button>
</div>
</div>
</div>
</div>
<!-- MODAL DETAIL & HISTORY -->
<div class="modal fade" id="modalDetail">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-info text-white">
<h5>Detail & History Asset</h5>
</div>
<div class="modal-body">
<div id="detailContent"></div>
<hr>
<h6>History Perubahan:</h6>
<div class="table-responsive">
<table class="table table-sm" id="tableHistory">
<thead>
<tr>
<th>Tanggal</th>
<th>Tipe</th>
<th>Nilai</th>
<th>Keterangan</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- MODAL DARI GUDANG -->
<div class="modal fade" id="modalAssetStock">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-success text-white">
<h5>Tambah Asset dari Gudang</h5>
</div>
<div class="modal-body">
<input type="hidden" id="s_item_id">
<input type="hidden" id="s_harga">
<label class="mt-2">Akun Asset (Debit)</label>
<select id="s_account_asset" class="form-control"></select>
<label class="mt-2">Lokasi Aset</label>
<select id="s_lokasi_asset" class="form-control"></select>
<label>Pilih Barang</label>
<select id="s_item" class="form-control"></select>
<label class="mt-2">Stok Tersedia</label>
<input type="text" id="s_stok" class="form-control" readonly>
<label class="mt-2">Tanggal</label>
<input type="date" id="s_tanggal" class="form-control">
<hr>
<label>Masa Manfaat (bulan)</label>
<input type="number" id="s_masa" class="form-control">
<label>Nilai Residu</label>
<input type="text" id="s_residu" class="form-control format-rupiah">
<label>Penyusutan / bulan</label>
<input type="text" id="s_susut" class="form-control format-rupiah" readonly>
</div>
<div class="modal-footer">
<button class="btn btn-success" id="btnSaveStock">Simpan</button>
</div>
</div>
</div>
</div>
<!-- MODAL DELETE -->
<div class="modal fade" id="modalDelete">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5>Hapus Asset</h5>
</div>
<div class="modal-body">
<input type="hidden" id="delete_id">
<label>Pilih Alasan:</label>
<select id="delete_alasan" class="form-control">
<option value="dijual_untung">Dijual (Untung)</option>
<option value="dijual_rugi">Dijual (Rugi)</option>
<option value="dihapus">Dihapus</option>
</select>
<label class="mt-2">Keterangan:</label>
<textarea id="delete_keterangan" class="form-control" rows="3" placeholder="Alasan detail penghapusan..."></textarea>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button type="button" class="btn btn-danger" id="btnConfirmDelete">Hapus</button>
</div>
</div>
</div>
</div>
<!-- MODAL DARI STOCK (SAMA) -->
<div class="modal fade" id="modalAssetStock">
<!-- sama seperti sebelumnya, tidak diubah -->
</div>
<script>
$(function(){
let action = 'add';
let table = $('#tableAsset').DataTable({
ajax:"<?= base_url('asset/get_data'); ?>"
});
// ================= HELPER =================
function formatRupiah(angka){
if(!angka) return '';
return angka.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ".");
}
function setSelectValue(selector, value){
setTimeout(()=>{
$(selector).val(value).trigger('change');
}, 200);
}
// ================= LOAD SELECTS =================
function loadKas(selected = null){
$.get("<?= base_url('asset/get_accounts_kas'); ?>",res=>{
let opt='<option value="">-- PILIH KAS --</option>';
res.forEach(i=>{
let sel = (selected && selected == i.id) ? 'selected' : '';
opt += `<option value="${i.id}" ${sel}>${i.nama_akun}</option>`;
});
$('#account_kas').html(opt);
},'json');
}
function loadAsset(selected = null){
$.get("<?= base_url('asset/get_accounts_asset'); ?>",res=>{
let optAsset='<option value="">-- PILIH AKUN ASET --</option>';
res.forEach(i=>{
let sel = (selected && selected == i.id) ? 'selected' : '';
optAsset += `<option value="${i.id}" ${sel}>${i.nama_akun}</option>`;
});
$('#account_asset, #s_account_asset').html(optAsset);
},'json');
}
function loadLokasiAsset(selected = null){
$.get("<?= base_url('asset/get_lokasi_asset'); ?>",res=>{
let opt='<option value="">-- PILIH LOKASI --</option>';
res.forEach(i=>{
let sel = (selected && selected == i.id) ? 'selected' : '';
opt += `<option value="${i.id}" ${sel}>${i.nama}</option>`;
});
$('#lokasi_asset, #edit_lokasi_asset, #s_lokasi_asset').html(opt);
},'json');
}
// ================= LOAD ITEMS =================
function loadItems(){
$.get("<?= base_url('asset/get_items'); ?>",res=>{
let opt='<option value="">-- PILIH BARANG --</option>';
res.forEach(i=>{
opt += `<option value="${i.id}" data-harga="${i.harga_beli}" data-stok="${i.stok}" data-nama="${i.nama_barang}">
${i.kode_detail} - ${i.nama_barang} - ( Stok: ${i.stok})
</option>`;
});
$('#s_item').html(opt);
},'json');
}
// ================= HITUNG PENYUSUTAN MANUAL =================
function hitungSusut(){
let harga = parseInt($('#harga').val().replace(/\D/g,'')) || 0;
let residu = parseInt($('#residu').val().replace(/\D/g,'')) || 0;
let masa = parseInt($('#masa').val()) || 1;
let susut = (harga - residu) / masa;
$('#susut').val(formatRupiah(Math.round(susut)));
}
$('#masa, #residu, #harga').on('keyup change',hitungSusut);
// ================= HITUNG SUSUT STOCK =================
$('#s_masa, #s_residu').on('keyup change',function(){
let harga = parseInt($('#s_harga').val()) || 0;
let residu = parseInt($('#s_residu').val().replace(/\D/g,'')) || 0;
let masa = parseInt($('#s_masa').val()) || 1;
let susut = (harga - residu) / masa;
$('#s_susut').val(formatRupiah(Math.round(susut)));
});
// ================= TAMBAH ASSET MANUAL =================
$('.btn-add-asset').click(function(){
action='add';
$('#modalTitleAsset').text('Tambah Asset');
$('#modalAsset input[type="text"], #modalAsset input[type="date"], #modalAsset input[type="number"], #modalAsset select').val('');
$('#id_asset').val('');
loadKas(); loadAsset(); loadLokasiAsset();
$('#modalAsset').modal('show');
});
// ================= DETAIL & HISTORY =================
$(document).on('click','.btn-detail',function(){
let id = $(this).data('id');
$.get("<?= base_url('asset/detail/'); ?>"+id, function(asset){
$('#detailContent').html(`
<table class="table table-borderless">
<tr><td><strong>Kode Asset</strong></td><td>: <strong>${asset.kode_asset || '-'}</strong></td></tr>
<tr><td><strong>Nama Asset</strong></td><td>: <strong>${asset.nama_asset}</strong></td></tr>
<tr><td><strong>Lokasi</strong></td><td>: <strong>${asset.nama_lokasi || '-'}</strong></td></tr>
<tr><td><strong>Harga/Unit</strong></td><td>: <strong>Rp ${formatRupiah(asset.harga_per_unit)}</strong></td></tr>
<tr><td><strong>Nilai Buku</strong></td><td>: <strong>Rp ${formatRupiah(asset.nilai_buku)}</strong></td></tr>
<tr><td><strong>Masa Manfaat</strong></td><td>: <strong>${asset.masa_manfaat} bulan</strong></td></tr>
<tr><td><strong>Sumber</strong></td><td>: <strong>${asset.sumber}</strong></td></tr>
<tr><td><strong>Keterangan</strong></td><td>: <strong>${asset.keterangan || '-'}</strong></td></tr>
</table>
`);
// Load history
$.get("<?= base_url('asset/get_history/'); ?>"+id, function(history){
let rows = '';
if(history.length === 0){
rows = '<tr><td colspan="5" class="text-center">Belum ada history</td></tr>';
}else{
history.forEach(h=>{
let badgeClass = h.tipe=='perolehan' ? 'bg-success' :
h.tipe=='penambahan' ? 'bg-info' :
h.tipe=='pengurangan' ? 'bg-warning' : 'bg-secondary';
rows += `<tr>
<td>${h.created_at}</td>
<td><span class="badge ${badgeClass}">${h.tipe}</span></td>
<td>Rp ${formatRupiah(h.nilai || 0)}</td>
<td>${h.keterangan}</td>
</tr>`;
});
}
$('#tableHistory tbody').html(rows);
},'json');
$('#modalDetail').modal('show');
},'json');
});
// ================= EDIT ASSET =================
$(document).on('click','.btn-edit',function(){
let id = $(this).data('id');
$.get("<?= base_url('asset/detail/'); ?>"+id,function(res){
$('#edit_id').val(res.id);
$('#edit_nama').val(res.nama_asset);
$('#edit_keterangan').val(res.keterangan || '');
loadLokasiAsset(res.lokasi_asset_id);
$('#modalEditAsset').modal('show');
},'json');
});
// ================= UPDATE ASSET =================
$('#btnUpdate').click(function(){
let btn = $(this);
let data = {
id: $('#edit_id').val(),
nama_asset: $('#edit_nama').val(),
lokasi_asset: $('#edit_lokasi_asset').val(),
keterangan: $('#edit_keterangan').val()
};
if(!data.nama_asset){
Swal.fire('Error','Nama asset wajib diisi','error');
return;
}
btn.prop('disabled', true).html('Menyimpan...');
$.post("<?= base_url('asset/update'); ?>", data, function(res){
if(res.status){
$('#modalEditAsset').modal('hide');
table.ajax.reload(null,false);
Swal.fire('Berhasil','Asset berhasil diupdate','success');
}
},'json').always(()=>{
btn.prop('disabled', false).html('Update');
});
});
// ================= DELETE =================
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
$('#delete_id').val(id);
$('#delete_keterangan').val('');
$('#modalDelete').modal('show');
});
$('#btnConfirmDelete').click(function(){
let btn = $(this);
let keterangan = $('#delete_keterangan').val().trim();
if(!keterangan){
Swal.fire('Error','Keterangan wajib diisi','error');
return;
}
let data = {
id: $('#delete_id').val(),
alasan: $('#delete_alasan').val(),
keterangan: keterangan
};
btn.prop('disabled', true).html('Menghapus...');
$.post("<?= base_url('asset/delete'); ?>", data, function(res){
if(res.status){
$('#modalDelete').modal('hide');
table.ajax.reload(null,false);
Swal.fire('Berhasil','Asset berhasil dihapus','success');
}
},'json').always(()=>{
btn.prop('disabled', false).html('Hapus');
});
});
// ================= SAVE ASSET MANUAL =================
$('#btnSave').click(function(){
let btn = $(this);
let data = {
id: $('#id_asset').val(),
nama_asset: $('#nama_asset').val(),
harga: $('#harga').val(),
tanggal: $('#tanggal').val(),
masa: $('#masa').val(),
residu: $('#residu').val(),
account_kas: $('#account_kas').val(),
account_asset: $('#account_asset').val(),
lokasi_asset: $('#lokasi_asset').val(),
keterangan: $('#keterangan').val()
};
// Validasi
if(!data.nama_asset || !data.harga || !data.tanggal || !data.account_kas || !data.account_asset){
Swal.fire('Error','Semua field wajib diisi','error');
return;
}
btn.prop('disabled', true).html('Menyimpan...');
let url = action==='add' ? "<?= base_url('asset/save'); ?>" : "<?= base_url('asset/update'); ?>";
$.post(url, data, function(res){
if(res.status){
$('#modalAsset').modal('hide');
table.ajax.reload(null,false);
Swal.fire({icon:'success',title:'Berhasil',timer:1500,showConfirmButton:false});
}
},'json').always(()=>{
btn.prop('disabled', false).html('Simpan');
});
});
// ================= FROM STOCK =================
$('.btn-from-stock').click(function(){
$('#modalAssetStock input[type="text"], #modalAssetStock input[type="date"], #modalAssetStock input[type="number"], #modalAssetStock select').val('');
$('#s_item_id, #s_harga').val('');
loadItems();
loadAsset();
loadLokasiAsset();
$('#modalAssetStock').modal('show');
});
// ================= PILIH ITEM STOCK =================
$(document).on('change','#s_item',function(){
let selected = $(this).find(':selected');
let harga = selected.data('harga') || 0;
let stok = selected.data('stok') || 0;
$('#s_item_id').val($(this).val());
$('#s_harga').val(harga);
$('#s_stok').val(stok);
});
// ================= SAVE FROM STOCK =================
$('#btnSaveStock').click(function(){
let btn = $(this);
let data = {
item_id: $('#s_item').val(),
s_tanggal: $('#s_tanggal').val(),
s_masa: $('#s_masa').val(),
s_residu: $('#s_residu').val(),
s_lokasi_asset: $('#s_lokasi_asset').val(),
account_asset: $('#s_account_asset').val()
};
// Validasi
if(!data.item_id || !data.s_tanggal || !data.account_asset || !data.s_lokasi_asset){
Swal.fire('Error','Item, tanggal, akun asset, dan lokasi wajib diisi','error');
return;
}
btn.prop('disabled', true).html('Menyimpan...');
$.post("<?= base_url('asset/save_from_stock'); ?>",data,function(res){
if(res.status){
$('#modalAssetStock').modal('hide');
table.ajax.reload(null,false);
Swal.fire({
icon:'success',
title:'Berhasil!',
text:'Asset dari gudang berhasil dibuat',
timer:2000,
showConfirmButton:false
});
}else{
Swal.fire('Error',res.message,'error');
}
},'json').always(()=>{
btn.prop('disabled', false).html('Simpan');
});
});
});
</script>
+146
View File
@@ -0,0 +1,146 @@
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Accounting</title>
<link rel="icon" href="<?= base_url('assets/img/accounting-color.png') ?>" type="image/png">
<!-- Assets from Theme -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<!-- Make sure the path is correct in a real scenario -->
<link href="<?= base_url('assets/css/style-custom.css'); ?>" rel="stylesheet">
<style>
:root {
--orange-main: #ff8c00;
--orange-dark: #e67700;
--orange-soft: rgba(255, 140, 0, 0.15);
}
body {
background-color: #fff8f1;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
}
.login-card {
width: 100%;
max-width: 450px;
border-radius: 16px;
padding: 2.5rem;
}
.login-title {
color: var(--orange-dark);
font-weight: 700;
font-size: 1.8rem;
text-align: center;
margin-bottom: 0.5rem;
}
.login-subtitle {
text-align: center;
color: #6c757d;
margin-bottom: 2.5rem;
}
.form-control {
border-radius: 12px;
padding: 0.8rem 1rem;
border: 1px solid #ced4da;
transition: 0.2s ease-in-out;
}
.form-control:focus {
border-color: var(--orange-main);
box-shadow: 0 0 0 0.25rem var(--orange-soft);
}
.btn-login {
width: 100%;
padding: 0.8rem;
font-size: 1rem;
font-weight: 600;
border-radius: 12px;
background: var(--orange-main);
border: none;
color: #fff;
transition: 0.2s ease;
}
.btn-login:hover {
background: var(--orange-dark);
}
</style>
</head>
<body>
<div class="card modern-card login-card shadow-lg">
<div class="card-body">
<center style="margin-bottom: 20px;">
<img src="<?= base_url('assets/img/accounting-color.png'); ?>" width="150px">
</center>
<hr>
<p class="login-subtitle">Silakan login untuk melanjutkan</p>
<?php if (!empty($error)): ?>
<div class="alert alert-danger" role="alert">
<?= $error; ?>
</div>
<?php endif; ?>
<form action="<?= base_url('auth/process_login'); ?>" method="post">
<div class="mb-3">
<label for="username" class="form-label fw-semibold">Username</label>
<div class="input-group">
<span class="input-group-text bg-light border-end-0"><i class="bi bi-person"></i></span>
<input type="text" class="form-control border-start-0" id="username" name="username" placeholder="Masukkan username Anda" required>
</div>
</div>
<div class="mb-4">
<label for="password" class="form-label fw-semibold">Password</label>
<div class="input-group">
<span class="input-group-text bg-light border-end-0"><i class="bi bi-lock"></i></span>
<input type="password" class="form-control border-start-0" id="password" name="password" placeholder="Masukkan password Anda" required>
</div>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-premium btn-login">Login</button>
</div>
</form>
</div>
</div>
<!-- Scripts from Theme -->
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
</body>
</html>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<?php if($this->session->flashdata('error')): ?>
<script>
Swal.fire({
icon: 'error',
title: 'Login Gagal',
text: '<?= $this->session->flashdata('error'); ?>',
confirmButtonText: 'Coba Lagi'
});
</script>
<?php endif; ?>
+339
View File
@@ -0,0 +1,339 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5 class="mb-0">Base Jurnal</h5>
<button class="btn btn-warning btn-add">Tambah Template</button>
</div>
<div class="table-responsive">
<table id="tableJurnal" class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th width="5%">No</th>
<th>Kode</th>
<th>Nama</th>
<th>Deskripsi</th>
<th>Status</th>
<th width="15%">Aksi</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalJurnal">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content rounded-4">
<div class="modal-header bg-warning text-white">
<h5 class="modal-title">Tambah Base Jurnal</h5>
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="inputId">
<!-- HEADER -->
<div class="row">
<div class="col-md-4">
<label>Kode</label>
<input type="text" class="form-control" id="kode">
</div>
<div class="col-md-8">
<label>Nama</label>
<input type="text" class="form-control" id="nama">
</div>
</div>
<label class="mt-2">Deskripsi</label>
<textarea class="form-control" id="deskripsi"></textarea>
<hr>
<!-- DETAIL -->
<div class="d-flex justify-content-between mb-2">
<strong>Detail Akun</strong>
<button type="button" class="btn btn-sm btn-primary" id="btnAddRow">
+ Tambah Baris
</button>
</div>
<table class="table table-bordered" id="tableDetail">
<thead>
<tr>
<th>Akun</th>
<th width="150">Posisi</th>
<th width="80">Aksi</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button class="btn btn-warning" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
// ================= DATATABLE
let table = $('#tableJurnal').DataTable({
processing: true,
serverSide: true,
ajax: {
url: "<?= base_url('basejurnal/get_data'); ?>",
type: "POST"
}
});
// ================= GLOBAL STATE
let accountList = [];
let isAccountLoaded = false;
// ================= LOAD ACCOUNT (PROMISE)
function loadAccounts(){
return new Promise((resolve, reject) => {
if(isAccountLoaded){
resolve();
return;
}
$.get("<?= base_url('basejurnal/get_list_account'); ?>", function(res){
accountList = res;
isAccountLoaded = true;
resolve();
}, 'json').fail(function(){
reject();
});
});
}
// ================= RENDER OPTION
function renderAccountOption(selected=null){
let html = `<option value="">-- Pilih Akun --</option>`;
accountList.forEach(a=>{
html += `<option value="${a.id}" ${selected==a.id?'selected':''}>
${a.kode_akun} - ${a.nama_akun}
</option>`;
});
return html;
}
// ================= ADD ROW
function addRow(data=null){
let row = `
<tr>
<td>
<select class="form-control account_id">
${renderAccountOption(data?.account_id)}
</select>
</td>
<td>
<select class="form-control posisi">
<option value="debit" ${data?.posisi=='debit'?'selected':''}>Debit</option>
<option value="kredit" ${data?.posisi=='kredit'?'selected':''}>Kredit</option>
</select>
</td>
<td class="text-center">
<button class="btn btn-danger btn-sm btn-remove">X</button>
</td>
</tr>
`;
$('#tableDetail tbody').append(row);
}
// ================= RESET FORM
function resetForm(){
$('#inputId, #kode, #nama, #deskripsi').val('');
$('#tableDetail tbody').html('');
}
// ================= OPEN MODAL ADD
$('.btn-add').click(async function(){
resetForm();
try {
await loadAccounts();
addRow();
} catch(e){
Swal.fire('Error','Gagal load data akun','error');
return;
}
$('#btnSimpan').data('action','add');
$('#modalJurnal .modal-title').text('Tambah Base Jurnal');
$('#modalJurnal').modal('show');
});
// ================= ADD ROW BUTTON
$('#btnAddRow').click(async function(){
if(!isAccountLoaded){
await loadAccounts();
}
addRow();
});
// ================= REMOVE ROW
$(document).on('click','.btn-remove',function(){
$(this).closest('tr').remove();
});
// ================= SAVE / UPDATE
$('#btnSimpan').click(function(){
let action = $(this).data('action');
let accounts = [];
let posisi = [];
$('#tableDetail tbody tr').each(function(){
let acc = $(this).find('.account_id').val();
let pos = $(this).find('.posisi').val();
if(acc){ // skip kosong
accounts.push(acc);
posisi.push(pos ? pos : 'debit');
}
});
let data = {
id: $('#inputId').val(),
kode: $('#kode').val().trim(),
nama: $('#nama').val().trim(),
deskripsi: $('#deskripsi').val().trim(),
account_id: accounts,
posisi: posisi
};
// VALIDASI
if(!data.kode || !data.nama){
Swal.fire('Warning','Kode & Nama wajib diisi','warning');
return;
}
if(accounts.length === 0){
Swal.fire('Warning','Minimal 1 akun harus dipilih','warning');
return;
}
let url = action === 'add'
? "<?= base_url('basejurnal/save'); ?>"
: "<?= base_url('basejurnal/update'); ?>";
$.ajax({
url: url,
type: 'POST',
data: data,
dataType: 'json',
success: function(res){
if(res.status){
$('#modalJurnal').modal('hide');
table.ajax.reload(null,false);
Swal.fire('Sukses', res.message, 'success');
} else {
Swal.fire('Error', res.message, 'error');
}
},
error: function(xhr){
console.log(xhr.responseText);
Swal.fire('Error','Terjadi kesalahan server','error');
}
});
});
// ================= EDIT
$(document).on('click','.btn-edit', async function(){
let id = $(this).data('id');
resetForm();
try {
await loadAccounts();
} catch(e){
Swal.fire('Error','Gagal load akun','error');
return;
}
$.get("<?= base_url('basejurnal/detail/'); ?>" + id, function(res){
$('#inputId').val(res.header.id);
$('#kode').val(res.header.kode);
$('#nama').val(res.header.nama);
$('#deskripsi').val(res.header.deskripsi);
$('#tableDetail tbody').html('');
if(res.detail.length > 0){
res.detail.forEach(d=>{
addRow(d);
});
} else {
addRow();
}
$('#btnSimpan').data('action','edit');
$('#modalJurnal .modal-title').text('Edit Base Jurnal');
$('#modalJurnal').modal('show');
}, 'json');
});
// ================= DELETE
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
Swal.fire({
title: 'Hapus data?',
text: 'Data tidak bisa dikembalikan!',
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus'
}).then((result)=>{
if(result.isConfirmed){
$.ajax({
url: "<?= base_url('basejurnal/delete/'); ?>" + id,
type: 'GET',
dataType: 'json',
success: function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire('Sukses', res.message, 'success');
} else {
Swal.fire('Error', res.message, 'error');
}
},
error: function(xhr){
console.log(xhr.responseText);
Swal.fire('Error','Terjadi kesalahan server','error');
}
});
}
});
});
});
</script>
+101
View File
@@ -0,0 +1,101 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5>Buku Besar</h5>
</div>
<div class="row mb-3">
<div class="col-md-4">
<label>Akun</label>
<select id="account_id" class="form-control select-search"></select>
</div>
<div class="col-md-3">
<label>Dari</label>
<input type="date" id="start_date" class="form-control">
</div>
<div class="col-md-3">
<label>Sampai</label>
<input type="date" id="end_date" class="form-control">
</div>
<div class="col-md-2 d-flex align-items-end">
<button class="btn btn-primary w-100" id="btnFilter">Filter</button>
</div>
</div>
<div class="table-responsive">
<table id="tableLedger" class="table modern-table align-middle w-100">
<thead>
<tr>
<th>Tanggal</th>
<th>No Ref</th>
<th>Keterangan</th>
<th>Debit</th>
<th>Kredit</th>
<th>Saldo</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<script>
$(function(){
// LOAD ACCOUNTS
$.get("<?= base_url('bukubesar/get_accounts'); ?>", function(res){
let opt = '';
res.forEach(a=>{
opt += `<option value="${a.id}">${a.kode_akun} - ${a.nama_akun}</option>`;
});
$('#account_id').html(opt);
},'json');
function loadData(){
let account_id = $('#account_id').val();
let start = $('#start_date').val();
let end = $('#end_date').val();
$.get("<?= base_url('bukubesar/get_data'); ?>", {
account_id: account_id,
start_date: start,
end_date: end
}, function(res){
let html = '';
res.forEach(r=>{
html += `
<tr>
<td>${r.tanggal}</td>
<td>${r.no_ref}</td>
<td>${r.keterangan}</td>
<td class="text-success">${parseFloat(r.debit).toLocaleString()}</td>
<td class="text-danger">${parseFloat(r.kredit).toLocaleString()}</td>
<td class="fw-bold">${parseFloat(r.saldo).toLocaleString()}</td>
</tr>
`;
});
$('#tableLedger tbody').html(html);
},'json');
}
// FILTER
$('#btnFilter').click(function(){
loadData();
});
});
</script>
+68
View File
@@ -0,0 +1,68 @@
<style>
.atm-card {
position: relative;
margin: auto;
background-size: cover;
background-position: center;
border-radius: 18px;
overflow: hidden;
box-shadow: 0 10px 25px rgba(0,0,0,0.2);
}
.text-block {
position: absolute;
color: white;
font-family: Arial, sans-serif;
}
/* Dinamis berdasarkan orientasi */
<?php if ($orientation == 'landscape'): ?>
.atm-card {
width: <?= $width ?>px;
height: <?= $height ?>px;
}
.text-block {
bottom: 40px;
left: 50px;
}
.text-block h2 { font-size: 48px; margin: 0; }
.text-block p { font-size: 30px; margin: 2px 0; }
<?php else: // portrait ?>
.atm-card {
width: <?= $width ?>px;
height: <?= $height ?>px;
}
.text-block {
bottom: 30px;
left: 40px;
}
.text-block h2 { font-size: 40px; margin: 0; }
.text-block p { font-size: 26px; margin: 2px 0; }
<?php endif; ?>
.btn-area {
margin-top: 20px;
text-align: center;
}
.btn-print {
padding: 10px 25px;
background: #28a745;
color: white;
border: none;
border-radius: 7px;
cursor: pointer;
}
</style>
<div class="atm-card" style="background-image: url('<?= $background ?>')">
<div class="text-block">
<h2><?= $nama ?></h2>
<p>NIS: <?= $nis ?></p>
<p>Kelas: <?= $kelas ?></p>
</div>
</div>
<div class="btn-area">
<button class="btn-print" onclick="window.print()">Print</button>
</div>
+165
View File
@@ -0,0 +1,165 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5>Data Customers</h5>
<button class="btn btn-warning btn-add">Tambah Customer</button>
</div>
<div class="table-responsive">
<table id="tableCustomers" class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th>No</th>
<th>Nama</th>
<th>Alamat</th>
<th>Telp</th>
<th>Email</th>
<th>Aksi</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalCustomer">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5 class="modal-title">Customer</h5>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="id">
<label>Nama</label>
<input type="text" id="nama" class="form-control">
<label class="mt-2">Alamat</label>
<textarea id="alamat" class="form-control"></textarea>
<label class="mt-2">Telp</label>
<input type="text" id="telp" class="form-control">
<label class="mt-2">Email</label>
<input type="email" id="email" class="form-control">
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button class="btn btn-warning" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<!-- CARA MANGGIL TABLE PERTAMA -->
<script>
$(function(){
let action = 'add';
let table = $('#tableCustomers').DataTable({
processing:true,
serverSide:true,
order:[[1,'asc']],
ajax:{
url:"<?= base_url('customers/get_data'); ?>",
type:"POST"
}
});
function resetForm(){
$('#id').val('');
$('#nama').val('');
$('#alamat').val('');
$('#telp').val('');
$('#email').val('');
}
// ADD
$('.btn-add').click(function(){
resetForm();
action = 'add';
$('#modalCustomer').modal('show');
});
// EDIT
$(document).on('click','.btn-edit',function(){
let id = $(this).data('id');
$.get("<?= base_url('customers/detail/'); ?>"+id,function(res){
$('#id').val(res.id);
$('#nama').val(res.nama);
$('#alamat').val(res.alamat);
$('#telp').val(res.telp);
$('#email').val(res.email);
action = 'edit';
$('#modalCustomer').modal('show');
},'json');
});
// SAVE
$('#btnSimpan').click(function(){
let data = {
id: $('#id').val(),
nama: $('#nama').val(),
alamat: $('#alamat').val(),
telp: $('#telp').val(),
email: $('#email').val()
};
if(!data.nama){
Swal.fire('Warning','Nama wajib diisi','warning');
return;
}
let url = action === 'add'
? "<?= base_url('customers/save'); ?>"
: "<?= base_url('customers/update'); ?>";
$.post(url,data,function(res){
if(res.status){
$('#modalCustomer').modal('hide');
table.ajax.reload(null,false);
Swal.fire('Sukses',res.message,'success');
} else {
Swal.fire('Error',res.message,'error');
}
},'json');
});
// DELETE
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
Swal.fire({
title:'Hapus data?',
icon:'warning',
showCancelButton:true
}).then(r=>{
if(r.isConfirmed){
$.get("<?= base_url('customers/delete/'); ?>"+id,function(res){
table.ajax.reload(null,false);
Swal.fire('Sukses',res.message,'success');
},'json');
}
});
});
});
</script>
@@ -0,0 +1,102 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5>Activity Log</h5>
<button class="btn btn-secondary btn-sm" id="btnReload">
Refresh
</button>
</div>
<div class="table-responsive">
<table id="tableActivity" class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th>Waktu</th>
<th>User</th>
<th>Module</th>
<th>Action</th>
<th>Deskripsi</th>
<th>Status</th>
<th>IP</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<script>
$(function () {
let table = $('#tableActivity').DataTable({
processing: true,
serverSide: true,
ajax: {
url: "<?= base_url('dashboard/get_activity_datatable'); ?>",
type: "POST"
},
order: [[0, 'desc']],
columns: [
{ data: 'created_at' },
{ data: 'nama' },
{ data: 'module' },
{ data: 'action' },
{ data: 'description' },
{ data: 'status' },
{ data: 'ip_address' }
],
columnDefs: [
// ================= MODULE =================
{
targets: 2,
render: function (data) {
return `<span class="badge bg-light text-dark border">${data ?? '-'}</span>`;
}
},
// ================= ACTION =================
{
targets: 3,
render: function (data) {
return `<span class="text-muted">${data ?? '-'}</span>`;
}
},
// ================= STATUS =================
{
targets: 5,
render: function (data) {
let cls = "text-muted";
if (data === "success") cls = "text-success";
if (data === "error") cls = "text-danger";
if (data === "warning") cls = "text-warning";
return `<span class="${cls}">${data ?? '-'}</span>`;
}
},
// ================= IP =================
{
targets: 6,
render: function (data) {
return `<small class="text-muted">${data ?? '-'}</small>`;
}
}
]
});
$('#btnReload').click(function () {
table.ajax.reload(null, false);
});
});
</script>
+849
View File
@@ -0,0 +1,849 @@
<?php $role = $this->session->userdata('role'); ?>
<style>
/* ======================================================
DASHBOARD CLEAN - Orange Theme (Minimal & Elegant)
====================================================== */
:root {
/* Orange Theme Colors */
--orange-primary: #ff8c00;
--orange-light: #ffd4a3;
--orange-dark: #e67700;
--orange-gradient: linear-gradient(135deg, #ff8c00 0%, #ff7b00 50%, #e67700 100%);
/* Neutral Colors */
--white: #ffffff;
--light-bg: #f8fafc;
--border-light: #e2e8f0;
--text-dark: #1e293b;
--text-muted: #64748b;
/* Shadows - Subtle */
--shadow-sm: 0 2px 8px rgba(0,0,0,0.06);
--shadow-md: 0 8px 24px rgba(0,0,0,0.08);
--shadow-lg: 0 16px 40px rgba(0,0,0,0.10);
}
/* Modern Card - Clean & Minimal */
.modern-card {
background: var(--white);
border: 1px solid var(--border-light);
border-radius: 16px;
box-shadow: var(--shadow-sm);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.modern-card::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 3px;
background: var(--orange-gradient);
transform: scaleX(0);
transition: transform 0.3s ease;
}
.modern-card:hover {
box-shadow: var(--shadow-lg);
transform: translateY(-2px);
}
.modern-card:hover::before {
transform: scaleX(1);
}
/* ======================================================
SUMMARY CARDS - Orange Accents
====================================================== */
.summary-card {
background: var(--white);
border: 1px solid var(--border-light);
border-radius: 16px;
padding: 24px;
transition: all 0.3s ease;
cursor: pointer;
position: relative;
}
.summary-card:hover {
box-shadow: var(--shadow-md);
transform: translateY(-4px);
}
.summary-icon {
width: 60px;
height: 60px;
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
font-size: 22px;
color: var(--white);
flex-shrink: 0;
box-shadow: 0 6px 20px rgba(255,140,0,0.3);
transition: all 0.3s ease;
}
/* Orange-based icon colors - subtle variations */
.summary-card:nth-child(1) .summary-icon { background: var(--orange-gradient); }
.summary-card:nth-child(2) .summary-icon {
background: linear-gradient(135deg, var(--orange-light) 0%, var(--orange-primary) 100%);
}
.summary-card:nth-child(3) .summary-icon {
background: linear-gradient(135deg, #ff9500 0%, var(--orange-primary) 100%);
}
.summary-card:nth-child(4) .summary-icon {
background: linear-gradient(135deg, var(--orange-dark) 0%, #d66a00 100%);
}
.summary-card:nth-child(5) .summary-icon {
background: linear-gradient(135deg, #ffb366 0%, var(--orange-primary) 100%);
}
.summary-card:nth-child(6) .summary-icon {
background: linear-gradient(135deg, #ffcc80 0%, #ffad42 100%);
}
.summary-card:nth-child(7) .summary-icon {
background: linear-gradient(135deg, #ffd180 0%, #ffab40 100%);
}
/* Quick Action Cards */
.quick-action .summary-card {
text-decoration: none;
color: inherit;
transition: all 0.3s ease;
}
.quick-action .summary-card:hover .summary-icon {
transform: scale(1.1);
box-shadow: 0 10px 30px rgba(255,140,0,0.4);
}
/* ======================================================
AI INSIGHT - Clean Cards
====================================================== */
.ai-box {
display: flex;
flex-direction: column;
gap: 12px;
max-height: 340px;
overflow-y: auto;
}
.ai-box::-webkit-scrollbar {
width: 4px;
}
.ai-box::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 10px;
}
.ai-box::-webkit-scrollbar-thumb {
background: var(--orange-light);
border-radius: 10px;
}
.ai-item {
padding: 16px 20px;
border-radius: 12px;
font-size: 14px;
line-height: 1.6;
border-left: 4px solid var(--orange-primary);
background: #fef7ee;
box-shadow: var(--shadow-sm);
transition: all 0.2s ease;
border: 1px solid #fee2c7;
}
.ai-item:hover {
box-shadow: var(--shadow-md);
transform: translateX(4px);
}
.ai-item i {
font-size: 16px;
margin-top: 1px;
flex-shrink: 0;
opacity: 0.9;
color: var(--orange-primary);
margin-right: 12px;
}
.ai-success {
border-left-color: #10b981;
background: #f0fdf4;
border-color: #bbf7d0;
}
.ai-success i {
color: #10b981;
}
.ai-warning {
border-left-color: #f59e0b;
background: #fef3c7;
border-color: #fed7aa;
}
.ai-warning i {
color: #f59e0b;
}
.ai-info {
border-left-color: #3b82f6;
background: #eff6ff;
border-color: #bfdbfe;
}
.ai-info i {
color: #3b82f6;
}
/* ======================================================
TABLE CLEAN & MODERN
====================================================== */
.modern-table-dashboard {
border-radius: 12px;
overflow: hidden;
box-shadow: var(--shadow-sm);
border: none;
background: var(--white);
}
.modern-table-dashboard thead th {
background: linear-gradient(135deg, #fef7ee 0%, #fff8f0 100%);
border: none;
padding: 18px 16px;
font-weight: 600;
color: var(--text-dark);
font-size: 0.95rem;
}
.modern-table-dashboard tbody tr {
transition: all 0.2s ease;
border: none;
}
.modern-table-dashboard tbody tr:hover {
background: rgba(255,140,0,0.04);
}
.modern-table-dashboard td {
padding: 18px 16px;
border-color: #f8fafc;
vertical-align: middle;
}
/* Modern Badges */
.badge {
padding: 8px 16px;
border-radius: 20px;
font-weight: 600;
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.3px;
box-shadow: var(--shadow-sm);
}
/* ======================================================
SUBTLE ANIMATIONS
====================================================== */
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(20px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.modern-card,
.summary-card {
animation: fadeInUp 0.6s ease forwards;
}
.modern-card:nth-child(1) { animation-delay: 0.1s; }
.modern-card:nth-child(2) { animation-delay: 0.15s; }
.modern-card:nth-child(3) { animation-delay: 0.2s; }
.modern-card:nth-child(4) { animation-delay: 0.25s; }
/* ======================================================
RESPONSIVE - Clean Mobile
====================================================== */
@media (max-width: 768px) {
.summary-card {
padding: 20px;
margin-bottom: 20px;
}
.summary-icon {
width: 52px;
height: 52px;
font-size: 20px;
}
.ai-item {
padding: 14px 16px;
font-size: 13.5px;
}
.modern-table-dashboard thead th,
.modern-table-dashboard td {
padding: 14px 12px;
font-size: 0.9rem;
}
.modern-card,
.summary-card {
margin-bottom: 16px;
}
}
/* Button enhancements */
.btn-outline-primary {
border-color: var(--orange-primary);
color: var(--orange-primary);
}
.btn-outline-primary:hover {
background: var(--orange-primary);
border-color: var(--orange-primary);
color: white;
}
/* Chart container */
.chart-container {
height: 300px;
position: relative;
}
</style>
<!-- ───────────────────────── DASHBOARD CONTENT ───────────────────────── -->
<div class="container mt-4 px-3 px-md-4">
<?php if($role == 'Admin'): ?>
<!-- ================= SUMMARY CARDS ================= -->
<div class="row g-4 mb-3">
<div class="col-12 col-md-4">
<div class="summary-card h-100">
<div class="d-flex align-items-center h-100">
<div class="summary-icon"><i class="bi bi-cash-stack"></i></div>
<div class="ms-3 flex-grow-1">
<div class="fw-bold fs-4 lh-1 text-dark">Rp <?= number_format($summary['total_revenue']); ?></div>
<small class="text-muted fw-medium">Total Pemasukan</small>
</div>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="summary-card h-100">
<div class="d-flex align-items-center h-100">
<div class="summary-icon"><i class="bi bi-arrow-down-circle"></i></div>
<div class="ms-3 flex-grow-1">
<div class="fw-bold fs-4 lh-1 text-danger">Rp <?= number_format($summary['total_expense']); ?></div>
<small class="text-muted fw-medium">Total Pengeluaran</small>
</div>
</div>
</div>
</div>
<div class="col-12 col-md-4">
<div class="summary-card h-100">
<div class="d-flex align-items-center h-100">
<div class="summary-icon"><i class="bi bi-graph-up-arrow"></i></div>
<div class="ms-3 flex-grow-1">
<div class="fw-bold fs-4 lh-1 text-success">Rp <?= number_format($summary['net_profit']); ?></div>
<small class="text-muted fw-medium">Keuntungan Bersih</small>
</div>
</div>
</div>
</div>
</div>
<!-- ================= SECOND ROW ================= -->
<div class="row g-4 mb-3">
<!-- ================= NERACA ================= -->
<div class="col-12 col-md-6">
<a href="<?= base_url('neraca'); ?>" class="text-decoration-none">
<div class="summary-card h-100">
<div class="d-flex align-items-center h-100">
<div class="summary-icon">
<i class="bi bi-bar-chart-line-fill"></i>
</div>
<div class="ms-3 flex-grow-1 position-relative">
<div class="fw-bold fs-4 lh-1 text-primary">
Neraca
</div>
<small class="text-muted fw-medium">
Laporan posisi keuangan perusahaan
</small>
<div class="position-absolute bottom-0 end-0 p-3">
<i class="bi bi-arrow-right fs-5 text-secondary opacity-75"></i>
</div>
</div>
</div>
</div>
</a>
</div>
<!-- ================= LABA RUGI ================= -->
<div class="col-12 col-md-6">
<a href="<?= base_url('labarugi'); ?>" class="text-decoration-none">
<div class="summary-card h-100">
<div class="d-flex align-items-center h-100">
<div class="summary-icon">
<i class="bi bi-graph-up-arrow"></i>
</div>
<div class="ms-3 flex-grow-1 position-relative">
<div class="fw-bold fs-4 lh-1 text-success">
Laba Rugi
</div>
<small class="text-muted fw-medium">
Analisis pendapatan dan beban usaha
</small>
<div class="position-absolute bottom-0 end-0 p-3">
<i class="bi bi-arrow-right fs-5 text-secondary opacity-75"></i>
</div>
</div>
</div>
</div>
</a>
</div>
</div>
<!-- ================= QUICK ACTIONS ================= -->
<div class="row g-4 mb-3 quick-action">
<!-- ================= INVOICE ================= -->
<div class="col-md-6 col-sm-6">
<a href="<?= base_url('invoices/draft'); ?>" class="text-decoration-none">
<div class="summary-card h-100 text-center p-4 position-relative overflow-hidden">
<div class="summary-icon mx-auto mb-3 d-inline-block">
<i class="bi bi-receipt"></i>
</div>
<h6 class="mb-2 fw-bold text-dark">Invoice</h6>
<small class="text-muted fw-medium d-block">
Kelola penjualan & penagihan pelanggan
</small>
<div class="position-absolute bottom-0 end-0 p-3">
<i class="bi bi-arrow-right fs-5 text-secondary opacity-75"></i>
</div>
</div>
</a>
</div>
<!-- ================= GUDANG / ITEMS ================= -->
<div class="col-md-6 col-sm-6">
<a href="<?= base_url('items'); ?>" class="text-decoration-none">
<div class="summary-card h-100 text-center p-4 position-relative overflow-hidden">
<div class="summary-icon mx-auto mb-3 d-inline-block">
<i class="bi bi-box-seam"></i>
</div>
<h6 class="mb-2 fw-bold text-dark">Persediaan</h6>
<small class="text-muted fw-medium d-block">
Kelola stok & barang gudang
</small>
<div class="position-absolute bottom-0 end-0 p-3">
<i class="bi bi-arrow-right fs-5 text-secondary opacity-75"></i>
</div>
</div>
</a>
</div>
</div>
<!-- ================= MAIN CONTENT ================= -->
<div class="row g-4">
<!-- Chart -->
<div class="col-xl-8">
<div class="modern-card h-100">
<div class="p-5">
<div class="d-flex justify-content-between align-items-start mb-4">
<div>
<h4 class="mb-1 fw-bold text-dark">Ringkasan Keuangan</h4>
<small class="text-muted">Performa keuangan</small>
</div>
<div class="btn-group" role="group">
<select id="yearFilter" class="form-select form-select-sm">
<option value="">Semua Tahun</option>
</select>
</div>
</div>
<div class="chart-container">
<canvas id="financeChart" height="140"></canvas>
</div>
</div>
</div>
</div>
<!-- AI INSIGHT -->
<div class="col-xl-4">
<div class="modern-card h-100 shadow-sm border-0 rounded-4">
<div class="p-4">
<!-- HEADER -->
<div class="d-flex align-items-center mb-4">
<div class="bg-primary rounded-circle p-3 me-3 shadow-sm">
<i class="bi bi-robot text-white fs-5"></i>
</div>
<div>
<h5 class="mb-0 fw-bold text-dark">AI Insight</h5>
<small class="text-muted">Rekomendasi otomatis sistem</small>
</div>
</div>
<!-- CONTENT -->
<div class="ai-box d-flex flex-column gap-2">
<?php if(empty($ai_insight)): ?>
<div class="text-center text-muted py-4">
<i class="bi bi-lightbulb-off" style="font-size:28px;"></i>
<div class="mt-2 small">Belum ada insight hari ini</div>
</div>
<?php else: ?>
<?php foreach($ai_insight as $ai): ?>
<?php
// =========================
// mapping icon & color
// =========================
$icon = 'bi-info-circle-fill';
$colorClass = 'ai-info';
if($ai->severity == 'critical'){
$icon = 'bi-x-octagon-fill';
$colorClass = 'ai-danger';
} elseif($ai->severity == 'warning'){
$icon = 'bi-exclamation-triangle-fill';
$colorClass = 'ai-warning';
} elseif($ai->severity == 'info'){
$icon = 'bi-info-circle-fill';
$colorClass = 'ai-info';
} elseif($ai->type == 'stock'){
$icon = 'bi-box-seam-fill';
$colorClass = 'ai-stock';
} elseif($ai->type == 'finance'){
$icon = 'bi-cash-coin';
$colorClass = 'ai-finance';
}
?>
<div class="ai-item <?= $colorClass ?>">
<div class="d-flex align-items-start gap-2">
<i class="bi <?= $icon ?> fs-5 mt-1"></i>
<div class="flex-grow-1">
<div class="small text-dark">
<?= $ai->message; ?>
</div>
<?php if(!empty($ai->meta)): ?>
<div class="text-muted small mt-1">
<i class="bi bi-info-circle"></i>
detail tambahan tersedia
</div>
<?php endif; ?>
</div>
</div>
</div>
<?php endforeach; ?>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<?php endif; ?>
<!-- ================= ACTIVITY FEED ================= -->
<div class="row mt-4">
<div class="col-12">
<div class="modern-card">
<div class="p-5">
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h4 class="mb-1 fw-bold text-dark">Aktivitas Terbaru</h4>
<small class="text-muted">Aktivitas terakhir di sistem</small>
</div>
<a href="<?= base_url('dashboard/log_activity') ?>" class="btn btn-outline-primary btn-sm px-4 rounded-pill">
Lihat Semua <i class="bi bi-arrow-right ms-1"></i>
</a>
</div>
<div class="table-responsive">
<table class="table modern-table-dashboard table-hover align-middle mb-0">
<thead>
<tr>
<th class="rounded-start">Waktu</th>
<th>Modul</th>
<th>Keterangan</th>
<th>Pengguna</th>
<th class="rounded-end">Status</th>
</tr>
</thead>
<tbody id="activityBody"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
</div> <!-- End Container -->
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script>
$(document).ready(function () {
let chartInstance = null;
// ================= INIT =================
const currentYear = new Date().getFullYear();
loadYearOptions();
loadChart(currentYear);
// set default dropdown setelah load
setTimeout(() => {
$('#yearFilter').val(currentYear);
}, 500);
// ================= LOAD YEAR =================
function loadYearOptions() {
$.get("<?= base_url('dashboard/get_years'); ?>", function (res) {
let html = ``;
res.forEach(y => {
html += `<option value="${y.year}">${y.year}</option>`;
});
$('#yearFilter').html(html);
}, 'json');
}
// ================= LOAD CHART =================
function loadChart(year = '') {
$.get("<?= base_url('dashboard/get_chart'); ?>", { year: year }, function (res) {
const ctx = document.getElementById('financeChart');
if (!ctx) return;
if (chartInstance) chartInstance.destroy();
const g1 = ctx.getContext('2d').createLinearGradient(0, 0, 0, 250);
g1.addColorStop(0, 'rgba(25, 135, 84, 0.18)');
g1.addColorStop(1, 'rgba(25, 135, 84, 0.02)');
const g2 = ctx.getContext('2d').createLinearGradient(0, 0, 0, 250);
g2.addColorStop(0, 'rgba(220, 53, 69, 0.18)');
g2.addColorStop(1, 'rgba(220, 53, 69, 0.02)');
chartInstance = new Chart(ctx, {
type: 'line',
data: {
labels: res.labels || [],
datasets: [
{
label: 'Revenue',
data: res.revenue || [],
borderColor: '#198754',
backgroundColor: g1,
borderWidth: 2,
tension: 0.35,
fill: true,
pointRadius: 2,
pointHoverRadius: 5
},
{
label: 'Expense',
data: res.expense || [],
borderColor: '#dc3545',
backgroundColor: g2,
borderWidth: 2,
tension: 0.35,
fill: true,
pointRadius: 2,
pointHoverRadius: 5
}
]
},
options: {
responsive: true,
maintainAspectRatio: false,
interaction: {
mode: 'index',
intersect: false
},
plugins: {
legend: {
position: 'bottom',
labels: {
usePointStyle: true,
pointStyle: 'circle',
padding: 18,
font: {
size: 12,
weight: '500'
}
}
},
tooltip: {
backgroundColor: 'rgba(33,33,33,0.9)',
padding: 10,
displayColors: false
}
},
scales: {
x: {
grid: {
display: false
},
ticks: {
font: {
size: 11
}
}
},
y: {
beginAtZero: true,
grid: {
color: 'rgba(0,0,0,0.05)'
},
ticks: {
font: {
size: 11
}
}
}
}
}
});
}, 'json');
}
// ================= FILTER =================
$('#yearFilter').on('change', function () {
loadChart($(this).val());
});
});
</script>
<script>
document.addEventListener("DOMContentLoaded", function () {
loadActivity();
});
function loadActivity() {
fetch("<?= base_url('dashboard/get_activity') ?>")
.then(res => res.json())
.then(data => {
let html = "";
if (!data || data.length === 0) {
html = `
<tr>
<td colspan="5" class="text-center text-muted py-2">
Tidak ada aktivitas
</td>
</tr>
`;
document.getElementById("activityBody").innerHTML = html;
return;
}
data.forEach(item => {
let time = new Date(item.created_at);
let jam = time.getHours().toString().padStart(2,'0') + ":" +
time.getMinutes().toString().padStart(2,'0');
html += `
<tr>
<td class="fw-semibold text-primary">${jam}</td>
<td>${item.module ?? '-'}</td>
<td>${item.description ?? '-'}</td>
<td class="fw-medium">${item.nama ?? 'System'}</td>
<td>${getStatusBadge(item.status)}</td>
</tr>
`;
});
document.getElementById("activityBody").innerHTML = html;
})
.catch(err => console.error(err));
}
// ================= STATUS BADGE (SIMPLE CLEAN) =================
function getStatusBadge(status) {
if (!status) status = 'success';
const map = {
success: "success",
failed: "danger",
pending: "warning"
};
let cls = map[status] || "secondary";
return `<span class="badge bg-${cls}">${status}</span>`;
}
</script>
@@ -0,0 +1,75 @@
<div class="container mt-4">
<div class="card border-0 shadow-sm rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">
Monitoring Absensi
</h5>
<input type="date"
id="filterDate"
class="form-control"
style="max-width:220px"
value="<?= date('Y-m-d') ?>">
</div>
<div class="table-responsive">
<table id="tableAttendance"
class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th>No</th>
<th>Kode</th>
<th>Nama</th>
<th>Shift</th>
<th>Checkin</th>
<th>Checkout</th>
<th>Status</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<script>
$(function(){
let table = $('#tableAttendance').DataTable({
ajax:{
url:`<?= base_url(); ?>attendancemonitoring/get_data`,
data:function(d){
d.date = $('#filterDate').val();
},
dataSrc:'data'
},
columns:[
{data:0},
{data:1},
{data:2},
{data:3},
{data:4},
{data:5},
{data:6}
]
});
$('#filterDate').change(function(){
table.ajax.reload();
});
});
</script>
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+334
View File
@@ -0,0 +1,334 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5 class="mb-0">
Master Holiday
</h5>
<button class="btn btn-warning btn-add">
Tambah Holiday
</button>
</div>
<div class="table-responsive">
<table id="tableHoliday"
class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th width="5%">No</th>
<th>Tanggal</th>
<th>Nama Holiday</th>
<th>Type</th>
<th width="15%">Aksi</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalHoliday">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-4">
<div class="modal-header bg-warning text-white">
<h5 class="modal-title">
Tambah Holiday
</h5>
<button class="btn-close btn-close-white"
data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="inputId">
<label class="fw-semibold">
Tanggal Holiday
</label>
<input type="date"
class="form-control"
id="holiday_date">
<label class="fw-semibold mt-2">
Nama Holiday
</label>
<input type="text"
class="form-control"
id="holiday_name">
<label class="fw-semibold mt-2">
Jenis Holiday
</label>
<select class="form-control"
id="is_national">
<option value="1">
Nasional
</option>
<option value="0">
Perusahaan
</option>
</select>
</div>
<div class="modal-footer">
<button class="btn btn-secondary"
data-bs-dismiss="modal">
Batal
</button>
<button class="btn btn-warning"
id="btnSimpan">
Simpan
</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
let table = $('#tableHoliday').DataTable({
processing:true,
responsive:true,
autoWidth:false,
ajax:{
url:`<?= base_url(); ?>holidays/get_data`,
dataSrc:'data'
},
order:[],
columns:[
{data:0, orderable:false},
{data:1},
{data:2},
{data:3},
{data:4, orderable:false}
],
language:{
processing:`
<div class="text-center">
<div class="spinner-border text-warning"></div>
</div>
`
}
});
// =========================================
// RESET
// =========================================
function resetForm(){
$('#inputId').val('');
$('#holiday_date').val('');
$('#holiday_name').val('');
$('#is_national').val(1);
$('#modalHoliday .modal-title')
.text('Tambah Holiday');
}
// =========================================
// ADD
// =========================================
$('.btn-add').click(function(){
resetForm();
$('#btnSimpan')
.data('action','add');
$('#modalHoliday').modal('show');
});
// =========================================
// SAVE UPDATE
// =========================================
$('#btnSimpan').click(function(){
let action = $(this).data('action');
let data = {
id: $('#inputId').val(),
holiday_date: $('#holiday_date').val(),
holiday_name: $('#holiday_name').val(),
is_national: $('#is_national').val()
};
if(
!data.holiday_date ||
!data.holiday_name
){
Swal.fire(
'Warning',
'Tanggal & Nama Holiday wajib diisi',
'warning'
);
return;
}
let url = action === 'add'
? `<?= base_url(); ?>holidays/save`
: `<?= base_url(); ?>holidays/update`;
$.ajax({
url:url,
type:'POST',
data:data,
dataType:'json',
success:function(res){
if(res.status){
$('#modalHoliday').modal('hide');
table.ajax.reload(null,false);
Swal.fire(
'Sukses',
res.message,
'success'
);
} else {
Swal.fire(
'Error',
res.message,
'error'
);
}
},
error:function(){
Swal.fire(
'Error',
'Terjadi kesalahan server',
'error'
);
}
});
});
// =========================================
// EDIT
// =========================================
$(document).on('click','.btn-edit',function(){
let id = $(this).data('id');
$.get(
`<?= base_url(); ?>holidays/detail/` + id,
function(res){
$('#inputId').val(res.id);
$('#holiday_date').val(res.holiday_date);
$('#holiday_name').val(res.holiday_name);
$('#is_national').val(res.is_national);
$('#modalHoliday .modal-title')
.text('Edit Holiday');
$('#btnSimpan')
.data('action','edit');
$('#modalHoliday').modal('show');
},
'json'
);
});
// =========================================
// DELETE
// =========================================
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
Swal.fire({
title:'Yakin hapus?',
text:'Data tidak bisa dikembalikan!',
icon:'warning',
showCancelButton:true,
confirmButtonText:'Ya, hapus'
}).then((result)=>{
if(result.isConfirmed){
$.get(
`<?= base_url(); ?>holidays/delete/` + id,
function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire(
'Sukses',
res.message,
'success'
);
} else {
Swal.fire(
'Error',
res.message,
'error'
);
}
},
'json'
);
}
});
});
});
</script>
@@ -0,0 +1,418 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5 class="mb-0">
Leave Requests
</h5>
<button class="btn btn-warning btn-add">
Tambah Leave
</button>
</div>
<div class="table-responsive">
<table id="tableLeave"
class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th width="5%">No</th>
<th>Finger ID</th>
<th>Employee</th>
<th>Jenis Leave</th>
<th>Tanggal Mulai</th>
<th>Tanggal Selesai</th>
<th>Total</th>
<th>Status</th>
<th width="15%">Aksi</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalLeave">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-4">
<div class="modal-header bg-warning text-white">
<h5 class="modal-title">
Tambah Leave
</h5>
<button class="btn-close btn-close-white"
data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="inputId">
<label class="fw-semibold">
Employee
</label>
<select class="form-control select-search"
id="employee_id">
<option value="">
Pilih Employee
</option>
<?php foreach($employees as $e): ?>
<option value="<?= $e->id ?>">
<?= $e->fingerprint_user_id ?>
-
<?= $e->full_name ?>
</option>
<?php endforeach; ?>
</select>
<label class="fw-semibold mt-2">
Jenis Cuti / Izin
</label>
<select class="form-control"
id="leave_type">
<option value="annual">
Cuti Tahunan
</option>
<option value="sick">
Sakit
</option>
<option value="permit">
Izin
</option>
<option value="maternity">
Cuti Melahirkan
</option>
<option value="unpaid">
Cuti Tidak Dibayar
</option>
</select>
<div class="row">
<div class="col-md-6">
<label class="fw-semibold mt-2">
Start Date
</label>
<input type="date"
class="form-control"
id="start_date">
</div>
<div class="col-md-6">
<label class="fw-semibold mt-2">
End Date
</label>
<input type="date"
class="form-control"
id="end_date">
</div>
</div>
<label class="fw-semibold mt-2">
Approval Status
</label>
<select class="form-control"
id="approval_status">
<option value="pending">
Pending
</option>
<option value="approved">
Approved
</option>
<option value="rejected">
Rejected
</option>
</select>
<label class="fw-semibold mt-2">
Reason
</label>
<textarea class="form-control"
id="reason"
rows="4"></textarea>
</div>
<div class="modal-footer">
<button class="btn btn-secondary"
data-bs-dismiss="modal">
Batal
</button>
<button class="btn btn-warning"
id="btnSimpan">
Simpan
</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
let table = $('#tableLeave').DataTable({
processing:true,
responsive:true,
autoWidth:false,
ajax:{
url:`<?= base_url(); ?>leaverequests/get_data`,
dataSrc:'data'
},
order:[]
});
// =====================================
// RESET
// =====================================
function resetForm(){
$('#inputId').val('');
$('#employee_id').val('');
$('#leave_type').val('annual');
$('#start_date').val('');
$('#end_date').val('');
$('#approval_status').val('pending');
$('#reason').val('');
$('#modalLeave .modal-title')
.text('Tambah Leave');
}
// =====================================
// ADD
// =====================================
$('.btn-add').click(function(){
resetForm();
$('#btnSimpan')
.data('action','add');
$('#modalLeave').modal('show');
});
// =====================================
// SAVE UPDATE
// =====================================
$('#btnSimpan').click(function(){
let action = $(this).data('action');
let data = {
id: $('#inputId').val(),
employee_id: $('#employee_id').val(),
leave_type: $('#leave_type').val(),
start_date: $('#start_date').val(),
end_date: $('#end_date').val(),
approval_status: $('#approval_status').val(),
reason: $('#reason').val()
};
if(
!data.employee_id ||
!data.start_date ||
!data.end_date
){
Swal.fire(
'Warning',
'Data wajib diisi',
'warning'
);
return;
}
let url = action == 'add'
? `<?= base_url(); ?>leaverequests/save`
: `<?= base_url(); ?>leaverequests/update`;
$.ajax({
url:url,
type:'POST',
data:data,
dataType:'json',
success:function(res){
if(res.status){
$('#modalLeave').modal('hide');
table.ajax.reload(null,false);
Swal.fire(
'Sukses',
res.message,
'success'
);
} else {
Swal.fire(
'Error',
res.message,
'error'
);
}
}
});
});
// =====================================
// EDIT
// =====================================
$(document).on('click','.btn-edit',function(){
let id = $(this).data('id');
$.get(
`<?= base_url(); ?>leaverequests/detail/` + id,
function(res){
$('#inputId').val(res.id);
$('#employee_id').val(res.employee_id);
$('#leave_type').val(res.leave_type);
$('#start_date').val(res.start_date);
$('#end_date').val(res.end_date);
$('#approval_status')
.val(res.approval_status);
$('#reason').val(res.reason);
$('#modalLeave .modal-title')
.text('Edit Leave');
$('#btnSimpan')
.data('action','edit');
$('#modalLeave').modal('show');
},
'json'
);
});
// =====================================
// DELETE
// =====================================
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
Swal.fire({
title:'Yakin hapus?',
text:'Data tidak bisa dikembalikan',
icon:'warning',
showCancelButton:true
}).then((result)=>{
if(result.isConfirmed){
$.get(
`<?= base_url(); ?>leaverequests/delete/` + id,
function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire(
'Sukses',
res.message,
'success'
);
}
},
'json'
);
}
});
});
});
</script>
+831
View File
@@ -0,0 +1,831 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<!-- HEADER -->
<div class="d-flex justify-content-between align-items-center mb-4">
<div>
<h4 class="mb-1">
Payroll Karyawan
</h4>
<small class="text-muted">
Generate dan kelola payroll karyawan
</small>
</div>
<div class="d-flex gap-2">
<input
type="month"
id="filterMonth"
class="form-control">
<button
class="btn btn-warning"
id="btnGeneratePayroll">
<i class="fa fa-refresh me-1"></i>
Generate Payroll
</button>
<button
class="btn btn-dark"
id="btnReload">
<i class="fa fa-search me-1"></i>
Filter
</button>
</div>
</div>
<!-- TABLE -->
<div class="table-responsive">
<table
id="tablePayroll"
class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th width="50">
No
</th>
<th>
Karyawan
</th>
<th>
Gaji Pokok
</th>
<th>
Pendapatan
</th>
<th>
Potongan
</th>
<th>
THP
</th>
<th width="180">
Status
</th>
<th width="180">
Aksi
</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<!-- ========================================= -->
<!-- MODAL DETAIL PAYROLL -->
<!-- ========================================= -->
<div class="modal fade" id="modalPayroll">
<div class="modal-dialog modal-xl modal-dialog-centered">
<div class="modal-content border-0 rounded-4">
<!-- HEADER -->
<div class="modal-header bg-warning text-white">
<div>
<h5 class="modal-title mb-0">
Detail Payroll
</h5>
<small id="employeeNameHeader"></small>
</div>
<button
class="btn-close"
data-bs-dismiss="modal">
</button>
</div>
<!-- BODY -->
<div class="modal-body">
<input type="hidden" id="payroll_id">
<div class="row">
<!-- LEFT -->
<div class="col-md-6">
<div class="card border-0 shadow-sm rounded-4 h-100">
<div class="card-body">
<div class="d-flex justify-content-between mb-3">
<h5 class="mb-0">
Pendapatan
</h5>
<button
class="btn btn-sm btn-success"
id="btnTambahPendapatan">
<i class="fa fa-plus"></i>
Tambah
</button>
</div>
<!-- BASIC SALARY -->
<div class="border rounded-4 p-3 mb-3 bg-light">
<div class="fw-bold">
Gaji Pokok
</div>
<div
class="text-success fw-bold"
id="basicSalaryText">
Rp 0
</div>
</div>
<!-- ITEMS -->
<div id="earningItems"></div>
</div>
</div>
</div>
<!-- RIGHT -->
<div class="col-md-6">
<div class="card border-0 shadow-sm rounded-4 h-100">
<div class="card-body">
<div class="d-flex justify-content-between mb-3">
<h5 class="mb-0">
Potongan
</h5>
<button
class="btn btn-sm btn-danger"
id="btnTambahPotongan">
<i class="fa fa-plus"></i>
Tambah
</button>
</div>
<!-- ITEMS -->
<div id="deductionItems"></div>
</div>
</div>
</div>
</div>
<!-- TAKE HOME PAY -->
<div class="card border-0 shadow-sm rounded-4 mt-4">
<div class="card-body">
<div class="row text-center">
<div class="col-md-4">
<small class="text-muted">
Total Pendapatan
</small>
<h5
class="text-success mt-1"
id="totalPendapatan">
Rp 0
</h5>
</div>
<div class="col-md-4">
<small class="text-muted">
Total Potongan
</small>
<h5
class="text-danger mt-1"
id="totalPotongan">
Rp 0
</h5>
</div>
<div class="col-md-4">
<small class="text-muted">
Take Home Pay
</small>
<h3
class="mt-1 fw-bold"
id="totalTakeHome">
Rp 0
</h3>
</div>
</div>
</div>
</div>
</div>
<!-- FOOTER -->
<div class="modal-footer">
<button
class="btn btn-secondary"
data-bs-dismiss="modal">
Tutup
</button>
<button
class="btn btn-warning"
id="btnSavePayroll">
<i class="fa fa-save me-1"></i>
Simpan Payroll
</button>
</div>
</div>
</div>
</div>
<!-- ========================================= -->
<!-- TEMPLATE ITEM -->
<!-- ========================================= -->
<script type="text/template" id="templatePayrollItem">
<div class="payroll-item border rounded-4 p-3 mb-3">
<div class="row">
<div class="col-md-5">
<label class="form-label">
Nama Item
</label>
<input
type="text"
class="form-control item-name"
placeholder="Contoh : Bonus Proyek">
</div>
<div class="col-md-3">
<label class="form-label">
Nominal
</label>
<input
type="number"
class="form-control item-amount"
value="0">
</div>
<div class="col-md-3">
<label class="form-label">
Tipe
</label>
<select class="form-control item-type">
<option value="allowance">
Tunjangan
</option>
<option value="bonus">
Bonus
</option>
<option value="overtime">
Lembur
</option>
<option value="deduction">
Potongan
</option>
<option value="bpjs">
BPJS
</option>
<option value="tax">
Pajak
</option>
</select>
</div>
<div class="col-md-1 d-flex align-items-end">
<button
class="btn btn-danger btn-remove-item w-100">
<i class="fa fa-trash"></i>
</button>
</div>
</div>
</div>
</script>
<script>
$(function(){
// =========================================
// DEFAULT MONTH
// =========================================
let now = new Date();
let month =
now.getFullYear()
+ '-'
+ String(now.getMonth()+1).padStart(2,'0');
$('#filterMonth').val(month);
// =========================================
// DATATABLE
// =========================================
let table = $('#tablePayroll').DataTable({
processing:true,
serverSide:false,
ajax:{
url:"<?= base_url('payroll/get_data'); ?>",
type:"POST",
data:function(d){
d.month = $('#filterMonth').val();
}
},
columnDefs:[
{
targets:[2,3,4,5],
className:'text-end'
}
]
});
// =========================================
// RELOAD
// =========================================
$('#btnReload').click(function(){
table.ajax.reload(null,false);
});
// =========================================
// GENERATE PAYROLL
// =========================================
$('#btnGeneratePayroll').click(function(){
let month = $('#filterMonth').val();
if(!month){
Swal.fire(
'Warning',
'Pilih bulan terlebih dahulu',
'warning'
);
return;
}
Swal.fire({
title:'Generate Payroll?',
text:'Payroll akan dibuat otomatis',
icon:'question',
showCancelButton:true,
confirmButtonText:'Generate'
}).then((r)=>{
if(r.isConfirmed){
$.post(
"<?= base_url('payroll/generate_payroll'); ?>",
{
month:month
},
function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire(
'Sukses',
res.message,
'success'
);
} else {
Swal.fire(
'Error',
res.message,
'error'
);
}
},
'json'
);
}
});
});
// =========================================
// OPEN DETAIL
// =========================================
$(document).on(
'click',
'.btn-edit',
function(){
let id = $(this).data('id');
$('#payroll_id').val(id);
$('#earningItems').html('');
$('#deductionItems').html('');
$.get(
"<?= base_url('payroll/detail/'); ?>"+id,
function(res){
$('#employeeNameHeader').html(
res.employee.full_name
);
$('#basicSalaryText').html(
'Rp ' +
parseFloat(
res.payroll.basic_salary
).toLocaleString('id-ID')
);
// =========================
// LOAD ITEMS
// =========================
$.each(res.items,function(i,item){
appendItem(
item.item_name,
item.amount,
item.item_type
);
});
calculateTHP();
$('#modalPayroll').modal('show');
},
'json'
);
}
);
// =========================================
// ADD EARNING
// =========================================
$('#btnTambahPendapatan').click(function(){
appendItem(
'',
0,
'allowance'
);
});
// =========================================
// ADD DEDUCTION
// =========================================
$('#btnTambahPotongan').click(function(){
appendItem(
'',
0,
'deduction'
);
});
// =========================================
// APPEND ITEM
// =========================================
function appendItem(
name='',
amount=0,
type='allowance'
){
let html =
$('#templatePayrollItem').html();
let el = $(html);
el.find('.item-name').val(name);
el.find('.item-amount').val(amount);
el.find('.item-type').val(type);
if(
type == 'allowance'
||
type == 'bonus'
||
type == 'overtime'
){
$('#earningItems').append(el);
} else {
$('#deductionItems').append(el);
}
}
// =========================================
// REMOVE ITEM
// =========================================
$(document).on(
'click',
'.btn-remove-item',
function(){
$(this)
.closest('.payroll-item')
.remove();
calculateTHP();
}
);
// =========================================
// CALCULATE
// =========================================
$(document).on(
'keyup change',
'.item-amount',
function(){
calculateTHP();
}
);
function calculateTHP(){
let basicSalary =
parseFloat(
$('#basicSalaryText')
.text()
.replace(/[^0-9]/g,'')
) || 0;
let totalEarning = 0;
let totalDeduction = 0;
$('#earningItems .payroll-item').each(function(){
totalEarning += parseFloat(
$(this)
.find('.item-amount')
.val()
) || 0;
});
$('#deductionItems .payroll-item').each(function(){
totalDeduction += parseFloat(
$(this)
.find('.item-amount')
.val()
) || 0;
});
let thp =
(
basicSalary
+
totalEarning
)
-
totalDeduction;
$('#totalPendapatan').html(
'Rp '+
totalEarning.toLocaleString('id-ID')
);
$('#totalPotongan').html(
'Rp '+
totalDeduction.toLocaleString('id-ID')
);
$('#totalTakeHome').html(
'Rp '+
thp.toLocaleString('id-ID')
);
}
// =========================================
// SAVE PAYROLL
// =========================================
$('#btnSavePayroll').click(function(){
let items = [];
$('#earningItems .payroll-item').each(function(){
items.push({
item_name:
$(this)
.find('.item-name')
.val(),
amount:
$(this)
.find('.item-amount')
.val(),
item_type:
$(this)
.find('.item-type')
.val()
});
});
$('#deductionItems .payroll-item').each(function(){
items.push({
item_name:
$(this)
.find('.item-name')
.val(),
amount:
$(this)
.find('.item-amount')
.val(),
item_type:
$(this)
.find('.item-type')
.val()
});
});
$.post(
"<?= base_url('payroll/save_items'); ?>",
{
payroll_id:
$('#payroll_id').val(),
items:items
},
function(res){
if(res.status){
$('#modalPayroll').modal('hide');
table.ajax.reload(null,false);
Swal.fire(
'Sukses',
res.message,
'success'
);
} else {
Swal.fire(
'Error',
res.message,
'error'
);
}
},
'json'
);
});
});
</script>
+411
View File
@@ -0,0 +1,411 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5 class="mb-0">
Master Shift
</h5>
<button class="btn btn-warning btn-add">
Tambah Shift
</button>
</div>
<div class="table-responsive">
<table id="tableShifts"
class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th width="5%">No</th>
<th>Kode</th>
<th>Nama Shift</th>
<th>Jam Masuk</th>
<th>Jam Pulang</th>
<th>Toleransi</th>
<th>Jam Kerja</th>
<th>Type</th>
<th width="15%">Aksi</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalShift">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-4">
<div class="modal-header bg-warning text-white">
<h5 class="modal-title">
Tambah Shift
</h5>
<button class="btn-close btn-close-white"
data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="inputId">
<label class="fw-semibold">
Kode Shift
</label>
<input type="text"
class="form-control"
id="shift_code">
<label class="fw-semibold mt-2">
Nama Shift
</label>
<input type="text"
class="form-control"
id="shift_name">
<div class="row">
<div class="col-md-6">
<label class="fw-semibold mt-2">
Jam Masuk
</label>
<input type="time"
class="form-control"
id="checkin_time">
</div>
<div class="col-md-6">
<label class="fw-semibold mt-2">
Jam Pulang
</label>
<input type="time"
class="form-control"
id="checkout_time">
</div>
</div>
<div class="row">
<div class="col-md-6">
<label class="fw-semibold mt-2">
Toleransi Telat
</label>
<input type="number"
class="form-control"
id="late_tolerance_minutes">
</div>
<div class="col-md-6">
<label class="fw-semibold mt-2">
Jam Kerja
</label>
<input type="number"
step="0.1"
class="form-control"
id="work_hours">
</div>
</div>
<label class="fw-semibold mt-2">
Jenis Shift
</label>
<select class="form-control"
id="is_night_shift">
<option value="0">
Normal Shift
</option>
<option value="1">
Night Shift
</option>
</select>
</div>
<div class="modal-footer">
<button class="btn btn-secondary"
data-bs-dismiss="modal">
Batal
</button>
<button class="btn btn-warning"
id="btnSimpan">
Simpan
</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
let table = $('#tableShifts').DataTable({
processing:true,
responsive:true,
autoWidth:false,
ajax:{
url: `<?= base_url(); ?>` + 'shifts/get_data',
dataSrc:'data'
},
order:[],
columns:[
{data:0, orderable:false},
{data:1},
{data:2},
{data:3},
{data:4},
{data:5},
{data:6},
{data:7},
{data:8, orderable:false}
],
language:{
processing:`
<div class="text-center">
<div class="spinner-border text-warning"></div>
</div>
`
}
});
// =========================================
// RESET
// =========================================
function resetForm(){
$('#inputId').val('');
$('#shift_code').val('');
$('#shift_name').val('');
$('#checkin_time').val('');
$('#checkout_time').val('');
$('#late_tolerance_minutes').val(0);
$('#work_hours').val(8);
$('#is_night_shift').val(0);
$('#modalShift .modal-title')
.text('Tambah Shift');
}
// =========================================
// ADD
// =========================================
$('.btn-add').click(function(){
resetForm();
$('#btnSimpan')
.data('action','add');
$('#modalShift').modal('show');
});
// =========================================
// SAVE UPDATE
// =========================================
$('#btnSimpan').click(function(){
let action = $(this).data('action');
let data = {
id: $('#inputId').val(),
shift_code: $('#shift_code').val(),
shift_name: $('#shift_name').val(),
checkin_time: $('#checkin_time').val(),
checkout_time: $('#checkout_time').val(),
late_tolerance_minutes: $('#late_tolerance_minutes').val(),
work_hours: $('#work_hours').val(),
is_night_shift: $('#is_night_shift').val()
};
if(
!data.shift_code ||
!data.shift_name
){
Swal.fire(
'Warning',
'Kode & Nama Shift wajib diisi',
'warning'
);
return;
}
let url = action === 'add'
? `<?= base_url(); ?>` + 'shifts/save'
: `<?= base_url(); ?>` + 'shifts/update';
$.ajax({
url:url,
type:'POST',
data:data,
dataType:'json',
success:function(res){
if(res.status){
$('#modalShift').modal('hide');
table.ajax.reload(null,false);
Swal.fire(
'Sukses',
res.message,
'success'
);
} else {
Swal.fire(
'Error',
res.message,
'error'
);
}
},
error:function(){
Swal.fire(
'Error',
'Terjadi kesalahan server',
'error'
);
}
});
});
// =========================================
// EDIT
// =========================================
$(document).on('click','.btn-edit',function(){
let id = $(this).data('id');
$.get(
`<?= base_url(); ?>` + 'shifts/detail/' + id,
function(res){
$('#inputId').val(res.id);
$('#shift_code').val(res.shift_code);
$('#shift_name').val(res.shift_name);
$('#checkin_time').val(res.checkin_time);
$('#checkout_time').val(res.checkout_time);
$('#late_tolerance_minutes').val(res.late_tolerance_minutes);
$('#work_hours').val(res.work_hours);
$('#is_night_shift').val(res.is_night_shift);
$('#modalShift .modal-title')
.text('Edit Shift');
$('#btnSimpan')
.data('action','edit');
$('#modalShift').modal('show');
},
'json'
);
});
// =========================================
// DELETE
// =========================================
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
Swal.fire({
title:'Yakin hapus?',
text:'Data tidak bisa dikembalikan!',
icon:'warning',
showCancelButton:true,
confirmButtonText:'Ya, hapus'
}).then((result)=>{
if(result.isConfirmed){
$.get(
`<?= base_url(); ?>` + 'shifts/delete/' + id,
function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire(
'Sukses',
res.message,
'success'
);
} else {
Swal.fire(
'Error',
res.message,
'error'
);
}
},
'json'
);
}
});
});
});
</script>
@@ -0,0 +1,8 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
echo "\nERROR: ",
$heading,
"\n\n",
$message,
"\n\n";
@@ -0,0 +1,8 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
echo "\nDatabase error: ",
$heading,
"\n\n",
$message,
"\n\n";
@@ -0,0 +1,21 @@
<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>
An uncaught Exception was encountered
Type: <?php echo get_class($exception), "\n"; ?>
Message: <?php echo $message, "\n"; ?>
Filename: <?php echo $exception->getFile(), "\n"; ?>
Line Number: <?php echo $exception->getLine(); ?>
<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>
Backtrace:
<?php foreach ($exception->getTrace() as $error): ?>
<?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>
File: <?php echo $error['file'], "\n"; ?>
Line: <?php echo $error['line'], "\n"; ?>
Function: <?php echo $error['function'], "\n\n"; ?>
<?php endif ?>
<?php endforeach ?>
<?php endif ?>
@@ -0,0 +1,8 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
echo "\nERROR: ",
$heading,
"\n\n",
$message,
"\n\n";
@@ -0,0 +1,21 @@
<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>
A PHP Error was encountered
Severity: <?php echo $severity, "\n"; ?>
Message: <?php echo $message, "\n"; ?>
Filename: <?php echo $filepath, "\n"; ?>
Line Number: <?php echo $line; ?>
<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>
Backtrace:
<?php foreach (debug_backtrace() as $error): ?>
<?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>
File: <?php echo $error['file'], "\n"; ?>
Line: <?php echo $error['line'], "\n"; ?>
Function: <?php echo $error['function'], "\n\n"; ?>
<?php endif ?>
<?php endforeach ?>
<?php endif ?>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+124
View File
@@ -0,0 +1,124 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title><?= $heading; ?></title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
/* ================================
🔥 GLOBAL STYLE
================================ */
body {
margin: 0;
font-family: 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #ff8c00, #ffb347);
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
/* ================================
🔥 CARD
================================ */
.error-card {
background: #fff;
padding: 40px 35px;
border-radius: 16px;
text-align: center;
max-width: 420px;
width: 90%;
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
animation: fadeIn 0.4s ease;
}
/* ================================
🔥 TITLE
================================ */
.error-code {
font-size: 60px;
font-weight: 700;
color: #ff8c00;
margin-bottom: 10px;
}
.error-title {
font-size: 20px;
font-weight: 600;
color: #333;
margin-bottom: 10px;
}
.error-message {
font-size: 14px;
color: #666;
margin-bottom: 25px;
}
/* ================================
🔥 BUTTON
================================ */
.btn-dashboard {
display: inline-block;
padding: 10px 20px;
background: #ff8c00;
color: #fff;
border-radius: 8px;
text-decoration: none;
font-weight: 500;
transition: 0.3s;
}
.btn-dashboard:hover {
background: #e67700;
}
/* ================================
🔥 ANIMATION
================================ */
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(15px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body>
<div class="error-card">
<!-- 🔥 KODE ERROR -->
<div class="error-code">
<?= strpos($heading, '404') !== false ? '404' : 'Error'; ?>
</div>
<!-- 🔥 TITLE -->
<div class="error-title">
<?= $heading; ?>
</div>
<!-- 🔥 MESSAGE -->
<div class="error-message">
<?= strip_tags($message); ?>
</div>
<!-- 🔥 BUTTON -->
<a href="<?= base_url('dashboard'); ?>" class="btn-dashboard">
Kembali ke Dashboard
</a>
</div>
</body>
</html>
@@ -0,0 +1,64 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Database Error</title>
<style type="text/css">
::selection { background-color: #E13300; color: white; }
::-moz-selection { background-color: #E13300; color: white; }
body {
background-color: #fff;
margin: 40px;
font: 13px/20px normal Helvetica, Arial, sans-serif;
color: #4F5155;
}
a {
color: #003399;
background-color: transparent;
font-weight: normal;
}
h1 {
color: #444;
background-color: transparent;
border-bottom: 1px solid #D0D0D0;
font-size: 19px;
font-weight: normal;
margin: 0 0 14px 0;
padding: 14px 15px 10px 15px;
}
code {
font-family: Consolas, Monaco, Courier New, Courier, monospace;
font-size: 12px;
background-color: #f9f9f9;
border: 1px solid #D0D0D0;
color: #002166;
display: block;
margin: 14px 0 14px 0;
padding: 12px 10px 12px 10px;
}
#container {
margin: 10px;
border: 1px solid #D0D0D0;
box-shadow: 0 0 8px #D0D0D0;
}
p {
margin: 12px 15px 12px 15px;
}
</style>
</head>
<body>
<div id="container">
<h1><?php echo $heading; ?></h1>
<?php echo $message; ?>
</div>
</body>
</html>
@@ -0,0 +1,32 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
?>
<div style="border:1px solid #990000;padding-left:20px;margin:0 0 10px 0;">
<h4>An uncaught Exception was encountered</h4>
<p>Type: <?php echo get_class($exception); ?></p>
<p>Message: <?php echo $message; ?></p>
<p>Filename: <?php echo $exception->getFile(); ?></p>
<p>Line Number: <?php echo $exception->getLine(); ?></p>
<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>
<p>Backtrace:</p>
<?php foreach ($exception->getTrace() as $error): ?>
<?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>
<p style="margin-left:10px">
File: <?php echo $error['file']; ?><br />
Line: <?php echo $error['line']; ?><br />
Function: <?php echo $error['function']; ?>
</p>
<?php endif ?>
<?php endforeach ?>
<?php endif ?>
</div>
@@ -0,0 +1,124 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="utf-8">
<title>403 Forbidden</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
body {
margin: 0;
font-family: 'Segoe UI', sans-serif;
background: linear-gradient(135deg, #ff8c00, #ffb347);
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
.error-card {
background: #fff;
padding: 40px 35px;
border-radius: 16px;
text-align: center;
max-width: 420px;
width: 90%;
box-shadow: 0 10px 30px rgba(0,0,0,0.15);
animation: fadeIn 0.4s ease;
}
.error-code {
font-size: 60px;
font-weight: 700;
color: #ff8c00;
margin-bottom: 10px;
}
.error-title {
font-size: 20px;
font-weight: 600;
color: #333;
margin-bottom: 10px;
}
.error-message {
font-size: 14px;
color: #666;
margin-bottom: 25px;
}
.btn-dashboard {
display: inline-block;
padding: 10px 20px;
background: #ff8c00;
color: #fff;
border-radius: 8px;
text-decoration: none;
font-weight: 500;
transition: 0.3s;
}
.btn-dashboard:hover {
background: #e67700;
}
.btn-back {
display: inline-block;
margin-left: 10px;
padding: 10px 20px;
background: #6c757d;
color: #fff;
border-radius: 8px;
text-decoration: none;
font-weight: 500;
transition: 0.3s;
}
.btn-back:hover {
background: #5a6268;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(15px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
</style>
</head>
<body>
<div class="error-card">
<!-- 🔥 ICON -->
<div style="font-size:50px; margin-bottom:10px;">🔒</div>
<!-- 🔥 CODE -->
<div class="error-code">403</div>
<!-- 🔥 TITLE -->
<div class="error-title">Akses Ditolak</div>
<!-- 🔥 MESSAGE -->
<div class="error-message">
Anda tidak memiliki izin untuk mengakses halaman ini.<br>
Silakan hubungi administrator jika ini adalah kesalahan.
</div>
<a href="javascript:history.back()" class="btn-back">
Kembali
</a>
</div>
</body>
</html>
@@ -0,0 +1,33 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
?>
<div style="border:1px solid #990000;padding-left:20px;margin:0 0 10px 0;">
<h4>A PHP Error was encountered</h4>
<p>Severity: <?php echo $severity; ?></p>
<p>Message: <?php echo $message; ?></p>
<p>Filename: <?php echo $filepath; ?></p>
<p>Line Number: <?php echo $line; ?></p>
<?php if (defined('SHOW_DEBUG_BACKTRACE') && SHOW_DEBUG_BACKTRACE === TRUE): ?>
<p>Backtrace:</p>
<?php foreach (debug_backtrace() as $error): ?>
<?php if (isset($error['file']) && strpos($error['file'], realpath(BASEPATH)) !== 0): ?>
<p style="margin-left:10px">
File: <?php echo $error['file'] ?><br />
Line: <?php echo $error['line'] ?><br />
Function: <?php echo $error['function'] ?>
</p>
<?php endif ?>
<?php endforeach ?>
<?php endif ?>
</div>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
@@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<style>
table { width: 100%; border-collapse: collapse; }
th, td { border:1px solid #000; padding:6px; font-size:12px; }
</style>
</head>
<body>
<h3>Data Santri</h3>
<table>
<tr>
<th>No</th>
<th>Nama</th>
<th>NIS</th>
<th>Kelas</th>
<th>Alamat</th>
<th>Nama Wali</th>
<th>No WA</th>
<th>Email</th>
<th>Tahun Masuk</th>
<th>Kamar</th>
<th>Gedung</th>
<th>Jumlah Kumjungan</th>
</tr>
<?php
$no = 1;
foreach ($siswa as $s) { ?>
<tr>
<td><?= $no ?></td>
<td><?= $s->nama ?></td>
<td><?= $s->nis ?></td>
<td><?= $s->nama_kelas ?></td>
<td><?= $s->alamat ?></td>
<td><?= $s->nama_wali ?></td>
<td><?= $s->nomor_whatsapp ?></td>
<td><?= $s->email ?></td>
<td><?= $s->tahun_masuk ?></td>
<td><?= $s->nama_kamar ?></td>
<td><?= $s->nama_gedung_kamar ?></td>
<td><?= $s->jumlah_kunjungan ?></td>
</tr>
<?php $no++; } ?>
</table>
</body>
</html>
+129
View File
@@ -0,0 +1,129 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">History Kunjungan</h5>
<input type="text" id="dateRangeFilter" class="form-control me-2" placeholder="Pilih rentang waktu..." style="max-width: 200px;">
</div>
<div class="table-responsive">
<table id="tableHistory" class="table modern-table align-middle w-100">
<thead>
<tr>
<th>No</th>
<th>Nama</th>
<th>NIS</th>
<th>Kelas</th>
<th>Kamar</th>
<th>Nama Wali</th>
<th>Alamat</th>
<th>Jam Masuk</th>
<th>Petugas Masuk</th>
<th>Jam Keluar</th>
<th>Petugas Keluar</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
let isInitialLoad = true;
$('#dateRangeFilter').val('');
$('#dateRangeFilter').daterangepicker({
autoUpdateInput: false,
locale: { cancelLabel: false, format: 'YYYY-MM-DD' },
linkedCalendars: false
});
$('#dateRangeFilter').on('apply.daterangepicker', function(ev, picker) {
$(this).val(picker.startDate.format('YYYY-MM-DD') + ' - ' + picker.endDate.format('YYYY-MM-DD'));
table.ajax.reload();
});
$('#dateRangeFilter').on('cancel.daterangepicker', function(ev, picker) {
$(this).val('');
table.ajax.reload();
});
let table = $('#tableHistory').DataTable({
dom: '<"dataTables_top d-flex justify-content-between mb-3"l f>rtip',
processing: true,
serverSide: true,
responsive: false,
autoWidth: false,
order: [],
ajax: {
url: "<?= base_url('history/get_data'); ?>",
type: "POST",
data: function(d) {
if (isInitialLoad) { isInitialLoad = false; return; }
let dateVal = $('#dateRangeFilter').val();
if (dateVal) {
let dates = dateVal.split(' - ');
if (dates[0]) d.startDate = dates[0];
if (dates[1]) d.endDate = dates[1];
}
// jika dateVal kosong, startDate dan endDate tidak dikirim
}
},
columns: [
{ data: 0, orderable: false }, // No
{ data: 1 }, // Nama
{ data: 2 }, // NIS
{ data: 3 }, // Kelas
{ data: 4 }, // Kelas
{ data: 5 }, // Nama Wali
{ data: 6 }, // Alamat
{
data: 7, // Jam Masuk
render: function(data) { return data ? data : ''; }
},
{
data: 8, // Media Masuk
orderable: false,
render: function(data) { return data ? data : ''; }
},
{
data: 9, // Jam Keluar
render: function(data) { return data ? data : ''; }
},
{
data: 10, // Media Keluar
orderable: false,
render: function(data) { return data ? data : ''; }
}
],
language: {
processing: `<div class="d-flex justify-content-center"><div class="spinner-border text-success"></div></div>`
}
});
$('.dataTables_filter input').attr('placeholder', 'Cari nama, nis, kelas...').css({
'padding': '6px 12px',
'border-radius': '6px',
'border': '1px solid #ced4da',
'font-size': '0.9rem',
'box-sizing': 'border-box'
});
table.on('draw', function() {
$('#tableHistory tbody tr').each(function() {
$(this).find('td').each(function(index) {
let header = $('#tableHistory thead th').eq(index).text().trim();
$(this).attr('data-label', header + ' : ');
});
});
});
});
</script>
+11
View File
@@ -0,0 +1,11 @@
<!DOCTYPE html>
<html>
<head>
<title>403 Forbidden</title>
</head>
<body>
<p>Directory access is forbidden.</p>
</body>
</html>
File diff suppressed because it is too large Load Diff
+575
View File
@@ -0,0 +1,575 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5>
<?php if($active_menu == 'invoice_unpaid') echo 'Invoice Berjalan'; ?>
<?php if($active_menu == 'invoice_paid') echo 'Riwayat Invoice'; ?>
<?php if($active_menu == 'invoice_draft') echo 'Draft Invoice'; ?>
</h5>
<?php if($active_menu == 'invoice_draft') : ?>
<button class="btn btn-warning btn-add">Buat Invoice</button>
<?php endif; ?>
</div>
<div class="table-responsive">
<table class="table modern-table align-middle w-100" id="tableInvoice">
<!-- <table id="tableInvoice" class="table table-bordered"> -->
<thead>
<tr>
<th>No</th>
<th>No Invoice</th>
<th>Tanggal</th>
<th>Jatuh Tempo</th>
<th>Customer</th>
<th>Total</th>
<th>Terbayar</th>
<th>Sisa</th>
<th>Status</th>
<th>Aksi</th>
</tr>
</thead>
</table>
</table>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalInvoice">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5>Buat Invoice</h5>
</div>
<div class="modal-body">
<div class="form-group mt-2">
<label for="customer_id">Customer</label>
<select id="customer_id" class="form-control mb-2"></select>
</div>
<div class="form-group mt-2">
<label for="tanggal">Tanggal</label>
<input type="date" id="tanggal" class="form-control mb-2">
</div>
<div class="form-group mt-2">
<label for="jatuh_tempo">Jatuh Tempo</label>
<input type="date" id="jatuh_tempo" class="form-control mb-3">
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="modalBayar">
<div class="modal-dialog modal-dialog-centered modal-lg">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5>
Bayar Invoice -
<span id="customerName"></span>
</h5>
</div>
<div class="modal-body">
<input type="hidden" id="bayar_invoice_id">
<!-- INFO BOX -->
<div class="row mb-3">
<div class="col-md-4">
<div class="p-2 border rounded">
<small>Total Invoice</small>
<h5 id="invoiceTotal">0</h5>
</div>
</div>
<div class="col-md-4">
<div class="p-2 border rounded">
<small>Total Bayar</small>
<h5 id="totalBayar">0</h5>
</div>
</div>
<div class="col-md-4">
<div class="p-2 border rounded">
<small>Sisa (Balance)</small>
<h5 id="balance">0</h5>
</div>
</div>
</div>
<label>Tanggal Bayar</label>
<input type="date" id="tanggal_bayar" class="form-control mb-2">
<table class="table" id="tableBayar">
<thead>
<tr>
<th>Account</th>
<th>Debit</th>
<th></th>
</tr>
</thead>
<tbody></tbody>
</table>
<button class="btn btn-sm btn-primary btn-add-bayar">
+ Tambah Pembayaran
</button>
</div>
<div class="modal-footer">
<button class="btn btn-warning" id="btnProsesBayar">
Proses Bayar
</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
let url = "";
let accountOptions = '';
let accountOptionsAssets = '';
// =========================
// URL BY MENU
// =========================
<?php if($active_menu == 'invoice_unpaid'): ?>
url = "<?= base_url('invoices/get_data/unpaid'); ?>";
<?php elseif($active_menu == 'invoice_paid'): ?>
url = "<?= base_url('invoices/get_data/paid'); ?>";
<?php elseif($active_menu == 'invoice_draft'): ?>
url = "<?= base_url('invoices/get_data/draft'); ?>";
<?php endif; ?>
// =========================
// LOAD CUSTOMER
// =========================
$.get("<?= base_url('invoices/get_customers'); ?>", res => {
let opt = '<option value="">Pilih Customer</option>';
res.forEach(c => opt += `<option value="${c.id}">${c.nama}</option>`);
$('#customer_id').html(opt);
}, 'json');
// =========================
// LOAD ACCOUNT (PENDAPATAN)
// =========================
$.get("<?= base_url('invoices/get_accounts'); ?>", res => {
accountOptions = '<option value="">Pilih Akun</option>';
res.forEach(a => {
accountOptions += `<option value="${a.id}">${a.kode_akun} - ${a.nama_akun}</option>`;
});
}, 'json');
// =========================
// LOAD ACCOUNT (ASSET / KAS)
// =========================
$.get("<?= base_url('invoices/get_accounts_assets'); ?>", res => {
accountOptionsAssets = '<option value="">Pilih Akun</option>';
res.forEach(a => {
accountOptionsAssets += `<option value="${a.id}">${a.kode_akun} - ${a.nama_akun}</option>`;
});
}, 'json');
// =========================
// DATATABLE
// =========================
let table = $('#tableInvoice').DataTable({
ajax:{
url: url,
type:"POST"
},
columnDefs:[
{ targets:[3], className:'text-end' }
]
});
// =========================
// ADD ITEM ROW
// =========================
$('.btn-add-row').click(function(){
if(!accountOptions){
Swal.fire('Warning','Account belum siap, tunggu sebentar','warning');
return;
}
$('#tableDetail tbody').append(`
<tr>
<td>
<select class="form-control account_id">${accountOptions}</select>
</td>
<td><input type="text" class="form-control nama_item"></td>
<td><input type="number" class="form-control qty" value="1"></td>
<td><input type="text" class="form-control harga format-rupiah"></td>
<td class="subtotal text-end">0</td>
<td><button class="btn btn-danger btn-remove">X</button></td>
</tr>
`);
});
// =========================
// HITUNG SUBTOTAL
// =========================
$(document).on('input','.qty,.harga',function(){
let tr = $(this).closest('tr');
let qty = parseFloat(tr.find('.qty').val()) || 0;
let harga = parseFloat(tr.find('.harga').val()) || 0;
let sub = qty * harga;
tr.find('.subtotal').text(sub.toLocaleString('id-ID'));
calcTotal();
});
// =========================
// TOTAL
// =========================
function calcTotal(){
let total = 0;
$('.subtotal').each(function(){
total += parseFloat($(this).text().replace(/\./g,'')) || 0;
});
$('#grandTotal').text(total.toLocaleString('id-ID'));
}
// =========================
// REMOVE ROW (GLOBAL)
// =========================
$(document).on('click','.btn-remove',function(){
$(this).closest('tr').remove();
calcTotal();
hitungBayar();
});
// =========================
// OPEN MODAL INVOICE
// =========================
$('.btn-add').click(function(){
$('#modalInvoice').modal('show');
$('#tableDetail tbody').html('');
$('#grandTotal').text('0');
});
// =========================
// SIMPAN INVOICE
// =========================
$('#btnSimpan').click(function(){
let nama_item=[], qty=[], harga=[], account_id=[];
$('#tableDetail tbody tr').each(function(){
nama_item.push($(this).find('.nama_item').val());
qty.push($(this).find('.qty').val());
harga.push($(this).find('.harga').val());
account_id.push($(this).find('.account_id').val());
});
$.post("<?= base_url('invoices/save'); ?>",{
customer_id: $('#customer_id').val(),
tanggal: $('#tanggal').val(),
jatuh_tempo: $('#jatuh_tempo').val()
}, function(res){
if(res.status){
Swal.fire({
icon:'success',
title:'Berhasil',
text:'Invoice berhasil disimpan',
timer:1500,
showConfirmButton:false
}).then(()=>{
window.location.href = "<?= base_url('invoices/detail/'); ?>" + res.invoice_id;
});
}else{
Swal.fire('Error', res.message || 'Gagal simpan', 'error');
}
},'json');
});
// =========================
// DETAIL
// =========================
$(document).on('click','.btn-detail',function(){
let id = $(this).data('id');
window.location.href = "<?= base_url('invoices/detail/'); ?>" + id;
});
// =========================
// OPEN MODAL BAYAR
// =========================
$(document).on('click','.btn-bayar',function(){
let id = $(this).data('id');
let total = parseFloat($(this).data('total') - $(this).data('terbayar')) || 0;
let customer = $(this).data('customer');
$('#bayar_invoice_id').val(id);
$('#customerName').text(customer);
$('#invoiceTotal').text(total.toLocaleString('id-ID'));
$('#tableBayar tbody').html('');
$('#totalBayar').text('0');
$('#balance').text(total.toLocaleString('id-ID'));
$('#modalBayar').modal('show');
});
// =========================
// ADD ROW BAYAR
// =========================
$('.btn-add-bayar').click(function(){
if(!accountOptionsAssets){
Swal.fire('Warning','Account belum siap','warning');
return;
}
$('#tableBayar tbody').append(`
<tr>
<td><select class="form-control account_id">${accountOptionsAssets}</select></td>
<td><input type="text" class="form-control debit text-end format-rupiah"></td>
<td><button class="btn btn-danger btn-remove">X</button></td>
</tr>
`);
});
// =========================
// HITUNG BAYAR
// =========================
$(document).on('input','.debit',function(){
hitungBayar();
});
function hitungBayar(){
let totalBayar = 0;
$('#tableBayar .debit').each(function(){
totalBayar += parseFloat($(this).val()) || 0;
});
let invoiceTotal = parseFloat($('#invoiceTotal').text().replace(/\./g,'')) || 0;
let balance = invoiceTotal - totalBayar;
$('#totalBayar').text(totalBayar.toLocaleString('id-ID'));
$('#balance').text(balance.toLocaleString('id-ID'));
if(balance < 0){
$('#balance').css('color','red');
}else if(balance === 0){
$('#balance').css('color','green');
}else{
$('#balance').css('color','orange');
}
}
// =========================
// PROSES BAYAR
// =========================
$('#btnProsesBayar').click(function(){
let id = $('#bayar_invoice_id').val();
let tanggal_bayar = $('#tanggal_bayar').val();
if(!tanggal_bayar){
Swal.fire('Warning','Tanggal bayar wajib diisi','warning');
return;
}
let detail = [];
let totalBayar = 0;
$('#tableBayar tbody tr').each(function(){
let account_id = $(this).find('.account_id').val();
let debit = parseFloat($(this).find('.debit').val()) || 0;
if(account_id && debit > 0){
detail.push({ account_id, amount: debit });
totalBayar += debit;
}
});
if(detail.length === 0){
Swal.fire('Warning','Isi minimal 1 pembayaran','warning');
return;
}
let invoiceTotal = parseFloat($('#invoiceTotal').text().replace(/\./g,'')) || 0;
if(totalBayar > invoiceTotal){
Swal.fire('Error','Pembayaran melebihi total invoice','error');
return;
}
Swal.fire({
title:'Memproses...',
allowOutsideClick:false,
didOpen:()=>Swal.showLoading()
});
$.post("<?= base_url('invoices/bayar'); ?>", {
invoice_id: id,
tanggal_bayar,
detail
}, function(res){
if(res.status){
Swal.fire('Success','Pembayaran sukses','success');
$('#modalBayar').modal('hide');
table.ajax.reload(null,false);
}else{
Swal.fire('Error', res.message || 'Gagal', 'error');
}
}, 'json');
});
// =========================
// POSTING
// =========================
$(document).on('click','.btn-posting',function(){
let id = $(this).data('id');
Swal.fire({
title:'Posting Invoice?',
text:'Akan masuk ke piutang',
icon:'warning',
showCancelButton:true
}).then(result=>{
if(result.isConfirmed){
Swal.fire({
title:'Processing...',
allowOutsideClick:false,
didOpen:()=>Swal.showLoading()
});
$.post("<?= base_url('invoices/posting'); ?>",{invoice_id:id},function(){
Swal.fire('Success','Berhasil diposting','success');
table.ajax.reload(null,false);
},'json');
}
});
});
// =========================
// DELETE
// =========================
$(document).on('click', '.btn-delete', function () {
let id = $(this).data('id');
Swal.fire({
title: 'Hapus Invoice?',
text: 'Data tidak bisa dikembalikan',
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#dc3545',
confirmButtonText: 'Ya, Hapus',
cancelButtonText: 'Batal'
}).then((result) => {
if (result.isConfirmed) {
Swal.fire({
title: 'Menghapus...',
allowOutsideClick: false,
didOpen: () => Swal.showLoading()
});
$.ajax({
url: "<?= base_url('invoices/delete/'); ?>" + id,
type: "GET",
dataType: "json"
})
.done(function (res) {
if (res.status) {
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: 'Invoice berhasil dihapus'
});
// reload datatable
if (typeof table !== 'undefined') {
table.ajax.reload(null, false);
}
} else {
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Tidak bisa dihapus'
});
}
})
.fail(function () {
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Terjadi kesalahan koneksi'
});
});
}
});
});
});
</script>
+757
View File
@@ -0,0 +1,757 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5>Data Barang Dalam Perjalanan</h5>
<div class="text-end">
<button class="btn btn-warning btn-add btn-add-item mr-2">Tambah Barang</button>
</div>
</div>
<div class="table-responsive">
<table id="tableItems" class="table modern-table w-100">
<thead>
<tr>
<th>No</th>
<th>Tanggal Beli</th>
<th>Kode Barang</th>
<th>Nama</th>
<th style="max-width: 150px;">Gudang</th>
<th>Stok</th>
<th>Harga Beli</th>
<th>Harga Jual</th>
<th>Create at</th>
<th>Aksi</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalItem">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5>Item</h5>
</div>
<div class="modal-body">
<input type="hidden" id="id">
<div id="group_tanggal">
<label>Tanggal Pembelian</label>
<input type="date" id="tanggal_beli" class="form-control">
</div>
<div id="group_kode">
<label class="mt-2">Kode Barang</label>
<select id="kode_barang" class="form-control select-search"></select>
</div>
<label>Nama Barang</label>
<input type="text" id="nama_barang" class="form-control">
<div id="group_qty">
<label class="mt-2">Jumlah Barang</label>
<input type="number" id="qty" class="form-control">
</div>
<label class="mt-2">Harga Beli</label>
<input type="text" id="harga_beli" class="form-control format-rupiah">
<label class="mt-2">Harga Jual</label>
<input type="text" id="harga_jual" class="form-control format-rupiah">
<div id="group_warehouse">
<label class="mt-2">Gudang</label>
<select id="warehouse_id" class="form-control"></select>
</div>
<div id="group_account">
<label class="mt-2">Akun Kas</label>
<select id="account_kas" class="form-control"></select>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<!-- MODAL BARANAG KELUAR -->
<div class="modal fade" id="modalKeluar">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5>Barang Keluar</h5>
</div>
<div class="modal-body">
<div id="group_account_biaya">
<label class="mt-2">Akun Biaya</label>
<select id="account_biaya" class="form-control"></select>
</div>
<div>
<label>Tanggal</label>
<input type="date" id="tanggal_keluar" class="form-control">
</div>
<div>
<label class="mt-2">Gudang</label>
<select id="warehouse_id_keluar" class="form-control"></select>
</div>
<div class="d-none" id="group_barang_keluar">
<label class="mt-2">Barang</label>
<select id="barang_id" class="form-control select-search"></select>
</div>
<div class="d-none" id="group_qty_keluar">
<label class="mt-2">Jumlah Barang</label>
<input type="number" id="qty_keluar" class="form-control">
</div>
<div class="mb-2 d-none" id="group_keterangan">
<label for="keterangan" class="form-label">Keterangan</label>
<textarea class="form-control" id="keterangan_keluar" rows="3"></textarea>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-danger" id="btnKeluar">Keluarkan</button>
</div>
</div>
</div>
</div>
<!-- MODAL DETAIL -->
<div class="modal fade" id="modalDetail">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5>Detail Item</h5>
</div>
<div class="modal-body" id="detailContent"></div>
</div>
</div>
</div>
<div class="modal fade" id="modalAdjust">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning">
<h5>Penyesuaian Stok</h5>
</div>
<div class="modal-body">
<input type="hidden" id="adjust_item_id">
<label>Qty Adjustment</label>
<input type="number" id="adjust_qty" class="form-control">
<small>Gunakan minus (-) jika mengurangi</small>
<label class="mt-2">Gudang</label>
<select id="adjust_warehouse" class="form-control"></select>
<label class="mt-2">Keterangan</label>
<input type="text" id="adjust_ket" class="form-control">
</div>
<div class="modal-footer">
<button class="btn btn-warning" id="btnAdjust">Simpan</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
let action = 'add';
let selectedItemId = null;
// ================= DATATABLE =================
let table = $('#tableItems').DataTable({
ajax: {
url: "<?= base_url('items/get_data'); ?>",
data: function (d) {
d.s = `draft`;
}
}
});
// ================= LOAD AKUN KAS =================
function loadKas(){
$.get("<?= base_url('items/get_accounts'); ?>",res=>{
let opt='<option value="">-- PILIH KAS --</option>';
res.forEach(a=>{
opt += `<option value="${a.id}">${a.nama_akun}</option>`;
});
$('#account_kas').html(opt);
},'json');
}
// ================= LOAD AKUN KAS =================
function loadKasOut(){
$.get("<?= base_url('items/get_accounts_biaya'); ?>",res=>{
let opt='<option value="">-- PILIH KAS --</option>';
res.forEach(a=>{
opt += `<option value="${a.id}">${a.nama_akun}</option>`;
});
$('#account_biaya').html(opt);
},'json');
}
// ================= LOAD GUDANG =================
function loadGudang(){
$.get("<?= base_url('warehouses/list'); ?>",res=>{
let opt='<option value="">-- PILIH GUDANG --</option>';
res.forEach(w=>{
opt += `<option value="${w.id}">${w.nama}</option>`;
});
$('#warehouse_id, #adjust_warehouse').html(opt);
},'json');
}
// ================= LOAD GUDANG =================
function loadGudangList(){
$.get("<?= base_url('warehouses/list'); ?>",res=>{
let opt='<option value="">-- PILIH GUDANG --</option>';
res.forEach(w=>{
opt += `<option value="${w.id}">${w.nama}</option>`;
});
$('#warehouse_id_keluar').html(opt);
},'json');
}
function loadKode(){
$.get("<?= base_url('kodebarang/list'); ?>",res=>{
let opt='<option value="">-- PILIH KODE --</option>';
res.forEach(a=>{
opt += `<option value="${a.id}">${a.kode_barang} - ${a.nama}</option>`;
});
$('#kode_barang').html(opt);
},'json');
}
// ================= RESET FORM =================
function resetForm(){
$('#id').val('');
$('#nama_barang').val('');
$('#qty').val('');
$('#harga_beli').val('');
$('#harga_jual').val('');
$('#tanggal_beli').val('');
}
// ================= INIT LOAD =================
loadGudang();
loadKas();
loadKode();
// ================= ADD =================
$('.btn-add-item').click(function(){
action='add';
resetForm();
$('#qty').closest('div').show();
$('#kode_barang').closest('div').show();
$('#warehouse_id').closest('div').show();
$('#account_kas').closest('div').show();
$('#modalItem').modal('show');
loadGudang();
loadKas();
loadKode();
});
// ================= EDIT =================
$(document).on('click','.btn-editItem',function(){
let id = $(this).data('id');
action='edit';
$.get("<?= base_url('items/detail_simple/'); ?>"+id,function(res){
$('#id').val(res.id);
$('#nama_barang').val(res.nama_barang);
$('#harga_beli').val(res.harga_beli);
$('#harga_jual').val(res.harga_jual);
// 🔥 FIX DI SINI
$('#group_qty').hide();
$('#group_account').hide();
$('#group_kode').hide();
$('#group_warehouse').hide();
$('#group_tanggal').hide();
$('#modalItem').modal('show');
},'json');
});
// ================= SAVE =================
$('#btnSimpan').click(function(){
let btn = $(this);
let data = {
id: $('#id').val(),
kode_id: $('#kode_barang').val(),
nama_barang: $('#nama_barang').val(),
qty: $('#qty').val(),
harga_beli: $('#harga_beli').val(),
harga_jual: $('#harga_jual').val(),
warehouse_id: $('#warehouse_id').val(),
account_kas: $('#account_kas').val(),
tanggal_beli: $('#tanggal_beli').val(),
status: `draft`
};
let url = action==='add'
? "<?= base_url('items/save'); ?>"
: "<?= base_url('items/update'); ?>";
// 🔥 loading state
btn.prop('disabled', true).html('Menyimpan...');
$.post(url, data, function(res){
if(res.status){
$('#modalItem').modal('hide');
table.ajax.reload(null,false);
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Data berhasil disimpan',
timer: 1500,
showConfirmButton: false
});
}else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Terjadi kesalahan'
});
}
},'json')
.fail(function(){
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Koneksi ke server gagal'
});
})
.always(function(){
btn.prop('disabled', false).html('Simpan');
});
});
// =====================================================
// ================= START URUSAN KELUAR ===============
// =====================================================
// ================= RESET FORM KELUAR =================
function resetFormKeluar(){
$('#tanggal_keluar').val('');
$('#qty_keluar').val('');
$('#keterangan_keluar').val('');
}
loadKasOut();
loadGudangList();
// ================= BARANG KELUAR ================= account_biaya warehouse_id barang_id
$('.btn-out').click(function(){
// action='add';
resetFormKeluar();
$('#warehouse_id').closest('div').show();
$('#account_biaya').closest('div').show();
$('#modalKeluar').modal('show');
loadGudangList();
loadKasOut();
});
$('#warehouse_id_keluar').on('change', function(){
let warehouse_id = $(this).val();
// $('#wrap_item_select').addClass('d-none');
$('#barang_id').html('<option value="">Loading...</option>');
loadItems(warehouse_id);
});
// ================= ITEMS =================
function loadItems(warehouse_id){
return $.get("<?= base_url('items/get_items_by_wh_id/'); ?>" + warehouse_id, function(res){
if(res && res.length){
let opt = '<option value="">-- PILIH BARANG --</option>';
res.forEach(i=>{
opt += `<option value="${i.id}">
${i.kode_detail} - ${i.nama_barang} (Stok: ${i.stok})
</option>`;
});
$('#barang_id').html(opt);
$('#group_barang_keluar').removeClass('d-none');
$('#group_qty_keluar').removeClass('d-none');
$('#group_keterangan').removeClass('d-none');
} else {
$('#barang_id').html(opt);
}
}, 'json');
}
// ================= SAVE =================
$('#btnKeluar').click(function(){
let btn = $(this);
let data = {
account_biaya: $('#account_biaya').val(),
tanggal_keluar: $('#tanggal_keluar').val(),
warehouse_id_keluar: $('#warehouse_id_keluar').val(),
barang_id: $('#barang_id').val(),
qty_keluar: $('#qty_keluar').val(),
keterangan_keluar: $('#keterangan_keluar').val()
};
let url = "<?= base_url('items/keluarkan'); ?>";
// 🔥 loading state
btn.prop('disabled', true).html('Menyimpan...');
$.post(url, data, function(res){
if(res.status){
$('#modalItem').modal('hide');
table.ajax.reload(null,false);
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Data berhasil disimpan',
timer: 1500,
showConfirmButton: false
});
}else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Terjadi kesalahan'
});
}
},'json')
.fail(function(){
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Koneksi ke server gagal'
});
})
.always(function(){
btn.prop('disabled', false).html('Keluarkan');
});
});
// =====================================================
// ================= END URUSAN KELUAR ===============
// =====================================================
// ================= DELETE =================
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
let btn = $(this);
Swal.fire({
title: 'Hapus data?',
text: "Data tidak bisa dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, hapus!',
cancelButtonText: 'Batal'
}).then((result) => {
if(result.isConfirmed){
btn.prop('disabled', true).html('Menghapus...');
$.get("<?= base_url('items/delete/'); ?>"+id,function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Data berhasil dihapus',
timer: 1500,
showConfirmButton: false
});
}else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Gagal menghapus data'
});
}
},'json')
.fail(function(){
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Koneksi ke server gagal'
});
})
.always(function(){
btn.prop('disabled', false).html('Hapus');
});
}
});
});
// ================= POSTING =================
$(document).on('click','.btn-updatePosting',function(){
let id = $(this).data('id');
let btn = $(this);
if(btn.data('loading')) return;
Swal.fire({
title: 'Konfirmasi Penerimaan Barang',
text: 'Pastikan barang sudah diterima dan sesuai pesanan. Stok akan otomatis ditambahkan ke gudang.',
icon: 'info',
showCancelButton: true,
confirmButtonColor: '#28a745',
cancelButtonColor: '#6c757d',
confirmButtonText: 'Ya, Terima Barang',
cancelButtonText: 'Batal'
}).then((result) => {
if(result.isConfirmed){
btn.data('loading', true)
.prop('disabled', true)
.html('Memposting...');
$.post("<?= base_url('items/posting'); ?>", {
id: id
// tambahkan CSRF kalau perlu
}, function(res){
if(res && res.status){
table.ajax.reload(null,false);
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Data berhasil diposting',
timer: 1500,
showConfirmButton: false
});
}else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res?.message || 'Gagal memposting data'
});
}
},'json')
.fail(function(xhr){
Swal.fire({
icon: 'error',
title: 'Error',
text: xhr.responseJSON?.message || 'Server error ('+xhr.status+')'
});
})
.always(function(){
btn.prop('disabled', false)
.html('Posting')
.data('loading', false);
});
}
});
});
// ================= DETAIL =================
$(document).on('click','.btn-detail',function(){
let id = $(this).data('id');
$.get("<?= base_url('items/detail/'); ?>"+id,function(res){
let html = `
<b>Nama :</b> ${res.item.nama_barang}<br>
<b>Harga Beli :</b> ${formatRupiah(res.item.harga_beli)}<br>
<b>Harga Jual :</b> ${formatRupiah(res.item.harga_jual)}
<hr>
<h6>Riwayat Stok</h6>
<table class="table table-sm table-bordered">
<tr>
<th>Tanggal</th>
<th>Gudang</th>
<th>Qty</th>
<th>Tipe</th>
<th>Keterangan</th>
</tr>
`;
res.logs.forEach(l=>{
html += `
<tr>
<td>${l.created_at ?? '-'}</td>
<td>${l.gudang ?? '-'}</td>
<td>${l.qty}</td>
<td>${l.tipe}</td>
<td>${l.keterangan ?? ''}</td>
</tr>`;
});
html += `</table>`;
$('#detailContent').html(html);
$('#modalDetail').modal('show');
},'json');
});
// ================= OPEN ADJUST =================
$(document).on('click','.btn-adjust',function(){
let id = $(this).data('id');
if(!id){
alert('Pilih item dari tabel (gunakan tombol Adjust di baris)');
return;
}
selectedItemId = id;
$('#adjust_item_id').val(id);
$('#adjust_qty').val('');
$('#adjust_ket').val('');
$('#modalAdjust').modal('show');
});
// ================= SAVE ADJUST =================
$('#btnAdjust').click(function(){
let btn = $(this);
let data = {
item_id: $('#adjust_item_id').val(),
qty: $('#adjust_qty').val(),
warehouse_id: $('#adjust_warehouse').val(),
keterangan: $('#adjust_ket').val()
};
// 🔥 loading state
btn.prop('disabled', true).html('Menyimpan...');
$.post("<?= base_url('items/adjust'); ?>", data, function(res){
if(res.status){
$('#modalAdjust').modal('hide');
table.ajax.reload(null,false);
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Stok berhasil disesuaikan',
timer: 1500,
showConfirmButton: false
});
}else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Gagal melakukan penyesuaian'
});
}
},'json')
.fail(function(){
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Koneksi ke server gagal'
});
})
.always(function(){
btn.prop('disabled', false).html('Simpan');
});
});
});
</script>
+685
View File
@@ -0,0 +1,685 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5>Data Barang</h5>
<div class="text-end">
<button class="btn btn-add bg-danger btn-out mr-2">Pengeluaran</button>
<button class="btn btn-warning btn-add btn-add-item mr-2">Tambah Barang</button>
</div>
</div>
<div class="table-responsive">
<table id="tableItems" class="table modern-table w-100">
<thead>
<tr>
<th>No</th>
<th>Tanggal Beli</th>
<th>Kode Barang</th>
<th>Nama</th>
<th style="max-width: 150px;">Gudang</th>
<th>Stok</th>
<th>Harga Beli</th>
<th>Harga Jual</th>
<th>Create at</th>
<th>Aksi</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalItem">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5>Item</h5>
</div>
<div class="modal-body">
<input type="hidden" id="id">
<div id="group_tanggal">
<label>Tanggal Pembelian</label>
<input type="date" id="tanggal_beli" class="form-control">
</div>
<div id="group_kode">
<label class="mt-2">Kode Barang</label>
<select id="kode_barang" class="form-control select-search"></select>
</div>
<label>Nama Barang</label>
<input type="text" id="nama_barang" class="form-control">
<div id="group_qty">
<label class="mt-2">Jumlah Barang</label>
<input type="number" id="qty" class="form-control">
</div>
<label class="mt-2">Harga Beli</label>
<input type="text" id="harga_beli" class="form-control format-rupiah">
<label class="mt-2">Harga Jual</label>
<input type="text" id="harga_jual" class="form-control format-rupiah">
<div id="group_warehouse">
<label class="mt-2">Gudang</label>
<select id="warehouse_id" class="form-control"></select>
</div>
<div id="group_account">
<label class="mt-2">Akun Kas</label>
<select id="account_kas" class="form-control"></select>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-warning" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<!-- MODAL BARANAG KELUAR -->
<div class="modal fade" id="modalKeluar">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5>Barang Keluar</h5>
</div>
<div class="modal-body">
<div id="group_account_biaya">
<label class="mt-2">Akun Biaya</label>
<select id="account_biaya" class="form-control"></select>
</div>
<div>
<label>Tanggal</label>
<input type="date" id="tanggal_keluar" class="form-control">
</div>
<div>
<label class="mt-2">Gudang</label>
<select id="warehouse_id_keluar" class="form-control"></select>
</div>
<div class="d-none" id="group_barang_keluar">
<label class="mt-2">Barang</label>
<select id="barang_id" class="form-control select-search"></select>
</div>
<div class="d-none" id="group_qty_keluar">
<label class="mt-2">Jumlah Barang</label>
<input type="number" id="qty_keluar" class="form-control">
</div>
<div class="mb-2 d-none" id="group_keterangan">
<label for="keterangan" class="form-label">Keterangan</label>
<textarea class="form-control" id="keterangan_keluar" rows="3"></textarea>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-danger" id="btnKeluar">Keluarkan</button>
</div>
</div>
</div>
</div>
<!-- MODAL DETAIL -->
<div class="modal fade" id="modalDetail">
<div class="modal-dialog modal-xl">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5>Detail Item</h5>
</div>
<div class="modal-body" id="detailContent"></div>
</div>
</div>
</div>
<div class="modal fade" id="modalAdjust">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning">
<h5>Penyesuaian Stok</h5>
</div>
<div class="modal-body">
<input type="hidden" id="adjust_item_id">
<label>Qty Adjustment</label>
<input type="number" id="adjust_qty" class="form-control">
<small>Gunakan minus (-) jika mengurangi</small>
<label class="mt-2">Gudang</label>
<select id="adjust_warehouse" class="form-control"></select>
<label class="mt-2">Keterangan</label>
<input type="text" id="adjust_ket" class="form-control">
</div>
<div class="modal-footer">
<button class="btn btn-warning" id="btnAdjust">Simpan</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
let action = 'add';
let selectedItemId = null;
// ================= DATATABLE =================
let table = $('#tableItems').DataTable({
ajax: {
url: "<?= base_url('items/get_data'); ?>",
data: function (d) {
d.s = `active`;
}
}
});
// ================= LOAD AKUN KAS =================
function loadKas(){
$.get("<?= base_url('items/get_accounts'); ?>",res=>{
let opt='<option value="">-- PILIH KAS --</option>';
res.forEach(a=>{
opt += `<option value="${a.id}">${a.nama_akun}</option>`;
});
$('#account_kas').html(opt);
},'json');
}
// ================= LOAD AKUN KAS =================
function loadKasOut(){
$.get("<?= base_url('items/get_accounts_biaya'); ?>",res=>{
let opt='<option value="">-- PILIH KAS --</option>';
res.forEach(a=>{
opt += `<option value="${a.id}">${a.nama_akun}</option>`;
});
$('#account_biaya').html(opt);
},'json');
}
// ================= LOAD GUDANG =================
function loadGudang(){
$.get("<?= base_url('warehouses/list'); ?>",res=>{
let opt='<option value="">-- PILIH GUDANG --</option>';
res.forEach(w=>{
opt += `<option value="${w.id}">${w.nama}</option>`;
});
$('#warehouse_id, #adjust_warehouse').html(opt);
},'json');
}
// ================= LOAD GUDANG =================
function loadGudangList(){
$.get("<?= base_url('warehouses/list'); ?>",res=>{
let opt='<option value="">-- PILIH GUDANG --</option>';
res.forEach(w=>{
opt += `<option value="${w.id}">${w.nama}</option>`;
});
$('#warehouse_id_keluar').html(opt);
},'json');
}
function loadKode(){
$.get("<?= base_url('kodebarang/list'); ?>",res=>{
let opt='<option value="">-- PILIH KODE --</option>';
res.forEach(a=>{
opt += `<option value="${a.id}">${a.kode_barang} - ${a.nama}</option>`;
});
$('#kode_barang').html(opt);
},'json');
}
// ================= RESET FORM =================
function resetForm(){
$('#id').val('');
$('#nama_barang').val('');
$('#qty').val('');
$('#harga_beli').val('');
$('#harga_jual').val('');
$('#tanggal_beli').val('');
}
// ================= INIT LOAD =================
loadGudang();
loadKas();
loadKode();
// ================= ADD =================
$('.btn-add-item').click(function(){
action='add';
resetForm();
$('#qty').closest('div').show();
$('#kode_barang').closest('div').show();
$('#warehouse_id').closest('div').show();
$('#account_kas').closest('div').show();
$('#modalItem').modal('show');
loadGudang();
loadKas();
loadKode();
});
// ================= EDIT =================
$(document).on('click','.btn-editItem',function(){
let id = $(this).data('id');
action='edit';
$.get("<?= base_url('items/detail_simple/'); ?>"+id,function(res){
$('#id').val(res.id);
$('#nama_barang').val(res.nama_barang);
$('#harga_beli').val(res.harga_beli);
$('#harga_jual').val(res.harga_jual);
// 🔥 FIX DI SINI
$('#group_qty').hide();
$('#group_account').hide();
$('#group_kode').hide();
$('#group_warehouse').hide();
$('#group_tanggal').hide();
$('#modalItem').modal('show');
},'json');
});
// ================= SAVE =================
$('#btnSimpan').click(function(){
let btn = $(this);
let data = {
id: $('#id').val(),
kode_id: $('#kode_barang').val(),
nama_barang: $('#nama_barang').val(),
qty: $('#qty').val(),
harga_beli: $('#harga_beli').val(),
harga_jual: $('#harga_jual').val(),
warehouse_id: $('#warehouse_id').val(),
account_kas: $('#account_kas').val(),
tanggal_beli: $('#tanggal_beli').val(),
status: `active`
};
let url = action==='add'
? "<?= base_url('items/save'); ?>"
: "<?= base_url('items/update'); ?>";
// 🔥 loading state
btn.prop('disabled', true).html('Menyimpan...');
$.post(url, data, function(res){
if(res.status){
$('#modalItem').modal('hide');
table.ajax.reload(null,false);
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Data berhasil disimpan',
timer: 1500,
showConfirmButton: false
});
}else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Terjadi kesalahan'
});
}
},'json')
.fail(function(){
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Koneksi ke server gagal'
});
})
.always(function(){
btn.prop('disabled', false).html('Simpan');
});
});
// =====================================================
// ================= START URUSAN KELUAR ===============
// =====================================================
// ================= RESET FORM KELUAR =================
function resetFormKeluar(){
$('#tanggal_keluar').val('');
$('#qty_keluar').val('');
$('#keterangan_keluar').val('');
}
loadKasOut();
loadGudangList();
// ================= BARANG KELUAR ================= account_biaya warehouse_id barang_id
$('.btn-out').click(function(){
// action='add';
resetFormKeluar();
$('#warehouse_id').closest('div').show();
$('#account_biaya').closest('div').show();
$('#modalKeluar').modal('show');
loadGudangList();
loadKasOut();
});
$('#warehouse_id_keluar').on('change', function(){
let warehouse_id = $(this).val();
// $('#wrap_item_select').addClass('d-none');
$('#barang_id').html('<option value="">Loading...</option>');
loadItems(warehouse_id);
});
// ================= ITEMS =================
function loadItems(warehouse_id){
return $.get("<?= base_url('items/get_items_by_wh_id/'); ?>" + warehouse_id, function(res){
if(res && res.length){
let opt = '<option value="">-- PILIH BARANG --</option>';
res.forEach(i=>{
opt += `<option value="${i.id}">
${i.kode_detail} - ${i.nama_barang} (Stok: ${i.stok})
</option>`;
});
$('#barang_id').html(opt);
$('#group_barang_keluar').removeClass('d-none');
$('#group_qty_keluar').removeClass('d-none');
$('#group_keterangan').removeClass('d-none');
} else {
$('#barang_id').html(opt);
}
}, 'json');
}
// ================= SAVE =================
$('#btnKeluar').click(function(){
let btn = $(this);
let data = {
account_biaya: $('#account_biaya').val(),
tanggal_keluar: $('#tanggal_keluar').val(),
warehouse_id_keluar: $('#warehouse_id_keluar').val(),
barang_id: $('#barang_id').val(),
qty_keluar: $('#qty_keluar').val(),
keterangan_keluar: $('#keterangan_keluar').val()
};
let url = "<?= base_url('items/keluarkan'); ?>";
// 🔥 loading state
btn.prop('disabled', true).html('Menyimpan...');
$.post(url, data, function(res){
if(res.status){
$('#modalItem').modal('hide');
table.ajax.reload(null,false);
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Data berhasil disimpan',
timer: 1500,
showConfirmButton: false
});
}else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Terjadi kesalahan'
});
}
},'json')
.fail(function(){
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Koneksi ke server gagal'
});
})
.always(function(){
btn.prop('disabled', false).html('Keluarkan');
});
});
// =====================================================
// ================= END URUSAN KELUAR ===============
// =====================================================
// ================= DELETE =================
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
let btn = $(this);
Swal.fire({
title: 'Hapus data?',
text: "Data tidak bisa dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, hapus!',
cancelButtonText: 'Batal'
}).then((result) => {
if(result.isConfirmed){
btn.prop('disabled', true).html('Menghapus...');
$.get("<?= base_url('items/delete/'); ?>"+id,function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Data berhasil dihapus',
timer: 1500,
showConfirmButton: false
});
}else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Gagal menghapus data'
});
}
},'json')
.fail(function(){
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Koneksi ke server gagal'
});
})
.always(function(){
btn.prop('disabled', false).html('Hapus');
});
}
});
});
// ================= DETAIL =================
$(document).on('click','.btn-detail',function(){
let id = $(this).data('id');
$.get("<?= base_url('items/detail/'); ?>"+id,function(res){
let html = `
<b>Nama :</b> ${res.item.nama_barang}<br>
<b>Harga Beli :</b> ${formatRupiah(res.item.harga_beli)}<br>
<b>Harga Jual :</b> ${formatRupiah(res.item.harga_jual)}
<hr>
<h6>Riwayat Stok</h6>
<table class="table table-sm table-bordered">
<tr>
<th>Tanggal</th>
<th>Gudang</th>
<th>Qty</th>
<th>Tipe</th>
<th>Keterangan</th>
</tr>
`;
res.logs.forEach(l=>{
html += `
<tr>
<td>${l.created_at ?? '-'}</td>
<td>${l.gudang ?? '-'}</td>
<td>${l.qty}</td>
<td>${l.tipe}</td>
<td>${l.keterangan ?? ''}</td>
</tr>`;
});
html += `</table>`;
$('#detailContent').html(html);
$('#modalDetail').modal('show');
},'json');
});
// ================= OPEN ADJUST =================
$(document).on('click','.btn-adjust',function(){
let id = $(this).data('id');
if(!id){
alert('Pilih item dari tabel (gunakan tombol Adjust di baris)');
return;
}
selectedItemId = id;
$('#adjust_item_id').val(id);
$('#adjust_qty').val('');
$('#adjust_ket').val('');
$('#modalAdjust').modal('show');
});
// ================= SAVE ADJUST =================
$('#btnAdjust').click(function(){
let btn = $(this);
let data = {
item_id: $('#adjust_item_id').val(),
qty: $('#adjust_qty').val(),
warehouse_id: $('#adjust_warehouse').val(),
keterangan: $('#adjust_ket').val()
};
// 🔥 loading state
btn.prop('disabled', true).html('Menyimpan...');
$.post("<?= base_url('items/adjust'); ?>", data, function(res){
if(res.status){
$('#modalAdjust').modal('hide');
table.ajax.reload(null,false);
Swal.fire({
icon: 'success',
title: 'Berhasil',
text: res.message || 'Stok berhasil disesuaikan',
timer: 1500,
showConfirmButton: false
});
}else{
Swal.fire({
icon: 'error',
title: 'Gagal',
text: res.message || 'Gagal melakukan penyesuaian'
});
}
},'json')
.fail(function(){
Swal.fire({
icon: 'error',
title: 'Error',
text: 'Koneksi ke server gagal'
});
})
.always(function(){
btn.prop('disabled', false).html('Simpan');
});
});
});
</script>
+510
View File
@@ -0,0 +1,510 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5>Jurnal Umum</h5>
<button class="btn btn-warning btn-add">Tambah Jurnal</button>
</div>
<!--<form id="filterForm" class="row mb-3">-->
<!-- <div class="col-md-4">-->
<!-- <label>Dari Tanggal</label>-->
<!-- <input type="date" id="tanggal_dari" class="form-control">-->
<!-- </div>-->
<!-- <div class="col-md-4">-->
<!-- <label>Sampai Tanggal</label>-->
<!-- <input type="date" id="tanggal_sampai" class="form-control">-->
<!-- </div>-->
<!-- <div class="col-md-4 d-flex align-items-end">-->
<!-- <button type="submit" class="btn btn-warning w-100">-->
<!-- Tampilkan-->
<!-- </button>-->
<!-- </div>-->
<!--</form>-->
<div class="table-responsive">
<table id="tableJurnal" class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th>No</th>
<th>Tanggal</th>
<th>No Ref</th>
<th>Keterangan</th>
<th class="text-end">Debit</th>
<th class="text-end">Kredit</th>
<th>Aksi</th>
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr class="fw-bold">
<td colspan="4" class="text-end">TOTAL</td>
<td class="text-end" id="totalDebitTable">0</td>
<td class="text-end" id="totalKreditTable">0</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL INPUT -->
<div class="modal fade" id="modalJurnal">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5>Tambah Jurnal</h5>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<!-- MODE -->
<div class="mb-3">
<div class="btn-group w-100">
<button class="btn btn-warning active" id="modeAuto">Otomatis</button>
<button class="btn btn-outline-secondary" id="modeManual">Manual</button>
</div>
</div>
<!-- TEMPLATE -->
<div id="autoSection" class="mb-3">
<label>Template Jurnal</label>
<select id="base_jurnal_id" class="form-control select-search">
<option value="">-- Pilih Template --</option>
</select>
</div>
<!-- FORM -->
<input type="date" id="tanggal" class="form-control mb-2">
<textarea id="keterangan" class="form-control mb-3" placeholder="Keterangan"></textarea>
<!-- TABLE -->
<table class="table" id="tableDetail">
<thead>
<tr>
<th>Akun</th>
<th>Debit</th>
<th>Kredit</th>
<th></th>
</tr>
</thead>
<tbody></tbody>
<tfoot>
<tr class="fw-bold">
<td class="text-end">TOTAL</td>
<td id="formTotalDebit">0</td>
<td id="formTotalKredit">0</td>
<td></td>
</tr>
</tfoot>
</table>
<button class="btn btn-warning btn-add-row">Tambah Baris</button>
</div>
<div class="modal-footer">
<button class="btn btn-warning" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<!-- MODAL DETAIL -->
<div class="modal fade" id="modalDetail">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5>Detail Jurnal</h5>
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="mb-3">
<strong>Tanggal:</strong> <span id="d_tanggal"></span><br>
<strong>No Ref:</strong> <span id="d_noref"></span><br>
<strong>Keterangan:</strong> <span id="d_keterangan"></span><br>
<strong>Dibuat oleh:</strong> <span id="d_createdby"></span>
</div>
<table class="table table-bordered">
<thead>
<tr>
<th>Akun</th>
<th>Debit</th>
<th>Kredit</th>
</tr>
</thead>
<tbody id="detailBody"></tbody>
<tfoot>
<tr>
<th>Total</th>
<th id="totalDebitDetail">0</th>
<th id="totalKreditDetail">0</th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
<style>
#tableJurnal tbody tr:hover {
background-color: #fff3cd;
}
</style>
<script>
$(function(){
let accounts = [];
let mode = 'auto';
// ================= LOAD ACCOUNTS
function loadAccounts(){
return $.get("<?= base_url('jurnal/get_accounts'); ?>", function(res){
accounts = res;
},'json');
}
// ================= LOAD BASE JURNAL
function loadBaseJurnal(){
return $.get("<?= base_url('jurnal/get_base_jurnal'); ?>", function(res){
let opt = '<option value="">-- Pilih Template --</option>';
res.forEach(b=>{
opt += `<option value="${b.id}">${b.kode} - ${b.nama}</option>`;
});
$('#base_jurnal_id').html(opt);
},'json');
}
function accountOptions(){
let opt = '<option value="">Pilih Akun</option>';
accounts.forEach(a=>{
opt += `<option value="${a.id}">${a.kode_akun} - ${a.nama_akun}</option>`;
});
return opt;
}
// ================= ADD ROW
function addRow(data=null){
let isAuto = (mode === 'auto');
let debitVal = data?.debit || '';
let kreditVal = data?.kredit || '';
let hideDebit = isAuto && data?.posisi === 'kredit';
let hideKredit = isAuto && data?.posisi === 'debit';
let row = `
<tr>
<td>
<select class="form-control account_id" ${isAuto ? 'disabled' : ''}>
${accountOptions()}
</select>
</td>
<td>
<input type="text" class="form-control debit format-rupiah"
value="${debitVal}"
${hideDebit ? 'style="display:none"' : ''}
>
</td>
<td>
<input type="text" class="form-control kredit format-rupiah"
value="${kreditVal}"
${hideKredit ? 'style="display:none"' : ''}
>
</td>
<td class="text-center">
${isAuto ? '' : '<button class="btn btn-danger btn-remove">X</button>'}
</td>
</tr>
`;
$('#tableDetail tbody').append(row);
if(data){
$('#tableDetail tbody tr:last .account_id').val(data.account_id);
}
}
// ================= LOAD TEMPLATE DETAIL
function loadTemplateDetail(id){
if(!id) return;
$.get("<?= base_url('jurnal/get_base_jurnal_detail/'); ?>" + id, function(res){
$('#tableDetail tbody').html('');
res.forEach(r=>{
addRow({
account_id: r.account_id,
posisi: r.posisi, // penting!
debit: r.posisi === 'debit' ? 0 : '',
kredit: r.posisi === 'kredit' ? 0 : ''
});
});
hitungTotalForm();
},'json');
}
$('#modeAuto').click(function(){
mode = 'auto';
$(this).addClass('btn-warning active').removeClass('btn-outline-warning');
$('#modeManual').removeClass('btn-warning active').addClass('btn-outline-secondary');
$('#autoSection').show();
// ✅ RESET TOTAL FORM
$('#tableDetail tbody').html('');
$('#base_jurnal_id').val('');
// reset total juga
$('#formTotalDebit').text('0');
$('#formTotalKredit').text('0');
// sembunyikan tombol tambah
$('.btn-add-row').hide();
});
$('#modeManual').click(function(){
mode = 'manual';
$(this).addClass('btn-warning active').removeClass('btn-outline-secondary');
$('#modeAuto').removeClass('btn-warning active').addClass('btn-outline-warning');
$('#autoSection').hide();
$('#tableDetail tbody').html('');
$('.btn-add-row').show(); // ⬅️ tampilkan lagi
addRow();
});
// ================= PILIH TEMPLATE
$('#base_jurnal_id').change(function(){
loadTemplateDetail($(this).val());
});
// ================= ADD ROW BUTTON
$('.btn-add-row').click(function(){
if(mode === 'manual'){
addRow();
}else{
Swal.fire('Info','Mode otomatis pakai template','info');
}
});
// ================= REMOVE ROW
$(document).on('click','.btn-remove',function(){
$(this).closest('tr').remove();
hitungTotalForm();
});
// ================= HITUNG TOTAL
function hitungTotalForm(){
let td=0, tk=0;
$('#tableDetail tbody tr').each(function(){
td += parseFloat($(this).find('.debit').val()) || 0;
tk += parseFloat($(this).find('.kredit').val()) || 0;
});
$('#formTotalDebit').text(td.toLocaleString('id-ID'));
$('#formTotalKredit').text(tk.toLocaleString('id-ID'));
}
$(document).on('keyup change','.debit, .kredit',hitungTotalForm);
// ================= OPEN MODAL
$('.btn-add').click(async function(){
await loadAccounts();
await loadBaseJurnal();
$('#modalJurnal').modal('show');
$('#tableDetail tbody').html('');
$('#base_jurnal_id').val('');
// reset total juga
$('#formTotalDebit').text('0');
$('#formTotalKredit').text('0');
$('#modeAuto').click(); // default
});
$('#btnSimpan').click(function(){
let account_id = [];
let debit = [];
let kredit = [];
let totalDebit = 0;
let totalKredit = 0;
$('#tableDetail tbody tr').each(function(){
let d = parseFloat($(this).find('.debit').val()) || 0;
let k = parseFloat($(this).find('.kredit').val()) || 0;
account_id.push($(this).find('.account_id').val());
debit.push(d);
kredit.push(k);
totalDebit += d;
totalKredit += k;
});
// ✅ VALIDASI DI SINI
if(totalDebit !== totalKredit){
Swal.fire('Warning','Total Debit dan Kredit harus sama!','warning');
return;
}
// lanjut simpan
$.post("<?= base_url('jurnal/save'); ?>", {
tanggal: $('#tanggal').val(),
keterangan: $('#keterangan').val(),
account_id: account_id,
debit: debit,
kredit: kredit
}, function(res){
if(res.status){
$('#modalJurnal').modal('hide');
$('#tableJurnal').DataTable().ajax.reload(null,false);
Swal.fire('Sukses',res.message,'success');
} else {
Swal.fire('Error',res.message,'error');
}
},'json');
});
// DATATABLE
let table = $('#tableJurnal').DataTable({
processing:true,
serverSide:true,
pageLength:10, // default 10
lengthMenu:[ [10,25,50,100], [10,25,50,100] ],
order:[[1,'desc']],
ajax:{
url:"<?= base_url('jurnal/get_data'); ?>",
type:"POST"
},
columnDefs:[
{ targets:[4,5], className:'text-end' }
],
drawCallback:function(){
let api = this.api();
let td = 0, tk = 0;
api.rows().every(function(){
let d = this.data();
td += parseFloat(d[4].replace(/\./g,'')) || 0;
tk += parseFloat(d[5].replace(/\./g,'')) || 0;
});
$('#totalDebitTable').text(td.toLocaleString('id-ID'));
$('#totalKreditTable').text(tk.toLocaleString('id-ID'));
}
});
// DELETE
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
Swal.fire({
title:'Hapus?',
icon:'warning',
showCancelButton:true
}).then(r=>{
if(r.isConfirmed){
$.get("<?= base_url('jurnal/delete/'); ?>"+id,function(){
table.ajax.reload(null,false);
});
}
});
});
});
// DETAIL
$(document).on('click','.btn-detail',function(){
let id = $(this).data('id');
$.get("<?= base_url('jurnal/detail/'); ?>" + id, function(res){
$('#d_tanggal').text(res.header.tanggal);
$('#d_noref').text(res.header.no_ref);
$('#d_keterangan').text(res.header.keterangan);
$('#d_createdby').text(res.user.nama);
let html = '';
let totalDebit = 0;
let totalKredit = 0;
res.detail.forEach(row => {
totalDebit += parseFloat(row.debit);
totalKredit += parseFloat(row.kredit);
html += `
<tr>
<td>${row.kode_akun} - ${row.nama_akun}</td>
<td>${parseFloat(row.debit).toLocaleString('id-ID')}</td>
<td>${parseFloat(row.kredit).toLocaleString('id-ID')}</td>
</tr>
`;
});
$('#detailBody').html(html);
$('#totalDebitDetail').text(totalDebit.toLocaleString('id-ID'));
$('#totalKreditDetail').text(totalKredit.toLocaleString('id-ID'));
$('#modalDetail').modal('show');
},'json');
});
</script>
+241
View File
@@ -0,0 +1,241 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">Data Kamar</h5>
<div>
<?php if(check_permission('kamar', 'can_create')): ?>
<button class="btn btn-success btn-add" style="margin-right: 5px;">
<i class="bi bi-plus-circle me-1"></i> Tambah Kamar
</button>
<?php endif; ?>
</div>
</div>
<div class="table-responsive">
<table id="tableKamar" class="table modern-table align-middle w-100">
<thead>
<tr>
<th>No</th>
<th>Nama Kamar</th>
<th>Gedung</th>
<th>Aksi</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL TAMBAH / EDIT KAMAR -->
<div class="modal fade" id="modalKamar">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-4">
<div class="modal-header bg-success text-white rounded-top-4">
<h5 class="modal-title">Tambah Kamar</h5>
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="inputId">
<label class="fw-semibold mt-2">Nama Kamar</label>
<input type="text" class="form-control" id="inputNama" placeholder="Masukkan nama kamar">
<label class="fw-semibold mt-2">Gedung</label>
<input type="text" class="form-control" id="inputGedung" placeholder="Masukkan nama gedung">
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button class="btn btn-success" id="btnSimpanKamar">Simpan</button>
</div>
</div>
</div>
</div>
<!-- MODAL DETAIL KAMAR -->
<div class="modal fade" id="modalDetailKamar">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-4 shadow">
<div class="modal-header bg-primary text-white rounded-top-4">
<h5 class="modal-title">Detail Kamar</h5>
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body p-4">
<div class="d-flex flex-column gap-2">
<div class="p-3 border rounded-3 bg-light">
<span class="text-success fw-bold">Nama Kamar:</span>
<div id="detailNamaKamar" class="fw-semibold"></div>
</div>
<div class="p-3 border rounded-3 bg-light">
<span class="text-success fw-bold">Gedung:</span>
<div id="detailGedung"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-bs-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function(){
let table = $('#tableKamar').DataTable({
dom: '<"dataTables_top d-flex justify-content-between mb-3"l f>rtip',
processing: true,
serverSide: true,
responsive: false,
autoWidth: false,
order: [],
ajax: {
url: "<?= base_url('kamar/get_data'); ?>",
type: "POST"
},
columns: [
{ data: 0, orderable: false },
{ data: 1 },
{ data: 2 },
{ data: 3, orderable: false } // Kolom Aksi
],
language: {
processing: `<div class="d-flex justify-content-center"><div class="spinner-border text-success"></div></div>`
}
});
$('.dataTables_filter input').attr('placeholder', 'Search...').css({
'min-width': '300px',
'padding': '6px 12px',
'border-radius': '6px',
'border': '1px solid #ced4da',
'font-size': '0.9rem',
'box-sizing': 'border-box'
});
table.on('draw', function () {
$('#tableKamar tbody tr').each(function() {
$(this).find('td').each(function(index) {
let header = $('#tableKamar thead th').eq(index).text().trim();
$(this).attr('data-label', header + ' : ');
});
});
});
// OPEN MODAL TAMBAH
$(document).on('click', '.btn-add', function(){
$('#inputId, #inputNama, #inputGedung').val('');
$('#modalKamar .modal-title').text('Tambah Kamar');
$('#btnSimpanKamar').text('Simpan').data('action','add').removeData('id');
$('#modalKamar').modal('show');
});
// SAVE / UPDATE KAMAR
$('#btnSimpanKamar').click(function(){
let action = $(this).data('action');
let id = $('#inputId').val() || '';
let formData = {
id: id,
nama: $('#inputNama').val(),
gedung: $('#inputGedung').val()
};
let url = action === 'add' ? "<?= base_url('kamar/save'); ?>" : "<?= base_url('kamar/update'); ?>";
$.ajax({
url: url,
type: 'POST',
data: formData,
dataType: 'json',
success: function(res){
if(res.status){
$('#modalKamar').modal('hide');
table.ajax.reload(null,false);
Swal.fire('Sukses', res.message || 'Berhasil!', 'success');
} else {
Swal.fire('Gagal', res.message || 'Gagal!', 'error');
}
},
error: function(){
Swal.fire('Error', 'Terjadi kesalahan server.', 'error');
}
});
});
// DETAIL KAMAR
$(document).on('click', '.btn-detail', function(){
let id = $(this).data('id');
$.ajax({
url: "<?= base_url('kamar/detail'); ?>/" + id,
type: "GET",
dataType: "json",
success: function(res){
$('#detailNamaKamar').text(res.nama);
$('#detailGedung').text(res.gedung);
$('#modalDetailKamar').modal('show');
},
error: function(){
Swal.fire('Error','Gagal memuat detail.','error');
}
});
});
// EDIT KAMAR
$(document).on('click', '.btn-edit', function(){
let id = $(this).data('id');
$.ajax({
url: "<?= base_url('kamar/detail'); ?>/" + id,
type: "GET",
dataType: "json",
success: function(res){
$('#inputId').val(res.id);
$('#inputNama').val(res.nama);
$('#inputGedung').val(res.gedung);
$('#modalKamar .modal-title').text('Edit Kamar');
$('#btnSimpanKamar').text('Update').data('action','edit');
$('#modalKamar').modal('show');
},
error: function(){
Swal.fire('Error','Gagal memuat data edit.','error');
}
});
});
// DELETE KAMAR
$(document).on('click', '.btn-delete', function(){
let id = $(this).data('id');
Swal.fire({
title: 'Yakin ingin hapus?',
text: "Data tidak dapat dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus',
cancelButtonText: 'Batal'
}).then((result) => {
if(result.isConfirmed){
$.ajax({
url: "<?= base_url('kamar/delete'); ?>/" + id,
type: "GET",
dataType: "json",
success: function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire('Terhapus!', res.message || 'Berhasil dihapus!', 'success');
} else {
Swal.fire('Gagal', res.message || 'Gagal menghapus!', 'error');
}
},
error: function(){
Swal.fire('Error','Terjadi kesalahan saat menghapus.','error');
}
});
}
});
});
});
</script>
+253
View File
@@ -0,0 +1,253 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">Data Kelas</h5>
<div>
<?php if(check_permission('kelas', 'can_create')): ?>
<button class="btn btn-success btn-add" style="margin-right: 5px;">
<i class="bi bi-plus-circle me-1"></i> Tambah Kelas
</button>
<?php endif; ?>
</div>
</div>
<div class="table-responsive">
<table id="tableKelas" class="table modern-table align-middle w-100">
<thead>
<tr>
<th>No</th>
<th>Nama Kelas</th>
<th>Gedung</th>
<th>Wali Kelas</th>
<th>Aksi</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL TAMBAH / EDIT KELAS -->
<div class="modal fade" id="modalKelas">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-4">
<div class="modal-header bg-success text-white rounded-top-4">
<h5 class="modal-title">Tambah Kelas</h5>
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="inputId">
<label class="fw-semibold mt-2">Nama Kelas</label>
<input type="text" class="form-control" id="inputNama" placeholder="Masukkan nama kelas">
<label class="fw-semibold mt-2">Gedung</label>
<input type="text" class="form-control" id="inputGedung" placeholder="Masukkan nama gedung">
<label class="fw-semibold mt-2">Nama Wali Kelas</label>
<input type="text" class="form-control" id="inputWaliKelas" placeholder="Masukkan nama wali kelas">
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button class="btn btn-success" id="btnSimpanKelas">Simpan</button>
</div>
</div>
</div>
</div>
<!-- MODAL DETAIL KELAS -->
<div class="modal fade" id="modalDetailKelas">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-4 shadow">
<div class="modal-header bg-primary text-white rounded-top-4">
<h5 class="modal-title">Detail Kelas</h5>
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body p-4">
<div class="d-flex flex-column gap-2">
<div class="p-3 border rounded-3 bg-light">
<span class="text-success fw-bold">Nama Kelas:</span>
<div id="detailNamaKelas" class="fw-semibold"></div>
</div>
<div class="p-3 border rounded-3 bg-light">
<span class="text-success fw-bold">Gedung:</span>
<div id="detailGedung"></div>
</div>
<div class="p-3 border rounded-3 bg-light">
<span class="text-success fw-bold">Nama Wali Kelas:</span>
<div id="detailWaliKelas"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" data-bs-dismiss="modal">Tutup</button>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function(){
let table = $('#tableKelas').DataTable({
dom: '<"dataTables_top d-flex justify-content-between mb-3"l f>rtip',
processing: true,
serverSide: true,
responsive: false,
autoWidth: false,
order: [],
ajax: {
url: "<?= base_url('kelas/get_data'); ?>", // Sesuaikan dengan controller Kelas
type: "POST"
},
columns: [
{ data: 0, orderable: false },
{ data: 1 },
{ data: 2 },
{ data: 3 },
{ data: 4, orderable: false } // Kolom Aksi
],
language: {
processing: `<div class="d-flex justify-content-center"><div class="spinner-border text-success"></div></div>`
}
});
$('.dataTables_filter input').attr('placeholder', 'Search...').css({
'min-width': '300px',
'padding': '6px 12px',
'border-radius': '6px',
'border': '1px solid #ced4da',
'font-size': '0.9rem',
'box-sizing': 'border-box'
});
table.on('draw', function () {
$('#tableKelas tbody tr').each(function() {
$(this).find('td').each(function(index) {
let header = $('#tableKelas thead th').eq(index).text().trim();
$(this).attr('data-label', header + ' : ');
});
});
});
// OPEN MODAL TAMBAH
$(document).on('click', '.btn-add', function(){
$('#inputId, #inputNama, #inputGedung, #inputWaliKelas').val('');
$('#modalKelas .modal-title').text('Tambah Kelas');
$('#btnSimpanKelas').text('Simpan').data('action','add').removeData('id');
$('#modalKelas').modal('show');
});
// SAVE / UPDATE KELAS
$('#btnSimpanKelas').click(function(){
let action = $(this).data('action');
let id = $('#inputId').val() || ''; // Ambil dari hidden input
let formData = {
id: id,
nama: $('#inputNama').val(),
gedung: $('#inputGedung').val(),
nama_wali_kelas: $('#inputWaliKelas').val()
};
let url = action === 'add' ? "<?= base_url('kelas/save'); ?>" : "<?= base_url('kelas/update'); ?>";
$.ajax({
url: url,
type: 'POST',
data: formData,
dataType: 'json',
success: function(res){
if(res.status){
$('#modalKelas').modal('hide');
table.ajax.reload(null,false);
Swal.fire('Sukses', res.message || 'Berhasil!', 'success');
} else {
Swal.fire('Gagal', res.message || 'Gagal!', 'error');
}
},
error: function(){
Swal.fire('Error', 'Terjadi kesalahan server.', 'error');
}
});
});
// DETAIL KELAS
$(document).on('click', '.btn-detail', function(){
let id = $(this).data('id');
$.ajax({
url: "<?= base_url('kelas/detail'); ?>/" + id, // Sesuaikan dengan controller Kelas
type: "GET",
dataType: "json",
success: function(res){
$('#detailNamaKelas').text(res.nama);
$('#detailGedung').text(res.gedung);
$('#detailWaliKelas').text(res.nama_wali_kelas);
$('#modalDetailKelas').modal('show');
},
error: function(){
Swal.fire('Error','Gagal memuat detail.','error');
}
});
});
// EDIT KELAS
$(document).on('click', '.btn-edit', function(){
let id = $(this).data('id');
$.ajax({
url: "<?= base_url('kelas/detail'); ?>/" + id, // Sesuaikan dengan controller Kelas
type: "GET",
dataType: "json",
success: function(res){
$('#inputId').val(res.id); // Set ID ke hidden input
$('#inputNama').val(res.nama);
$('#inputGedung').val(res.gedung);
$('#inputWaliKelas').val(res.nama_wali_kelas);
$('#modalKelas .modal-title').text('Edit Kelas');
$('#btnSimpanKelas').text('Update').data('action','edit'); // ID sudah ada di hidden input
$('#modalKelas').modal('show');
},
error: function(){
Swal.fire('Error','Gagal memuat data edit.','error');
}
});
});
// DELETE KELAS
$(document).on('click', '.btn-delete', function(){
let id = $(this).data('id');
Swal.fire({
title: 'Yakin ingin hapus?',
text: "Data tidak dapat dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus',
cancelButtonText: 'Batal'
}).then((result) => {
if(result.isConfirmed){
$.ajax({
url: "<?= base_url('kelas/delete'); ?>/" + id, // Sesuaikan dengan controller Kelas
type: "GET",
dataType: "json",
success: function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire('Terhapus!', res.message || 'Berhasil dihapus!', 'success');
} else {
Swal.fire('Gagal', res.message || 'Gagal menghapus!', 'error');
}
},
error: function(){
Swal.fire('Error','Terjadi kesalahan saat menghapus.','error');
}
});
}
});
});
});
</script>
+168
View File
@@ -0,0 +1,168 @@
<!--Lokasi nya : /views/employees/employees.php-->
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5>Kode Barang</h5>
<button class="btn btn-warning btn-add">Tambah</button>
</div>
<div class="table-responsive">
<table id="tableWarehouse" class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th>No</th>
<th>Kode</th>
<th>Nama</th>
<th>Limit Stok</th>
<th>Aksi</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalKodebarang">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5 class="modal-title">Kode Barang</h5>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="id">
<label>Kode Barang</label>
<input type="text" id="kode" class="form-control">
<label>Nama Barang</label>
<input type="text" id="nama" class="form-control">
<label>Limit Stok</label>
<input type="number" id="limit_stock" class="form-control">
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button class="btn btn-warning" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
let action = 'add';
let table = $('#tableWarehouse').DataTable({
ajax:{
url:"<?= base_url('kodebarang/get_data'); ?>",
type:"POST"
}
});
function resetForm(){
$('#id').val('');
$('#kode').val('');
$('#nama').val('');
$('#limit_stock').val('');
}
// ADD
$('.btn-add').click(function(){
resetForm();
action = 'add';
$('#modalKodebarang').modal('show');
});
// EDIT
$(document).on('click','.btn-editkode',function(){
let id = $(this).data('id');
$.get("<?= base_url('kodebarang/detail/'); ?>"+id,function(res){
$('#id').val(res.id);
$('#kode').val(res.kode_barang);
$('#nama').val(res.nama);
$('#limit_stock').val(res.limit_stock);
action = 'edit';
$('#modalKodebarang').modal('show');
},'json');
});
// SAVE
$('#btnSimpan').click(function(){
let data = {
id: $('#id').val(),
kode: $('#kode').val(),
nama: $('#nama').val(),
limit_stock: $('#limit_stock').val()
};
if(!data.kode || !data.nama || !data.limit_stock){
Swal.fire('Warning','Nama gudang wajib diisi','warning');
return;
}
let url = action === 'add'
? "<?= base_url('kodebarang/save'); ?>"
: "<?= base_url('kodebarang/update'); ?>";
$.post(url,data,function(res){
if(res.status){
$('#modalKodebarang').modal('hide');
table.ajax.reload(null,false);
Swal.fire('Sukses',res.message,'success');
} else {
Swal.fire('Error',res.message,'error');
}
},'json');
});
// DELETE
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
Swal.fire({
title:'Hapus gudang?',
text:'Pastikan tidak dipakai item',
icon:'warning',
showCancelButton:true
}).then(r=>{
if(r.isConfirmed){
$.get("<?= base_url('kodebarang/delete/'); ?>"+id,function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire('Sukses',res.message,'success');
} else {
Swal.fire('Error',res.message,'error');
}
},'json');
}
});
});
});
</script>
+172
View File
@@ -0,0 +1,172 @@
<div class="container mt-4">
<div class="card shadow-sm border-0">
<div class="card-body">
<h4 class="mb-4 fw-bold text-center">Laporan Laba Rugi</h4>
<form id="filterForm" class="row g-2">
<div class="col-md-4">
<label>Dari Tanggal</label>
<input type="date" id="tanggal_dari" class="form-control">
</div>
<div class="col-md-4">
<label>Sampai Tanggal</label>
<input type="date" id="tanggal_sampai" class="form-control">
</div>
<div class="col-md-4 d-flex align-items-end">
<button type="submit" class="btn btn-warning w-100">
Tampilkan
</button>
</div>
</form>
<div id="content" class="mt-3"></div>
</div>
</div>
</div>
<script>
$(function(){
function loadData(dari = '', sampai = ''){
$.get("<?= base_url('labarugi/get_data'); ?>", {
tanggal_dari: dari,
tanggal_sampai: sampai
}, function(res){
let html = '<div class="row">';
// ================= PENDAPATAN
html += `
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-body">
<h5 class="text-success mb-3">Pendapatan</h5>
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Kode</th>
<th>Akun</th>
<th class="text-end">Jumlah</th>
</tr>
</thead>
<tbody>
`;
res.data.forEach(r=>{
if(r.tipe=='revenue'){
let saldo = (parseFloat(r.kredit) - parseFloat(r.debit)) || 0;
html += `
<tr>
<td>${r.kode_akun}</td>
<td>${r.nama_akun}</td>
<td class="text-end">${saldo.toLocaleString()}</td>
</tr>
`;
}
});
html += `
</tbody>
<tfoot>
<tr class="fw-bold border-top">
<td></td>
<td>Total Pendapatan</td>
<td class="text-end text-success">${Number(res.revenue).toLocaleString()}</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
`;
// ================= BEBAN
html += `
<div class="col-md-6">
<div class="card border-0 shadow-sm h-100">
<div class="card-body">
<h5 class="text-danger mb-3">Beban</h5>
<table class="table table-sm table-striped">
<thead>
<tr>
<th>Kode</th>
<th>Akun</th>
<th class="text-end">Jumlah</th>
</tr>
</thead>
<tbody>
`;
res.data.forEach(r=>{
if(r.tipe=='expense'){
let saldo = (parseFloat(r.debit) - parseFloat(r.kredit)) || 0;
html += `
<tr>
<td>${r.kode_akun}</td>
<td>${r.nama_akun}</td>
<td class="text-end">${saldo.toLocaleString()}</td>
</tr>
`;
}
});
html += `
</tbody>
<tfoot>
<tr class="fw-bold border-top">
<td></td>
<td>Total Beban</td>
<td class="text-end text-danger">${Number(res.expense).toLocaleString()}</td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
`;
html += '</div>';
// ================= LABA
html += `
<div class="mt-4">
<div class="card border-0 shadow-sm">
<div class="card-body text-center">
<h5 class="mb-2">Laba Bersih</h5>
<h3 class="${res.laba >= 0 ? 'text-success' : 'text-danger'} fw-bold">
${Number(res.laba).toLocaleString()}
</h3>
</div>
</div>
</div>
`;
$('#content').html(html);
},'json');
}
// LOAD AWAL
loadData();
// FILTER
$('#filterForm').on('submit', function(e){
e.preventDefault();
let dari = $('#tanggal_dari').val();
let sampai = $('#tanggal_sampai').val();
loadData(dari, sampai);
});
});
</script>
+151
View File
@@ -0,0 +1,151 @@
<div class="container mt-4">
<!-- ================= FILTER PERIODE -->
<div class="card mb-3">
<div class="card-body">
<h4 class="mb-4 fw-bold text-center">NERACA</h4><hr>
<form id="filterForm" class="row g-2">
<div class="col-md-4">
<label>Dari Tanggal</label>
<input type="date" name="tanggal_dari" id="tanggal_dari" class="form-control">
</div>
<div class="col-md-4">
<label>Sampai Tanggal</label>
<input type="date" name="tanggal_sampai" id="tanggal_sampai" class="form-control">
</div>
<div class="col-md-4 d-flex align-items-end">
<button type="submit" class="btn btn-warning w-100">
Tampilkan
</button>
</div>
</form>
</div>
</div>
<div class="row">
<!-- ACTIVA -->
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5>ACTIVA</h5>
<table class="table">
<tbody id="activaBody"></tbody>
<tfoot>
<tr class="fw-bold">
<td>Total Activa</td>
<td id="totalActiva"></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
<!-- PASIVA -->
<div class="col-md-6">
<div class="card">
<div class="card-body">
<h5>PASIVA</h5>
<table class="table">
<tbody id="pasivaBody"></tbody>
<tfoot>
<tr class="fw-bold">
<td>Total Pasiva</td>
<td id="totalPasiva"></td>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
$(function(){
function loadData(tanggal_dari = '', tanggal_sampai = ''){
$.get("<?= base_url('neraca/get_data'); ?>", {
tanggal_dari: tanggal_dari,
tanggal_sampai: tanggal_sampai
}, function(res){
let act = '', pas = '';
// ================= ACTIVA
res.activa.forEach(a=>{
act += `<tr>
<td>${a.kode} - ${a.nama}</td>
<td class="text-end">${Number(a.saldo).toLocaleString()}</td>
</tr>`;
});
// ================= PASIVA
res.pasiva.forEach(p=>{
pas += `<tr>
<td>${p.kode} - ${p.nama}</td>
<td class="text-end">${Number(p.saldo).toLocaleString()}</td>
</tr>`;
});
$('#activaBody').html(act);
$('#pasivaBody').html(pas);
$('#totalActiva').text(Number(res.total_activa).toLocaleString());
$('#totalPasiva').text(Number(res.total_pasiva).toLocaleString());
// reset warna dulu
$('#totalActiva, #totalPasiva')
.removeClass('text-danger text-success');
// ================= VALIDASI BALANCE
if(Math.round(res.total_activa) !== Math.round(res.total_pasiva)){
$('#totalActiva').addClass('text-danger');
$('#totalPasiva').addClass('text-danger');
Swal.fire({
icon: 'warning',
title: 'Neraca Tidak Balance!',
text: 'Selisih: ' + Number(res.selisih).toLocaleString()
});
} else {
$('#totalActiva').addClass('text-success');
$('#totalPasiva').addClass('text-success');
}
},'json');
}
// ================= LOAD AWAL
loadData();
// ================= SUBMIT FILTER
$('#filterForm').on('submit', function(e){
e.preventDefault();
let dari = $('#tanggal_dari').val();
let sampai = $('#tanggal_sampai').val();
loadData(dari, sampai);
});
});
</script>
@@ -0,0 +1,66 @@
<div class="container mt-4">
<div class="card shadow-sm border-0">
<div class="card-body">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="mb-0 fw-bold">Neraca Saldo</h4>
</div>
<div class="table-responsive">
<table class="table table-sm table-striped table-hover align-middle">
<thead class="table-light">
<tr>
<th style="width: 120px;">Kode</th>
<th>Akun</th>
<th class="text-end">Debit</th>
<th class="text-end">Kredit</th>
</tr>
</thead>
<tbody id="tb"></tbody>
<tfoot class="table-light">
<tr class="fw-bold">
<th colspan="2" class="text-end">Total</th>
<th class="text-end text-primary" id="td"></th>
<th class="text-end text-danger" id="tk"></th>
</tr>
</tfoot>
</table>
</div>
</div>
</div>
</div>
<script>
$(function(){
$.get("<?= base_url('neracasaldo/get_data'); ?>", function(res){
let html = '';
let td = 0, tk = 0;
res.forEach(r=>{
let debit = parseFloat(r.saldo_debit) || 0;
let kredit = parseFloat(r.saldo_kredit) || 0;
td += debit;
tk += kredit;
html += `
<tr>
<td class="fw-semibold text-muted">${r.kode_akun}</td>
<td>${r.nama_akun}</td>
<td class="text-end">${debit.toLocaleString()}</td>
<td class="text-end">${kredit.toLocaleString()}</td>
</tr>`;
});
$('#tb').html(html);
$('#td').text(td.toLocaleString());
$('#tk').text(tk.toLocaleString());
}, 'json');
});
</script>
+153
View File
@@ -0,0 +1,153 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5>Data Lokasi Aset</h5>
<button class="btn btn-warning btn-add">Tambah</button>
</div>
<div class="table-responsive">
<table id="tableWarehouse" class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th>No</th>
<th>Nama Lokasi</th>
<th>Aksi</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalWarehouse">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5 class="modal-title">Lokasi Aset</h5>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="id">
<label>Nama Lokasi Aset</label>
<input type="text" id="nama" class="form-control">
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button class="btn btn-warning" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
let action = 'add';
let table = $('#tableWarehouse').DataTable({
ajax:{
url:"<?= base_url('lokasiasset/get_data'); ?>",
type:"POST"
}
});
function resetForm(){
$('#id').val('');
$('#nama').val('');
}
// ADD
$('.btn-add').click(function(){
resetForm();
action = 'add';
$('#modalWarehouse').modal('show');
});
// EDIT
$(document).on('click','.btn-editgudang',function(){
let id = $(this).data('id');
$.get("<?= base_url('lokasiasset/detail/'); ?>"+id,function(res){
$('#id').val(res.id);
$('#nama').val(res.nama);
action = 'edit';
$('#modalWarehouse').modal('show');
},'json');
});
// SAVE
$('#btnSimpan').click(function(){
let data = {
id: $('#id').val(),
nama: $('#nama').val()
};
if(!data.nama){
Swal.fire('Warning','Nama gudang wajib diisi','warning');
return;
}
let url = action === 'add'
? "<?= base_url('lokasiasset/save'); ?>"
: "<?= base_url('lokasiasset/update'); ?>";
$.post(url,data,function(res){
if(res.status){
$('#modalWarehouse').modal('hide');
table.ajax.reload(null,false);
Swal.fire('Sukses',res.message,'success');
} else {
Swal.fire('Error',res.message,'error');
}
},'json');
});
// DELETE
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
Swal.fire({
title:'Hapus gudang?',
text:'Pastikan tidak dipakai item',
icon:'warning',
showCancelButton:true
}).then(r=>{
if(r.isConfirmed){
$.get("<?= base_url('lokasiasset/delete/'); ?>"+id,function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire('Sukses',res.message,'success');
} else {
Swal.fire('Error',res.message,'error');
}
},'json');
}
});
});
});
</script>
+261
View File
@@ -0,0 +1,261 @@
<!-- Bootstrap JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script>
// =====================================================
// 🔥 FORMAT NUMBER
// contoh:
// 1000 => 1,000
// 1000.5 => 1,000.5
// 1000.55 => 1,000.55
// =====================================================
function formatNumber(value){
if(value === null || value === undefined){
return '';
}
value = value.toString();
// hanya angka dan titik
value = value.replace(/[^0-9.]/g, '');
// hanya boleh 1 titik
let parts = value.split('.');
let integerPart = parts[0] || '';
let decimalPart = parts[1] || '';
// format ribuan pakai koma
integerPart = integerPart.replace(/,/g, '');
integerPart = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
// max 2 digit decimal
decimalPart = decimalPart.substring(0,2);
// kalau ada desimal
if(parts.length > 1 || value.endsWith('.')){
return integerPart + '.' + decimalPart;
}
return integerPart;
}
// =====================================================
// 🔥 CLEAN NUMBER
// hasil:
// 1,000.25 => 1000.25
// =====================================================
function cleanNumber(value){
if(!value) return '';
return value.toString().replace(/,/g, '');
}
// =====================================================
// 🔥 AUTO FORMAT INPUT
// =====================================================
$(document).on('input', '.format-rupiah', function(){
let cursorPos = this.selectionStart;
let beforeLength = $(this).val().length;
let formatted = formatNumber($(this).val());
$(this).val(formatted);
let afterLength = formatted.length;
cursorPos = cursorPos + (afterLength - beforeLength);
this.setSelectionRange(cursorPos, cursorPos);
});
// =====================================================
// 🔥 FORMAT SAAT BLUR
// =====================================================
$(document).on('blur', '.format-rupiah', function(){
$(this).val(
formatNumber($(this).val())
);
});
// =====================================================
// 🔥 AUTO CLEAN SAAT SUBMIT
// =====================================================
$(document).on('submit', 'form', function(){
$(this).find('.format-rupiah').each(function(){
$(this).val(
cleanNumber($(this).val())
);
});
});
// =====================================================
// 🔥 AUTO FORMAT VALUE AWAL
// =====================================================
$(document).ready(function(){
$('.format-rupiah').each(function(){
let val = $(this).val();
if(val !== ''){
$(this).val(
formatNumber(val)
);
}
});
});
// =====================================================
// 🔥 OVERRIDE jQuery .val()
// =====================================================
(function($){
const originalVal = $.fn.val;
$.fn.val = function(value){
// setter
if(typeof value !== 'undefined'){
if(this.hasClass('format-rupiah')){
return originalVal.call(
this,
formatNumber(value)
);
}
return originalVal.apply(this, arguments);
}
// getter
if(this.hasClass('format-rupiah')){
return cleanNumber(
originalVal.call(this)
);
}
return originalVal.call(this);
};
})(jQuery);
</script>
<script>
function formatRupiah(num){
return 'Rp ' + new Intl.NumberFormat('id-ID').format(num || 0);
}
function formatTglIndo(dateString){
if(!dateString) return '-';
let date = new Date(dateString);
const bulan = [
"Januari","Februari","Maret","April","Mei","Juni",
"Juli","Agustus","September","Oktober","November","Desember"
];
let tgl = date.getDate();
let bln = bulan[date.getMonth()];
let thn = date.getFullYear();
return `${tgl} ${bln} ${thn}`;
}
</script>
<script>
$(function(){
function initSelectSearch(scope = document){
if (typeof $.fn.select2 === 'undefined') {
console.error('Select2 belum ke-load!');
return;
}
$(scope).find('select.select-search').each(function(){
let $this = $(this);
if ($this.hasClass("select2-hidden-accessible")) return;
let parent = $this.closest('.modal');
parent = parent.length ? parent : $(document.body);
$this.select2({
width: '100%',
placeholder: $this.attr('placeholder') || '-- Pilih --',
allowClear: true,
dropdownParent: parent
});
});
}
// ✅ INIT AWAL
initSelectSearch();
// $(window).on('load', function(){
// setTimeout(() => {
// initSelectSearch();
// }, 100);
// });
// ✅ AUTO INIT untuk dynamic content (modal, ajax, dll)
const observer = new MutationObserver(function(mutations){
mutations.forEach(function(mutation){
mutation.addedNodes.forEach(function(node){
if (node.nodeType === 1){ // element
initSelectSearch(node);
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
});
</script>
</body>
</html>
+394
View File
@@ -0,0 +1,394 @@
<?php $role = $this->session->userdata('role'); ?>
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Accounting</title>
<link rel="icon" href="<?= base_url('assets/img/accounting-color.png') ?>" type="image/png">
<!-- Bootstrap 5 -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.css" rel="stylesheet">
<!-- Google Font -->
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap" rel="stylesheet">
<link href="<?= base_url('assets/css/style-custom.css'); ?>" rel="stylesheet">
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
<link rel="stylesheet" href="https://cdn.datatables.net/responsive/2.4.1/css/responsive.bootstrap5.min.css">
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/responsive/2.4.1/js/dataTables.responsive.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="https://unpkg.com/html5-qrcode" type="text/javascript"></script>
<!-- Daterange Picker -->
<script type="text/javascript" src="https://cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script>
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/daterangepicker/daterangepicker.css" />
<!-- Select2 STABLE -->
<link href="https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/css/select2.min.css" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/select2@4.0.13/dist/js/select2.min.js"></script>
</head>
<body>
<!-- ───────────────────────── NAVBAR ───────────────────────── -->
<nav class="navbar navbar-expand-lg fixed-top">
<div class="container">
<!-- Mobile menu button -->
<button class="btn text-white d-lg-none me-2" data-bs-toggle="offcanvas" data-bs-target="#mobileMenu">
<i class="bi bi-list fs-3"></i>
</button>
<img src="<?= base_url('assets/img/accounting-white.png'); ?>" width="50px">
<div class="collapse navbar-collapse d-none d-lg-flex">
<ul class="navbar-nav ms-4">
<!-- DASHBOARD -->
<li class="nav-item">
<a class="nav-link <?= ($active_menu == 'dashboard') ? 'active' : ''?>" href="<?= base_url(); ?>">
<i class="bi bi-speedometer2"></i> Dashboard
</a>
</li>
<li class="nav-item">
<a class="nav-link <?= ($active_menu == 'jurnal') ? 'active' : ''?>" href="<?= base_url('jurnal'); ?>">
<i class="bi bi-journal-plus"></i> Jurnal Umum
</a>
</li>
<?php if($role == 'Admin'): ?>
<li class="nav-item">
<a class="nav-link <?= ($active_menu == 'buku_besar') ? 'active' : ''?>" href="<?= base_url('bukubesar'); ?>">
<i class="bi bi-book"></i> Buku Besar
</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle <?= in_array($active_menu, ['laba_rugi','neraca','neraca_saldo']) ? 'active' : '' ?>" href="#" data-bs-toggle="dropdown">
<i class="bi bi-graph-up"></i> Laporan
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item <?= ($active_menu == 'neraca_saldo') ? 'active' : '' ?>" href="<?= base_url('neracasaldo'); ?>">
<i class="bi bi-clipboard-data me-2"></i> Neraca Saldo</a>
</li>
<li><a class="dropdown-item <?= ($active_menu == 'laba_rugi') ? 'active' : '' ?>" href="<?= base_url('labarugi'); ?>">
<i class="bi bi-graph-up-arrow me-2"></i> Laba Rugi</a>
</li>
<li><a class="dropdown-item <?= ($active_menu == 'neraca') ? 'active' : '' ?>" href="<?= base_url('neraca'); ?>">
<i class="bi bi-bar-chart-line me-2"></i> Neraca</a>
</li>
</ul>
</li>
<?php endif; ?>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle <?= in_array($active_menu, ['accounts','basejurnal']) ? 'active' : '' ?>" href="#" data-bs-toggle="dropdown">
<i class="bi bi-list-nested"></i> Daftar Akun
</a>
<ul class="dropdown-menu">
<?php if($role == 'Admin'): ?>
<li>
<a class="dropdown-item <?= ($active_menu == 'accounts') ? 'active' : '' ?>"
href="<?= base_url('accounts'); ?>">
<i class="bi bi-list-nested"></i> Base Akun
</a>
</li>
<?php endif; ?>
<li>
<a class="dropdown-item <?= ($active_menu == 'basejurnal') ? 'active' : '' ?>"
href="<?= base_url('basejurnal'); ?>">
<i class="bi bi-arrow-left-right"></i> Base Jurnal
</a>
</li>
</ul>
</li>
<!--<li class="nav-item">-->
<!-- <a class="nav-link <?= ($active_menu == 'asset') ? 'active' : ''?>" href="<?= base_url('asset'); ?>">-->
<!-- <i class="bi bi-book"></i> Asset-->
<!-- </a>-->
<!--</li>-->
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle <?= in_array($active_menu, ['asset','lokasiasset']) ? 'active' : '' ?>"
href="#" data-bs-toggle="dropdown">
<i class="bi bi-archive"></i> Asset
</a>
<ul class="dropdown-menu">
<!-- Daftar Asset -->
<li>
<a class="dropdown-item <?= ($active_menu == 'asset') ? 'active' : '' ?>"
href="<?= base_url('asset'); ?>">
<i class="bi bi-archive me-2"></i> Daftar Asset
</a>
</li>
<li><hr class="dropdown-divider"></li>
<!-- Lokasi Asset -->
<li>
<a class="dropdown-item <?= ($active_menu == 'lokasiasset') ? 'active' : '' ?>"
href="<?= base_url('lokasiasset'); ?>">
<i class="bi bi-geo-alt me-2"></i> Lokasi Asset
</a>
</li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle <?= in_array($active_menu, ['warehouses','items', 'kode_barang', 'draft_items']) ? 'active' : '' ?>" href="#" data-bs-toggle="dropdown">
<i class="bi bi-box-seam"></i> Gudang
</a>
<ul class="dropdown-menu">
<li>
<a class="dropdown-item <?= ($active_menu == 'warehouses') ? 'active' : '' ?>"
href="<?= base_url('warehouses'); ?>">
<i class="bi bi-building me-2"></i> Daftar Gudang
</a>
</li><hr class="dropdown-divider">
<li>
<a class="dropdown-item <?= ($active_menu == 'draft_items') ? 'active' : '' ?>"
href="<?= base_url('items/draft_items'); ?>">
<i class="bi bi-truck me-2"></i> Daftar Pending
</a>
</li>
<li>
<a class="dropdown-item <?= ($active_menu == 'items') ? 'active' : '' ?>"
href="<?= base_url('items'); ?>">
<i class="bi bi-box me-2"></i> Daftar Barang
</a>
</li><hr class="dropdown-divider">
<li>
<a class="dropdown-item <?= ($active_menu == 'kode_barang') ? 'active' : '' ?>"
href="<?= base_url('kodebarang'); ?>">
<i class="bi bi-code me-2"></i> Daftar Kode Barang
</a>
</li>
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle <?= in_array($active_menu, ['customers','invoice_unpaid','invoice_paid','payments','invoice_draft']) ? 'active' : '' ?>" href="#" data-bs-toggle="dropdown">
<i class="bi bi-receipt"></i> Invoice
</a>
<ul class="dropdown-menu">
<!-- Customers -->
<li>
<a class="dropdown-item <?= ($active_menu == 'customers') ? 'active' : '' ?>" href="<?= base_url('customers'); ?>">
<i class="bi bi-people me-2"></i> Customers
</a>
</li>
<li><hr class="dropdown-divider"></li>
<!-- Draft -->
<li>
<a class="dropdown-item <?= ($active_menu == 'invoice_draft') ? 'active' : '' ?>" href="<?= base_url('invoices/draft'); ?>">
<i class="bi bi-pencil-square me-2"></i> Draft Invoice
</a>
</li>
<!-- Riwayat Invoice -->
<li>
<a class="dropdown-item <?= ($active_menu == 'invoice_paid') ? 'active' : '' ?>" href="<?= base_url('invoices/paid'); ?>">
<i class="bi bi-check-circle me-2"></i> Riwayat Invoice
</a>
</li>
<!-- Pembayaran -->
<!-- <li>
<a class="dropdown-item <?= ($active_menu == 'payments') ? 'active' : '' ?>" href="<?= base_url('payments'); ?>">
<i class="bi bi-cash-stack me-2"></i> Pembayaran
</a>
</li> -->
</ul>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle <?= in_array($active_menu, ['employees','shift','holidays','attendance_monitoring','leave_requests','generate_schedule']) ? 'active' : '' ?>"
href="#"
data-bs-toggle="dropdown">
<i class="bi bi-people-fill me-1"></i>
Kepegawaian
</a>
<ul class="dropdown-menu shadow border-0 rounded-4 p-2">
<li>
<a class="dropdown-item rounded-3 <?= ($active_menu == 'employees') ? 'active' : '' ?>"
href="<?= base_url('employees'); ?>">
<i class="bi bi-person-vcard-fill me-2"></i>
Data Karyawan
</a>
</li>
<li>
<a class="dropdown-item rounded-3 <?= ($active_menu == 'generate_schedule') ? 'active' : '' ?>"
href="<?= base_url('generateschedule'); ?>">
<i class="bi bi-calendar3-week-fill me-2"></i>
Jadwal Kerja
</a>
</li>
<li>
<a class="dropdown-item rounded-3 <?= ($active_menu == 'attendance_monitoring') ? 'active' : '' ?>"
href="<?= base_url('attendancemonitoring'); ?>">
<i class="bi bi-fingerprint me-2"></i>
Monitoring Absensi
</a>
</li>
<hr class="dropdown-divider">
<li>
<a class="dropdown-item rounded-3 <?= ($active_menu == 'shift') ? 'active' : '' ?>"
href="<?= base_url('shifts'); ?>">
<i class="bi bi-alarm-fill me-2"></i>
Data Shift
</a>
</li>
<li>
<a class="dropdown-item rounded-3 <?= ($active_menu == 'holidays') ? 'active' : '' ?>"
href="<?= base_url('holidays'); ?>">
<i class="bi bi-calendar2-heart-fill me-2"></i>
Hari Libur
</a>
</li>
<li>
<a class="dropdown-item rounded-3 <?= ($active_menu == 'leave_requests') ? 'active' : '' ?>"
href="<?= base_url('leaverequests'); ?>">
<i class="bi bi-calendar-check-fill me-2"></i>
Cuti & Izin
</a>
</li>
<hr class="dropdown-divider">
<li>
<a class="dropdown-item rounded-3 <?= ($active_menu == 'payroll') ? 'active' : '' ?>"
href="<?= base_url('payroll'); ?>">
<i class="bi bi-cash-stack me-2"></i>
Penggajihan
</a>
</li>
</ul>
</li>
<?php if($role == 'Admin'): ?>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle <?= in_array($active_menu, ['umum','users']) ? 'active' : '' ?>" href="#" data-bs-toggle="dropdown">
<i class="bi bi-gear"></i>
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item <?= ($active_menu == 'umum') ? 'active' : '' ?>" href="<?= base_url('umum'); ?>">
<i class="bi bi-sliders me-2"></i> Umum</a>
</li>
<li><a class="dropdown-item <?= ($active_menu == 'users') ? 'active' : '' ?>" href="<?= base_url('users'); ?>">
<i class="bi bi-person-gear me-2"></i> Users</a>
</li>
</ul>
</li>
<?php endif; ?>
</ul>
<ul class="navbar-nav ms-auto">
<li class="nav-item dropdown">
<a class="btn btn-premium dropdown-toggle" href="#" data-bs-toggle="dropdown">
<i class="bi bi-person-circle me-1"></i> <?= $this->session->username; ?>
</a>
<ul class="dropdown-menu dropdown-menu-end">
<li><a class="dropdown-item <?= ($active_menu == 'profile') ? 'active' : '' ?>" href="<?= base_url('profile'); ?>">
<i class="bi bi-person me-2"></i> Profile</a></li>
<li><a class="dropdown-item" href="<?= base_url('auth/logout'); ?>">
<i class="bi bi-box-arrow-right me-2"></i> Logout</a></li>
</ul>
</li>
</ul>
</div>
</div>
</nav>
<div style="height: 70px;"></div>
<!-- ───────────────────────── SIDEBAR MOBILE ───────────────────────── -->
<div class="offcanvas offcanvas-start" tabindex="-1" id="mobileMenu">
<div class="offcanvas-header">
<span class="brand-mobile">🌿 Sekolah Islami</span>
<button class="btn-close" data-bs-dismiss="offcanvas"></button>
</div>
<div class="offcanvas-body">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link <?= ($active_menu == 'dashboard') ? 'active' : ''?>" href="<?= base_url(); ?>">
<span class="menu-wrap"><i class="bi bi-speedometer2"></i> Dashboard</span>
</a>
</li>
</ul>
<div class="sidebar-divider"></div>
<a href="<?= base_url('logout'); ?>" class="btn btn-logout">
<i class="bi bi-box-arrow-right me-2"></i> Logout
</a>
</div>
</div>
+109
View File
@@ -0,0 +1,109 @@
<style>
/* Style to truncate long text in table cells */
.truncate-text {
max-width: 300px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
cursor: pointer;
}
</style>
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">Riwayat Pesan</h5>
</div>
<div class="table-responsive">
<table id="tablePesan" class="table modern-table align-middle w-100">
<thead>
<tr>
<th>No</th>
<th>Waktu</th>
<th>Tipe</th>
<th>Penerima</th>
<th>Pesan</th>
<th>Status</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- Modal for full message -->
<div class="modal fade" id="pesanModal" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Isi Pesan Lengkap</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body" style="white-space: pre-wrap; word-wrap: break-word;">
...
</div>
</div>
</div>
</div>
<script>
$(document).ready(function(){
let table = $('#tablePesan').DataTable({
dom: '<"dataTables_top d-flex justify-content-between mb-3"l f>rtip',
processing: true,
serverSide: true,
responsive: false,
autoWidth: false,
order: [],
ajax: {
url: "<?= base_url('pesan/get_data'); ?>",
type: "POST"
},
columns: [
{ data: 0, orderable: false },
{ data: 1 },
{ data: 2 },
{ data: 3 },
{ data: 4 },
{ data: 5 }
],
columnDefs: [
{
targets: 4, // Target the 'Pesan' column
createdCell: function (td, cellData, rowData, row, col) {
$(td).attr('title', 'Klik untuk lihat lengkap');
}
}
],
language: {
processing: `<div class="d-flex justify-content-center"><div class="spinner-border text-success"></div></div>`
}
});
$('.dataTables_filter input').attr('placeholder', 'Cari pesan, nomor, email...').css({
'min-width': '300px',
'padding': '6px 12px',
'border-radius': '6px',
'border': '1px solid #ced4da',
'font-size': '0.9rem',
'box-sizing': 'border-box'
});
// Handle click on truncated text to show full message in modal
$('#tablePesan tbody').on('click', 'td .truncate-text', function () {
var fullText = $(this).text();
$('#pesanModal .modal-body').text(fullText);
var pesanModal = new bootstrap.Modal(document.getElementById('pesanModal'));
pesanModal.show();
});
});
</script>
+207
View File
@@ -0,0 +1,207 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4 text-center">
<h5 id="judulscan">Silahkan Scan QR <?= ucfirst($scan_type); ?></h5>
<div id="reader" style="max-width: 50%; margin: 0 auto;"></div>
<div id="result" class="mt-4">Hasil scan akan muncul di sini</div>
<button class="btn btn-success btn-add mt-3" id="backBtn" style="display:none;">Ulangi Scan</button>
<a href="<?= base_url(); ?>" class="btn btn-success btn-add mt-3">Kembali</a>
</div>
</div>
</div>
<style>
/* Modern Detail Card Style */
.detail-card {
background: #fff;
border-radius: 12px;
padding: 20px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
max-width: 450px;
margin: 20px auto;
}
.detail-card img {
width: 120px;
height: 120px;
border-radius: 50%;
object-fit: cover;
border: 3px solid #fb6340;
}
.detail-card h2 {
margin-top: 15px;
color: #333;
font-weight: 700;
}
.detail-row {
display: flex;
justify-content: space-between;
padding: 8px 0;
border-bottom: 1px solid #f0f0f0;
color: #555;
}
.detail-row:last-child {
border-bottom: none;
}
.detail-label {
font-weight: 600;
color: #fb6340;
}
</style>
<script>
let scanner;
const resultDiv = document.getElementById("result");
const backBtn = document.getElementById("backBtn");
const judul = document.getElementById("judulscan");
function startScanner() {
scanner = new Html5Qrcode("reader");
Html5Qrcode.getCameras().then(cameras => {
if (cameras && cameras.length) {
let cameraId = cameras[0].id;
const backCamera = cameras.find(cam =>
cam.label.toLowerCase().includes("back")
);
if (backCamera) cameraId = backCamera.id;
scanner.start(
cameraId,
{ fps: 10, qrbox: { width: 250, height: 250 } },
qrCodeMessage => {
// Stop scanner
scanner.stop().then(() => {
scanner.clear();
// Kirim data ke server
fetch('<?= site_url("cameraqrscan/get_data"); ?>', {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body: new URLSearchParams({
qr_code: qrCodeMessage,
scan_type: '<?= $scan_type ?>'
})
})
.then(res => res.json())
.then(data => {
// Jika sukses (status true)
if (data.status) {
// Jika ada peringatan 5 menit
if (data.message && data.message.includes("5 menit")) {
showDetail(data.data, true, data.message);
}
// Sukses normal
else {
showDetail(data.data, false, "");
}
} else {
// Jika error lain
resultDiv.innerHTML = `
<p class="text-danger">${data.message}</p>
`;
backBtn.style.display = "inline-block";
}
});
});
},
errorMessage => {
console.warn(errorMessage);
}
).catch(err => {
console.error("Start scanner error:", err);
alert("Gagal membuka kamera: " + err);
});
} else {
alert("Tidak ada kamera terdeteksi");
}
}).catch(err => {
console.error("Camera error:", err);
alert("Tidak bisa mengakses kamera: " + err);
});
}
// ===========================================
// TAMPIL DETAIL DATA + WARNING JIKA PERLU
// ===========================================
function showDetail(data, isWarning = false, warningText = "") {
const foto = data.foto
? `<?= base_url('uploads/siswa/') ?>` + data.foto
: 'https://storage.manjapro.net/file/users/avatar/default.jpg';
let warningHtml = "";
if (isWarning) {
warningHtml = `
<div class="alert alert-warning mt-3">
⚠️ ${warningText}
</div>
`;
}
let html = `
${warningHtml}
<h2>Scan <?= ucfirst($scan_type) ?> Berhasil</h2>
<div class="detail-card text-center">
<img src="${foto}" alt="Foto Siswa">
<h2>${data.nama}</h2>
<div class="detail-row mt-3">
<div class="detail-label">Kelas</div>
<div>${data.nama_kelas}</div>
</div>
<div class="detail-row mt-3">
<div class="detail-label">Kamar</div>
<div>${data.nama_kamar} (${data.nama_gedung_kamar})</div>
</div>
<div class="detail-row">
<div class="detail-label">Alamat</div>
<div>${data.alamat}</div>
</div>
<div class="detail-row">
<div class="detail-label">Nama Orang Tua</div>
<div>${data.nama_wali}</div>
</div>
<div class="detail-row">
<div class="detail-label">Waktu</div>
<div>${data.created_at}</div>
</div>
</div>
`;
resultDiv.innerHTML = html;
backBtn.style.display = "inline-block";
judul.style.display = "none";
}
// ===========================================
// TOMBOL ULANGI SCAN
// ===========================================
backBtn.addEventListener("click", () => {
resultDiv.innerText = "Hasil scan akan muncul di sini";
backBtn.style.display = "none";
judul.style.display = "block";
startScanner();
});
// ===========================================
// MULAI SCANNER SAAT PAGE LOAD
// ===========================================
window.onload = startScanner;
</script>
+444
View File
@@ -0,0 +1,444 @@
<?php if(check_permission('setting', 'can_view')): ?>
<div class="container mt-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">Pengaturan</h5>
</div>
<div class="row lign-items-stretch">
<!-- ================== 1. PENGATURAN UMUM ================== -->
<div class="col-md-6 mb-3">
<div class="card modern-card shadow-sm border-0 rounded-4 h-100">
<div class="card-body p-4">
<h6 class="mb-0">Umum</h6>
<hr>
<form action="<?= base_url('setting/save_umum') ?>" method="POST">
<div class="mb-3">
<label class="form-label">Nama Lembaga</label>
<input type="text" name="nama_lembaga" class="form-control" value="<?= $config->nama_lembaga ?>">
</div>
<div class="mb-3">
<label class="form-label">Pimpinan Lembaga</label>
<input type="text" name="pimpinan_lembaga" class="form-control" value="<?= $config->pimpinan_lembaga ?>">
</div>
<button type="submit" class="btn btn-success btn-add rounded-3">
Simpan
</button>
</form>
</div>
</div>
</div>
<!-- ================== 2. SETTING CARD ================== -->
<div class="col-md-6 mb-3">
<div class="card modern-card shadow-sm border-0 rounded-4 h-100">
<div class="card-body p-4">
<h6 class="mb-0">Setting Card</h6>
<hr>
<div class="items-card-center">
<img id="defaultBg" src="<?= base_url($config->card_background) ?>" hidden>
<div id="cardPreview"></div>
<small class="text-muted d-block mt-2 mb-4">Preview akan berubah otomatis</small>
</div>
<form action="<?= base_url('setting/save_card') ?>" method="POST" enctype="multipart/form-data">
<div class="mb-3">
<label class="form-label">Upload Background Kartu (Ukuran 300 x 480 pixel)</label>
<input type="file" name="card_background" id="bgInput" class="form-control" accept="image/*">
</div>
<button type="submit" class="btn btn-success btn-add rounded-3">
Simpan Background
</button>
</form>
</div>
</div>
</div>
<!-- ================== 3. SETTING WHATSAPP ================== -->
<div class="col-md-6 mb-3">
<div class="card modern-card shadow-sm border-0 rounded-4 h-100">
<div class="card-body p-4">
<h6 class="mb-0">Setting Whatsapp</h6>
<hr>
<form action="<?= base_url('setting/save_whatsapp') ?>" method="POST">
<div class="mb-3">
<label class="form-label">Mode WhatsApp</label>
<select class="form-select" id="waMode" name="wa_mode">
<option value="">-- Pilih --</option>
<option value="official" <?= ($config->wa_mode == 'official' ) ? 'selected' : '' ?>>Official API</option>
<option value="unofficial" <?= ($config->wa_mode == 'unofficial' ) ? 'selected' : '' ?>>Unofficial Gateway</option>
</select>
</div>
<!-- OFFICIAL -->
<div id="officialForm" style="display:none;">
<div class="mb-3">
<label class="form-label">Phone Number</label>
<input type="text" name="wa_phone" class="form-control" value="<?= $config->wa_phone; ?>">
</div>
<div class="mb-3">
<label class="form-label">Phone Number ID</label>
<input type="text" name="wa_phone_id" class="form-control" value="<?= $config->wa_phone_id; ?>">
</div>
<div class="mb-3">
<label class="form-label">WABA ID</label>
<input type="text" name="wa_waba_id" class="form-control" value="<?= $config->wa_waba_id; ?>">
</div>
<div class="mb-3">
<label class="form-label">Access Token</label>
<input type="text" name="wa_access_token" class="form-control" value="<?= $config->wa_access_token; ?>">
</div>
</div>
<!-- UNOFFICIAL -->
<div id="unofficialForm" style="display:none;">
<div class="mb-3">
<label class="form-label">Endpoint URL</label>
<input type="text" name="wa_endpoint" class="form-control" value="<?= $config->wa_endpoint; ?>">
</div>
<div class="mb-3">
<label class="form-label">API Key</label>
<input type="text" name="wa_api_key" class="form-control" value="<?= $config->wa_api_key; ?>">
</div>
<center>
<img id="waStatusImage" src="" style="width:220px; margin-top:15px;"><br>
<button type="button" id="scanWhatsappQR" class="btn btn-success btn-add rounded-3 mb-4">
Cek Tautan Whatsapp Gateway
</button>
</center>
</div>
<button type="submit" class="btn btn-success btn-add rounded-3">
Simpan
</button>
</form>
</div>
</div>
</div>
<!-- ================== 4. TEMPLATE PESAN ================== -->
<div class="col-md-6 mb-3">
<div class="card modern-card shadow-sm border-0 rounded-4 h-100">
<div class="card-body p-4">
<h6 class="mb-0">Setting Template Pesan</h6>
<hr>
<table class="table table-hover align-middle">
<thead>
<tr>
<th width="5%">No</th>
<th>Nama</th>
<th>Pesan</th>
<th width="10%">Aksi</th>
</tr>
</thead>
<tbody>
<?php foreach ($template_pesan as $i => $row): ?>
<tr>
<td><?= $i+1 ?></td>
<td><?= $row->nama_template ?></td>
<td><?= $row->isi_template ?></td>
<td>
<button class="btn btn-sm btn-primary" data-bs-toggle="modal" data-bs-target="#editModal<?= $row->id ?>">
Edit
</button>
</td>
</tr>
<!-- Modal Edit -->
<div class="modal fade" id="editModal<?= $row->id ?>">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-4">
<div class="modal-header">
<h6 class="modal-title">Edit Pesan</h6>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<form action="<?= base_url('setting/update_template_pesan') ?>" method="POST">
<div class="modal-body">
<input type="hidden" name="id" value="<?= $row->id ?>">
<div class="mb-3">
<label class="form-label">Nama</label>
<input type="text" name="nama_template" class="form-control" value="<?= $row->nama_template ?>">
</div>
<div class="mb-3">
<label class="form-label">Pesan</label>
<textarea name="isi_template" class="form-control" rows="12"><?= $row->isi_template ?></textarea>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button class="btn btn-primary">Simpan</button>
</div>
</form>
</div>
</div>
</div>
<?php endforeach; ?>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- JS untuk dynamic show/hide -->
<script>
function showWaMode(mode) {
document.getElementById("officialForm").style.display = (mode === "official") ? "block" : "none";
document.getElementById("unofficialForm").style.display = (mode === "unofficial") ? "block" : "none";
}
// Saat dropdown berubah
document.getElementById("waMode").addEventListener("change", function() {
showWaMode(this.value);
});
// Saat halaman pertama kali load → pakai value dari dropdown (sudah diset PHP)
document.addEventListener("DOMContentLoaded", function() {
let initial = document.getElementById("waMode").value;
showWaMode(initial);
});
// =====================================================
// 1. LOAD BACKGROUND DEFAULT SAAT PAGE AWAL TERBUKA
// =====================================================
window.addEventListener("DOMContentLoaded", function () {
let defaultBgElement = document.getElementById("defaultBg");
let preview = document.getElementById("cardPreview");
if (!defaultBgElement) {
console.error("defaultBg tidak ditemukan!");
return;
}
if (!preview) {
console.error("cardPreview tidak ditemukan!");
return;
}
let defaultBg = defaultBgElement.src;
if (defaultBg && defaultBg !== "") {
validateAndLoadCard(defaultBg);
}
});
// =====================================================
// 2. EVENT: USER UPLOAD GAMBAR BACKGROUND BARU
// =====================================================
document.getElementById("bgInput").addEventListener("change", function (e) {
let file = e.target.files[0];
if (!file) return;
let reader = new FileReader();
reader.onload = function (evt) {
validateAndLoadCard(evt.target.result);
};
reader.readAsDataURL(file);
});
// =====================================================
// 3. VALIDASI RASIO GAMBAR (PORTRAIT / LANDSCAPE)
// =====================================================
function validateAndLoadCard(imgSrc) {
let img = new Image();
img.onload = function () {
let width = img.width;
let height = img.height;
let ratio = width / height;
console.log("Rasio gambar:", ratio);
let isPortrait = ratio < 0.9; // 2:3 atau sejenis
let isLandscape = ratio > 1.2; // 3:2 atau sejenis
if (!isPortrait && !isLandscape) {
alert("⚠️ Gambar tidak sesuai rasio ID Card!\nGunakan potret (2:3) atau landscape (3:2).");
return;
}
loadCardPreviewFromImage(imgSrc, isPortrait);
};
img.onerror = function () {
console.error("Gambar tidak bisa diload:", imgSrc);
};
img.src = imgSrc;
}
// =====================================================
// 4. LOAD PREVIEW + GANTI TEMPLATE
// =====================================================
function loadCardPreviewFromImage(imgSrc, isPortrait) {
let preview = document.getElementById("cardPreview");
if (!preview) {
console.error("Element #cardPreview tidak ditemukan!");
return;
}
// Set background image
preview.style.backgroundImage = `url('${imgSrc}')`;
// Reset semua class
preview.className = "";
// Tambahkan class sesuai orientasi
if (isPortrait) {
preview.classList.add("id-card-potret");
preview.innerHTML = portraitTemplate();
} else {
preview.classList.add("id-card-lemscape");
preview.innerHTML = landscapeTemplate();
}
console.log("Template loaded:", isPortrait ? "Potret" : "Landscape");
}
function portraitTemplate() {
return `
<div class="card-inner">
<div class="card-logo">
<img src="assets/img/logo.png">
</div>
<div class="card-title">Kartu Tanda Wali Santri</div>
<div class="photo-wrapper">
<img src="assets/img/foto.jpg">
</div>
<div class="card-info">
<div class="nama">Nama Santri</div>
<div class="nis">NIS: 00000000</div>
<div class="kelas">Tahun Masuk: -</div>
<div class="alamat">Alamat lengkap tampil di sini...</div>
</div>
<div class="qr-wrapper">
<img src="assets/img/qr.png" width="100%">
</div>
</div>
`;
}
function landscapeTemplate() {
return `
<div class="card-inner">
<div class="card-logo">
<img src="assets/img/logo.png">
</div>
<div class="card-title">Kartu Tanda Wali Santri</div>
<div class="landscape-content">
<div class="photo-wrapper">
<img src="assets/img/foto.jpg">
</div>
<div class="card-info">
<div class="nama">Nama Santri</div>
<div class="nis">NIS: 00000000</div>
<div class="kelas">Tahun Masuk: -</div>
<div class="alamat">Alamat lengkap tampil di sini...</div>
</div>
<div class="qr-wrapper">
<img src="assets/img/qr.png" width="100%">
</div>
</div>
</div>
`;
}
// -----------------------------
// Perintah untuk cek status whatsapp
// -----------------------------
document.getElementById("scanWhatsappQR").addEventListener("click", function () {
let counter = 0;
let imgTag = document.getElementById("waStatusImage");
function checkStatusLoop() {
fetch(`<?= base_url('setting/cek_whatsapp_status') ?>`)
.then(res => res.json())
.then(data => {
console.log("Status:", data.status);
imgTag.src = data.img; // tampilkan gambar ke <img>
if (data.status === "CONNECTED") {
console.log("Whatsapp sudah terhubung, stop loop");
return; // STOP AUTOMATIS
}
counter++;
if (counter < 50) {
setTimeout(checkStatusLoop, 2000);
} else {
console.log("Loop selesai 50x");
}
})
.catch(err => console.error(err));
}
checkStatusLoop();
});
</script>
<?php endif; ?>
+462
View File
@@ -0,0 +1,462 @@
<?php
if(check_permission('siswa', 'can_view')):
$fotoSiswa = ($siswa->foto) ? base_url('uploads/siswa/'.$siswa->foto) : base_url('uploads/siswa/foto.jpg') ;
?>
<script src="https://cdnjs.cloudflare.com/ajax/libs/html2pdf.js/0.10.1/html2pdf.bundle.min.js"></script>
<div class="container mt-4">
<?php if(check_permission('siswa', 'can_update')): ?>
<div class="text-end mb-3">
<button class="btn btn-success btn-eximport d-none d-lg-inline" id="downloadPdfBtn">
<i class="bi bi-file-earmark-pdf-fill me-1"></i> Export PDF Halaman Ini
</button>
</div>
<?php endif; ?>
<div id="containerDownload">
<div class="d-flex justify-content-between align-items-center mb-3">
<h4 class="mb-0 fw-bold">Detail Santri</h4>
</div>
<div class="row align-items-stretch">
<!-- ================== DETAIL SANTRI ================== -->
<div class="col-md-6 mb-3">
<div class="card shadow-sm border-0 rounded-4 h-100">
<div class="card-body p-4">
<h5 class="fw-semibold">Informasi Santri</h5>
<hr>
<center>
<img class="foto-santri rounded-4 shadow-sm mb-3"
src="<?= $fotoSiswa; ?>"
style="width:160px;height:200px;object-fit:cover;">
</center>
<table class="table table-borderless">
<tr>
<th width="200">Nama</th>
<td>: <?= $siswa->nama; ?></td>
</tr>
<tr>
<th>NIS</th>
<td>: <?= $siswa->nis; ?></td>
</tr>
<tr>
<th>Kelas</th>
<td>: <?= $siswa->nama_kelas; ?></td>
</tr>
<tr>
<th>Kamar</th>
<td>: <?= $siswa->nama_kamar . ' ('.$siswa->nama_gedung_kamar.')'; ?></td>
</tr>
<tr>
<th>Tahun Masuk</th>
<td>: <?= $siswa->tahun_masuk; ?></td>
</tr>
<tr>
<th>Alamat</th>
<td>: <?= $siswa->alamat; ?></td>
</tr>
<tr>
<th>Nama Wali Santri</th>
<td>: <?= $siswa->nama_wali; ?></td>
</tr>
<tr>
<th>No Whatsapp Wali Santri</th>
<td>: <?= $siswa->nomor_whatsapp; ?></td>
</tr>
<tr>
<th>Email Wali Santri</th>
<td>: <?= $siswa->email; ?></td>
</tr>
<tr>
<th>Jumlah Kunjungan</th>
<td>: <?= $siswa->jumlah_kunjungan; ?></td>
</tr>
</table>
</div>
</div>
</div>
<!-- ================== KARTU WALI SANTRI ================== -->
<div class="col-md-6 mb-3">
<div class="card shadow-sm border-0 rounded-4 h-100">
<div class="card-body p-4">
<h5 class="fw-semibold">Kartu Tanda Wali Santri</h5>
<hr>
<div class="items-card-center">
<img id="defaultBg" src="<?= base_url($setting->card_background) ?>" hidden>
<div id="cardPreview"></div>
</div>
<div class="text-center mt-4">
<?php if(check_permission('siswa', 'can_update')): ?>
<button class="btn btn-success btn-add rounded-3 mt-4" onclick="printCardPDF()">
<i class="bi bi-printer me-1"></i> Print ID Card
</button>
<?php endif; ?>
</div>
</div>
</div>
</div>
</div>
<div class="card modern-card shadow-sm border-0 rounded-4 mt-3 mb-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-center">
<h5 class="mb-1">Riwayat Kunjungan</h5>
</div>
<div class="table-responsive">
<table id="tableHistory" class="table modern-table align-middle w-100">
<thead>
<tr>
<th>No</th>
<th>Nama</th>
<th>NIS</th>
<th>Kelas</th>
<th>Kamar</th>
<th>Nama Wali</th>
<th>Alamat</th>
<th>Jam Masuk</th>
<th>Petugas Masuk</th>
<th>Jam Keluar</th>
<th>Petugas Keluar</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function() {
let isInitialLoad = true;
$('#dateRangeFilter').val('');
$('#dateRangeFilter').daterangepicker({
autoUpdateInput: false,
locale: { cancelLabel: false, format: 'YYYY-MM-DD' },
linkedCalendars: false
});
$('#dateRangeFilter').on('apply.daterangepicker', function(ev, picker) {
$(this).val(picker.startDate.format('YYYY-MM-DD') + ' - ' + picker.endDate.format('YYYY-MM-DD'));
table.ajax.reload();
});
$('#dateRangeFilter').on('cancel.daterangepicker', function(ev, picker) {
$(this).val('');
table.ajax.reload();
});
let table = $('#tableHistory').DataTable({
dom: '<"dataTables_top d-flex justify-content-between mb-3"l f>rtip',
processing: true,
serverSide: true,
responsive: false,
autoWidth: false,
order: [],
ajax: {
url: "<?= base_url('siswa/get_kunjungan'); ?>",
type: "POST",
data: function(d) {
// Tambahkan id_siswa dari PHP
d.id_siswa = `<?= $siswa->id; ?>`;
// Tambahkan filter tanggal
var picker = $('#dateRangeFilter').data('daterangepicker');
if (picker && picker.startDate && picker.startDate.isValid()) {
d.startDate = picker.startDate.format('YYYY-MM-DD');
}
if (picker && picker.endDate && picker.endDate.isValid()) {
d.endDate = picker.endDate.format('YYYY-MM-DD');
}
}
},
columns: [
{ data: 0, orderable: false }, // No
{ data: 1 }, // Nama
{ data: 2 }, // NIS
{ data: 3 }, // Kelas
{ data: 4 }, // Kamar
{ data: 5 }, // Nama Wali
{ data: 6 }, // Alamat
{
data: 7, // Jam Masuk
render: function(data) {
return data ? data : '';
}
},
{
data: 8, // Media Masuk
orderable: false,
render: function(data) {
return data ? data : '';
}
},
{
data: 9, // Jam Keluar
render: function(data) {
return data ? data : '';
}
},
{
data: 10, // Media Keluar
orderable: false,
render: function(data) {
return data ? data : '';
}
}
],
language: {
processing: `<div class="d-flex justify-content-center"><div class="spinner-border text-success"></div></div>`
}
});
$('.dataTables_filter input').attr('placeholder', 'Cari nama, nis, kelas...').css({
'padding': '6px 12px',
'border-radius': '6px',
'border': '1px solid #ced4da',
'font-size': '0.9rem',
'box-sizing': 'border-box'
});
table.on('draw', function() {
$('#tableHistory tbody tr').each(function() {
$(this).find('td').each(function(index) {
let header = $('#tableHistory thead th').eq(index).text().trim();
$(this).attr('data-label', header + ' : ');
});
});
});
});
</script>
<script>
/* ============================================================
1. DETEKSI ORIENTASI BACKGROUND & LOAD TEMPLATE OTOMATIS
============================================================ */
window.addEventListener("DOMContentLoaded", function () {
let bg = document.getElementById("defaultBg");
if (!bg) {
console.error("defaultBg tidak ditemukan!");
return;
}
// Tunggu gambar background selesai diload
bg.onload = function () {
let isPortrait = bg.naturalHeight > bg.naturalWidth;
console.log("Background orientation:",
isPortrait ? "POTRAIT" : "LANDSCAPE"
);
loadCardPreviewFromImage(bg.src, isPortrait);
};
// Jika gambar sudah cached & tidak trigger onload
if (bg.complete) {
bg.onload();
}
});
/* ============================================================
2. LOAD TEMPLATE CARD
============================================================ */
function loadCardPreviewFromImage(imgSrc, isPortrait) {
let preview = document.getElementById("cardPreview");
if (!preview) return;
preview.style.backgroundImage = "url('" + imgSrc + "')";
preview.className = "";
if (isPortrait) {
preview.classList.add("id-card-potret");
preview.innerHTML = portraitTemplate();
} else {
preview.classList.add("id-card-lemscape");
preview.innerHTML = landscapeTemplate();
}
}
/* ============================================================
3. TEMPLATE POTRAIT
============================================================ */
function portraitTemplate() {
return `
<div class="card-inner">
<div class="card-logo">
<img src="<?= base_url('assets/img/logo.png'); ?>">
</div>
<div class="card-title">Kartu Tanda Wali Santri</div>
<div class="photo-wrapper">
<img src="<?= $fotoSiswa; ?>">
</div>
<div class="card-info">
<div class="nama"><?= $siswa->nama; ?></div>
<div class="nis">NIS: <?= $siswa->nis; ?></div>
<div class="kelas">Tahun Masuk: <?= $siswa->tahun_masuk; ?></div>
<div class="alamat"><?= $siswa->alamat; ?></div>
</div>
<div class="qr-wrapper">
<img src="<?= base_url('qr/show/' . urlencode($siswa->qr_id)) ?>" width="100%">
</div>
</div>
`;
}
/* ============================================================
4. TEMPLATE LANDSCAPE
============================================================ */
function landscapeTemplate() {
return `
<div class="card-inner">
<div class="card-logo">
<img src="<?= base_url('assets/img/logo.png'); ?>">
</div>
<div class="card-title">Kartu Tanda Wali Santri</div>
<div class="landscape-content">
<div class="photo-wrapper">
<img src="<?= $fotoSiswa; ?>">
</div>
<div class="card-info">
<div class="nama"><?= $siswa->nama; ?></div>
<div class="nis">NIS: <?= $siswa->nis; ?></div>
<div class="kelas">Tahun Masuk: <?= $siswa->tahun_masuk; ?></div>
<div class="alamat"><?= $siswa->alamat; ?></div>
</div>
<div class="qr-wrapper">
<img src="<?= base_url('qr/show/' . urlencode($siswa->qr_id)) ?>" width="100%">
</div>
</div>
</div>
`;
}
/* ============================================================
5. PRINT CARD DALAM MODE PDF
============================================================ */
function printCardPDF() {
let card = document.getElementById("cardPreview");
if (!card) {
alert("Card Preview tidak ditemukan!");
return;
}
// Ambil semua CSS di halaman
let styles = "";
document.querySelectorAll("style, link[rel='stylesheet']").forEach((el) => {
styles += el.outerHTML;
});
// Buka jendela print
let w = window.open("", "_blank");
w.document.write(`
<html>
<head>
<title>Cetak Kartu Santri</title>
${styles} <!-- COPY semua CSS -->
<style>
body {
margin: 0;
padding: 20px;
display: flex;
zoom: 69%;
background: #fff !important;
}
/* Pastikan background ikut ke print */
@media print {
#cardPreview {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
* {
-webkit-print-color-adjust: exact !important;
print-color-adjust: exact !important;
}
}
</style>
</head>
<body>
${card.outerHTML}
</body>
</html>
`);
w.document.close();
// Tunggu elemen selesai render
setTimeout(() => {
w.print();
// w.close(); // Kalau mau ditutup otomatis
}, 500);
}
</script>
<?php endif; ?>
<script>
document.getElementById('downloadPdfBtn').addEventListener('click', function() {
// Ambil seluruh container
const container = document.getElementById('containerDownload');
if (!container) {
alert("Container tidak ditemukan!");
return;
}
// Opsi PDF
const opt = {
margin: 0.5, // Margin dalam inch
filename: 'Detail_Santri_<?= $siswa->nama ?>.pdf',
image: { type: 'jpeg', quality: 0.98 },
html2canvas: { scale: 2, logging: true, letterRendering: true, useCORS: true },
jsPDF: { unit: 'in', format: 'a4', orientation: 'landscape' }
};
// Generate PDF
html2pdf().set(opt).from(container).save();
});
</script>
+601
View File
@@ -0,0 +1,601 @@
<style>
.modern-tabs .nav-link {
border: none;
font-weight: 600;
color: #555;
border-radius: 8px 8px 0 0;
}
.modern-tabs .nav-link.active {
background: #0d6efd;
color: white;
}
.import-dropzone {
border: 2px dashed #c7d6ff;
padding: 30px;
border-radius: 12px;
transition: .3s;
cursor: pointer;
}
.import-dropzone.dragover {
background: #eef5ff;
border-color: #0d6efd;
}
.export-card {
border: 1px solid #eaeaea;
background: #fafafa;
}
</style>
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">Data Santri</h5>
<div>
<?php if(check_permission('siswa', 'can_create')): ?>
<button class="btn btn-success btn-add" style="margin-right: 5px;">
<i class="bi bi-plus-circle me-1"></i> Tambah
</button>
<?php endif; ?>
<?php if(check_permission('siswa', 'can_export')): ?>
<button class="btn btn-success btn-eximport d-none d-lg-inline" style="margin-right: 5px;" data-bs-toggle="modal" data-bs-target="#modalImport">
<i class="bi bi-upload me-1"></i> Import
</button>
<?php endif; ?>
<?php if(check_permission('siswa', 'can_import')): ?>
<button class="btn btn-success btn-eximport d-none d-lg-inline" data-bs-toggle="modal" data-bs-target="#modalExport">
<i class="bi bi-download me-1"></i> Export
</button>
<?php endif; ?>
</div>
</div>
<div class="table-responsive">
<table id="tableSiswa" class="table modern-table align-middle w-100">
<thead>
<tr>
<th>No</th>
<th>Foto</th>
<th>Nama</th>
<th>NIS</th>
<th>Kelas</th>
<th>Kamar</th>
<th>Alamat</th>
<th>Nama Wali</th>
<th>Nomor Whatsapp</th>
<th>Jumlah Kunjungan</th>
<th>Aksi</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL TAMBAH / EDIT -->
<div class="modal fade" id="modalTambah">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content rounded-4">
<div class="modal-header bg-success text-white rounded-top-4">
<h5 class="modal-title">Tambah Santri</h5>
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="card-foto-santri">
<div class="foto-santri" id="previewFoto"></div>
<input type="file" class="form-control d-none" id="imageSantri">
</div>
<div class="row">
<div class="col-md-6">
<label class="fw-semibold mt-2">Nama</label>
<input type="text" class="form-control" id="inputNama">
<label class="fw-semibold mt-2">NIS</label>
<input type="text" class="form-control" id="inputNIS">
<label class="fw-semibold mt-2">Kelas</label>
<select class="form-control" id="inputKelas">
<option value="">-- Pilih Kelas --</option>
</select>
<label class="fw-semibold mt-2">Kamar</label>
<select class="form-control" id="inputKamar">
<option value="">-- Pilih Kamar --</option>
</select>
<label class="fw-semibold mt-2">Nama Wali Santri</label>
<input type="text" class="form-control" id="inputWali">
</div>
<div class="col-md-6">
<label class="fw-semibold mt-2">Nomor Whatsapp</label>
<input type="text" class="form-control" id="inputWhatsapp">
<label class="fw-semibold mt-2">Email Wali Santri</label>
<input type="text" class="form-control" id="inputEmail">
<label class="fw-semibold mt-2">Alamat</label>
<textarea class="form-control" id="inputAlamat" rows="4"></textarea>
<label class="fw-semibold mt-2">Tahun Masuk</label>
<select class="form-control" id="inputTahun">
<option value="">-- Pilih Tahun --</option>
</select>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button class="btn btn-success" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<!-- Modal Import -->
<div class="modal fade" id="modalImport" tabindex="-1">
<div class="modal-dialog modal-lg modal-dialog-centered">
<div class="modal-content shadow-lg rounded-4 border-0">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold">
<i class="bi bi-upload me-2"></i> Import Data Siswa
</h5>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="import-dropzone text-center" id="dropzone">
<i class="bi bi-cloud-arrow-up fs-1 mb-2 text-primary"></i>
<h6 class="fw-bold">Tarik File ke Sini</h6>
<p class="small text-muted mb-2">Atau klik untuk memilih file Excel (.xlsx)</p>
<input type="file" id="fileImport" accept=".xlsx" hidden>
<button class="btn btn-outline-primary btn-sm mt-2" id="btnSelectFile">
Pilih File
</button>
<div id="fileName" class="mt-3 fw-semibold text-success"></div>
</div>
<button class="btn btn-success w-100 mt-3" id="btnUpload">
<i class="bi bi-check-circle me-2"></i> Upload & Proses
</button>
<hr>
<a href="<?= base_url('import/template'); ?>" class="btn btn-secondary w-100">
<i class="bi bi-file-earmark-spreadsheet me-1"></i> Download Template Excel
</a>
</div>
</div>
</div>
</div>
<!-- Modal Export -->
<div class="modal fade" id="modalExport" tabindex="-1">
<div class="modal-dialog modal-md modal-dialog-centered">
<div class="modal-content shadow-lg rounded-4 border-0">
<div class="modal-header border-0 pb-0">
<h5 class="modal-title fw-bold">
<i class="bi bi-download me-2"></i> Export Data Siswa
</h5>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<div class="export-card p-3 rounded-3">
<h6 class="fw-bold mb-3"><i class="bi bi-file-earmark-arrow-down me-1"></i> Pilih Format</h6>
<div class="d-flex gap-2">
<a href="<?= base_url('export/siswa_excel'); ?>" class="btn btn-primary flex-fill">
<i class="bi bi-file-earmark-excel me-1"></i> Excel
</a>
<a href="<?= base_url('export/siswa_pdf'); ?>" class="btn btn-danger flex-fill">
<i class="bi bi-file-earmark-pdf me-1"></i> PDF
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function(){
// ========================================================
// INIT DATATABLE
// ========================================================
let table = $('#tableSiswa').DataTable({
dom: '<"dataTables_top d-flex justify-content-between mb-3"lf>rtip',
processing: true,
serverSide: true,
responsive: false,
autoWidth: false,
order: [],
ajax: {
url: "<?= base_url('siswa/get_data'); ?>",
type: "POST"
},
columns: [
{ data: 0, orderable: false },
{ data: 1 },
{ data: 2 },
{ data: 3 },
{ data: 4 },
{ data: 5 },
{ data: 6 },
{ data: 7 },
{ data: 8 },
{ data: 9 },
{ data: 10, orderable: false }
],
language: {
processing: `<div class="d-flex justify-content-center"><div class="spinner-border text-success"></div></div>`
}
});
$('.dataTables_filter input')
.attr('placeholder','Search...')
.css({
'min-width':'300px',
'padding':'6px 12px',
'border-radius':'6px',
'border':'1px solid #ced4da',
'font-size':'0.9rem'
});
table.on('draw', function () {
$('#tableSiswa tbody tr').each(function() {
$(this).find('td').each(function(index) {
let header = $('#tableSiswa thead th').eq(index).text().trim();
$(this).attr('data-label', header + ' : ');
});
});
});
// ========================================================
// MODAL ADD
// ========================================================
$(document).on('click', '.btn-add', function(){
resetModal();
$('#modalTambah .modal-title').text('Tambah Santri');
$('#btnSimpan').text('Simpan').data('action','add').removeData('id');
loadKelasList();
loadKamarList();
loadListTahun();
$('#modalTambah').modal('show');
// default foto
$('#previewFoto').css('background-image', `url('uploads/siswa/foto.jpg')`);
});
// ========================================================
// MODAL EDIT
// ========================================================
$(document).on('click', '.btn-edit', function(){
resetModal();
let id = $(this).data('id');
$.ajax({
url: "<?= base_url('siswa/detailEdit'); ?>/" + id,
type: "GET",
dataType: "json",
success: function(res){
$('#inputNama').val(res.nama);
$('#inputNIS').val(res.nis);
$('#inputAlamat').val(res.alamat);
$('#inputWali').val(res.nama_wali);
$('#inputWhatsapp').val(res.nomor_whatsapp);
$('#inputEmail').val(res.email);
loadKelasList(res.id_kelas);
loadKamarList(res.id_kamar);
loadListTahun(res.tahun_masuk);
if(res.foto){
$('#previewFoto').css('background-image', `url('uploads/siswa/${res.foto}')`);
} else {
$('#previewFoto').css('background-image', `url('uploads/siswa/foto.jpg')`);
}
$('#modalTambah .modal-title').text('Edit Santri');
$('#btnSimpan').text('Update').data('action','edit').data('id', res.id);
$('#modalTambah').modal('show');
},
error: function(){
Swal.fire('Error','Gagal memuat data edit.','error');
}
});
});
// ========================================================
// DELETE
// ========================================================
$(document).on('click', '.btn-delete', function(){
let id = $(this).data('id');
Swal.fire({
title: 'Yakin ingin hapus?',
text: "Data tidak dapat dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus',
cancelButtonText: 'Batal'
}).then((result)=>{
if(result.isConfirmed){
$.ajax({
url: "<?= base_url('siswa/delete'); ?>/" + id,
type: "GET",
dataType: "json",
success: function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire('Terhapus!', res.message || 'Berhasil dihapus!', 'success');
} else {
Swal.fire('Gagal', res.message || 'Gagal menghapus!', 'error');
}
},
error: function(){
Swal.fire('Error','Terjadi kesalahan server.','error');
}
});
}
});
});
// ========================================================
// FOTO PREVIEW + COMPRESS (SEND COMPRESSED)
// ========================================================
const fotoDiv = document.getElementById("previewFoto");
const fileInput = document.getElementById("imageSantri");
fotoDiv.addEventListener("click", () => fileInput.click());
fileInput.addEventListener("change", function(e){
const file = e.target.files[0];
if(!file || !file.type.startsWith("image/")) return;
const reader = new FileReader();
reader.onload = function(evt){
const img = new Image();
img.onload = function(){
const canvas = document.createElement("canvas");
let {width, height} = img;
const maxDim = 800;
if(width > maxDim || height > maxDim){
if(width > height){
height = Math.round(height * maxDim / width);
width = maxDim;
} else {
width = Math.round(width * maxDim / height);
height = maxDim;
}
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext("2d");
ctx.drawImage(img, 0, 0, width, height);
let quality = 0.7;
let compressed = canvas.toDataURL("image/jpeg", quality);
while(compressed.length/1024 > 250 && quality > 0.1){
quality -= 0.05;
compressed = canvas.toDataURL("image/jpeg", quality);
}
fotoDiv.style.backgroundImage = `url('${compressed}')`;
// simpan hasil kompres ke element
$('#previewFoto').data('compressed', compressed);
};
img.src = evt.target.result;
};
reader.readAsDataURL(file);
});
// ========================================================
// SAVE / UPDATE
// ========================================================
$('#btnSimpan').click(function(){
let action = $(this).data('action');
let id = $(this).data('id') || '';
let formData = new FormData();
formData.append('id', id);
formData.append('nama', $('#inputNama').val());
formData.append('nis', $('#inputNIS').val());
formData.append('id_kelas', $('#inputKelas').val());
formData.append('alamat', $('#inputAlamat').val());
formData.append('nama_wali', $('#inputWali').val());
formData.append('nomor_whatsapp', $('#inputWhatsapp').val());
formData.append('email', $('#inputEmail').val());
formData.append('tahun_masuk', $('#inputTahun').val());
formData.append('id_kamar', $('#inputKamar').val());
// Ambil hasil kompres
const compressedImage = $('#previewFoto').data('compressed');
if(compressedImage){
formData.append('foto', compressedImage);
}
let url = action === 'add' ? "<?= base_url('siswa/save'); ?>"
: "<?= base_url('siswa/update'); ?>";
$.ajax({
url: url,
type: 'POST',
data: formData,
processData: false,
contentType: false,
dataType: 'json',
success: function(res){
if(res.status){
$('#modalTambah').modal('hide');
table.ajax.reload(null,false);
Swal.fire('Sukses', res.message || 'Berhasil!', 'success');
} else {
Swal.fire('Gagal', res.message || 'Gagal!', 'error');
}
},
error: function(){
Swal.fire('Error','Terjadi kesalahan server.','error');
}
});
});
// ========================================================
// FUNCTIONS
// ========================================================
function resetModal(){
$('#imageSantri').val('');
$('#inputNama,#inputNIS,#inputAlamat,#inputWali,#inputWhatsapp,#inputEmail').val('');
$('#inputKelas,#inputKamar,#inputTahun').val('');
$('#previewFoto').css('background-image', `url('uploads/siswa/foto.jpg')`);
$('#previewFoto').removeData('compressed');
}
function loadKelasList(selectedId=''){
$.ajax({
url: "<?= base_url('kelas/get_list'); ?>",
type: "GET",
dataType: "json",
success: function(res){
const select = $('#inputKelas');
select.empty().append('<option value="">-- Pilih Kelas --</option>');
res.forEach(k=>{
select.append(`<option value="${k.id}" ${k.id==selectedId?'selected':''}>${k.nama}</option>`);
});
}
});
}
function loadKamarList(selectedId=''){
$.ajax({
url: "<?= base_url('kamar/get_list'); ?>",
type: "GET",
dataType: "json",
success: function(res){
const select = $('#inputKamar');
select.empty().append('<option value="">-- Pilih Kamar --</option>');
res.forEach(k=>{
select.append(`<option value="${k.id}" ${k.id==selectedId?'selected':''}>${k.nama} (${k.gedung})</option>`);
});
}
});
}
function loadListTahun(selected=''){
const select = $('#inputTahun');
select.empty().append('<option value="">-- Pilih Tahun --</option>');
for(let tahun=2015; tahun<=2030; tahun++){
select.append(`<option value="${tahun}" ${tahun==selected?'selected':''}>${tahun}</option>`);
}
}
});
</script>
<script>
$("#btnSelectFile").click(function () {
$("#fileImport").click();
});
$("#fileImport").on("change", function () {
$("#fileName").text(this.files[0].name);
});
let dropzone = document.getElementById("dropzone");
dropzone.addEventListener("click", () => $("#fileImport").click());
dropzone.addEventListener("dragover", (e) => {
e.preventDefault(); dropzone.classList.add("dragover");
});
dropzone.addEventListener("dragleave", () => dropzone.classList.remove("dragover"));
dropzone.addEventListener("drop", (e) => {
e.preventDefault(); dropzone.classList.remove("dragover");
let file = e.dataTransfer.files[0];
$("#fileImport")[0].files = e.dataTransfer.files;
$("#fileName").text(file.name);
});
$("#btnUpload").click(function () {
let file = $("#fileImport")[0].files[0];
if (!file) {
Swal.fire("Oops!", "Silakan pilih file terlebih dahulu!", "warning");
return;
}
let formData = new FormData();
formData.append("file", file);
Swal.fire({ title: "Mengupload...", didOpen: () => Swal.showLoading() });
$.ajax({
url: "<?= base_url('import/siswa'); ?>",
type: "POST",
data: formData,
contentType: false,
processData: false,
success: function () {
Swal.fire("Berhasil!", "Data berhasil di-import.", "success");
},
error: function () {
Swal.fire("Error!", "Gagal upload file.", "error");
}
});
});
</script>
+291
View File
@@ -0,0 +1,291 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between align-items-center mb-3">
<h5 class="mb-0">Manajemen Pengguna</h5>
<button class="btn btn-warning btn-add">
<i class="bi bi-plus-circle me-1"></i> Tambah Pengguna
</button>
</div>
<div class="table-responsive">
<table id="tableUsers" class="table modern-table align-middle w-100">
<thead>
<tr>
<th>ID</th>
<th>Username</th>
<th>Nama</th>
<th>Role</th>
<th>Aksi</th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalUser">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content rounded-4">
<div class="modal-header bg-warning text-white rounded-top-4">
<h5 class="modal-title">Tambah Pengguna</h5>
<button class="btn-close btn-close-white" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="inputId">
<div class="mb-3">
<label class="fw-semibold">Username</label>
<input type="text" class="form-control" id="inputUsername">
</div>
<div class="mb-3">
<label class="fw-semibold">Nama</label>
<input type="text" class="form-control" id="inputNama">
</div>
<div class="mb-3">
<label class="fw-semibold">Role</label>
<select class="form-control" id="inputRole">
<option value="">-- Pilih Role --</option>
</select>
</div>
<div class="password-group">
<div class="mb-3">
<label class="fw-semibold">Password</label>
<input type="password" class="form-control" id="inputPassword">
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button class="btn btn-warning" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
let modal = new bootstrap.Modal(document.getElementById('modalUser'));
// ======================
// DATATABLE
// ======================
let table = $('#tableUsers').DataTable({
processing: true,
serverSide: true,
responsive: true,
ajax: {
url: "<?= base_url('users/get_data'); ?>",
type: "POST"
},
columns: [
{ data: 0 },
{ data: 1 },
{ data: 2 },
{ data: 3 },
{ data: 4, orderable: false }
]
});
// ======================
// OPEN ADD MODAL
// ======================
$('.btn-add').on('click', function () {
resetModal();
$('#modalUser .modal-title').text('Tambah Pengguna');
$('#btnSimpan').data('action', 'add');
$('.password-group').show();
modal.show();
});
// ======================
// SAVE
// ======================
$('#btnSimpan').on('click', function () {
let action = $(this).data('action');
let formData = {
id: $('#inputId').val(),
username: $('#inputUsername').val(),
nama: $('#inputNama').val(),
role_id: $('#inputRole').val(),
password: $('#inputPassword').val()
};
let url = (action === 'add')
? "<?= base_url('users/store'); ?>"
: "<?= base_url('users/update'); ?>";
$.ajax({
url: url,
type: "POST",
data: formData,
dataType: "json",
success: function (res) {
if (res.status) {
modal.hide();
table.ajax.reload(null, false);
Swal.fire('Sukses', res.message, 'success');
} else {
Swal.fire('Gagal', res.message, 'error');
}
},
error: function () {
Swal.fire('Error', 'Server bermasalah', 'error');
}
});
});
// ======================
// EDIT
// ======================
$('#tableUsers').on('click', '.btn-edit', function () {
let id = $(this).data('id');
$.ajax({
url: "<?= base_url('users/edit'); ?>/" + id,
type: "GET",
dataType: "json",
success: function (res) {
if (res.status) {
resetModal();
$('#modalUser .modal-title').text('Edit Pengguna');
$('#btnSimpan').data('action', 'edit');
$('#inputId').val(res.data.id);
$('#inputUsername').val(res.data.username);
$('#inputNama').val(res.data.nama);
loadRole(res.data.role_id);
$('.password-group').hide();
$('#inputPassword').val('');
modal.show();
} else {
Swal.fire('Gagal', res.message, 'error');
}
},
error: function () {
Swal.fire('Error', 'Gagal load data edit', 'error');
}
});
});
// ======================
// DELETE
// ======================
$('#tableUsers').on('click', '.btn-delete', function () {
let id = $(this).data('id');
Swal.fire({
title: 'Hapus user?',
text: "Data tidak bisa dikembalikan!",
icon: 'warning',
showCancelButton: true,
confirmButtonText: 'Ya, hapus!'
}).then((result) => {
if (result.isConfirmed) {
$.ajax({
url: "<?= base_url('users/delete'); ?>/" + id,
type: "POST",
dataType: "json",
success: function (res) {
if (res.status) {
table.ajax.reload(null, false);
Swal.fire('Terhapus', res.message, 'success');
} else {
Swal.fire('Gagal', res.message, 'error');
}
},
error: function () {
Swal.fire('Error', 'Server error', 'error');
}
});
}
});
});
// ======================
// LOAD ROLE (FIXED)
// ======================
function loadRole(selected = '') {
$.ajax({
url: "<?= base_url('users/get_role'); ?>",
type: "GET",
dataType: "json",
success: function (res) {
let select = $('#inputRole');
select.empty();
select.append('<option value="">-- Pilih Role --</option>');
if (Array.isArray(res)) {
res.forEach(r => {
let selectedAttr = (r.id == selected) ? 'selected' : '';
select.append(`<option value="${r.id}" ${selectedAttr}>${r.nama_role}</option>`);
});
}
},
error: function () {
Swal.fire('Error', 'Gagal load role', 'error');
}
});
}
// ======================
// RESET MODAL
// ======================
function resetModal() {
$('#inputId').val('');
$('#inputUsername').val('');
$('#inputNama').val('');
$('#inputPassword').val('');
$('#inputRole').val('');
loadRole();
}
});
</script>
+153
View File
@@ -0,0 +1,153 @@
<div class="container mt-4">
<div class="card modern-card shadow-sm border-0 rounded-4">
<div class="card-body p-4">
<div class="d-flex justify-content-between mb-3">
<h5>Data Gudang</h5>
<button class="btn btn-warning btn-add">Tambah Gudang</button>
</div>
<div class="table-responsive">
<table id="tableWarehouse" class="table modern-table align-middle w-100">
<thead class="table-light">
<tr>
<th>No</th>
<th>Nama Gudang</th>
<th>Aksi</th>
</tr>
</thead>
</table>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal fade" id="modalWarehouse">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header bg-warning text-white">
<h5 class="modal-title">Gudang</h5>
<button class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body">
<input type="hidden" id="id">
<label>Nama Gudang</label>
<input type="text" id="nama" class="form-control">
</div>
<div class="modal-footer">
<button class="btn btn-secondary" data-bs-dismiss="modal">Batal</button>
<button class="btn btn-warning" id="btnSimpan">Simpan</button>
</div>
</div>
</div>
</div>
<script>
$(function(){
let action = 'add';
let table = $('#tableWarehouse').DataTable({
ajax:{
url:"<?= base_url('warehouses/get_data'); ?>",
type:"POST"
}
});
function resetForm(){
$('#id').val('');
$('#nama').val('');
}
// ADD
$('.btn-add').click(function(){
resetForm();
action = 'add';
$('#modalWarehouse').modal('show');
});
// EDIT
$(document).on('click','.btn-editgudang',function(){
let id = $(this).data('id');
$.get("<?= base_url('warehouses/detail/'); ?>"+id,function(res){
$('#id').val(res.id);
$('#nama').val(res.nama);
action = 'edit';
$('#modalWarehouse').modal('show');
},'json');
});
// SAVE
$('#btnSimpan').click(function(){
let data = {
id: $('#id').val(),
nama: $('#nama').val()
};
if(!data.nama){
Swal.fire('Warning','Nama gudang wajib diisi','warning');
return;
}
let url = action === 'add'
? "<?= base_url('warehouses/save'); ?>"
: "<?= base_url('warehouses/update'); ?>";
$.post(url,data,function(res){
if(res.status){
$('#modalWarehouse').modal('hide');
table.ajax.reload(null,false);
Swal.fire('Sukses',res.message,'success');
} else {
Swal.fire('Error',res.message,'error');
}
},'json');
});
// DELETE
$(document).on('click','.btn-delete',function(){
let id = $(this).data('id');
Swal.fire({
title:'Hapus gudang?',
text:'Pastikan tidak dipakai item',
icon:'warning',
showCancelButton:true
}).then(r=>{
if(r.isConfirmed){
$.get("<?= base_url('warehouses/delete/'); ?>"+id,function(res){
if(res.status){
table.ajax.reload(null,false);
Swal.fire('Sukses',res.message,'success');
} else {
Swal.fire('Error',res.message,'error');
}
},'json');
}
});
});
});
</script>
+100
View File
@@ -0,0 +1,100 @@
<?php
defined('BASEPATH') OR exit('No direct script access allowed');
?><!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Welcome to CodeIgniter</title>
<style type="text/css">
::selection { background-color: #E13300; color: white; }
::-moz-selection { background-color: #E13300; color: white; }
body {
background-color: #fff;
margin: 40px;
font: 13px/20px normal Helvetica, Arial, sans-serif;
color: #4F5155;
}
a {
color: #003399;
background-color: transparent;
font-weight: normal;
text-decoration: none;
}
a:hover {
color: #97310e;
}
h1 {
color: #444;
background-color: transparent;
border-bottom: 1px solid #D0D0D0;
font-size: 19px;
font-weight: normal;
margin: 0 0 14px 0;
padding: 14px 15px 10px 15px;
}
code {
font-family: Consolas, Monaco, Courier New, Courier, monospace;
font-size: 12px;
background-color: #f9f9f9;
border: 1px solid #D0D0D0;
color: #002166;
display: block;
margin: 14px 0 14px 0;
padding: 12px 10px 12px 10px;
}
#body {
margin: 0 15px 0 15px;
min-height: 96px;
}
p {
margin: 0 0 10px;
padding:0;
}
p.footer {
text-align: right;
font-size: 11px;
border-top: 1px solid #D0D0D0;
line-height: 32px;
padding: 0 10px 0 10px;
margin: 20px 0 0 0;
}
#container {
margin: 10px;
border: 1px solid #D0D0D0;
box-shadow: 0 0 8px #D0D0D0;
}
</style>
</head>
<body>
<div id="container">
<h1>Welcome to CodeIgniter!</h1>
<div id="body">
<p>The page you are looking at is being generated dynamically by CodeIgniter.</p>
<p>If you would like to edit this page you'll find it located at:</p>
<code>application/views/welcome_message.php</code>
<p>The corresponding controller for this page is found at:</p>
<code>application/controllers/Welcome.php</code>
<p>If you are exploring CodeIgniter for the very first time, you should start by reading the <a href="userguide3/">User Guide</a>.</p>
</div>
<p class="footer">Page rendered in <strong>{elapsed_time}</strong> seconds. <?php echo (ENVIRONMENT === 'development') ? 'CodeIgniter Version <strong>' . CI_VERSION . '</strong>' : '' ?></p>
</div>
</body>
</html>