Ahmed version 2

This commit is contained in:
Flatlogic Bot 2026-02-09 16:59:02 +00:00
parent eaeab96245
commit 70f713fec0
7 changed files with 605 additions and 239 deletions

View File

@ -4,50 +4,151 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Searing Sandwiches | POS & Stock</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&display=swap" rel="stylesheet">
<title>{% block title %}Searing Sandwiches | POS & Stock{% endblock %}</title>
<!-- Fonts -->
<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=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap" rel="stylesheet">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<!-- Bootstrap Icons -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.0/font/bootstrap-icons.css">
<style>
:root {
--deep-obsidian: #1A1A1D;
--vibrant-accent: #FF4500;
--slate-gray: #4E4E50;
--soft-white: #F5F5F7;
--primary-vibrant: #FF4500;
--primary-soft: #FFF5F2;
--secondary-vibrant: #00D1B2;
--dark-neutral: #1A1D23;
--light-neutral: #F8F9FA;
--glass-bg: rgba(255, 255, 255, 0.8);
--border-soft: rgba(0, 0, 0, 0.08);
}
body {
font-family: 'Inter', sans-serif;
background-color: var(--deep-obsidian);
color: var(--soft-white);
font-family: 'Plus Jakarta Sans', sans-serif;
background-color: var(--light-neutral);
color: var(--dark-neutral);
min-height: 100vh;
overflow-x: hidden;
}
/* Modern Navbar */
.navbar {
background-color: rgba(26, 26, 29, 0.9);
backdrop-filter: blur(10px);
border-bottom: 1px solid var(--slate-gray);
}
.vibrant-text {
color: var(--vibrant-accent);
}
.btn-vibrant {
background-color: var(--vibrant-accent);
color: white;
border: none;
background-color: var(--glass-bg);
backdrop-filter: blur(12px);
-webkit-backdrop-filter: blur(12px);
border-bottom: 1px solid var(--border-soft);
padding: 0.85rem 0;
transition: all 0.3s ease;
}
.navbar-brand {
font-weight: 800;
letter-spacing: -1.5px;
font-size: 1.6rem;
}
.brand-accent {
color: var(--primary-vibrant);
}
.nav-link {
font-weight: 600;
color: var(--dark-neutral) !important;
margin: 0 0.5rem;
transition: color 0.2s;
}
.nav-link:hover {
color: var(--primary-vibrant) !important;
}
/* Buttons */
.btn-vibrant {
background-color: var(--primary-vibrant);
color: white;
border-radius: 12px;
padding: 0.6rem 1.5rem;
font-weight: 700;
border: none;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 4px 12px rgba(255, 69, 0, 0.2);
}
.btn-vibrant:hover {
transform: translateY(-2px);
box-shadow: 0 4px 15px rgba(255, 69, 0, 0.4);
box-shadow: 0 8px 20px rgba(255, 69, 0, 0.3);
color: white;
background-color: #E63E00;
}
.btn-outline-vibrant {
border: 2px solid var(--primary-vibrant);
color: var(--primary-vibrant);
border-radius: 12px;
padding: 0.5rem 1.4rem;
font-weight: 700;
transition: all 0.2s;
}
.btn-outline-vibrant:hover {
background-color: var(--primary-vibrant);
color: white;
}
/* Cards & Containers */
.card {
background-color: #242428;
border: 1px solid var(--slate-gray);
border: 1px solid var(--border-soft);
border-radius: 20px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.03);
transition: transform 0.3s ease, box-shadow 0.3s ease;
background-color: white;
}
.card:hover {
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.06);
}
.section-title {
font-weight: 800;
letter-spacing: -1px;
margin-bottom: 1.5rem;
}
/* Utility Classes */
.text-vibrant { color: var(--primary-vibrant); }
.bg-soft { background-color: var(--primary-soft); }
/* Animations */
@keyframes fadeIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.animate-fade-in {
animation: fadeIn 0.5s ease forwards;
}
/* Custom Scrollbar */
::-webkit-scrollbar { width: 8px; }
::-webkit-scrollbar-track { background: var(--light-neutral); }
::-webkit-scrollbar-thumb { background: #D1D5DB; border-radius: 10px; }
::-webkit-scrollbar-thumb:hover { background: var(--primary-vibrant); }
/* Responsive POS Adjustments */
@media (max-width: 991.98px) {
.cart-sticky {
position: fixed !important;
bottom: 0;
left: 0;
right: 0;
top: auto !important;
z-index: 1030;
max-height: 60vh !important;
border-radius: 24px 24px 0 0 !important;
box-shadow: 0 -10px 40px rgba(0,0,0,0.1) !important;
}
}
</style>
{% block extra_css %}{% endblock %}
@ -55,31 +156,36 @@
<body>
<nav class="navbar navbar-expand-lg sticky-top">
<div class="container">
<a class="navbar-brand fw-bolder fs-3" href="{% url 'home' %}">
<span class="vibrant-text">SEARING</span> SANDWICHES
<a class="navbar-brand" href="{% url 'home' %}">
<span class="brand-accent">SEARING</span> SANDWICHES
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
<button class="navbar-toggler border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="bi bi-list fs-1"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto align-items-center">
{% if user.is_authenticated %}
{% if user.profile.role == 'manager' %}
{% if user.profile and user.profile.role == 'manager' %}
<li class="nav-item">
<a class="nav-link text-light me-3" href="{% url 'dashboard' %}">Dashboard</a>
<a class="nav-link" href="{% url 'dashboard' %}">Dashboard</a>
</li>
<li class="nav-item">
<a class="nav-link text-light me-3" href="{% url 'manage_users' %}">Users</a>
<a class="nav-link" href="{% url 'manage_users' %}">Users</a>
</li>
{% endif %}
<li class="nav-item">
<a class="nav-link text-light me-3" href="{% url 'pos' %}">POS</a>
<a class="nav-link" href="{% url 'pos' %}">POS</a>
</li>
<li class="nav-item">
<span class="text-secondary me-3">Hi, {{ user.username }}</span>
</li>
<li class="nav-item">
<a href="{% url 'logout' %}" class="btn btn-sm btn-outline-secondary">Logout</a>
<li class="nav-item dropdown ms-lg-3">
<a class="nav-link dropdown-toggle d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown">
<div class="bg-soft rounded-circle p-2 me-2 d-inline-flex">
<i class="bi bi-person text-vibrant"></i>
</div>
{{ user.username }}
</a>
<ul class="dropdown-menu dropdown-menu-end border-0 shadow-lg rounded-4 p-2">
<li><a class="dropdown-item rounded-3 py-2" href="{% url 'logout' %}"><i class="bi bi-box-arrow-right me-2"></i>Logout</a></li>
</ul>
</li>
{% else %}
<li class="nav-item">
@ -91,10 +197,25 @@
</div>
</nav>
<main>
{% block content %}{% endblock %}
<div class="container mt-3">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{% if message.tags == 'error' %}danger{% else %}primary{% endif %} border-0 shadow-sm rounded-4 alert-dismissible fade show" role="alert">
<i class="bi {% if message.tags == 'error' %}bi-exclamation-circle{% else %}bi-check-circle{% endif %} me-2"></i>
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
</div>
{% endfor %}
{% endif %}
</div>
<main class="py-4">
<div class="container">
{% block content %}{% endblock %}
</div>
</main>
<!-- Bootstrap Bundle JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{% block extra_js %}{% endblock %}
</body>

View File

@ -1,31 +1,43 @@
{% extends 'base.html' %}
{% block content %}
<div class="container py-5">
<div class="d-flex justify-content-between align-items-center mb-5">
<h1 class="display-4 fw-bold" style="color: var(--vibrant-accent);">Manager Dashboard</h1>
<div class="animate-fade-in">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5 gap-3">
<div>
<a href="{% url 'manage_users' %}" class="btn btn-outline-light me-2">Manage Users</a>
<h1 class="section-title mb-1">Manager Dashboard</h1>
<p class="text-secondary mb-0">Overview of your restaurant's performance and stock.</p>
</div>
<div class="d-flex gap-2">
<a href="{% url 'manage_users' %}" class="btn btn-outline-vibrant">Manage Users</a>
<a href="{% url 'pos' %}" class="btn btn-vibrant px-4">Go to POS</a>
</div>
</div>
<div class="row mb-5">
<div class="row g-4 mb-5">
<div class="col-md-4">
<div class="card p-4 shadow-sm h-100">
<h5 class="text-secondary mb-3">Total Sales (All Time)</h5>
<h2 class="display-5 fw-bold">${{ total_sales|stringformat:".2f" }}</h2>
<div class="card p-4 h-100 border-0 shadow-sm" style="background: linear-gradient(135deg, #FF4500 0%, #FF7A45 100%); color: white;">
<h5 class="opacity-75 mb-3 fw-bold text-uppercase small letter-spacing-1">Total Sales (All Time)</h5>
<h2 class="display-5 fw-bold mb-0">{{ total_sales|stringformat:".2f" }} <small class="fs-4">EGP</small></h2>
<div class="mt-4">
<span class="badge bg-white bg-opacity-25 rounded-pill px-3 py-2"><i class="bi bi-graph-up me-1"></i> Live tracking</span>
</div>
</div>
</div>
<div class="col-md-8">
<div class="card p-4 shadow-sm h-100">
<h5 class="text-secondary mb-3">Ingredient Stock Levels</h5>
<div class="row">
<div class="card p-4 h-100 border-0 shadow-sm">
<div class="d-flex justify-content-between align-items-center mb-4">
<h5 class="fw-bold mb-0">Ingredient Stock Levels</h5>
<span class="small text-secondary"><i class="bi bi-info-circle me-1"></i> Updated in real-time</span>
</div>
<div class="row g-3">
{% for ingredient in ingredients %}
<div class="col-md-4 mb-3">
<div class="p-3 bg-dark rounded border {% if ingredient.stock_quantity < 500 %}border-danger{% else %}border-secondary{% endif %}">
<div class="small text-secondary">{{ ingredient.name }}</div>
<div class="h4 mb-0">{{ ingredient.stock_quantity|floatformat:0 }} <small class="fs-6">{{ ingredient.unit }}</small></div>
<div class="col-md-4">
<div class="p-3 rounded-4 border {% if ingredient.stock_quantity < 500 %}bg-danger bg-opacity-10 border-danger border-opacity-25{% else %}bg-light border-light{% endif %} h-100">
<div class="small text-secondary text-uppercase fw-bold mb-1" style="font-size: 0.7rem;">{{ ingredient.name }}</div>
<div class="h3 mb-0 fw-bold {% if ingredient.stock_quantity < 500 %}text-danger{% endif %}">
{{ ingredient.stock_quantity|floatformat:0 }}
<small class="fs-6 fw-normal text-secondary">{{ ingredient.unit }}</small>
</div>
</div>
</div>
{% endfor %}
@ -34,40 +46,66 @@
</div>
</div>
<div class="card shadow-sm">
<div class="card border-0 shadow-sm">
<div class="card-body p-4">
<h3 class="mb-4">Recent Orders</h3>
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="fw-bold mb-0">Recent Orders</h4>
<button class="btn btn-sm btn-light rounded-pill px-3">View All</button>
</div>
<div class="table-responsive">
<table class="table table-dark table-hover align-middle border-0">
<thead class="text-secondary">
<tr>
<th>Order #</th>
<th>Time</th>
<th>Items</th>
<th>Notes</th>
<th class="text-end">Total</th>
<th></th>
<table class="table table-hover align-middle">
<thead class="bg-light">
<tr class="text-secondary">
<th class="py-3 border-0 rounded-start">Order #</th>
<th class="py-3 border-0">Time</th>
<th class="py-3 border-0">Items</th>
<th class="py-3 border-0">Notes</th>
<th class="py-3 border-0 text-end">Total</th>
<th class="py-3 border-0 rounded-end"></th>
</tr>
</thead>
<tbody>
{% for order in orders %}
<tr>
<td class="fw-bold">{{ order.order_number }}</td>
<td>{{ order.created_at|date:"H:i" }} <small class="text-secondary">{{ order.created_at|date:"d M" }}</small></td>
<td class="fw-bold py-3 text-vibrant">{{ order.order_number }}</td>
<td>
{% for item in order.items.all %}
<span class="badge bg-secondary">{{ item.quantity }}x {{ item.menu_item.name }}</span>
{% endfor %}
<div class="fw-bold text-dark">{{ order.created_at|date:"H:i" }}</div>
<div class="small text-secondary">{{ order.created_at|date:"d M, Y" }}</div>
</td>
<td><small class="text-secondary">{{ order.customer_notes|truncatechars:30 }}</small></td>
<td class="text-end fw-bold">${{ order.total_price }}</td>
<td>
<div class="d-flex flex-wrap gap-1">
{% for item in order.items.all %}
<span class="badge bg-soft text-vibrant border border-vibrant border-opacity-10 fw-normal">
{{ item.quantity }}x {{ item.menu_item.name }}
</span>
{% endfor %}
</div>
</td>
<td>
<small class="text-secondary">
{% if order.customer_notes %}
<i class="bi bi-chat-left-text me-1"></i> {{ order.customer_notes|truncatechars:30 }}
{% else %}
-
{% endif %}
</small>
</td>
<td class="text-end fw-bold">{{ order.total_price|floatformat:2 }} EGP</td>
<td class="text-end">
<a href="{% url 'receipt' order.order_number %}" class="btn btn-sm btn-outline-light">Receipt</a>
<a href="{% url 'receipt' order.order_number %}" class="btn btn-sm btn-outline-dark rounded-pill" target="_blank">
<i class="bi bi-printer me-1"></i> Receipt
</a>
</td>
</tr>
{% empty %}
<tr>
<td colspan="6" class="text-center py-5 text-secondary">No orders found yet.</td>
<td colspan="6" class="text-center py-5">
<div class="py-4">
<i class="bi bi-inbox display-1 text-light"></i>
<p class="text-secondary mt-3">No orders found yet. Start by creating one in the POS.</p>
<a href="{% url 'pos' %}" class="btn btn-vibrant mt-2">Open POS</a>
</div>
</td>
</tr>
{% endfor %}
</tbody>

View File

@ -1,34 +1,62 @@
{% extends 'base.html' %}
{% block title %}Welcome | Liver & Sausage POS{% endblock %}
{% block title %}Welcome | Searing Sandwiches{% endblock %}
{% block content %}
<div class="row justify-content-center align-items-center" style="min-height: 80vh;">
<div class="col-lg-8 text-center">
<h1 class="display-3 fw-bold mb-4" style="color: var(--text-light);">
Professional <span style="color: var(--orange);">Cashier System</span>
<div class="row justify-content-center align-items-center animate-fade-in" style="min-height: 80vh;">
<div class="col-lg-10 text-center">
<div class="mb-4">
<span class="badge bg-soft text-vibrant px-3 py-2 rounded-pill fw-bold text-uppercase letter-spacing-1">Next-Gen POS Solution</span>
</div>
<h1 class="display-2 fw-extrabold mb-4" style="letter-spacing: -3px;">
The Smarter way to <br><span class="text-vibrant">Manage Your Kitchen.</span>
</h1>
<p class="lead mb-5 text-secondary">
Streamlined order management, automated stock tracking, and real-time reporting for your restaurant.
<p class="lead mb-5 text-secondary px-lg-5 mx-lg-5">
Empower your team with a lightning-fast cashier interface, automated inventory control, and detailed sales analytics.
</p>
<div class="row g-4 justify-content-center">
<div class="row g-4 justify-content-center mt-2">
<div class="col-md-5">
<div class="card h-100 p-4 border-0 shadow-lg text-start" style="background: linear-gradient(145deg, #242427, #1c1c1f);">
<div class="display-6 mb-3 text-orange" style="color: var(--orange);"><i class="bi bi-cart4"></i></div>
<h3 class="fw-bold">Cashier POS</h3>
<p class="text-secondary">Create orders, customize sandwiches, and print receipts instantly.</p>
<a href="{% url 'pos' %}" class="btn btn-primary mt-auto">Open POS</a>
<div class="card h-100 p-4 border-0 shadow-lg text-start rounded-5">
<div class="item-icon mb-4 ms-0" style="width: 50px; height: 50px; font-size: 1.5rem;">
<i class="bi bi-lightning-charge-fill"></i>
</div>
<h3 class="fw-bold mb-3">Speedy Checkout</h3>
<p class="text-secondary mb-4">A touch-friendly interface designed for high-volume service. Add items, take notes, and print receipts in seconds.</p>
<a href="{% url 'pos' %}" class="btn btn-vibrant mt-auto rounded-pill px-4 py-2">
Start Selling <i class="bi bi-arrow-right ms-2"></i>
</a>
</div>
</div>
<div class="col-md-5">
<div class="card h-100 p-4 border-0 shadow-lg text-start" style="background: linear-gradient(145deg, #242427, #1c1c1f);">
<div class="display-6 mb-3 text-info"><i class="bi bi-graph-up"></i></div>
<h3 class="fw-bold">Manager Dashboard</h3>
<p class="text-secondary">Track liver, sausage, and fries stock. View sales reports and manage prices.</p>
<a href="{% url 'dashboard' %}" class="btn btn-outline-light mt-auto">View Reports</a>
<div class="card h-100 p-4 border-0 shadow-lg text-start rounded-5">
<div class="item-icon mb-4 ms-0" style="width: 50px; height: 50px; font-size: 1.5rem; background-color: #E8F5FF; color: #007BFF;">
<i class="bi bi-pie-chart-fill"></i>
</div>
<h3 class="fw-bold mb-3">Smart Insights</h3>
<p class="text-secondary mb-4">Real-time stock tracking for liver, sausage, and ingredients. Detailed sales reports to help you grow your business.</p>
<a href="{% url 'dashboard' %}" class="btn btn-outline-dark mt-auto rounded-pill px-4 py-2">
View Analytics <i class="bi bi-bar-chart-line ms-2"></i>
</a>
</div>
</div>
</div>
<div class="mt-5 pt-5 opacity-50">
<p class="small text-uppercase fw-bold letter-spacing-2 text-secondary">Trusted by modern fast-food chains</p>
<div class="d-flex justify-content-center gap-5 fs-3 text-secondary">
<i class="bi bi-fire"></i>
<i class="bi bi-cup-hot"></i>
<i class="bi bi-box-seam"></i>
<i class="bi bi-egg"></i>
</div>
</div>
</div>
</div>
<style>
.fw-extrabold { font-weight: 800; }
.letter-spacing-1 { letter-spacing: 1px; }
.letter-spacing-2 { letter-spacing: 2px; }
.rounded-5 { border-radius: 2rem !important; }
</style>
{% endblock %}

View File

@ -1,32 +1,58 @@
{% extends 'base.html' %}
{% block content %}
<div class="row justify-content-center align-items-center" style="min-height: 80vh;">
<div class="col-md-4">
<div class="card bg-dark text-light border-0 shadow-lg" style="border-radius: 15px;">
<div class="row justify-content-center align-items-center animate-fade-in" style="min-height: 80vh;">
<div class="col-md-5 col-lg-4">
<div class="card border-0 shadow-lg rounded-5 overflow-hidden">
<div class="card-body p-5">
<h2 class="text-center mb-4" style="color: var(--vibrant-accent);">Login</h2>
<div class="text-center mb-5">
<div class="bg-soft text-vibrant d-inline-flex p-4 rounded-circle mb-4">
<i class="bi bi-shield-lock-fill display-5"></i>
</div>
<h2 class="fw-bold h1" style="letter-spacing: -1.5px;">Sign In</h2>
<p class="text-secondary">Enter your credentials to access the system</p>
</div>
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label for="id_username" class="form-label">Username</label>
<input type="text" name="username" class="form-control bg-secondary text-light border-0" id="id_username" required>
<div class="mb-4">
<label for="id_username" class="form-label text-secondary small fw-bold text-uppercase ms-1">Username</label>
<div class="input-group">
<span class="input-group-text bg-light border-0 rounded-start-4 px-3"><i class="bi bi-person text-secondary"></i></span>
<input type="text" name="username" class="form-control bg-light border-0 rounded-end-4 py-3" id="id_username" placeholder="Username" required>
</div>
</div>
<div class="mb-4">
<label for="id_password" class="form-label">Password</label>
<input type="password" name="password" class="form-control bg-secondary text-light border-0" id="id_password" required>
<label for="id_password" class="form-label text-secondary small fw-bold text-uppercase ms-1">Password</label>
<div class="input-group">
<span class="input-group-text bg-light border-0 rounded-start-4 px-3"><i class="bi bi-key text-secondary"></i></span>
<input type="password" name="password" class="form-control bg-light border-0 rounded-end-4 py-3" id="id_password" placeholder="Password" required>
</div>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-lg" style="background-color: var(--vibrant-accent); color: white; font-weight: bold;">Sign In</button>
<div class="d-grid gap-2 mt-5">
<button type="submit" class="btn btn-vibrant btn-lg py-3 rounded-4 shadow-sm">
LOGIN <i class="bi bi-arrow-right-short ms-1 fs-4"></i>
</button>
</div>
</form>
{% if form.errors %}
<div class="alert alert-danger mt-3 bg-danger text-light border-0">
Invalid username or password.
<div class="alert alert-danger mt-4 border-0 rounded-4 bg-danger bg-opacity-10 text-danger small text-center" role="alert">
<i class="bi bi-exclamation-triangle-fill me-2"></i> Invalid username or password.
</div>
{% endif %}
</div>
<div class="card-footer bg-light border-0 py-4 text-center">
<small class="text-secondary fw-bold text-uppercase letter-spacing-1">Searing Sandwiches v1.0</small>
</div>
</div>
</div>
</div>
<style>
.rounded-start-4 { border-radius: 1rem 0 0 1rem !important; }
.rounded-end-4 { border-radius: 0 1rem 1rem 0 !important; }
.rounded-4 { border-radius: 1rem !important; }
.rounded-5 { border-radius: 2rem !important; }
</style>
{% endblock %}

View File

@ -1,93 +1,196 @@
{% extends 'base.html' %}
{% block title %}POS | Cashier Interface{% endblock %}
{% block title %}POS | Searing Sandwiches{% endblock %}
{% block extra_css %}
.menu-item-card {
cursor: pointer;
transition: all 0.2s;
}
.menu-item-card:hover {
border-color: var(--orange);
background-color: #2a2a2d;
}
.cart-sticky {
position: sticky;
top: 90px;
max-height: calc(100vh - 120px);
overflow-y: auto;
}
.cart-item {
border-bottom: 1px solid var(--slate);
padding: 10px 0;
}
.cart-item:last-child {
border-bottom: none;
}
<style>
.menu-item-card {
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
border: 1px solid var(--border-soft);
border-radius: 20px;
background: white;
height: 100%;
overflow: hidden;
}
.menu-item-card:hover {
border-color: var(--primary-vibrant);
transform: translateY(-8px);
box-shadow: 0 15px 35px rgba(255, 69, 0, 0.1);
}
.menu-item-card:active {
transform: scale(0.95);
}
.item-icon {
width: 60px;
height: 60px;
background-color: var(--primary-soft);
color: var(--primary-vibrant);
border-radius: 16px;
display: flex;
align-items: center;
justify-content: center;
font-size: 1.8rem;
margin: 0 auto 1rem;
transition: all 0.3s;
}
.menu-item-card:hover .item-icon {
background-color: var(--primary-vibrant);
color: white;
transform: rotate(-10deg);
}
.cart-sticky {
position: sticky;
top: 100px;
max-height: calc(100vh - 140px);
display: flex;
flex-direction: column;
border-radius: 24px;
border: 0;
box-shadow: 0 10px 40px rgba(0,0,0,0.08);
}
.cart-items-container {
overflow-y: auto;
flex-grow: 1;
padding-right: 5px;
}
.cart-item {
background: white;
border-radius: 12px;
padding: 12px;
margin-bottom: 10px;
border: 1px solid var(--border-soft);
transition: all 0.2s;
}
.cart-item:hover {
border-color: var(--primary-vibrant);
}
.checkout-footer {
background: #F8F9FA;
margin: 0 -1.5rem -1.5rem;
padding: 1.5rem;
border-radius: 0 0 24px 24px;
border-top: 1px solid var(--border-soft);
}
@media (max-width: 991.98px) {
.pos-container { margin-bottom: 350px; }
.cart-sticky {
padding: 1.5rem;
height: 320px;
top: auto;
}
}
</style>
{% endblock %}
{% block content %}
<div class="row g-4">
<!-- Menu Section -->
<div class="col-lg-8">
<h2 class="mb-4 fw-bold">Menu</h2>
<div class="row g-3">
{% for item in menu_items %}
<div class="col-md-4">
<div class="card h-100 menu-item-card shadow-sm" onclick="addToCart({{ item.id }}, '{{ item.name }}', {{ item.price }})">
<div class="card-body text-center py-4">
<h5 class="fw-bold mb-1">{{ item.name }}</h5>
<p class="text-orange fw-bold mb-0" style="color: var(--orange);">{{ item.price }} EGP</p>
<div class="pos-container animate-fade-in">
<div class="row g-4">
<!-- Menu Section -->
<div class="col-lg-8">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-4 gap-3">
<div>
<h2 class="section-title mb-1">Our Menu</h2>
<p class="text-secondary mb-0">Select items to start a new order</p>
</div>
<div class="input-group" style="max-width: 300px;">
<span class="input-group-text bg-white border-end-0"><i class="bi bi-search text-secondary"></i></span>
<input type="text" class="form-control border-start-0" placeholder="Search menu...">
</div>
</div>
<div class="row g-3">
{% for item in menu_items %}
<div class="col-6 col-md-4">
<div class="card menu-item-card p-3 shadow-sm" onclick="addToCart({{ item.id }}, '{{ item.name }}', {{ item.price }})">
<div class="card-body text-center p-2">
<div class="item-icon">
<i class="bi bi-egg-fried"></i>
</div>
<h5 class="fw-bold mb-1 text-truncate">{{ item.name }}</h5>
<p class="text-vibrant fw-bold mb-0 fs-5">{{ item.price|floatformat:2 }} <small class="fw-normal small">EGP</small></p>
</div>
</div>
</div>
{% empty %}
<div class="col-12 text-center py-5">
<div class="card border-0 bg-transparent">
<i class="bi bi-shop display-1 text-light"></i>
<h4 class="mt-3 text-secondary">No active menu items.</h4>
<p>Please add items in the admin panel.</p>
</div>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
<!-- Cart Section -->
<div class="col-lg-4">
<div class="card cart-sticky shadow-lg">
<div class="card-header py-3">
<h4 class="mb-0 fw-bold"><i class="bi bi-receipt me-2"></i> Current Order</h4>
</div>
<div class="card-body">
<div id="cart-items" class="mb-4">
<p class="text-center text-secondary py-4" id="empty-cart-msg">Cart is empty</p>
<!-- Cart Section -->
<div class="col-lg-4">
<div class="card cart-sticky p-4 h-100">
<div class="d-flex align-items-center mb-4">
<div class="bg-soft text-vibrant p-2 rounded-3 me-3">
<i class="bi bi-bag-check-fill fs-4"></i>
</div>
<h4 class="mb-0 fw-bold">Current Order</h4>
</div>
<div class="mb-3">
<label class="form-label small text-secondary">Customer Notes / Customization</label>
<textarea id="customer-notes" class="form-control bg-dark text-white border-secondary" rows="2" placeholder="e.g., No onion, extra spicy..."></textarea>
<div class="cart-items-container" id="cart-items-wrapper">
<div id="cart-items">
<div class="text-center py-5 text-secondary" id="empty-cart-msg">
<i class="bi bi-cart-x fs-1 opacity-25 d-block mb-3"></i>
Your cart is empty
</div>
</div>
</div>
<div class="d-flex justify-content-between mb-2">
<span class="text-secondary">Subtotal</span>
<span id="subtotal">0.00 EGP</span>
</div>
<div class="d-flex justify-content-between mb-4 border-top pt-2">
<h4 class="fw-bold">Total</h4>
<h4 class="fw-bold" id="total" style="color: var(--orange);">0.00 EGP</h4>
<div class="mt-3">
<label class="form-label small text-secondary fw-bold text-uppercase">Special Instructions</label>
<input type="text" id="customer-notes" class="form-control rounded-3 py-2 border-soft" placeholder="e.g. No onions, extra spicy">
</div>
<button class="btn btn-primary w-100 py-3 fw-bold" onclick="completeOrder()" id="checkout-btn" disabled>
COMPLETE ORDER
</button>
<div class="checkout-footer mt-4">
<div class="d-flex justify-content-between mb-2">
<span class="text-secondary fw-medium">Subtotal</span>
<span id="subtotal" class="fw-bold">0.00 EGP</span>
</div>
<div class="d-flex justify-content-between align-items-center pt-2 mb-4">
<h4 class="fw-bold mb-0">Total</h4>
<h3 class="fw-bold mb-0 text-vibrant" id="total">0.00 EGP</h3>
</div>
<button class="btn btn-vibrant w-100 py-3 fw-bold text-uppercase" onclick="completeOrder()" id="checkout-btn" disabled>
Place Order <i class="bi bi-arrow-right-short ms-1 fs-4"></i>
</button>
</div>
</div>
</div>
</div>
</div>
<!-- Success Modal -->
<div class="modal fade" id="successModal" tabindex="-1" aria-hidden="true">
<div class="modal fade" id="successModal" data-bs-backdrop="static" tabindex="-1">
<div class="modal-dialog modal-dialog-centered">
<div class="card w-100">
<div class="card-body text-center py-5">
<div class="display-1 text-success mb-4"><i class="bi bi-check-circle"></i></div>
<h2 class="fw-bold mb-3">Order Completed!</h2>
<p class="text-secondary mb-4" id="order-confirm-msg"></p>
<div class="d-grid gap-2">
<a id="print-receipt-btn" href="#" class="btn btn-primary" target="_blank">PRINT RECEIPT</a>
<button type="button" class="btn btn-outline-light" onclick="location.reload()">NEW ORDER</button>
<div class="modal-content border-0 rounded-5 overflow-hidden">
<div class="card border-0 w-100">
<div class="card-body text-center py-5">
<div class="mb-4">
<div class="d-inline-flex bg-success bg-opacity-10 text-success p-4 rounded-circle">
<i class="bi bi-check2-circle display-1"></i>
</div>
</div>
<h2 class="fw-bold mb-2">Order Confirmed!</h2>
<p class="text-secondary mb-4 px-4" id="order-confirm-msg"></p>
<div class="d-grid gap-3 px-4">
<a id="print-receipt-btn" href="#" class="btn btn-vibrant py-3 fw-bold" target="_blank">
<i class="bi bi-printer me-2"></i> PRINT RECEIPT
</a>
<button type="button" class="btn btn-light py-2 rounded-3 text-secondary" onclick="location.reload()">NEW ORDER</button>
</div>
</div>
</div>
</div>
@ -134,26 +237,30 @@
total += item.price * item.quantity;
const itemDiv = document.createElement('div');
itemDiv.className = 'cart-item d-flex justify-content-between align-items-center';
itemDiv.className = 'cart-item d-flex justify-content-between align-items-center animate-fade-in';
itemDiv.innerHTML = `
<div>
<h6 class="mb-0 fw-bold">${item.name}</h6>
<small class="text-secondary">${item.price} x ${item.quantity}</small>
<div class="flex-grow-1">
<div class="fw-bold text-dark">${item.name}</div>
<div class="small text-secondary">${item.price.toFixed(2)} x ${item.quantity}</div>
</div>
<div class="d-flex align-items-center">
<span class="fw-bold me-3">${(item.price * item.quantity).toFixed(2)}</span>
<button class="btn btn-sm btn-outline-danger" onclick="removeFromCart(${id})"><i class="bi bi-dash"></i></button>
<div class="d-flex align-items-center gap-3">
<span class="fw-bold text-vibrant">${(item.price * item.quantity).toFixed(2)}</span>
<div class="btn-group btn-group-sm rounded-pill overflow-hidden border">
<button class="btn btn-light px-2" onclick="removeFromCart(${id})"><i class="bi bi-dash"></i></button>
<button class="btn btn-white px-2 disabled fw-bold">${item.quantity}</button>
<button class="btn btn-light px-2" onclick="addToCart(${id}, '${item.name}', ${item.price})"><i class="bi bi-plus"></i></button>
</div>
</div>
`;
cartItemsDiv.appendChild(itemDiv);
}
if (itemCount > 0) {
emptyMsg.style.display = 'none';
if (emptyMsg) emptyMsg.style.display = 'none';
checkoutBtn.disabled = false;
} else {
cartItemsDiv.appendChild(emptyMsg);
emptyMsg.style.display = 'block';
cartItemsDiv.appendChild(emptyMsg || createEmptyMsg());
if (emptyMsg) emptyMsg.style.display = 'block';
checkoutBtn.disabled = true;
}
@ -161,11 +268,19 @@
document.getElementById('total').innerText = total.toFixed(2) + ' EGP';
}
function createEmptyMsg() {
const div = document.createElement('div');
div.id = 'empty-cart-msg';
div.className = 'text-center py-5 text-secondary';
div.innerHTML = '<i class="bi bi-cart-x fs-1 opacity-25 d-block mb-3"></i>Your cart is empty';
return div;
}
async function completeOrder() {
const btn = document.getElementById('checkout-btn');
const notes = document.getElementById('customer-notes').value;
btn.disabled = true;
btn.innerText = 'PROCESSING...';
btn.innerHTML = '<span class="spinner-border spinner-border-sm me-2"></span> PROCESSING...';
const cartArray = Object.values(cart);
@ -181,19 +296,20 @@
const result = await response.json();
if (result.success) {
document.getElementById('order-confirm-msg').innerText = 'Order #' + result.order_number + ' has been successfully placed and stock has been updated.';
document.getElementById('order-confirm-msg').innerText = 'Order #' + result.order_number + ' has been successfully placed.';
document.getElementById('print-receipt-btn').href = '/receipt/' + result.order_number + '/';
const modal = new bootstrap.Modal(document.getElementById('successModal'));
const modalElement = document.getElementById('successModal');
const modal = new bootstrap.Modal(modalElement);
modal.show();
} else {
alert('Error: ' + result.error);
btn.disabled = false;
btn.innerText = 'COMPLETE ORDER';
btn.innerHTML = 'Place Order <i class="bi bi-arrow-right-short ms-1 fs-4"></i>';
}
} catch (error) {
alert('Something went wrong. Please check stock levels.');
btn.disabled = false;
btn.innerText = 'COMPLETE ORDER';
btn.innerHTML = 'Place Order <i class="bi bi-arrow-right-short ms-1 fs-4"></i>';
}
}
</script>

