Files
MyKeyser/admin/menus.php
T
Ty Clifford 25327e3302 - [index.php](/Users/tyemeclifford/Documents/GH/MyKeyser/index.php): events now appear before featured businesses, plus forum pretty-route fallback.
[events.php](/Users/tyemeclifford/Documents/GH/MyKeyser/events.php) and 
[admin/events.php](/Users/tyemeclifford/Documents/GH/MyKeyser/admin/events.php): 
event image uploads.
[menu.php](/Users/tyemeclifford/Documents/GH/MyKeyser/menu.php), 
[admin/menus.php](/Users/tyemeclifford/Documents/GH/MyKeyser/admin/menus.php), 
and 
[business.php](/Users/tyemeclifford/Documents/GH/MyKeyser/business.php): 
one configurable menu item image slot.
[account.php](/Users/tyemeclifford/Documents/GH/MyKeyser/account.php): 
circular avatar upload and crop controls.
[forum.php](/Users/tyemeclifford/Documents/GH/MyKeyser/forum.php), 
[admin/forum.php](/Users/tyemeclifford/Documents/GH/MyKeyser/admin/forum.php), 
[.htaccess](/Users/tyemeclifford/Documents/GH/MyKeyser/.htaccess), and 
[forum/index.php](/Users/tyemeclifford/Documents/GH/MyKeyser/forum/index.php): 
forum, categories, topic/reply uploads, permalinks, toggle/moderation.
[includes/db.php](/Users/tyemeclifford/Documents/GH/MyKeyser/includes/db.php) 
and 
[includes/functions.php](/Users/tyemeclifford/Documents/GH/MyKeyser/includes/functions.php): 
schema upgrades and shared upload/image helpers.
[assets/css/style.css](/Users/tyemeclifford/Documents/GH/MyKeyser/assets/css/style.css): 
neon styling for forum, images, avatars, attachments.
2026-06-19 10:49:48 -04:00

