Complete admin dashboard with all pages: profile editor (cover photo, business hours, social links), settings (account, notifications, security), enhanced trades view with detail modal, and orders with status workflow. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
282 lines
12 KiB
HTML
282 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Grubflip Admin — Restaurant Profile</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Plus+Jakarta+Sans:wght@600;700;800&display=swap" rel="stylesheet">
|
|
<link rel="stylesheet" href="css/tokens.css">
|
|
<link rel="stylesheet" href="css/admin.css">
|
|
</head>
|
|
<body>
|
|
<div class="admin-layout">
|
|
<div class="sidebar-overlay" id="sidebarOverlay"></div>
|
|
|
|
<aside class="sidebar" id="sidebar" role="navigation" aria-label="Admin navigation">
|
|
<div class="sidebar-brand">
|
|
<div class="logo-icon">GF</div>
|
|
<span>Grubflip</span>
|
|
</div>
|
|
<nav class="sidebar-nav">
|
|
<div class="sidebar-section-title">Main</div>
|
|
<a href="dashboard.html" class="sidebar-link"><span class="icon">📊</span> Dashboard</a>
|
|
<a href="meals.html" class="sidebar-link"><span class="icon">🍔</span> Meals</a>
|
|
<a href="orders.html" class="sidebar-link"><span class="icon">📦</span> Orders</a>
|
|
<a href="trades.html" class="sidebar-link"><span class="icon">🔄</span> Trades</a>
|
|
<div class="sidebar-section-title">Settings</div>
|
|
<a href="profile.html" class="sidebar-link active"><span class="icon">🏪</span> Restaurant Profile</a>
|
|
<a href="settings.html" class="sidebar-link"><span class="icon">⚙️</span> Settings</a>
|
|
</nav>
|
|
<div class="sidebar-footer">
|
|
<div class="sidebar-user">
|
|
<div class="avatar" id="userAvatar">DR</div>
|
|
<div class="user-info">
|
|
<div class="user-name" id="userName">Demo Restaurant</div>
|
|
<div class="user-role" id="userRole">Owner</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
|
|
<main class="main-content">
|
|
<header class="topbar">
|
|
<div class="topbar-left">
|
|
<button class="menu-toggle" id="menuToggle" aria-label="Toggle menu">☰</button>
|
|
<h1 class="topbar-title">Restaurant Profile</h1>
|
|
</div>
|
|
<div class="topbar-right">
|
|
<button class="topbar-btn" aria-label="Sign out" id="logoutBtn">⏻</button>
|
|
</div>
|
|
</header>
|
|
|
|
<div class="page-content" style="max-width:48rem">
|
|
<div class="page-header">
|
|
<h1>Restaurant Profile</h1>
|
|
<p class="subtitle">This is how your restaurant appears to customers on Grubflip.</p>
|
|
</div>
|
|
|
|
<!-- Cover Photo -->
|
|
<div class="card mb-6">
|
|
<div class="card-header">
|
|
<h2>Cover Photo</h2>
|
|
</div>
|
|
<div class="img-preview-wrapper" id="coverPreview" style="height:12rem">
|
|
<div class="upload-label">
|
|
<span>🖼</span>
|
|
Click to upload a cover photo
|
|
</div>
|
|
</div>
|
|
<input type="file" id="coverInput" accept="image/*" class="hidden">
|
|
<p class="form-hint mt-2">Recommended: 1200x400px. Max 5MB.</p>
|
|
</div>
|
|
|
|
<!-- Basic Info -->
|
|
<div class="card mb-6">
|
|
<div class="card-header">
|
|
<h2>Basic Information</h2>
|
|
</div>
|
|
<form id="profileForm">
|
|
<div class="form-group">
|
|
<label for="restName" class="form-label">Restaurant Name *</label>
|
|
<input type="text" id="restName" class="form-input" placeholder="e.g. Downtown Grill">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="restDesc" class="form-label">Description</label>
|
|
<textarea id="restDesc" class="form-textarea" placeholder="Tell customers about your restaurant, your style of food, what makes you unique..."></textarea>
|
|
</div>
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="restCuisine" class="form-label">Cuisine Type</label>
|
|
<select id="restCuisine" class="form-select">
|
|
<option value="">Select cuisine</option>
|
|
<option value="american">American</option>
|
|
<option value="mexican">Mexican</option>
|
|
<option value="italian">Italian</option>
|
|
<option value="asian">Asian</option>
|
|
<option value="japanese">Japanese</option>
|
|
<option value="indian">Indian</option>
|
|
<option value="mediterranean">Mediterranean</option>
|
|
<option value="fusion">Fusion</option>
|
|
<option value="other">Other</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="restPrice" class="form-label">Price Range</label>
|
|
<select id="restPrice" class="form-select">
|
|
<option value="$">$ — Budget</option>
|
|
<option value="$$" selected>$$ — Moderate</option>
|
|
<option value="$$$">$$$ — Upscale</option>
|
|
<option value="$$$$">$$$$ — Fine Dining</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="form-group">
|
|
<label for="restAddress" class="form-label">Address</label>
|
|
<input type="text" id="restAddress" class="form-input" placeholder="123 Main St, City, State ZIP">
|
|
</div>
|
|
|
|
<div class="form-row">
|
|
<div class="form-group">
|
|
<label for="restPhone" class="form-label">Phone</label>
|
|
<input type="tel" id="restPhone" class="form-input" placeholder="(555) 123-4567">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="restWebsite" class="form-label">Website</label>
|
|
<input type="url" id="restWebsite" class="form-input" placeholder="https://yourrestaurant.com">
|
|
</div>
|
|
</div>
|
|
|
|
<button type="submit" class="btn btn-primary">Save Profile</button>
|
|
</form>
|
|
</div>
|
|
|
|
<!-- Hours -->
|
|
<div class="card mb-6">
|
|
<div class="card-header">
|
|
<h2>Business Hours</h2>
|
|
</div>
|
|
<div id="hoursEditor">
|
|
</div>
|
|
<button class="btn btn-ghost btn-sm mt-4" id="saveHoursBtn">Save Hours</button>
|
|
</div>
|
|
|
|
<!-- Social Links -->
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h2>Social Links</h2>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="socialInsta" class="form-label">Instagram</label>
|
|
<input type="text" id="socialInsta" class="form-input" placeholder="@yourrestaurant">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="socialFacebook" class="form-label">Facebook</label>
|
|
<input type="url" id="socialFacebook" class="form-input" placeholder="https://facebook.com/yourpage">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="socialTwitter" class="form-label">X (Twitter)</label>
|
|
<input type="text" id="socialTwitter" class="form-input" placeholder="@yourrestaurant">
|
|
</div>
|
|
<button class="btn btn-primary btn-sm" id="saveSocialBtn">Save Social Links</button>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
|
|
<div class="toast-container" id="toastContainer"></div>
|
|
|
|
<style>
|
|
.hours-row {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--gf-space-3);
|
|
padding: var(--gf-space-2) 0;
|
|
border-bottom: 1px solid var(--gf-neutral-100);
|
|
}
|
|
.hours-row:last-child { border-bottom: none; }
|
|
.hours-day {
|
|
width: 5rem;
|
|
font-size: var(--gf-text-sm);
|
|
font-weight: var(--gf-weight-semibold);
|
|
color: var(--gf-neutral-700);
|
|
}
|
|
.hours-inputs {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--gf-space-2);
|
|
flex: 1;
|
|
}
|
|
.hours-inputs input {
|
|
width: 6rem;
|
|
padding: 0.5rem 0.75rem;
|
|
font-size: var(--gf-text-sm);
|
|
}
|
|
.hours-closed {
|
|
font-size: var(--gf-text-sm);
|
|
color: var(--gf-neutral-400);
|
|
}
|
|
</style>
|
|
|
|
<script src="js/admin-app.js"></script>
|
|
<script>
|
|
if (!AdminApp.requireAuth()) throw new Error('Not authenticated');
|
|
var user = AdminApp.getUser();
|
|
if (user.name) {
|
|
document.getElementById('userName').textContent = user.name;
|
|
document.getElementById('userAvatar').textContent = user.name.split(' ').map(function(w){ return w[0]; }).join('').slice(0,2).toUpperCase();
|
|
document.getElementById('restName').value = user.name;
|
|
}
|
|
if (user.role) document.getElementById('userRole').textContent = user.role.charAt(0).toUpperCase() + user.role.slice(1);
|
|
|
|
AdminApp.initSidebar();
|
|
document.getElementById('logoutBtn').addEventListener('click', AdminApp.logout);
|
|
|
|
// --- Cover Photo ---
|
|
document.getElementById('coverPreview').addEventListener('click', function() {
|
|
document.getElementById('coverInput').click();
|
|
});
|
|
document.getElementById('coverInput').addEventListener('change', function(e) {
|
|
var file = e.target.files[0];
|
|
if (!file) return;
|
|
if (file.size > 5 * 1024 * 1024) { AdminApp.toast('Image must be under 5MB', 'error'); return; }
|
|
var reader = new FileReader();
|
|
reader.onload = function(ev) {
|
|
document.getElementById('coverPreview').innerHTML = '<img src="' + ev.target.result + '" alt="Cover">';
|
|
};
|
|
reader.readAsDataURL(file);
|
|
});
|
|
|
|
// --- Hours Editor ---
|
|
var days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'];
|
|
var defaultHours = { open: '11:00', close: '21:00' };
|
|
var hoursEditor = document.getElementById('hoursEditor');
|
|
|
|
hoursEditor.innerHTML = days.map(function(day, i) {
|
|
var isClosed = (i >= 6); // Sunday closed by default
|
|
return '<div class="hours-row">' +
|
|
'<div class="hours-day">' + day.slice(0, 3) + '</div>' +
|
|
'<div class="hours-inputs">' +
|
|
'<label class="toggle" style="flex-shrink:0"><input type="checkbox" data-day="' + i + '"' + (isClosed ? '' : ' checked') + '><span class="toggle-slider"></span></label>' +
|
|
'<input type="time" class="form-input" data-field="open-' + i + '" value="' + (isClosed ? '' : defaultHours.open) + '"' + (isClosed ? ' disabled' : '') + '>' +
|
|
'<span class="text-muted text-sm">to</span>' +
|
|
'<input type="time" class="form-input" data-field="close-' + i + '" value="' + (isClosed ? '' : defaultHours.close) + '"' + (isClosed ? ' disabled' : '') + '>' +
|
|
'</div>' +
|
|
'</div>';
|
|
}).join('');
|
|
|
|
// Toggle hours
|
|
hoursEditor.addEventListener('change', function(e) {
|
|
if (e.target.dataset.day !== undefined) {
|
|
var i = e.target.dataset.day;
|
|
var open = document.querySelector('[data-field="open-' + i + '"]');
|
|
var close = document.querySelector('[data-field="close-' + i + '"]');
|
|
open.disabled = !e.target.checked;
|
|
close.disabled = !e.target.checked;
|
|
if (!e.target.checked) { open.value = ''; close.value = ''; }
|
|
else { open.value = defaultHours.open; close.value = defaultHours.close; }
|
|
}
|
|
});
|
|
|
|
// --- Save Profile ---
|
|
document.getElementById('profileForm').addEventListener('submit', function(e) {
|
|
e.preventDefault();
|
|
var name = document.getElementById('restName').value.trim();
|
|
if (!name) { AdminApp.toast('Restaurant name is required', 'error'); return; }
|
|
AdminApp.toast('Profile saved!', 'success');
|
|
});
|
|
|
|
// --- Save Hours ---
|
|
document.getElementById('saveHoursBtn').addEventListener('click', function() {
|
|
AdminApp.toast('Business hours saved!', 'success');
|
|
});
|
|
|
|
// --- Save Social ---
|
|
document.getElementById('saveSocialBtn').addEventListener('click', function() {
|
|
AdminApp.toast('Social links saved!', 'success');
|
|
});
|
|
</script>
|
|
</body>
|
|
</html>
|