View File

@ -18,12 +18,12 @@
<body onload="window.print()">
<div class="no-print" style="margin-bottom: 20px; text-align: center;">
<button onclick="window.print()">Print Receipt</button>
<button onclick="window.close()">Close</button>
<button onclick="location.href='{% url 'pos' %}'">Back to POS</button>
</div>
<div class="text-center header">
<h2>LIVER & SAUSAGE</h2>
<p>RESTAURANT POS</p>
<h2 style="margin-bottom: 0;">SEARING SANDWICHES</h2>
<p style="margin-top: 5px;">Quality Fresh Food</p>
<p>Order #: {{ order.order_number }}</p>
<p>{{ order.created_at|date:"Y-m-d H:i" }}</p>
</div>
@ -32,7 +32,7 @@
{% for item in order.items.all %}
<div class="item">
<span>{{ item.quantity }} x {{ item.menu_item.name }}</span>
<span>{{ item.price_at_order }}</span>
<span>{{ item.price_at_order|floatformat:2 }}</span>
</div>
{% endfor %}
</div>
@ -40,7 +40,7 @@
<div class="footer">
<div class="item total">
<span>TOTAL</span>
<span>{{ order.total_price }} EGP</span>
<span>{{ order.total_price|floatformat:2 }} EGP</span>
</div>
{% if order.customer_notes %}
<div style="margin-top: 10px; font-size: 0.9em;">
@ -51,7 +51,8 @@
</div>
<div class="text-center" style="margin-top: 20px;">
<p>THANK YOU!</p>
<p>THANK YOU FOR YOUR ORDER!</p>
<p style="font-size: 0.8em;">Visit us again soon</p>
</div>
</body>
</html>

