251 lines
14 KiB
Plaintext
251 lines
14 KiB
Plaintext
<!DOCTYPE html>
|
|
<html lang="<%= lang || 'id' %>">
|
|
<head>
|
|
<meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
|
<title><%= t('admin.disc.title','Diskon Tagihan') %> | <%= company %></title>
|
|
<link rel="stylesheet" href="/vendor/bootstrap-icons/bootstrap-icons.min.css">
|
|
<link rel="stylesheet" href="/css/admin.css">
|
|
<style>
|
|
.disc-filter{display:flex;gap:10px;align-items:center;flex-wrap:wrap}
|
|
.cust-row.suspended td{opacity:.6}
|
|
.disc-badge{background:rgba(173,255,47,.16);color:#ADFF2F;border:1px solid rgba(173,255,47,.35);border-radius:999px;padding:2px 8px;font-size:11px;font-weight:700}
|
|
.disc-badge.amount{background:rgba(56,189,248,.14);color:#38BDF8;border-color:rgba(56,189,248,.35)}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<%- include('partials/sidebar', { activePage: 'discounts', company }) %>
|
|
<div class="mw">
|
|
<div class="topbar">
|
|
<div class="tb-title"><button class="hb-menu" onclick="toggleSidebar()"><i class="bi bi-list"></i></button><i class="bi bi-percent"></i> <%= t('admin.disc.title','Diskon Tagihan') %></div>
|
|
<div class="tb-right">
|
|
<span class="adm-badge"><i class="bi bi-person-fill"></i> <%= (typeof session !== 'undefined' && session.isCashier) ? 'Kasir' : 'Admin' %></span>
|
|
<a href="/admin/logout" class="btn btn-g btn-sm"><i class="bi bi-box-arrow-left"></i></a>
|
|
</div>
|
|
</div>
|
|
<div class="page">
|
|
|
|
<% if (msg) { %>
|
|
<div class="alert alert-<%= msg.type==='success'?'s':'d' %>" data-flash="1">
|
|
<i class="bi bi-<%= msg.type==='success'?'check-circle':'exclamation-circle' %>"></i> <%= msg.text %>
|
|
</div>
|
|
<% } %>
|
|
|
|
<form method="POST" action="/admin/discounts/apply" id="discForm">
|
|
<!-- Konfigurasi diskon -->
|
|
<div class="card mb16">
|
|
<div class="card-hd"><h6><i class="bi bi-sliders"></i> <%= t('admin.disc.settings_title','Pengaturan Diskon') %></h6></div>
|
|
<div class="card-bd">
|
|
<div class="form-row">
|
|
<div class="fg">
|
|
<label class="fl">Target</label>
|
|
<select class="fs" name="apply_mode" id="applyMode">
|
|
<option value="selected" selected><%= t('admin.disc.opt_selected','Pelanggan terpilih (batch)') %></option>
|
|
<option value="single"><%= t('admin.disc.opt_single','Satu pelanggan (1 per 1)') %></option>
|
|
<option value="all"><%= t('admin.disc.opt_all','Semua pelanggan aktif') %></option>
|
|
</select>
|
|
</div>
|
|
<div class="fg" id="singleWrap" style="display:none">
|
|
<label class="fl"><%= t('admin.disc.pick_customer','Pilih Pelanggan') %></label>
|
|
<select class="fs" name="customer_id">
|
|
<option value=""><%= t('admin.disc.ph_pick_customer','-- pilih pelanggan --') %></option>
|
|
<% customers.forEach(function(c){ %>
|
|
<option value="<%= c.id %>"><%= c.customer_code || c.id %> — <%= c.name %><%= c.area ? ' ('+c.area+')' : '' %></option>
|
|
<% }); %>
|
|
</select>
|
|
</div>
|
|
<div class="fg">
|
|
<label class="fl"><%= t('admin.disc.disc_type','Tipe Diskon') %></label>
|
|
<select class="fs" name="discount_type" id="discType">
|
|
<option value="percent"><%= t('admin.disc.opt_percent','Persen (%)') %></option>
|
|
<option value="amount"><%= t('admin.disc.opt_amount','Nominal (Rp)') %></option>
|
|
</select>
|
|
</div>
|
|
<div class="fg">
|
|
<label class="fl"><%= t('admin.disc.disc_value','Nilai Diskon') %></label>
|
|
<input class="fc" type="number" name="discount_value" id="discValue" min="1" step="1" placeholder="<%= t('admin.disc.ph_value','mis. 10 (persen) / 20000 (Rp)') %>" required>
|
|
<div class="hint" id="discHint"><%= t('admin.disc.disc_hint','Persen dihitung dari harga dasar (sebelum PPN/USO).') %></div>
|
|
</div>
|
|
</div>
|
|
<div class="form-row">
|
|
<div class="fg">
|
|
<label class="fl"><%= t('admin.disc.period_month','Periode Tagihan — Bulan') %></label>
|
|
<select class="fs" name="period_month">
|
|
<% const mNames=['Januari','Februari','Maret','April','Mei','Juni','Juli','Agustus','September','Oktober','November','Desember'];
|
|
for(let m=1;m<=12;m++){ %>
|
|
<option value="<%= m %>" <%= (timeInfo.month===m)?'selected':'' %>><%= mNames[m-1] %></option>
|
|
<% } %>
|
|
</select>
|
|
</div>
|
|
<div class="fg">
|
|
<label class="fl"><%= t('admin.disc.year','Tahun') %></label>
|
|
<select class="fs" name="period_year">
|
|
<% for(let y=(timeInfo.year||new Date().getFullYear())+1; y>=2023; y--){ %>
|
|
<option value="<%= y %>" <%= (timeInfo.year===y)?'selected':'' %>><%= y %></option>
|
|
<% } %>
|
|
</select>
|
|
</div>
|
|
<div class="fg" style="flex:2">
|
|
<label class="fl"><%= t('admin.disc.note_opt','Catatan (opsional)') %></label>
|
|
<input class="fc" name="note" placeholder="<%= t('admin.disc.ph_note','mis. promo bulan kemerdekaan') %>">
|
|
</div>
|
|
</div>
|
|
<div style="display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;margin-top:6px">
|
|
<div class="hint"><i class="bi bi-info-circle"></i> <%- t('admin.disc.apply_hint','Diskon berlaku ke tagihan <b>belum bayar</b> pada periode terpilih. Tagihan lunas/hangus tidak diubah. Kode unik QRIS akan digenerate ulang dari nominal baru.') %></div>
|
|
<button type="submit" class="btn btn-p" id="applyBtn"><i class="bi bi-percent"></i> <%= t('admin.disc.apply','Terapkan Diskon') %></button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Pilih pelanggan (batch) -->
|
|
<div class="card mb16" id="custCard">
|
|
<div class="card-hd">
|
|
<div style="display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap;width:100%">
|
|
<h6 style="margin:0"><i class="bi bi-people"></i> <%= t('admin.disc.pick_customer','Pilih Pelanggan') %> (<%= customers.length %>)</h6>
|
|
<div class="disc-filter">
|
|
<span class="disc-badge" id="selCount" title="Jumlah pelanggan terpilih">0 <%= t('admin.disc.selected','dipilih') %></span>
|
|
<label style="display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer">
|
|
<input type="checkbox" id="selectAll" style="width:16px;height:16px"> Pilih semua yang tampil
|
|
</label>
|
|
<input class="fc" id="custSearch" placeholder="Cari nama / ID / HP..." style="max-width:220px">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tbl-wrap" style="max-height:460px;overflow:auto">
|
|
<table class="dtbl" id="custTable">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:34px"></th>
|
|
<th style="width:110px">ID Pelanggan</th>
|
|
<th><%= t('pwa.field.name','Nama') %></th>
|
|
<th style="width:130px">No. HP</th>
|
|
<th style="width:110px">Area</th>
|
|
<th style="width:90px"><%= t('common.status','Status') %></th>
|
|
<th style="width:70px">Tunggakan</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<% if (customers.length===0) { %>
|
|
<tr><td colspan="7" class="text-center text-muted" style="padding:24px"><%= t('admin.disc.empty_cust','Tidak ada pelanggan.') %></td></tr>
|
|
<% } %>
|
|
<% customers.forEach(function(c){ %>
|
|
<tr class="cust-row <%= c.status==='suspended'?'suspended':'' %>">
|
|
<td><input type="checkbox" class="cust-sel" name="customer_ids" value="<%= c.id %>" style="width:16px;height:16px"></td>
|
|
<td class="mono" style="font-size:12px"><%= c.customer_code || ('#'+c.id) %></td>
|
|
<td><div class="fw6"><%= c.name %></div><div style="font-size:11px;color:var(--muted)"><%= c.pppoe_username||'' %></div></td>
|
|
<td style="font-size:12px"><%= c.phone||'-' %></td>
|
|
<td style="font-size:12px"><%= c.area||'-' %></td>
|
|
<td><span class="badge <%= c.status==='active'?'bs':(c.status==='suspended'?'bd':'bm') %>"><%= c.status %></span></td>
|
|
<td style="font-size:12px"><%= Number(c.unpaid_count||0) %></td>
|
|
</tr>
|
|
<% }); %>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
|
|
<!-- Riwayat -->
|
|
<div class="card">
|
|
<div class="card-hd"><h6><i class="bi bi-clock-history"></i> <%= t('admin.disc.history','Riwayat Diskon (100 terakhir)') %></h6></div>
|
|
<div class="tbl-wrap">
|
|
<table class="dtbl">
|
|
<thead>
|
|
<tr>
|
|
<th style="width:150px"><%= t('collector.col_time','Waktu') %></th>
|
|
<th><%= t('common.customer','Pelanggan') %></th>
|
|
<th style="width:110px"><%= t('pwa.field.period','Periode') %></th>
|
|
<th style="width:100px"><%= t('common.type','Tipe') %></th>
|
|
<th class="num" style="width:120px">Potongan</th>
|
|
<th class="num" style="width:120px">Awal → Akhir</th>
|
|
<th style="width:110px">Oleh</th>
|
|
<th><%= t('admin.disc.th_note','Catatan') %></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<% if (!logs || logs.length===0) { %>
|
|
<tr><td colspan="8" class="text-center text-muted" style="padding:24px">Belum ada riwayat diskon.</td></tr>
|
|
<% } %>
|
|
<% (logs||[]).forEach(function(l){ %>
|
|
<tr>
|
|
<td style="font-size:11px;color:var(--muted)"><%= l.created_at %></td>
|
|
<td style="font-size:12px"><%= l.customer_name || ('#'+l.customer_id) %><div style="font-size:10px;color:var(--muted)"><%= l.customer_code||'' %></div></td>
|
|
<td style="font-size:12px"><%= l.period_month %>/<%= l.period_year %></td>
|
|
<td><span class="disc-badge <%= l.discount_type==='amount'?'amount':'' %>"><%= l.discount_type==='percent' ? (l.discount_value+'%') : 'Rp' %></span></td>
|
|
<td class="num" style="color:var(--danger);font-weight:700">- Rp <%= Number(l.discount_amount||0).toLocaleString('id-ID') %></td>
|
|
<td class="num" style="font-size:12px">Rp <%= Number(l.amount_before||0).toLocaleString('id-ID') %> → <b>Rp <%= Number(l.amount_after||0).toLocaleString('id-ID') %></b></td>
|
|
<td style="font-size:12px"><%= l.applied_by||'-' %></td>
|
|
<td style="font-size:11px;color:var(--muted)"><%= l.note||'' %></td>
|
|
</tr>
|
|
<% }); %>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
(function(){
|
|
var mode = document.getElementById('applyMode');
|
|
var singleWrap = document.getElementById('singleWrap');
|
|
var custCard = document.getElementById('custCard');
|
|
var type = document.getElementById('discType');
|
|
var hint = document.getElementById('discHint');
|
|
function syncMode(){
|
|
var m = mode.value;
|
|
singleWrap.style.display = (m === 'single') ? '' : 'none';
|
|
custCard.style.display = (m === 'selected') ? '' : 'none';
|
|
}
|
|
mode.addEventListener('change', function(){ syncMode(); updateSelCount(); }); syncMode();
|
|
|
|
function syncHint(){
|
|
hint.textContent = (type.value === 'percent')
|
|
? '<%= t('admin.disc.disc_hint','Persen dihitung dari harga dasar (sebelum PPN/USO).') %> Contoh: 10 = potong 10%.'
|
|
: 'Nominal potongan langsung dalam Rupiah. Contoh: 20000 = potong Rp 20.000.';
|
|
}
|
|
type.addEventListener('change', syncHint); syncHint();
|
|
|
|
var all = document.getElementById('selectAll');
|
|
function visibleBoxes(){
|
|
var rows = document.querySelectorAll('#custTable tbody tr');
|
|
var out = [];
|
|
rows.forEach(function(tr){ if (tr.style.display !== 'none') { var b = tr.querySelector('.cust-sel'); if (b) out.push(b); } });
|
|
return out;
|
|
}
|
|
var selCount = document.getElementById('selCount');
|
|
var applyBtn = document.getElementById('applyBtn');
|
|
function updateSelCount(){
|
|
var n = document.querySelectorAll('#custTable .cust-sel:checked').length;
|
|
if (selCount) selCount.textContent = n + ' <%= t('admin.disc.selected','dipilih') %>';
|
|
if (applyBtn) applyBtn.innerHTML = '<i class="bi bi-percent"></i> <%= t('admin.disc.apply','Terapkan Diskon') %>' + ((mode.value === 'selected' && n) ? ' (' + n + ')' : '');
|
|
}
|
|
all.addEventListener('change', function(){ visibleBoxes().forEach(function(b){ b.checked = all.checked; }); updateSelCount(); });
|
|
document.getElementById('custTable').addEventListener('change', function(e){ if (e.target && e.target.classList.contains('cust-sel')) updateSelCount(); });
|
|
|
|
var search = document.getElementById('custSearch');
|
|
search.addEventListener('input', function(){
|
|
var s = search.value.toLowerCase();
|
|
document.querySelectorAll('#custTable tbody tr').forEach(function(tr){
|
|
if (!tr.querySelector('.cust-sel')) return;
|
|
tr.style.display = tr.textContent.toLowerCase().indexOf(s) !== -1 ? '' : 'none';
|
|
});
|
|
all.checked = false;
|
|
});
|
|
|
|
document.getElementById('discForm').addEventListener('submit', function(e){
|
|
var m = mode.value;
|
|
if (m === 'selected') {
|
|
var n = document.querySelectorAll('#custTable .cust-sel:checked').length;
|
|
if (n === 0) { e.preventDefault(); alert('Pilih minimal 1 pelanggan (atau pilih mode Semua/Satu).'); }
|
|
} else if (m === 'single') {
|
|
var sel = document.querySelector('select[name="customer_id"]');
|
|
if (!sel || !sel.value) { e.preventDefault(); alert('Pilih pelanggan terlebih dahulu.'); }
|
|
}
|
|
});
|
|
updateSelCount();
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|