Files
MyKeyser/admin/biz_edits.php
T
2026-06-18 12:50:44 -04:00

347 lines
16 KiB
PHP

<?php
$adminTitle = 'Owner Edit Requests';
include __DIR__.'/admin_header.php';
// ── Field labels & apply logic ────────────────────────────
$fieldLabels = [
'description' => 'Description',
'address' => 'Address',
'phone' => 'Phone',
'email' => 'Email',
'website' => 'Website',
'video_url' => 'Video URL',
'hours' => 'Hours of Operation',
'is_active' => 'Listing Status (Open/Closed)',
];
// ── POST: approve / reject individual or all ─────────────
if (isPost()) {
csrfCheck();
$act = p('_act');
// Approve single field change
if ($act === 'approve') {
$reqId = (int)p('req_id');
$req = qone("SELECT * FROM business_edit_requests WHERE id=?", [$reqId]);
if ($req && $req['status'] === 'pending') {
$field = $req['field'];
$val = $req['new_value'];
// Validate field is one we allow
if (array_key_exists($field, $fieldLabels)) {
if ($field === 'is_active') {
qrun("UPDATE businesses SET is_active=? WHERE id=?", [(int)$val, $req['business_id']]);
} elseif ($field === 'hours') {
// Validate JSON before applying
$decoded = json_decode($val, true);
if (is_array($decoded)) {
qrun("UPDATE businesses SET hours=? WHERE id=?", [$val, $req['business_id']]);
}
} elseif ($field === 'video_url') {
if (validBusinessVideoUrl($val)) {
qrun("UPDATE businesses SET video_url=? WHERE id=?", [$val, $req['business_id']]);
}
} else {
qrun("UPDATE businesses SET $field=? WHERE id=?", [$val, $req['business_id']]);
}
qrun("UPDATE business_edit_requests SET status='approved', reviewed_at=datetime('now') WHERE id=?", [$reqId]);
flash('Change approved and applied.', 'success');
}
}
go('/admin/biz_edits.php'.(p('biz_id') ? '?biz_id='.p('biz_id') : ''));
}
// Reject single field change
if ($act === 'reject') {
$reqId = (int)p('req_id');
$notes = ps('admin_notes');
qrun("UPDATE business_edit_requests SET status='rejected', admin_notes=?, reviewed_at=datetime('now') WHERE id=?",
[$notes, $reqId]);
flash('Change rejected.', 'info');
go('/admin/biz_edits.php'.(p('biz_id') ? '?biz_id='.p('biz_id') : ''));
}
// Approve ALL pending for a business
if ($act === 'approve_all') {
$bizId = (int)p('biz_id');
$reqs = qall("SELECT * FROM business_edit_requests WHERE business_id=? AND status='pending'", [$bizId]);
$count = 0;
foreach ($reqs as $req) {
$field = $req['field'];
$val = $req['new_value'];
if (!array_key_exists($field, $fieldLabels)) continue;
if ($field === 'is_active') {
qrun("UPDATE businesses SET is_active=? WHERE id=?", [(int)$val, $bizId]);
} elseif ($field === 'hours') {
$decoded = json_decode($val, true);
if (is_array($decoded)) {
qrun("UPDATE businesses SET hours=? WHERE id=?", [$val, $bizId]);
}
} elseif ($field === 'video_url') {
if (validBusinessVideoUrl($val)) {
qrun("UPDATE businesses SET video_url=? WHERE id=?", [$val, $bizId]);
}
} else {
qrun("UPDATE businesses SET $field=? WHERE id=?", [$val, $bizId]);
}
qrun("UPDATE business_edit_requests SET status='approved', reviewed_at=datetime('now') WHERE id=?", [$req['id']]);
$count++;
}
flash("All $count pending change".($count!=1?'s':'')." approved and applied.", 'success');
go('/admin/biz_edits.php?biz_id='.$bizId);
}
// Reject ALL pending for a business
if ($act === 'reject_all') {
$bizId = (int)p('biz_id');
$notes = ps('admin_notes');
qrun("UPDATE business_edit_requests SET status='rejected', admin_notes=?, reviewed_at=datetime('now')
WHERE business_id=? AND status='pending'",
[$notes, $bizId]);
flash('All pending changes rejected.', 'info');
go('/admin/biz_edits.php?biz_id='.$bizId);
}
}
// ── Filter by business ───────────────────────────────────
$bizId = iget('biz_id');
$filterStatus = gs('status') ?: 'pending';
// Grouped: businesses that have edit requests
$bizWithEdits = qall("
SELECT b.id, b.name, b.slug,
COUNT(CASE WHEN ber.status='pending' THEN 1 END) AS pending_count,
COUNT(CASE WHEN ber.status='approved' THEN 1 END) AS approved_count,
COUNT(CASE WHEN ber.status='rejected' THEN 1 END) AS rejected_count,
MAX(ber.created_at) AS latest_request
FROM business_edit_requests ber
JOIN businesses b ON b.id = ber.business_id
GROUP BY b.id
ORDER BY pending_count DESC, latest_request DESC
");
// Requests for selected business
$requests = [];
$selectedBiz = null;
if ($bizId) {
$selectedBiz = qone("SELECT * FROM businesses WHERE id=?", [$bizId]);
$whereStatus = $filterStatus === 'all' ? "" : "AND ber.status=?";
$params = $filterStatus === 'all' ? [$bizId] : [$bizId, $filterStatus];
$requests = qall("
SELECT ber.*, u.username
FROM business_edit_requests ber
JOIN users u ON u.id = ber.user_id
WHERE ber.business_id=? $whereStatus
ORDER BY CASE ber.status WHEN 'pending' THEN 0 ELSE 1 END, ber.created_at DESC
", $params);
}
?>
<?php adminTitle('Owner Edit Requests', 'Review and approve or reject listing changes submitted by assigned business owners.') ?>
<div style="display:grid;grid-template-columns:280px 1fr;gap:1.5rem;align-items:start">
<!-- ── Left: business list ─────────────────────────────── -->
<div>
<div style="font-family:'Oswald',sans-serif;font-size:.72rem;letter-spacing:.18em;color:var(--gold);margin-bottom:.65rem">
BUSINESSES WITH EDITS
</div>
<?php if ($bizWithEdits): ?>
<?php foreach ($bizWithEdits as $bwe): ?>
<a href="/admin/biz_edits.php?biz_id=<?=$bwe['id']?>"
style="display:block;padding:.75rem 1rem;border-radius:var(--r);margin-bottom:.35rem;
background:<?=$bizId===$bwe['id']?'var(--gold-dim)':'var(--bg3)'?>;
border:1px solid <?=$bizId===$bwe['id']?'var(--gold-d)':'var(--bdr)'?>;
text-decoration:none;transition:all .18s"
class="<?=$bizId===$bwe['id']?'':'asl-hover'?>">
<div style="font-weight:600;color:var(--text);font-size:.9rem;margin-bottom:.25rem"><?=e($bwe['name'])?></div>
<div class="flex-row" style="gap:.4rem;flex-wrap:wrap">
<?php if ($bwe['pending_count']): ?>
<span class="badge badge-gold"><?=$bwe['pending_count']?> pending</span>
<?php endif; ?>
<?php if ($bwe['approved_count']): ?>
<span class="badge badge-green"><?=$bwe['approved_count']?> approved</span>
<?php endif; ?>
<?php if ($bwe['rejected_count']): ?>
<span class="badge badge-gray"><?=$bwe['rejected_count']?> rejected</span>
<?php endif; ?>
</div>
<div style="font-size:.73rem;color:var(--text-d);margin-top:.25rem">
Last: <?=ago($bwe['latest_request'] ?? '')?>
</div>
</a>
<?php endforeach; ?>
<?php else: ?>
<div style="color:var(--text-d);font-size:.88rem;padding:.75rem">No edit requests yet.</div>
<?php endif; ?>
</div>
<!-- ── Right: requests for selected business ───────────── -->
<div>
<?php if ($selectedBiz): ?>
<!-- Business header -->
<div style="display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem;margin-bottom:1.25rem">
<div>
<div style="font-family:'Oswald',sans-serif;font-size:1.1rem;color:var(--text)"><?=e($selectedBiz['name'])?></div>
<div style="font-size:.82rem;color:var(--text-d)">
<a href="/business.php?slug=<?=e($selectedBiz['slug'])?>" target="_blank" style="color:var(--gold)">View public listing ↗</a>
</div>
</div>
<!-- Status filter -->
<div class="flex-row" style="gap:.4rem">
<?php foreach(['pending'=>'Pending','approved'=>'Approved','rejected'=>'Rejected','all'=>'All'] as $k=>$lbl): ?>
<a href="/admin/biz_edits.php?biz_id=<?=$bizId?>&status=<?=$k?>"
class="btn btn-xs <?=$filterStatus===$k?'btn-primary':'btn-outline'?>"><?=$lbl?></a>
<?php endforeach; ?>
</div>
</div>
<?php
$pendingReqs = array_filter($requests, fn($r) => $r['status'] === 'pending');
?>
<!-- Approve all / Reject all bar -->
<?php if (count($pendingReqs) > 1): ?>
<div style="background:var(--bg3);border:1px solid var(--bdr);border-radius:var(--r);padding:1rem 1.25rem;margin-bottom:1.25rem;display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:.75rem">
<span style="font-size:.88rem;color:var(--text-m)">
<strong style="color:var(--gold)"><?=count($pendingReqs)?></strong> pending changes for this listing
</span>
<div class="flex-row" style="gap:.5rem">
<form method="POST">
<?=csrfField()?>
<input type="hidden" name="_act" value="approve_all">
<input type="hidden" name="biz_id" value="<?=$bizId?>">
<button class="btn btn-success btn-sm" data-confirm="Approve ALL <?=count($pendingReqs)?> pending changes for <?=e($selectedBiz['name'])?>?">
✅ Approve All
</button>
</form>
<form method="POST" id="rejectAllForm">
<?=csrfField()?>
<input type="hidden" name="_act" value="reject_all">
<input type="hidden" name="biz_id" value="<?=$bizId?>">
<input type="text" name="admin_notes" class="finput" style="width:200px;font-size:.8rem;padding:.35rem .6rem" placeholder="Reason (optional)">
<button class="btn btn-danger btn-sm" data-confirm="Reject ALL pending changes?">
✕ Reject All
</button>
</form>
</div>
</div>
<?php endif; ?>
<!-- Individual requests -->
<?php if ($requests): ?>
<?php foreach ($requests as $req): ?>
<?php
$isPending = $req['status'] === 'pending';
$label = $fieldLabels[$req['field']] ?? ucfirst($req['field']);
$isHours = $req['field'] === 'hours';
$isStatus = $req['field'] === 'is_active';
?>
<div class="ibox" style="margin-bottom:1.1rem;<?= $isPending ? 'border-color:var(--gold-d)' : '' ?>">
<div style="display:flex;align-items:flex-start;justify-content:space-between;gap:.75rem;flex-wrap:wrap">
<div>
<span style="font-family:'Oswald',sans-serif;font-size:.82rem;letter-spacing:.1em;color:var(--gold)"><?=e($label)?></span>
<span style="margin-left:.6rem">
<?php if ($isPending): ?>
<span class="badge badge-gold">PENDING</span>
<?php elseif ($req['status']==='approved'): ?>
<span class="badge badge-green">APPROVED</span>
<?php else: ?>
<span class="badge badge-gray">REJECTED</span>
<?php endif; ?>
</span>
<span style="font-size:.75rem;color:var(--text-d);margin-left:.5rem">
by <strong><?=e($req['username'] ?? '')?></strong> · <?=ago($req['created_at'] ?? '')?>
</span>
</div>
</div>
<!-- Old vs New values -->
<div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin:1rem 0">
<div>
<div style="font-family:'Oswald',sans-serif;font-size:.65rem;letter-spacing:.16em;color:var(--text-d);margin-bottom:.4rem">CURRENT VALUE</div>
<div style="background:var(--bg2);border:1px solid var(--bdr);border-radius:4px;padding:.65rem .85rem;font-size:.84rem;color:var(--text-m)">
<?php if ($isHours): ?>
<?php $oldH = parseHours($req['old_value']); ?>
<?php if ($oldH): ?>
<?php foreach ($oldH as $d => $t): ?>
<div style="display:flex;gap:.5rem;font-size:.8rem"><span style="color:var(--text-d);min-width:32px"><?=e($d)?></span><span><?=e($t)?></span></div>
<?php endforeach; ?>
<?php else: ?><em style="color:var(--text-d)">Not set</em><?php endif; ?>
<?php elseif ($isStatus): ?>
<?= $req['old_value'] === '1' ? '<span style="color:var(--green-l)">✅ Open / Active</span>' : '<span style="color:var(--text-d)">🔴 Closed / Inactive</span>' ?>
<?php else: ?>
<?= $req['old_value'] !== '' ? e($req['old_value']) : '<em style="color:var(--text-d)">Empty</em>' ?>
<?php endif; ?>
</div>
</div>
<div>
<div style="font-family:'Oswald',sans-serif;font-size:.65rem;letter-spacing:.16em;color:var(--green-l);margin-bottom:.4rem">PROPOSED CHANGE</div>
<div style="background:rgba(42,122,71,.1);border:1px solid rgba(58,165,94,.35);border-radius:4px;padding:.65rem .85rem;font-size:.84rem;color:var(--text)">
<?php if ($isHours): ?>
<?php $newH = parseHours($req['new_value']); ?>
<?php if ($newH): ?>
<?php foreach ($newH as $d => $t): ?>
<div style="display:flex;gap:.5rem;font-size:.8rem"><span style="color:var(--gold);min-width:32px"><?=e($d)?></span><span><?=e($t)?></span></div>
<?php endforeach; ?>
<?php else: ?><em style="color:var(--text-d)">Clear hours</em><?php endif; ?>
<?php elseif ($isStatus): ?>
<?= $req['new_value'] === '1' ? '<span style="color:var(--green-l)">✅ Open / Active</span>' : '<span style="color:var(--text-d)">🔴 Closed / Inactive</span>' ?>
<?php else: ?>
<?= $req['new_value'] !== '' ? e($req['new_value']) : '<em style="color:var(--text-d)">Empty</em>' ?>
<?php endif; ?>
</div>
</div>
</div>
<?php if (!empty($req['admin_notes'])): ?>
<div style="font-size:.82rem;color:var(--text-d);margin-bottom:.75rem">
<strong>Admin note:</strong> <?=e($req['admin_notes'])?>
</div>
<?php endif; ?>
<?php if ($isPending): ?>
<div class="flex-row" style="gap:.6rem;flex-wrap:wrap">
<form method="POST" style="display:inline">
<?=csrfField()?>
<input type="hidden" name="_act" value="approve">
<input type="hidden" name="req_id" value="<?=$req['id']?>">
<input type="hidden" name="biz_id" value="<?=$bizId?>">
<button class="btn btn-success btn-sm">✅ Approve &amp; Apply</button>
</form>
<form method="POST" style="display:flex;gap:.4rem;align-items:center;flex-wrap:wrap">
<?=csrfField()?>
<input type="hidden" name="_act" value="reject">
<input type="hidden" name="req_id" value="<?=$req['id']?>">
<input type="hidden" name="biz_id" value="<?=$bizId?>">
<input type="text" name="admin_notes" class="finput"
style="width:200px;font-size:.8rem;padding:.35rem .6rem"
placeholder="Reason for rejection (optional)">
<button class="btn btn-danger btn-sm">✕ Reject</button>
</form>
</div>
<?php endif; ?>
</div>
<?php endforeach; ?>
<?php else: ?>
<div class="empty" style="padding:2rem">
<div class="empty-ico">📋</div>
<h3>No <?= $filterStatus !== 'all' ? $filterStatus : '' ?> requests for this business</h3>
</div>
<?php endif; ?>
<?php else: ?>
<div class="empty" style="padding:3rem">
<div class="empty-ico">📋</div>
<h3>Select a business from the left to review its edit requests</h3>
<?php if (empty($bizWithEdits)): ?>
<p style="margin-top:.75rem;color:var(--text-d);font-size:.88rem">
No edit requests have been submitted yet. Assigned owners can propose changes from their business listing page.
</p>
<?php endif; ?>
</div>
<?php endif; ?>
</div>
</div><!-- /grid -->
<?php include __DIR__.'/admin_footer.php'; ?>