View File

@ -1,78 +1,110 @@
{% extends 'base.html' %}
{% block content %}
<div class="container py-5">
<div class="d-flex justify-content-between align-items-center mb-5">
<h1 class="display-4 fw-bold" style="color: var(--vibrant-accent);">User Management</h1>
<a href="{% url 'dashboard' %}" class="btn btn-outline-light">Back to Dashboard</a>
<div class="py-4 animate-fade-in">
<div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5 gap-3">
<div>
<h1 class="section-title mb-1">User Management</h1>
<p class="text-secondary mb-0">Create and manage access for your team members.</p>
</div>
<a href="{% url 'dashboard' %}" class="btn btn-outline-dark rounded-pill">
<i class="bi bi-arrow-left me-2"></i> Back to Dashboard
</a>
</div>
<div class="row">
<div class="row g-4">
<!-- Create User Form -->
<div class="col-md-4">
<div class="card bg-dark text-light border-0 shadow-lg mb-4" style="border-radius: 15px;">
<div class="card h-100 border-0 shadow-sm rounded-4">
<div class="card-body p-4">
<h3 class="mb-4">Create New Account</h3>
<div class="d-flex align-items-center mb-4">
<div class="bg-soft text-vibrant p-2 rounded-3 me-3">
<i class="bi bi-person-plus-fill fs-4"></i>
</div>
<h3 class="fw-bold mb-0">Add User</h3>
</div>
<form method="post">
{% csrf_token %}
<div class="mb-3">
<label class="form-label">Username</label>
<input type="text" name="username" class="form-control bg-secondary text-light border-0" required>
<label class="form-label text-secondary small fw-bold text-uppercase ms-1">Username</label>
<input type="text" name="username" class="form-control rounded-3 py-2" placeholder="Enter username" required>
</div>
<div class="mb-3">
<label class="form-label">Password</label>
<input type="password" name="password" class="form-control bg-secondary text-light border-0" required>
<label class="form-label text-secondary small fw-bold text-uppercase ms-1">Password</label>
<input type="password" name="password" class="form-control rounded-3 py-2" placeholder="Enter password" required>
</div>
<div class="mb-3">
<label class="form-label">Role</label>
<select name="role" class="form-select bg-secondary text-light border-0" required>
<option value="cashier">Cashier</option>
<option value="manager">Manager</option>
<div class="mb-4">
<label class="form-label text-secondary small fw-bold text-uppercase ms-1">Access Role</label>
<select name="role" class="form-select rounded-3 py-2" required>
<option value="cashier" selected>Cashier (POS Only)</option>
<option value="manager">Manager (Full Access)</option>
</select>
</div>
<div class="d-grid mt-4">
<button type="submit" class="btn" style="background-color: var(--vibrant-accent); color: white;">Create User</button>
<button type="submit" class="btn btn-vibrant py-3 rounded-4 shadow-sm">
<i class="bi bi-plus-lg me-1"></i> CREATE ACCOUNT
</button>
</div>
</form>
{% if messages %}
{% for message in messages %}
<div class="alert alert-{% if message.tags == 'error' %}danger{% else %}success{% endif %} mt-3 bg-opacity-25 border-0">
{{ message }}
</div>
{% endfor %}
{% endif %}
</div>
</div>
</div>
<!-- User List -->
<div class="col-md-8">
<div class="card bg-dark text-light border-0 shadow-lg" style="border-radius: 15px;">
<div class="card border-0 shadow-sm rounded-4">
<div class="card-body p-4">
<h3 class="mb-4">Existing Accounts</h3>
<div class="d-flex justify-content-between align-items-center mb-4">
<h4 class="fw-bold mb-0">System Accounts</h4>
<span class="badge bg-light text-dark rounded-pill px-3">{{ users|length }} Total Users</span>
</div>
<div class="table-responsive">
<table class="table table-dark table-hover border-0">
<thead>
<table class="table table-hover align-middle">
<thead class="bg-light">
<tr class="text-secondary">
<th>Username</th>
<th>Role</th>
<th>Date Joined</th>
<th class="py-3 border-0 rounded-start">User</th>
<th class="py-3 border-0">Role</th>
<th class="py-3 border-0">Joined Date</th>
<th class="py-3 border-0 rounded-end"></th>
</tr>
</thead>
<tbody>
{% for user in users %}
{% for u in users %}
<tr>
<td class="fw-bold">{{ user.username }}</td>
<td>
<span class="badge {% if user.profile.role == 'manager' %}bg-primary{% else %}bg-info{% endif %}">
{{ user.profile.role|title }}
</span>
<td class="py-3">
<div class="d-flex align-items-center">
<div class="bg-light rounded-circle p-2 me-3">
<i class="bi bi-person-circle fs-4 text-secondary"></i>
</div>
<span class="fw-bold text-dark">{{ u.username }}</span>
</div>
</td>
<td>
{% if u.profile and u.profile.role == 'manager' %}
<span class="badge bg-primary bg-opacity-10 text-primary border border-primary border-opacity-10 px-3 py-2 rounded-pill">
<i class="bi bi-shield-check me-1"></i> Manager
</span>
{% else %}
<span class="badge bg-info bg-opacity-10 text-info border border-info border-opacity-10 px-3 py-2 rounded-pill">
<i class="bi bi-cart me-1"></i> Cashier
</span>
{% endif %}
</td>
<td class="text-secondary">
<div class="fw-medium">{{ u.date_joined|date:"M d, Y" }}</div>
<small class="opacity-50">at {{ u.date_joined|date:"H:i" }}</small>
</td>
<td class="text-end">
<button class="btn btn-sm btn-light rounded-circle" title="Edit User"><i class="bi bi-pencil"></i></button>
</td>
<td class="text-secondary">{{ user.date_joined|date:"M d, Y" }}</td>
</tr>
{% empty %}
<tr>
<td colspan="3" class="text-center text-secondary py-4">No users found.</td>
<td colspan="4" class="text-center py-5 text-secondary">
No users found.
</td>
</tr>
{% endfor %}
</tbody>
@ -83,4 +115,8 @@
</div>
</div>
</div>
<style>
.rounded-4 { border-radius: 1rem !important; }
</style>
{% endblock %}