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> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Searing Sandwiches | POS & Stock</title> <title>{% block title %}Searing Sandwiches | POS & Stock{% endblock %}</title>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;800&display=swap" rel="stylesheet"> <!-- 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"> <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> <style>
:root { :root {
--deep-obsidian: #1A1A1D; --primary-vibrant: #FF4500;
--vibrant-accent: #FF4500; --primary-soft: #FFF5F2;
--slate-gray: #4E4E50; --secondary-vibrant: #00D1B2;
--soft-white: #F5F5F7; --dark-neutral: #1A1D23;
--light-neutral: #F8F9FA;
--glass-bg: rgba(255, 255, 255, 0.8);
--border-soft: rgba(0, 0, 0, 0.08);
} }
body { body {
font-family: 'Inter', sans-serif; font-family: 'Plus Jakarta Sans', sans-serif;
background-color: var(--deep-obsidian); background-color: var(--light-neutral);
color: var(--soft-white); color: var(--dark-neutral);
min-height: 100vh; min-height: 100vh;
overflow-x: hidden;
} }
/* Modern Navbar */
.navbar { .navbar {
background-color: rgba(26, 26, 29, 0.9); background-color: var(--glass-bg);
backdrop-filter: blur(10px); backdrop-filter: blur(12px);
border-bottom: 1px solid var(--slate-gray); -webkit-backdrop-filter: blur(12px);
} border-bottom: 1px solid var(--border-soft);
padding: 0.85rem 0;
.vibrant-text {
color: var(--vibrant-accent);
}
.btn-vibrant {
background-color: var(--vibrant-accent);
color: white;
border: none;
transition: all 0.3s ease; 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 { .btn-vibrant:hover {
transform: translateY(-2px); 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; color: white;
} }
/* Cards & Containers */
.card { .card {
background-color: #242428; border: 1px solid var(--border-soft);
border: 1px solid var(--slate-gray); 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> </style>
{% block extra_css %}{% endblock %} {% block extra_css %}{% endblock %}
@ -55,31 +156,36 @@
<body> <body>
<nav class="navbar navbar-expand-lg sticky-top"> <nav class="navbar navbar-expand-lg sticky-top">
<div class="container"> <div class="container">
<a class="navbar-brand fw-bolder fs-3" href="{% url 'home' %}"> <a class="navbar-brand" href="{% url 'home' %}">
<span class="vibrant-text">SEARING</span> SANDWICHES <span class="brand-accent">SEARING</span> SANDWICHES
</a> </a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav"> <button class="navbar-toggler border-0" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span> <span class="bi bi-list fs-1"></span>
</button> </button>
<div class="collapse navbar-collapse" id="navbarNav"> <div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto align-items-center"> <ul class="navbar-nav ms-auto align-items-center">
{% if user.is_authenticated %} {% if user.is_authenticated %}
{% if user.profile.role == 'manager' %} {% if user.profile and user.profile.role == 'manager' %}
<li class="nav-item"> <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>
<li class="nav-item"> <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> </li>
{% endif %} {% endif %}
<li class="nav-item"> <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>
<li class="nav-item"> <li class="nav-item dropdown ms-lg-3">
<span class="text-secondary me-3">Hi, {{ user.username }}</span> <a class="nav-link dropdown-toggle d-flex align-items-center" href="#" role="button" data-bs-toggle="dropdown">
</li> <div class="bg-soft rounded-circle p-2 me-2 d-inline-flex">
<li class="nav-item"> <i class="bi bi-person text-vibrant"></i>
<a href="{% url 'logout' %}" class="btn btn-sm btn-outline-secondary">Logout</a> </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> </li>
{% else %} {% else %}
<li class="nav-item"> <li class="nav-item">
@ -91,10 +197,25 @@
</div> </div>
</nav> </nav>
<main> <div class="container mt-3">
{% block content %}{% endblock %} {% 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> </main>
<!-- Bootstrap Bundle JS -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
{% block extra_js %}{% endblock %} {% block extra_js %}{% endblock %}
</body> </body>

View File

@ -1,31 +1,43 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<div class="container py-5"> <div class="animate-fade-in">
<div class="d-flex justify-content-between align-items-center mb-5"> <div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5 gap-3">
<h1 class="display-4 fw-bold" style="color: var(--vibrant-accent);">Manager Dashboard</h1>
<div> <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> <a href="{% url 'pos' %}" class="btn btn-vibrant px-4">Go to POS</a>
</div> </div>
</div> </div>
<div class="row mb-5"> <div class="row g-4 mb-5">
<div class="col-md-4"> <div class="col-md-4">
<div class="card p-4 shadow-sm h-100"> <div class="card p-4 h-100 border-0 shadow-sm" style="background: linear-gradient(135deg, #FF4500 0%, #FF7A45 100%); color: white;">
<h5 class="text-secondary mb-3">Total Sales (All Time)</h5> <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">${{ total_sales|stringformat:".2f" }}</h2> <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> </div>
<div class="col-md-8"> <div class="col-md-8">
<div class="card p-4 shadow-sm h-100"> <div class="card p-4 h-100 border-0 shadow-sm">
<h5 class="text-secondary mb-3">Ingredient Stock Levels</h5> <div class="d-flex justify-content-between align-items-center mb-4">
<div class="row"> <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 %} {% for ingredient in ingredients %}
<div class="col-md-4 mb-3"> <div class="col-md-4">
<div class="p-3 bg-dark rounded border {% if ingredient.stock_quantity < 500 %}border-danger{% else %}border-secondary{% endif %}"> <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">{{ ingredient.name }}</div> <div class="small text-secondary text-uppercase fw-bold mb-1" style="font-size: 0.7rem;">{{ ingredient.name }}</div>
<div class="h4 mb-0">{{ ingredient.stock_quantity|floatformat:0 }} <small class="fs-6">{{ ingredient.unit }}</small></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>
</div> </div>
{% endfor %} {% endfor %}
@ -34,40 +46,66 @@
</div> </div>
</div> </div>
<div class="card shadow-sm"> <div class="card border-0 shadow-sm">
<div class="card-body p-4"> <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"> <div class="table-responsive">
<table class="table table-dark table-hover align-middle border-0"> <table class="table table-hover align-middle">
<thead class="text-secondary"> <thead class="bg-light">
<tr> <tr class="text-secondary">
<th>Order #</th> <th class="py-3 border-0 rounded-start">Order #</th>
<th>Time</th> <th class="py-3 border-0">Time</th>
<th>Items</th> <th class="py-3 border-0">Items</th>
<th>Notes</th> <th class="py-3 border-0">Notes</th>
<th class="text-end">Total</th> <th class="py-3 border-0 text-end">Total</th>
<th></th> <th class="py-3 border-0 rounded-end"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for order in orders %} {% for order in orders %}
<tr> <tr>
<td class="fw-bold">{{ order.order_number }}</td> <td class="fw-bold py-3 text-vibrant">{{ order.order_number }}</td>
<td>{{ order.created_at|date:"H:i" }} <small class="text-secondary">{{ order.created_at|date:"d M" }}</small></td>
<td> <td>
{% for item in order.items.all %} <div class="fw-bold text-dark">{{ order.created_at|date:"H:i" }}</div>
<span class="badge bg-secondary">{{ item.quantity }}x {{ item.menu_item.name }}</span> <div class="small text-secondary">{{ order.created_at|date:"d M, Y" }}</div>
{% endfor %}
</td> </td>
<td><small class="text-secondary">{{ order.customer_notes|truncatechars:30 }}</small></td> <td>
<td class="text-end fw-bold">${{ order.total_price }}</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"> <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> </td>
</tr> </tr>
{% empty %} {% empty %}
<tr> <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> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -1,34 +1,62 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block title %}Welcome | Liver & Sausage POS{% endblock %} {% block title %}Welcome | Searing Sandwiches{% endblock %}
{% block content %} {% block content %}
<div class="row justify-content-center align-items-center" style="min-height: 80vh;"> <div class="row justify-content-center align-items-center animate-fade-in" style="min-height: 80vh;">
<div class="col-lg-8 text-center"> <div class="col-lg-10 text-center">
<h1 class="display-3 fw-bold mb-4" style="color: var(--text-light);"> <div class="mb-4">
Professional <span style="color: var(--orange);">Cashier System</span> <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> </h1>
<p class="lead mb-5 text-secondary"> <p class="lead mb-5 text-secondary px-lg-5 mx-lg-5">
Streamlined order management, automated stock tracking, and real-time reporting for your restaurant. Empower your team with a lightning-fast cashier interface, automated inventory control, and detailed sales analytics.
</p> </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="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="card h-100 p-4 border-0 shadow-lg text-start rounded-5">
<div class="display-6 mb-3 text-orange" style="color: var(--orange);"><i class="bi bi-cart4"></i></div> <div class="item-icon mb-4 ms-0" style="width: 50px; height: 50px; font-size: 1.5rem;">
<h3 class="fw-bold">Cashier POS</h3> <i class="bi bi-lightning-charge-fill"></i>
<p class="text-secondary">Create orders, customize sandwiches, and print receipts instantly.</p> </div>
<a href="{% url 'pos' %}" class="btn btn-primary mt-auto">Open POS</a> <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> </div>
<div class="col-md-5"> <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="card h-100 p-4 border-0 shadow-lg text-start rounded-5">
<div class="display-6 mb-3 text-info"><i class="bi bi-graph-up"></i></div> <div class="item-icon mb-4 ms-0" style="width: 50px; height: 50px; font-size: 1.5rem; background-color: #E8F5FF; color: #007BFF;">
<h3 class="fw-bold">Manager Dashboard</h3> <i class="bi bi-pie-chart-fill"></i>
<p class="text-secondary">Track liver, sausage, and fries stock. View sales reports and manage prices.</p> </div>
<a href="{% url 'dashboard' %}" class="btn btn-outline-light mt-auto">View Reports</a> <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>
</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>
</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 %} {% endblock %}

View File

@ -1,32 +1,58 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<div class="row justify-content-center align-items-center" style="min-height: 80vh;"> <div class="row justify-content-center align-items-center animate-fade-in" style="min-height: 80vh;">
<div class="col-md-4"> <div class="col-md-5 col-lg-4">
<div class="card bg-dark text-light border-0 shadow-lg" style="border-radius: 15px;"> <div class="card border-0 shadow-lg rounded-5 overflow-hidden">
<div class="card-body p-5"> <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"> <form method="post">
{% csrf_token %} {% csrf_token %}
<div class="mb-3"> <div class="mb-4">
<label for="id_username" class="form-label">Username</label> <label for="id_username" class="form-label text-secondary small fw-bold text-uppercase ms-1">Username</label>
<input type="text" name="username" class="form-control bg-secondary text-light border-0" id="id_username" required> <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>
<div class="mb-4"> <div class="mb-4">
<label for="id_password" class="form-label">Password</label> <label for="id_password" class="form-label text-secondary small fw-bold text-uppercase ms-1">Password</label>
<input type="password" name="password" class="form-control bg-secondary text-light border-0" id="id_password" required> <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>
<div class="d-grid"> <div class="d-grid gap-2 mt-5">
<button type="submit" class="btn btn-lg" style="background-color: var(--vibrant-accent); color: white; font-weight: bold;">Sign In</button> <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> </div>
</form> </form>
{% if form.errors %} {% if form.errors %}
<div class="alert alert-danger mt-3 bg-danger text-light border-0"> <div class="alert alert-danger mt-4 border-0 rounded-4 bg-danger bg-opacity-10 text-danger small text-center" role="alert">
Invalid username or password. <i class="bi bi-exclamation-triangle-fill me-2"></i> Invalid username or password.
</div> </div>
{% endif %} {% endif %}
</div> </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> </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 %} {% endblock %}

