- v1.1.0 - Redesign
This commit is contained in:
@@ -6,6 +6,7 @@ $pageTitle = ($adminTitle ?? 'Admin') . ' — Keyser WV Admin';
|
||||
include dirname(__DIR__).'/includes/header.php';
|
||||
// Pending owner edit count for badge
|
||||
$_pendingEdits = (int)qval("SELECT COUNT(*) FROM business_edit_requests WHERE status='pending'");
|
||||
$_pendingClaims = (int)qval("SELECT COUNT(*) FROM business_claim_requests WHERE status='pending'");
|
||||
?>
|
||||
<div class="admin-wrap">
|
||||
<aside class="admin-side">
|
||||
@@ -29,7 +30,12 @@ $_pendingEdits = (int)qval("SELECT COUNT(*) FROM business_edit_requests WHERE st
|
||||
<a class="asl" href="/admin/reports.php">🚩 Reports</a>
|
||||
<div class="admin-side-title" style="margin-top:1rem">USERS</div>
|
||||
<a class="asl" href="/admin/users.php">👥 Users</a>
|
||||
<a class="asl" href="/admin/owners.php">🔑 Assign Owners</a>
|
||||
<a class="asl" href="/admin/owners.php" style="display:flex;align-items:center;justify-content:space-between">
|
||||
<span>🔑 Assign Owners</span>
|
||||
<?php if ($_pendingClaims): ?>
|
||||
<span style="background:var(--gold);color:var(--bg0);font-family:'Oswald',sans-serif;font-size:.65rem;padding:.1rem .42rem;border-radius:10px;font-weight:600"><?=$_pendingClaims?></span>
|
||||
<?php endif; ?>
|
||||
</a>
|
||||
<div class="admin-side-title" style="margin-top:1rem">SETTINGS</div>
|
||||
<a class="asl" href="/admin/settings.php">⚙️ Site Settings</a>
|
||||
<hr style="border-color:var(--bdr);margin:1rem 0">
|
||||
|
||||
@@ -8,6 +8,8 @@ $fieldLabels = [
|
||||
'address' => 'Address',
|
||||
'phone' => 'Phone',
|
||||
'email' => 'Email',
|
||||
'website' => 'Website',
|
||||
'video_url' => 'Video URL',
|
||||
'hours' => 'Hours of Operation',
|
||||
'is_active' => 'Listing Status (Open/Closed)',
|
||||
];
|
||||
@@ -34,6 +36,10 @@ if (isPost()) {
|
||||
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']]);
|
||||
}
|
||||
@@ -70,6 +76,10 @@ if (isPost()) {
|
||||
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]);
|
||||
}
|
||||
|
||||
+14
-2
@@ -40,6 +40,7 @@ if (isPost()) {
|
||||
'phone' => ps('phone'),
|
||||
'email' => ps('email'),
|
||||
'website' => ps('website'),
|
||||
'video_url' => ps('video_url'),
|
||||
'hours' => json_encode($hrs),
|
||||
'lat' => (float)p('lat', 39.4364),
|
||||
'lng' => (float)p('lng', -78.9762),
|
||||
@@ -47,12 +48,17 @@ if (isPost()) {
|
||||
'is_featured' => (int)p('is_featured', 0),
|
||||
];
|
||||
|
||||
if (!validBusinessVideoUrl($fields['video_url'])) {
|
||||
flash('Business videos must be a YouTube or Vimeo URL.','error');
|
||||
go('/admin/businesses.php'.($id?"?edit=$id":''));
|
||||
}
|
||||
|
||||
if ($act === 'add') {
|
||||
qrun("INSERT INTO businesses(name,slug,category_id,subcategory,description,address,phone,email,website,hours,lat,lng,is_active,is_featured)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?)", array_values($fields));
|
||||
qrun("INSERT INTO businesses(name,slug,category_id,subcategory,description,address,phone,email,website,video_url,hours,lat,lng,is_active,is_featured)VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", array_values($fields));
|
||||
flash('Business added!','success');
|
||||
go('/admin/businesses.php');
|
||||
} else {
|
||||
qrun("UPDATE businesses SET name=?,slug=?,category_id=?,subcategory=?,description=?,address=?,phone=?,email=?,website=?,hours=?,lat=?,lng=?,is_active=?,is_featured=? WHERE id=?", array_merge(array_values($fields), [$id]));
|
||||
qrun("UPDATE businesses SET name=?,slug=?,category_id=?,subcategory=?,description=?,address=?,phone=?,email=?,website=?,video_url=?,hours=?,lat=?,lng=?,is_active=?,is_featured=? WHERE id=?", array_merge(array_values($fields), [$id]));
|
||||
flash('Business updated!','success');
|
||||
go('/admin/businesses.php?edit='.$id);
|
||||
}
|
||||
@@ -152,6 +158,12 @@ $bizs = qall("SELECT b.*,c.name cat FROM businesses b LEFT JOIN categories c
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fg">
|
||||
<label class="flabel">VIDEO URL</label>
|
||||
<input type="url" name="video_url" class="finput" value="<?=e($editing['video_url']??'')?>" placeholder="YouTube or Vimeo URL">
|
||||
<div class="fhint">Optional. Business listing videos are rendered through lone-embed.php and should use YouTube or Vimeo.</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<div class="fg">
|
||||
<label class="flabel">LATITUDE</label>
|
||||
|
||||
+40
-2
@@ -7,6 +7,7 @@ $stats = [
|
||||
'Reviews' => qval("SELECT COUNT(*) FROM reviews"),
|
||||
'Events' => qval("SELECT COUNT(*) FROM events WHERE status='approved'"),
|
||||
'Pending Events' => qval("SELECT COUNT(*) FROM events WHERE status='pending'"),
|
||||
'Pending Claims' => qval("SELECT COUNT(*) FROM business_claim_requests WHERE status='pending'"),
|
||||
'Pending Edits' => qval("SELECT COUNT(*) FROM business_edit_requests WHERE status='pending'"),
|
||||
'Open Reports' => qval("SELECT COUNT(*) FROM reports WHERE status='open'"),
|
||||
'Active Alerts' => qval("SELECT COUNT(*) FROM alerts WHERE is_active=1"),
|
||||
@@ -15,6 +16,15 @@ $recentBiz = qall("SELECT * FROM businesses ORDER BY created_at DESC LIMIT 5"
|
||||
$recentUsers = qall("SELECT * FROM users ORDER BY created_at DESC LIMIT 5");
|
||||
$pendingEvts = qall("SELECT e.*,u.username FROM events e LEFT JOIN users u ON u.id=e.submitted_by WHERE e.status='pending' ORDER BY e.created_at DESC LIMIT 5");
|
||||
$openReports = qall("SELECT r.*,b.name biz,u.username reporter FROM reports r JOIN businesses b ON b.id=r.business_id JOIN users u ON u.id=r.user_id WHERE r.status='open' ORDER BY r.created_at DESC LIMIT 5");
|
||||
$pendingClaims = qall("
|
||||
SELECT bcr.*, b.name biz_name, b.slug biz_slug, u.username, u.email
|
||||
FROM business_claim_requests bcr
|
||||
JOIN businesses b ON b.id = bcr.business_id
|
||||
JOIN users u ON u.id = bcr.user_id
|
||||
WHERE bcr.status = 'pending'
|
||||
ORDER BY bcr.created_at DESC
|
||||
LIMIT 10
|
||||
");
|
||||
$pendingEdits = qall("
|
||||
SELECT ber.*, b.name biz_name, b.slug biz_slug, b.id biz_id, u.username
|
||||
FROM business_edit_requests ber
|
||||
@@ -30,7 +40,7 @@ $pendingEdits = qall("
|
||||
<div class="stat-cards">
|
||||
<?php foreach($stats as $lbl=>$val): ?>
|
||||
<div class="stat-card">
|
||||
<div class="sc-n" style="<?=$lbl==='Pending Edits'&&$val>0?'color:var(--gold)':($lbl==='Open Reports'&&$val>0?'color:var(--red-l)':'')?>">
|
||||
<div class="sc-n" style="<?=($lbl==='Pending Edits'||$lbl==='Pending Claims')&&$val>0?'color:var(--gold)':($lbl==='Open Reports'&&$val>0?'color:var(--red-l)':'')?>">
|
||||
<?=$val?>
|
||||
</div>
|
||||
<div class="sc-l"><?=e($lbl)?></div>
|
||||
@@ -38,6 +48,34 @@ $pendingEdits = qall("
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<!-- Pending owner claims — verification queue -->
|
||||
<?php if ($pendingClaims): ?>
|
||||
<div style="margin-bottom:2rem">
|
||||
<div class="admin-sec-title" style="color:var(--gold)">
|
||||
🔑 BUSINESS PAGE CLAIM REQUESTS (<?=count($pendingClaims)?>)
|
||||
</div>
|
||||
<div class="tbl-wrap">
|
||||
<table class="dtbl">
|
||||
<thead><tr><th>BUSINESS</th><th>REQUESTED BY</th><th>CONTACT PHONE</th><th>SUBMITTED</th><th></th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach ($pendingClaims as $claim): ?>
|
||||
<tr>
|
||||
<td><a href="/business.php?slug=<?=e($claim['biz_slug'])?>" target="_blank" class="td-name"><?=e($claim['biz_name'])?></a></td>
|
||||
<td>
|
||||
<div class="td-name"><?=e($claim['username'])?></div>
|
||||
<?php if ($claim['email']): ?><div style="font-size:.76rem;color:var(--text-d)"><?=e($claim['email'])?></div><?php endif; ?>
|
||||
</td>
|
||||
<td style="font-size:.86rem;color:var(--text)"><?=e($claim['contact_phone'])?></td>
|
||||
<td style="font-size:.78rem;color:var(--text-d)"><?=ago($claim['created_at'] ?? '')?></td>
|
||||
<td><a href="/admin/owners.php#claims" class="btn btn-xs btn-primary">Verify</a></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Pending owner edits — top priority -->
|
||||
<?php if ($pendingEdits): ?>
|
||||
<div style="margin-bottom:2rem">
|
||||
@@ -50,7 +88,7 @@ $pendingEdits = qall("
|
||||
<tbody>
|
||||
<?php
|
||||
$fieldLabels = ['description'=>'Description','address'=>'Address','phone'=>'Phone',
|
||||
'email'=>'Email','hours'=>'Hours','is_active'=>'Status'];
|
||||
'email'=>'Email','website'=>'Website','video_url'=>'Video','hours'=>'Hours','is_active'=>'Status'];
|
||||
foreach ($pendingEdits as $pe):
|
||||
$preview = $pe['field'] === 'hours'
|
||||
? '(hours update)'
|
||||
|
||||
@@ -16,6 +16,23 @@ if (isPost()) {
|
||||
qrun("DELETE FROM business_owners WHERE user_id=? AND business_id=?",[(int)p('user_id'),(int)p('business_id')]);
|
||||
flash('Owner removed.','success');
|
||||
}
|
||||
if ($act === 'approve_claim') {
|
||||
$claimId = (int)p('claim_id');
|
||||
$claim = qone("SELECT * FROM business_claim_requests WHERE id=? AND status='pending'", [$claimId]);
|
||||
if ($claim) {
|
||||
qrun("INSERT OR IGNORE INTO business_owners(user_id,business_id)VALUES(?,?)", [(int)$claim['user_id'], (int)$claim['business_id']]);
|
||||
qrun("UPDATE business_claim_requests SET status='approved', reviewed_at=datetime('now') WHERE id=?", [$claimId]);
|
||||
flash('Business claim approved and owner assigned.', 'success');
|
||||
}
|
||||
go('/admin/owners.php#claims');
|
||||
}
|
||||
if ($act === 'reject_claim') {
|
||||
$claimId = (int)p('claim_id');
|
||||
qrun("UPDATE business_claim_requests SET status='rejected', admin_notes=?, reviewed_at=datetime('now') WHERE id=? AND status='pending'",
|
||||
[ps('admin_notes'), $claimId]);
|
||||
flash('Business claim rejected.', 'info');
|
||||
go('/admin/owners.php#claims');
|
||||
}
|
||||
go('/admin/owners.php'.($_GET?'?'.http_build_query($_GET):''));
|
||||
}
|
||||
|
||||
@@ -24,9 +41,66 @@ $filterBiz = iget('business_id');
|
||||
$users = qall("SELECT id,username FROM users WHERE is_active=1 ORDER BY username");
|
||||
$bizs = qall("SELECT id,name FROM businesses WHERE is_active=1 ORDER BY name");
|
||||
$owners = qall("SELECT bo.*,u.username,b.name biz_name FROM business_owners bo JOIN users u ON u.id=bo.user_id JOIN businesses b ON b.id=bo.business_id ORDER BY b.name,u.username");
|
||||
$claims = qall("
|
||||
SELECT bcr.*, u.username, u.email, b.name biz_name, b.slug biz_slug
|
||||
FROM business_claim_requests bcr
|
||||
JOIN users u ON u.id=bcr.user_id
|
||||
JOIN businesses b ON b.id=bcr.business_id
|
||||
WHERE bcr.status='pending'
|
||||
ORDER BY bcr.created_at DESC
|
||||
");
|
||||
?>
|
||||
<?php adminTitle('Assign Business Owners','Link user accounts to businesses so they can manage listings, post alerts, and update menus.') ?>
|
||||
|
||||
<div id="claims" class="admin-sec-title">PENDING BUSINESS CLAIMS (<?=count($claims)?>)</div>
|
||||
<?php if($claims): ?>
|
||||
<div class="tbl-wrap" style="margin-bottom:2rem">
|
||||
<table class="dtbl">
|
||||
<thead><tr><th>BUSINESS</th><th>REQUESTED BY</th><th>CONTACT PHONE</th><th>PENDING EDITS</th><th>SUBMITTED</th><th>ACTIONS</th></tr></thead>
|
||||
<tbody>
|
||||
<?php foreach($claims as $claim): ?>
|
||||
<?php $editCount = (int)qval("SELECT COUNT(*) FROM business_edit_requests WHERE business_id=? AND user_id=? AND status='pending'", [$claim['business_id'], $claim['user_id']]); ?>
|
||||
<tr>
|
||||
<td><a href="/business.php?slug=<?=e($claim['biz_slug'])?>" target="_blank" class="td-name"><?=e($claim['biz_name'])?></a></td>
|
||||
<td>
|
||||
<div class="td-name"><?=e($claim['username'])?></div>
|
||||
<?php if($claim['email']): ?><div style="font-size:.76rem;color:var(--text-d)"><?=e($claim['email'])?></div><?php endif; ?>
|
||||
</td>
|
||||
<td style="color:var(--text)"><?=e($claim['contact_phone'])?></td>
|
||||
<td>
|
||||
<?php if($editCount): ?>
|
||||
<a href="/admin/biz_edits.php?biz_id=<?=$claim['business_id']?>" class="badge badge-gold"><?=$editCount?> edit<?=$editCount!==1?'s':''?></a>
|
||||
<?php else: ?>
|
||||
<span style="color:var(--text-d)">None</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td style="font-size:.78rem;color:var(--text-d)"><?=ago($claim['created_at'] ?? '')?></td>
|
||||
<td>
|
||||
<div class="flex-row" style="gap:.35rem">
|
||||
<form method="POST" style="display:inline">
|
||||
<?=csrfField()?>
|
||||
<input type="hidden" name="_act" value="approve_claim">
|
||||
<input type="hidden" name="claim_id" value="<?=$claim['id']?>">
|
||||
<button class="btn btn-xs btn-success" data-confirm="Approve this claim and assign <?=e($claim['username'])?> as owner of <?=e($claim['biz_name'])?>?">Approve</button>
|
||||
</form>
|
||||
<form method="POST" style="display:flex;gap:.35rem;align-items:center">
|
||||
<?=csrfField()?>
|
||||
<input type="hidden" name="_act" value="reject_claim">
|
||||
<input type="hidden" name="claim_id" value="<?=$claim['id']?>">
|
||||
<input type="text" name="admin_notes" class="finput" style="width:150px;font-size:.75rem;padding:.25rem .45rem" placeholder="Reason">
|
||||
<button class="btn btn-xs btn-danger">Reject</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<div class="empty" style="padding:1.75rem;margin-bottom:2rem"><div class="empty-ico">🔑</div><h3>No pending business claims</h3></div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="ibox" style="border-color:var(--gold-d);margin-bottom:2rem">
|
||||
<div class="ibox-title">🔑 ASSIGN OWNER TO BUSINESS</div>
|
||||
<form method="POST">
|
||||
|
||||
@@ -12,6 +12,7 @@ if (isPost()) {
|
||||
setSetting('contact_email', ps('contact_email'));
|
||||
setSetting('registration_enabled',p('registration_enabled','0'));
|
||||
setSetting('events_require_approval',p('events_require_approval','0'));
|
||||
setSetting('home_random_interval',p('home_random_interval','2hours') === 'day' ? 'day' : '2hours');
|
||||
flash('Settings saved!','success');
|
||||
go('/admin/settings.php');
|
||||
}
|
||||
@@ -33,6 +34,7 @@ $evtApproval = setting('events_require_approval','1');
|
||||
$siteName = setting('site_name','Discover Keyser WV');
|
||||
$siteTagline = setting('site_tagline','The Friendliest City in the U.S.A.');
|
||||
$contactEmail = setting('contact_email','info@discoverkeyser.com');
|
||||
$homeRandomInterval = setting('home_random_interval','2hours');
|
||||
$me = qone("SELECT * FROM users WHERE id=?",[uid()]);
|
||||
?>
|
||||
<?php adminTitle('Site Settings') ?>
|
||||
@@ -79,6 +81,20 @@ $me = qone("SELECT * FROM users WHERE id=?",[uid()]);
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="fg">
|
||||
<label class="flabel">HOMEPAGE RANDOM BUSINESS ROTATION</label>
|
||||
<div style="margin-top:.4rem">
|
||||
<label style="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.9rem;margin-bottom:.35rem">
|
||||
<input type="radio" name="home_random_interval" value="2hours"<?=$homeRandomInterval==='2hours'?' checked':''?>>
|
||||
<span>Refresh the random list every two hours</span>
|
||||
</label>
|
||||
<label style="display:flex;align-items:center;gap:.4rem;cursor:pointer;font-size:.9rem">
|
||||
<input type="radio" name="home_random_interval" value="day"<?=$homeRandomInterval==='day'?' checked':''?>>
|
||||
<span>Refresh the random list once per day</span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary">Save Settings</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user