236 lines
14 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
require_once dirname(__DIR__).'/includes/boot.php';
requireAdmin();
$adminTitle = 'Menus';
$bizId = iget('biz_id');
$biz = $bizId ? qone("SELECT * FROM businesses WHERE id=?",[$bizId]) : null;
$menuImagesEnabled = max(0, min(1, (int)setting('menu_item_image_limit','1'))) > 0;
if (isPost()) {
csrfCheck();
$act = p('_act');
if ($act === 'add_section') {
$bid = (int)p('business_id'); $name = ps('sec_name');
if ($bid && $name) {
$ord = (int)qval("SELECT COALESCE(MAX(sort_order),0)+1 FROM menu_sections WHERE business_id=?",[$bid]);
qrun("INSERT INTO menu_sections(business_id,name,description,sort_order)VALUES(?,?,?,?)",[$bid,$name,ps('sec_desc'),$ord]);
flash('Section added!','success');
}
go("/admin/menus.php?biz_id=$bid");
}
if ($act === 'edit_section') {
$sid = (int)p('sid');
qrun("UPDATE menu_sections SET name=?,description=?,sort_order=? WHERE id=?",[ps('sec_name'),ps('sec_desc'),(int)p('sec_order'),$sid]);
flash('Section updated!','success'); go("/admin/menus.php?biz_id=$bizId");
}
if ($act === 'del_section') {
qrun("DELETE FROM menu_sections WHERE id=?",[(int)p('sid')]);
flash('Section deleted.','success'); go("/admin/menus.php?biz_id=$bizId");
}
if ($act === 'add_item') {
$sid = (int)p('section_id');
$name = ps('item_name');
if ($sid && $name) {
$imagePath = '';
if ($menuImagesEnabled) {
$upload = storeSingleUpload(
'item_image',
'menus',
imageExts(),
mbToBytes(setting('menu_upload_max_mb','5'), 5),
[
'image_only' => true,
'resize_over_bytes' => mbToBytes(setting('forum_image_resize_threshold_mb','1'), 1),
'image_max_width' => (int)setting('forum_image_max_width','1600'),
'image_quality' => (int)setting('forum_image_quality','82'),
]
);
if (!$upload['ok']) { flash($upload['error'], 'error'); go("/admin/menus.php?biz_id=$bizId"); }
$imagePath = (string)($upload['path'] ?? '');
}
$ord = (int)qval("SELECT COALESCE(MAX(sort_order),0)+1 FROM menu_items WHERE section_id=?",[$sid]);
qrun("INSERT INTO menu_items(section_id,name,description,price,image_path,is_available,sort_order)VALUES(?,?,?,?,?,?,?)",[$sid,$name,ps('item_desc'),(float)p('item_price',0),$imagePath,(int)p('is_available',1),$ord]);
flash('Item added!','success');
}
go("/admin/menus.php?biz_id=$bizId");
}
if ($act === 'edit_item') {
$iid = (int)p('iid');
$item = qone("SELECT * FROM menu_items WHERE id=?", [$iid]);
$imagePath = (string)($item['image_path'] ?? '');
if ($menuImagesEnabled && p('remove_item_image','0') === '1') $imagePath = '';
if ($menuImagesEnabled) {
$upload = storeSingleUpload(
'item_image',
'menus',
imageExts(),
mbToBytes(setting('menu_upload_max_mb','5'), 5),
[
'image_only' => true,
'resize_over_bytes' => mbToBytes(setting('forum_image_resize_threshold_mb','1'), 1),
'image_max_width' => (int)setting('forum_image_max_width','1600'),
'image_quality' => (int)setting('forum_image_quality','82'),
]
);
if (!$upload['ok']) { flash($upload['error'], 'error'); go("/admin/menus.php?biz_id=$bizId"); }
if (!empty($upload['path'])) $imagePath = (string)$upload['path'];
}
qrun("UPDATE menu_items SET name=?,description=?,price=?,image_path=?,is_available=?,sort_order=? WHERE id=?",[ps('item_name'),ps('item_desc'),(float)p('item_price',0),$imagePath,(int)p('is_available',1),(int)p('item_order'),$iid]);
flash('Item updated!','success'); go("/admin/menus.php?biz_id=$bizId");
}
if ($act === 'del_item') {
qrun("DELETE FROM menu_items WHERE id=?",[(int)p('iid')]);
flash('Item removed.','success'); go("/admin/menus.php?biz_id=$bizId");
}
}
$bizList = qall("SELECT id,name FROM businesses WHERE is_active=1 ORDER BY name");
if ($biz) {
$sections = qall("SELECT * FROM menu_sections WHERE business_id=? ORDER BY sort_order",[$bizId]);
$items = [];
foreach ($sections as $s) $items[$s['id']] = qall("SELECT * FROM menu_items WHERE section_id=? ORDER BY sort_order",[$s['id']]);
}
include __DIR__.'/admin_header.php';
?>
<?php adminTitle('Manage Menus','Edit menus, sections, and items for any business.') ?>
<!-- Business selector -->
<div class="ibox" style="max-width:500px;margin-bottom:2rem">
<div class="ibox-title">SELECT BUSINESS TO MANAGE MENU</div>
<form method="GET" style="display:flex;gap:.65rem">
<select name="biz_id" class="fselect" style="flex:1">
<option value="">— Choose a business —</option>
<?php foreach($bizList as $b): ?><option value="<?=$b['id']?>"<?=$bizId===$b['id']?' selected':''?>><?=e($b['name'])?></option><?php endforeach; ?>
</select>
<button type="submit" class="btn btn-primary btn-sm">Load Menu</button>
</form>
</div>
<?php if($biz): ?>
<div style="display:flex;align-items:center;gap:1rem;margin-bottom:1.5rem">
<h2 style="font-size:1.3rem;color:var(--gold);font-family:'Oswald',sans-serif;letter-spacing:.1em"><?=e($biz['name'])?> — Menu</h2>
<a href="/business.php?slug=<?=e($biz['slug'])?>" target="_blank" class="btn btn-outline btn-xs">View Listing ↗</a>
</div>
<!-- Add section -->
<div class="ibox" style="border-color:var(--gold-d);margin-bottom:1.5rem">
<div class="ibox-title"> ADD MENU SECTION</div>
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="add_section"><input type="hidden" name="business_id" value="<?=$bizId?>">
<div class="form-row">
<div class="fg"><label class="flabel">SECTION NAME *</label><input type="text" name="sec_name" class="finput" required placeholder="e.g. Appetizers, Pasta, Desserts"></div>
<div class="fg"><label class="flabel">DESCRIPTION</label><input type="text" name="sec_desc" class="finput" placeholder="Optional description"></div>
</div>
<button type="submit" class="btn btn-primary btn-sm">Add Section</button>
</form>
</div>
<?php foreach($sections as $sec): ?>
<div class="ibox" style="margin-bottom:1.5rem">
<!-- Section header -->
<div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:.9rem;padding-bottom:.6rem;border-bottom:1px solid var(--bdr)">
<div>
<span style="font-family:'Oswald',sans-serif;color:var(--gold);letter-spacing:.1em"><?=e($sec['name'])?></span>
<?php if($sec['description']): ?><span style="font-size:.78rem;color:var(--text-d);margin-left:.5rem"><?=e($sec['description'])?></span><?php endif; ?>
</div>
<div class="flex-row" style="gap:.3rem">
<button onclick="document.getElementById('esf<?=$sec['id']?>').classList.toggle('hidden')" class="btn btn-xs btn-secondary">✏️ Edit Section</button>
<form method="POST" style="display:inline"><?=csrfField()?><input type="hidden" name="_act" value="del_section"><input type="hidden" name="sid" value="<?=$sec['id']?>"><button class="btn btn-xs btn-danger" data-confirm="Delete section and ALL its items?">🗑️ Delete</button></form>
</div>
</div>
<!-- Edit section -->
<div id="esf<?=$sec['id']?>" class="hidden" style="background:var(--bg4);border:1px solid var(--bdr);border-radius:4px;padding:.85rem;margin-bottom:.85rem">
<form method="POST"><?=csrfField()?><input type="hidden" name="_act" value="edit_section"><input type="hidden" name="sid" value="<?=$sec['id']?>">
<div class="form-row">
<div class="fg"><label class="flabel">NAME</label><input type="text" name="sec_name" class="finput" value="<?=e($sec['name'])?>" required></div>
<div class="fg"><label class="flabel">DESCRIPTION</label><input type="text" name="sec_desc" class="finput" value="<?=e($sec['description'])?>"></div>
</div>
<div class="fg"><label class="flabel">SORT ORDER</label><input type="number" name="sec_order" class="finput" style="max-width:100px" value="<?=$sec['sort_order']?>"></div>
<button type="submit" class="btn btn-success btn-sm">Save Section</button>
</form>
</div>
<!-- Items -->
<?php if($items[$sec['id']]): ?>
<div class="tbl-wrap" style="margin-bottom:.85rem">
<table class="dtbl">
<thead><tr><th>ITEM NAME</th><th>IMAGE</th><th>DESCRIPTION</th><th>PRICE</th><th>AVAIL.</th><th>ORDER</th><th></th></tr></thead>
<tbody>
<?php foreach($items[$sec['id']] as $item): ?>
<tr>
<td class="td-name"><?=e($item['name'])?></td>
<td><?php if(!empty($item['image_path'])): ?><img src="<?=e($item['image_path'])?>" alt="<?=e($item['name'])?>" class="admin-menu-thumb"><?php else: ?>—<?php endif; ?></td>
<td style="font-size:.8rem;max-width:180px"><?=e(substr($item['description'],0,80))?></td>
<td style="color:var(--gold);font-family:'Oswald',sans-serif"><?=$item['price']>0?money($item['price']):'—'?></td>
<td><?=$item['is_available']?'<span class="badge badge-green">Yes</span>':'<span class="badge badge-gray">No</span>'?></td>
<td><?=$item['sort_order']?></td>
<td>
<div class="flex-row" style="gap:.3rem">
<button onclick="document.getElementById('eif<?=$item['id']?>').classList.toggle('hidden')" class="btn btn-xs btn-outline">✏️</button>
<form method="POST" style="display:inline"><?=csrfField()?><input type="hidden" name="_act" value="del_item"><input type="hidden" name="iid" value="<?=$item['id']?>"><button class="btn btn-xs btn-danger" data-confirm="Delete item?">🗑️</button></form>
</div>
<div id="eif<?=$item['id']?>" class="hidden" style="background:var(--bg2);border:1px solid var(--bdr);border-radius:4px;padding:.75rem;margin-top:.4rem;min-width:320px">
<form method="POST" enctype="multipart/form-data"><?=csrfField()?><input type="hidden" name="_act" value="edit_item"><input type="hidden" name="iid" value="<?=$item['id']?>">
<div class="form-row">
<div class="fg"><label class="flabel">NAME *</label><input type="text" name="item_name" class="finput" value="<?=e($item['name'])?>" required></div>
<div class="fg"><label class="flabel">PRICE</label><input type="number" name="item_price" class="finput" value="<?=$item['price']?>" step="0.01" min="0"></div>
</div>
<div class="fg"><label class="flabel">DESCRIPTION</label><input type="text" name="item_desc" class="finput" value="<?=e($item['description'])?>"></div>
<?php if($menuImagesEnabled): ?>
<div class="fg">
<label class="flabel">ITEM IMAGE</label>
<?php if(!empty($item['image_path'])): ?>
<div class="menu-admin-image">
<img src="<?=e($item['image_path'])?>" alt="<?=e($item['name'])?>">
<label style="display:flex;align-items:center;gap:.45rem;font-size:.82rem;color:var(--text-m);cursor:pointer"><input type="checkbox" name="remove_item_image" value="1"> Remove current image</label>
</div>
<?php endif; ?>
<input type="file" name="item_image" class="finput" accept="image/*">
<div class="fhint">One image allowed. Max <?=e(setting('menu_upload_max_mb','5'))?> MB.</div>
</div>
<?php endif; ?>
<div class="form-row">
<div class="fg"><label class="flabel">AVAILABLE</label><select name="is_available" class="fselect"><option value="1"<?=$item['is_available']?' selected':''?>>Yes</option><option value="0"<?=!$item['is_available']?' selected':''?>>No</option></select></div>
<div class="fg"><label class="flabel">SORT ORDER</label><input type="number" name="item_order" class="finput" value="<?=$item['sort_order']?>"></div>
</div>
<button type="submit" class="btn btn-success btn-sm">Save Item</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
</div>
<?php endif; ?>
<!-- Add item -->
<details><summary style="cursor:pointer;font-size:.82rem;color:var(--gold);font-family:'Oswald',sans-serif;letter-spacing:.08em"> Add Item to "<?=e($sec['name'])?>"</summary>
<div style="padding:.75rem 0">
<form method="POST" enctype="multipart/form-data"><?=csrfField()?><input type="hidden" name="_act" value="add_item"><input type="hidden" name="section_id" value="<?=$sec['id']?>">
<div class="form-row">
<div class="fg"><label class="flabel">ITEM NAME *</label><input type="text" name="item_name" class="finput" required placeholder="e.g. Spaghetti Carbonara"></div>
<div class="fg"><label class="flabel">PRICE ($)</label><input type="number" name="item_price" class="finput" value="0" step="0.01" min="0"></div>
</div>
<div class="fg"><label class="flabel">DESCRIPTION</label><input type="text" name="item_desc" class="finput" placeholder="Brief description…"></div>
<?php if($menuImagesEnabled): ?>
<div class="fg"><label class="flabel">ITEM IMAGE</label><input type="file" name="item_image" class="finput" accept="image/*"><div class="fhint">Optional. One image allowed, max <?=e(setting('menu_upload_max_mb','5'))?> MB.</div></div>
<?php endif; ?>
<div class="fg"><label class="flabel">AVAILABLE</label><select name="is_available" class="fselect"><option value="1">Yes</option><option value="0">No</option></select></div>
<button type="submit" class="btn btn-primary btn-sm">Add Item</button>
</form>
</div>
</details>
</div>
<?php endforeach; ?>
<?php if(empty($sections)): ?>
<div class="empty" style="padding:2rem"><div class="empty-ico">🍽️</div><h3>No menu sections yet</h3><p style="margin-top:.5rem">Use the form above to add the first section.</p></div>
<?php endif; ?>
<?php endif; ?>
<style>.hidden{display:none!important}</style>
<?php include __DIR__.'/admin_footer.php'; ?>