View File

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

View File

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

View File

@ -1,78 +1,110 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% block content %} {% block content %}
<div class="container py-5"> <div class="py-4 animate-fade-in">
<div class="d-flex justify-content-between align-items-center mb-5"> <div class="d-flex flex-column flex-md-row justify-content-between align-items-md-center mb-5 gap-3">
<h1 class="display-4 fw-bold" style="color: var(--vibrant-accent);">User Management</h1> <div>
<a href="{% url 'dashboard' %}" class="btn btn-outline-light">Back to Dashboard</a> <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>
<div class="row"> <div class="row g-4">
<!-- Create User Form --> <!-- Create User Form -->
<div class="col-md-4"> <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"> <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"> <form method="post">
{% csrf_token %} {% csrf_token %}
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Username</label> <label class="form-label text-secondary small fw-bold text-uppercase ms-1">Username</label>
<input type="text" name="username" class="form-control bg-secondary text-light border-0" required> <input type="text" name="username" class="form-control rounded-3 py-2" placeholder="Enter username" required>
</div> </div>
<div class="mb-3"> <div class="mb-3">
<label class="form-label">Password</label> <label class="form-label text-secondary small fw-bold text-uppercase ms-1">Password</label>
<input type="password" name="password" class="form-control bg-secondary text-light border-0" required> <input type="password" name="password" class="form-control rounded-3 py-2" placeholder="Enter password" required>
</div> </div>
<div class="mb-3"> <div class="mb-4">
<label class="form-label">Role</label> <label class="form-label text-secondary small fw-bold text-uppercase ms-1">Access Role</label>
<select name="role" class="form-select bg-secondary text-light border-0" required> <select name="role" class="form-select rounded-3 py-2" required>
<option value="cashier">Cashier</option> <option value="cashier" selected>Cashier (POS Only)</option>
<option value="manager">Manager</option> <option value="manager">Manager (Full Access)</option>
</select> </select>
</div> </div>
<div class="d-grid mt-4"> <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> </div>
</form> </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> </div>
</div> </div>
<!-- User List --> <!-- User List -->
<div class="col-md-8"> <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"> <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"> <div class="table-responsive">
<table class="table table-dark table-hover border-0"> <table class="table table-hover align-middle">
<thead> <thead class="bg-light">
<tr class="text-secondary"> <tr class="text-secondary">
<th>Username</th> <th class="py-3 border-0 rounded-start">User</th>
<th>Role</th> <th class="py-3 border-0">Role</th>
<th>Date Joined</th> <th class="py-3 border-0">Joined Date</th>
<th class="py-3 border-0 rounded-end"></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for user in users %} {% for u in users %}
<tr> <tr>
<td class="fw-bold">{{ user.username }}</td> <td class="py-3">
<td> <div class="d-flex align-items-center">
<span class="badge {% if user.profile.role == 'manager' %}bg-primary{% else %}bg-info{% endif %}"> <div class="bg-light rounded-circle p-2 me-3">
{{ user.profile.role|title }} <i class="bi bi-person-circle fs-4 text-secondary"></i>
</span> </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>
<td class="text-secondary">{{ user.date_joined|date:"M d, Y" }}</td>
</tr> </tr>
{% empty %} {% empty %}
<tr> <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> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -83,4 +115,8 @@
</div> </div>
</div> </div>
</div> </div>
<style>
.rounded-4 { border-radius: 1rem !important; }
</style>
{% endblock %} {% endblock %}