goiintra/public_html/lib/customer_closing_days_popup...

217 lines
8.8 KiB
PHP

<?php
/* =====================================================================
* /lib/customer_closing_days_popup.php
* - Customer Closing Days (closure date ranges) editor popup
* - Opened as a modal (iframe) from customer_detail.php; reusable on other screens
* - On successful save, notifies parent via window.parent.closeCustomerPopup(true)
* - Save = "full replace" within current/future only; past ranges are not shown and are left untouched
* ===================================================================== */
include getenv("DOCUMENT_ROOT")."/include/session_include.php";
require_once getenv("DOCUMENT_ROOT")."/assets/dbCon.php";
$c_uid = isset($_REQUEST['c_uid']) ? intval($_REQUEST['c_uid']) : 0;
$goStr = $_GET['goStr'] ?? '';
$user = $_SESSION['ss_INITIAL'] ?? ($_SESSION['ss_UID'] ?? 'system');
// mbstring 미설치 환경 대비: UTF-8 문자 기준 N자로 자르기
if (!function_exists('utf8_cut')) {
function utf8_cut($s, $n) {
if (function_exists('mb_substr')) return mb_substr($s, 0, $n);
preg_match_all('/./us', (string)$s, $mm);
return (count($mm[0]) <= $n) ? $s : implode('', array_slice($mm[0], 0, $n));
}
}
/* =====================================================================
* AJAX save
* ===================================================================== */
if (($_POST['action'] ?? '') === 'save') {
header('Content-Type: application/json; charset=utf-8');
if ($c_uid <= 0) { echo json_encode(['ok' => false, 'msg' => 'invalid c_uid']); exit; }
$rows = json_decode($_POST['data'] ?? '[]', true);
if (!is_array($rows)) $rows = [];
$clean = [];
foreach ($rows as $r) {
$start = trim($r['start'] ?? '');
$end = trim($r['end'] ?? '');
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $start)) continue; // start date required
if ($end === '') $end = $start; // empty end = same day
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/', $end)) continue;
if ($end < $start) { $tmp = $start; $start = $end; $end = $tmp; } // auto-fix if reversed
$note = isset($r['note']) ? trim($r['note']) : '';
$note = utf8_cut($note, 200);
$clean[] = ['start' => $start, 'end' => $end, 'note' => $note];
}
usort($clean, function ($a, $b) { return strcmp($a['start'], $b['start']); });
$uEsc = addslashes($user);
// full replace
// full replace — 현재/미래만 관리, 과거 행은 건드리지 않음
qry("DELETE FROM tbl_customer_closing_days
WHERE ccd_customer_uid = " . $c_uid . " AND ccd_end_date >= CURDATE()");
foreach ($clean as $r) {
$noteV = ($r['note'] === '') ? "NULL" : "'" . addslashes($r['note']) . "'";
qry("INSERT INTO tbl_customer_closing_days
(ccd_customer_uid, ccd_start_date, ccd_end_date, ccd_note, ccd_createdby, ccd_created_at)
VALUES
($c_uid, '{$r['start']}', '{$r['end']}', $noteV, '$uEsc', NOW())");
}
echo json_encode(['ok' => true]);
exit;
}
/* =====================================================================
* GET: load existing (current/future only — past is not shown)
* ===================================================================== */
$existing = [];
if ($c_uid > 0) {
$res = qry("SELECT ccd_start_date, ccd_end_date, ccd_note
FROM tbl_customer_closing_days
WHERE ccd_customer_uid = $c_uid AND ccd_end_date >= CURDATE()
ORDER BY ccd_start_date");
while ($row = fetch_array($res)) {
$existing[] = [
'start' => $row['ccd_start_date'],
'end' => $row['ccd_end_date'],
'note' => $row['ccd_note'],
];
}
}
$existingJson = json_encode($existing, JSON_UNESCAPED_UNICODE);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Closing Days</title>
<style>
* { box-sizing: border-box; }
body { font-family: -apple-system, "Segoe UI", Arial, sans-serif; margin: 0; padding: 14px 16px 80px; color: #333; font-size: 14px; background:#fff; }
h2 { font-size: 16px; margin: 0 0 10px; }
.toolbar { display:flex; gap:8px; flex-wrap:wrap; margin-bottom: 10px; }
.add { border: 1px solid #7fb069; background: #7fb069; color: #fff; border-radius: 4px; font-size: 13px; padding: 6px 12px; cursor: pointer; }
.add:hover { background: #6da356; }
.rows { display: flex; flex-direction: column; gap: 6px; }
.empty { color: #bbb; font-size: 13px; padding: 6px 0; }
.row { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; padding: 4px 0; }
.row input { padding: 5px 7px; border: 1px solid #ccc; border-radius: 4px; font-size: 13px; }
.row input[type=date] { width: 150px; }
.row input.note { flex: 1; min-width: 120px; }
.tilde { color: #999; }
.rm { border: none; background: #fbecec; color: #c0392b; border-radius: 4px; width: 26px; height: 28px; cursor: pointer; font-size: 15px; line-height: 1; }
.rm:hover { background: #c0392b; color: #fff; }
.footer { position: fixed; left: 0; right: 0; bottom: 0; background: #fff; border-top: 1px solid #e3e3e3; padding: 10px 16px; display: flex; justify-content: flex-end; gap: 8px; }
.btn { padding: 8px 18px; border-radius: 4px; border: 1px solid #ccc; background: #fff; cursor: pointer; font-size: 14px; }
.btn-save { background: #7fb069; border-color: #7fb069; color: #fff; }
.btn-save:hover { background: #6da356; }
.btn:disabled { opacity: .6; cursor: default; }
</style>
</head>
<body>
<h2>Closing Days</h2>
<div class="toolbar">
<button type="button" class="add" id="addBtn">+ Add closing</button>
</div>
<div class="rows" id="rows"></div>
<div class="footer">
<button type="button" class="btn" id="closeBtn">Close</button>
<button type="button" class="btn btn-save" id="saveBtn">Save</button>
</div>
<script>
const existing = <?=$existingJson?> || [];
const C_UID = <?=$c_uid?>;
const rowsEl = document.getElementById('rows');
function esc(s){ return (s||'').replace(/"/g,'&quot;'); }
function makeRow(d) {
d = d || { start: '', end: '', note: '' };
const row = document.createElement('div');
row.className = 'row';
row.innerHTML =
'<input type="date" class="start" value="' + (d.start || '') + '">' +
'<span class="tilde">~</span>' +
'<input type="date" class="end" value="' + (d.end || '') + '">' +
'<input type="text" class="note" maxlength="200" placeholder="Note (optional)" value="' + esc(d.note) + '">' +
'<button type="button" class="rm" title="Delete">&times;</button>';
row.querySelector('.rm').addEventListener('click', function () { row.remove(); refreshEmpty(); });
return row;
}
function refreshEmpty() {
const has = rowsEl.querySelector('.row');
let emptyEl = rowsEl.querySelector('.empty');
if (!has && !emptyEl) {
emptyEl = document.createElement('div');
emptyEl.className = 'empty';
emptyEl.textContent = 'No closing days.';
rowsEl.appendChild(emptyEl);
} else if (has && emptyEl) {
emptyEl.remove();
}
}
existing.forEach(function (d) { rowsEl.appendChild(makeRow(d)); });
refreshEmpty();
document.getElementById('addBtn').addEventListener('click', function () { rowsEl.appendChild(makeRow()); refreshEmpty(); });
function closeUp(changed) {
if (window.parent && window.parent.closeCustomerPopup) {
window.parent.closeCustomerPopup(!!changed);
} else {
if (changed) location.reload(); else history.back();
}
}
document.getElementById('closeBtn').addEventListener('click', function () { closeUp(false); });
document.getElementById('saveBtn').addEventListener('click', function () {
const btn = this;
const rows = [];
let bad = false;
document.querySelectorAll('.row').forEach(function (row) {
const start = row.querySelector('.start').value.trim();
let end = row.querySelector('.end').value.trim();
const note = row.querySelector('.note').value.trim();
if (!start) return;
if (!end) end = start;
if (end < start) bad = true;
rows.push({ start: start, end: end, note: note });
});
if (bad) { alert('Some rows have an end date earlier than the start date.'); return; }
btn.disabled = true;
const body = new URLSearchParams();
body.set('action', 'save');
body.set('c_uid', C_UID);
body.set('data', JSON.stringify(rows));
fetch(location.href, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body.toString() })
.then(function (r) { return r.json(); })
.then(function (res) {
if (res && res.ok) { closeUp(true); }
else { alert('Save failed: ' + (res && res.msg ? res.msg : 'unknown')); btn.disabled = false; }
})
.catch(function (e) { alert('Save error: ' + e); btn.disabled = false; });
});
</script>
</body>
</html>