mep
This commit is contained in:
parent
e958163a4a
commit
b8a5a1ff58
79 changed files with 15113 additions and 0 deletions
21
templates/components/flash-messages.twig
Normal file
21
templates/components/flash-messages.twig
Normal file
|
@ -0,0 +1,21 @@
|
|||
{% if flash %}
|
||||
<div class="flash-messages">
|
||||
{% for type, messages in flash %}
|
||||
{% for message in messages %}
|
||||
<div class="flash-message flash-{{ type }}">
|
||||
{% if type == 'success' %}
|
||||
🎉🎊 SUCCESS! 🎊🎉 {{ message }} 🥳🙌 WOOHOO! 🌟✨
|
||||
{% elseif type == 'error' %}
|
||||
❌😱 ERROR! 😱❌ {{ message }} 💥😭 OH NO! 💔🔥
|
||||
{% elseif type == 'warning' %}
|
||||
⚠️😬 WARNING! 😬⚠️ {{ message }} 🚨🚧 BE CAREFUL! 🚧🚨
|
||||
{% elseif type == 'info' %}
|
||||
ℹ️🧐 INFO! 🧐ℹ️ {{ message }} 📝💡 GOOD TO KNOW! 💭📊
|
||||
{% else %}
|
||||
💌✨ {{ message }} ✨💌
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endif %}
|
73
templates/components/form.twig
Normal file
73
templates/components/form.twig
Normal file
|
@ -0,0 +1,73 @@
|
|||
{# 🌈✨ Super Amazing Form Components 🎨🎉 #}
|
||||
|
||||
{# 📝 Input Field 📝 #}
|
||||
{% macro input(name, label, value, type = 'text', required = false, placeholder = '', class = '') %}
|
||||
<div class="form-group">
|
||||
<label for="{{ name }}">✨ {{ label }} ✨{% if required %} <span class="required">⭐ REQUIRED ⭐</span>{% endif %}</label>
|
||||
<input type="{{ type }}" id="{{ name }}" name="{{ name }}" value="{{ value|default('') }}"
|
||||
{% if required %}required{% endif %}
|
||||
{% if placeholder %}placeholder="{{ placeholder }} 💫"{% endif %}
|
||||
class="form-control {{ class }}">
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{# 📄 Textarea Field 📄 #}
|
||||
{% macro textarea(name, label, value, required = false, rows = 3, placeholder = '', class = '') %}
|
||||
<div class="form-group">
|
||||
<label for="{{ name }}">📝 {{ label }} 📝{% if required %} <span class="required">⭐ MUST FILL ⭐</span>{% endif %}</label>
|
||||
<textarea id="{{ name }}" name="{{ name }}" rows="{{ rows }}"
|
||||
{% if required %}required{% endif %}
|
||||
{% if placeholder %}placeholder="{{ placeholder }} ✏️"{% endif %}
|
||||
class="form-control {{ class }}">{{ value|default('') }}</textarea>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{# 📋 Select Field 📋 #}
|
||||
{% macro select(name, label, options, selected, required = false, class = '') %}
|
||||
<div class="form-group">
|
||||
<label for="{{ name }}">🔽 {{ label }} 🔽{% if required %} <span class="required">⭐ PICK ONE ⭐</span>{% endif %}</label>
|
||||
<select id="{{ name }}" name="{{ name }}" class="form-control {{ class }}" {% if required %}required{% endif %}>
|
||||
<option value="">-- 🔍 Select {{ label }} 🔎 --</option>
|
||||
{% for value, text in options %}
|
||||
<option value="{{ value }}" {% if selected == value %}selected{% endif %}>✅ {{ text }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{# ✅ Checkbox Field ✅ #}
|
||||
{% macro checkbox(name, label, checked = false, value = '1', class = '') %}
|
||||
<div class="form-check">
|
||||
<input type="checkbox" id="{{ name }}" name="{{ name }}" value="{{ value }}"
|
||||
{% if checked %}checked{% endif %} class="form-check-input {{ class }}">
|
||||
<label class="form-check-label" for="{{ name }}">✅ {{ label }} ✅</label>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{# 🔘 Radio Button 🔘 #}
|
||||
{% macro radio(name, label, value, checked = false, class = '') %}
|
||||
<div class="form-check">
|
||||
<input type="radio" id="{{ name }}_{{ value }}" name="{{ name }}" value="{{ value }}"
|
||||
{% if checked %}checked{% endif %} class="form-check-input {{ class }}">
|
||||
<label class="form-check-label" for="{{ name }}_{{ value }}">🔘 {{ label }} 🔘</label>
|
||||
</div>
|
||||
{% endmacro %}
|
||||
|
||||
{# 🚀 Submit Button 🚀 #}
|
||||
{% macro submit(label = 'Submit', class = 'btn-primary') %}
|
||||
<button type="submit" class="btn {{ class }}">🚀 {{ label }} 🚀</button>
|
||||
{% endmacro %}
|
||||
|
||||
{# 🔳 Button 🔳 #}
|
||||
{% macro button(label, type = 'button', class = 'btn-secondary', attributes = '') %}
|
||||
<button type="{{ type }}" class="btn {{ class }}" {{ attributes|raw }}>🔳 {{ label }} 🔳</button>
|
||||
{% endmacro %}
|
||||
|
||||
{# ⚠️ Form Error Display ⚠️ #}
|
||||
{% macro errors(error) %}
|
||||
{% if error %}
|
||||
<div class="alert alert-danger">
|
||||
⚠️😱 ERROR ALERT! 😱⚠️ {{ error }} 💥💔
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endmacro %}
|
9
templates/components/navigation.twig
Normal file
9
templates/components/navigation.twig
Normal file
|
@ -0,0 +1,9 @@
|
|||
<nav>
|
||||
<ul>
|
||||
<li><a href="/">🏠 Dashboard 📊</a></li>
|
||||
<li><a href="/drink-types">🍹 Drink Types 🍸🥤</a></li>
|
||||
<li><a href="/inventory">📦 Inventory 🗃️🧮</a></li>
|
||||
<li><a href="/orders">🛒 Orders 📝🚚</a></li>
|
||||
<li><a href="/settings">⚙️ Settings 🔧🛠️</a></li>
|
||||
</ul>
|
||||
</nav>
|
145
templates/dashboard/index.twig
Normal file
145
templates/dashboard/index.twig
Normal file
|
@ -0,0 +1,145 @@
|
|||
{% extends "layout.twig" %}
|
||||
|
||||
{% block title %}🏠 Dashboard 📊 - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="dashboard">
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>🎉✨ WELCOME TO THE SUPER AMAZING DRINKS INVENTORY SYSTEM!!! 🍹🥂</h2>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mb-4">
|
||||
<!-- Main Dashboard Content -->
|
||||
<div class="col-lg-8">
|
||||
<!-- Low Stock Alerts -->
|
||||
{% if showLowStockAlerts %}
|
||||
{% include 'dashboard/low-stock-alerts.twig' %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Dashboard Summary Card -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-rainbow text-white">
|
||||
<h5 class="card-title mb-0">🌟 DASHBOARD SUMMARY 🌟</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>🚀 This AWESOME system helps you manage your drinks inventory 🍺, track stock levels 📊, and create orders 📝!!! 🌟</p>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="d-grid">
|
||||
<a href="/drink-types" class="btn btn-primary btn-lg">
|
||||
<i class="fas fa-cocktail me-2"></i>🍸 Manage Drink Types 🍹
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="d-grid">
|
||||
<a href="/inventory" class="btn btn-success btn-lg">
|
||||
<i class="fas fa-boxes me-2"></i>📦 Update Inventory 🔄
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="d-grid">
|
||||
<a href="/orders" class="btn btn-warning btn-lg">
|
||||
<i class="fas fa-shopping-cart me-2"></i>🛒 View Orders 📋
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Getting Started Card -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h5 class="card-title mb-0">🚀 GETTING STARTED 🚀</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ol class="list-group list-group-numbered mb-0">
|
||||
<li class="list-group-item d-flex justify-content-between align-items-start">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">🍹 Add drink types</div>
|
||||
Define your products and set desired stock levels 🎯
|
||||
</div>
|
||||
<a href="/drink-types/create" class="btn btn-sm btn-primary">Add Now</a>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-start">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">📦 Update inventory</div>
|
||||
Keep your stock levels accurate and up-to-date 🔄
|
||||
</div>
|
||||
<a href="/inventory/update" class="btn btn-sm btn-primary">Update</a>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-start">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">🛒 Create orders</div>
|
||||
Order more drinks based on inventory needs 📝
|
||||
</div>
|
||||
<a href="/orders/create" class="btn btn-sm btn-primary">Order</a>
|
||||
</li>
|
||||
<li class="list-group-item d-flex justify-content-between align-items-start">
|
||||
<div class="ms-2 me-auto">
|
||||
<div class="fw-bold">📋 Track orders</div>
|
||||
Update inventory when orders are fulfilled ✅
|
||||
</div>
|
||||
<a href="/orders" class="btn btn-sm btn-primary">View</a>
|
||||
</li>
|
||||
</ol>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Sidebar Content -->
|
||||
<div class="col-lg-4">
|
||||
<!-- Quick Update Form -->
|
||||
{% if showQuickUpdateForm %}
|
||||
{% include 'dashboard/quick-update-form.twig' %}
|
||||
{% endif %}
|
||||
|
||||
<!-- Fun Facts Card -->
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="card-title mb-0">💫 FUN FACTS ABOUT DRINKS!!! 💫</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="list-group">
|
||||
<div class="list-group-item">
|
||||
<i class="fas fa-beer text-warning me-2"></i>🍺 Beer is one of the oldest and most widely consumed alcoholic drinks in the world! 🌍
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<i class="fas fa-wine-glass-alt text-danger me-2"></i>🍷 Wine has been produced for thousands of years! The earliest evidence of wine is from Georgia (6000 BC)! 🕰️
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<i class="fas fa-glass-martini-alt text-info me-2"></i>🍹 The world's most expensive cocktail is the "Diamonds Are Forever" martini, costing $22,600! 💎
|
||||
</div>
|
||||
<div class="list-group-item">
|
||||
<i class="fas fa-glass-whiskey text-success me-2"></i>🥤 The average person will drink about 20,000 beverages in their lifetime! 😮
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Motivation Card -->
|
||||
<div class="card">
|
||||
<div class="card-header bg-pride-purple text-white">
|
||||
<h5 class="card-title mb-0">💪 MOTIVATION FOR TODAY!!! 💪</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-success">
|
||||
<i class="fas fa-star me-2"></i>🌟 Keep your inventory STOCKED and your customers HAPPY! 😄 You're doing AMAZING work! 🎉
|
||||
</div>
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-rocket me-2"></i>🚀 Remember: Every bottle tracked is a problem solved! 🧠 Stay AWESOME and keep CRUSHING it! 💯
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer bg-light">
|
||||
<p class="mb-0 text-center">✨ You're doing GREAT! Keep it up! ✨</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
49
templates/dashboard/low-stock-alerts.twig
Normal file
49
templates/dashboard/low-stock-alerts.twig
Normal file
|
@ -0,0 +1,49 @@
|
|||
{# 🚨 Super Important Low Stock Alerts Component 🚨 #}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-warning text-white">
|
||||
<h5 class="card-title mb-0">🚨 OMG!!! LOW STOCK ALERTS!!! 🚨</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if lowStockItems is empty %}
|
||||
<p class="text-success">✅✅✅ WOOHOO!!! All stock levels are SUPER ADEQUATE!!! 🎉🎊 PARTY TIME!!! 🥳🍾</p>
|
||||
{% else %}
|
||||
<div class="alert alert-danger">
|
||||
<h6>😱 OH NO!!! WE'RE RUNNING OUT OF DRINKS!!! 😱</h6>
|
||||
<p>🆘 EMERGENCY SITUATION!!! 🆘 We need to restock ASAP!!! ⏰⏰⏰</p>
|
||||
</div>
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>🍹 Drink Type 🍸</th>
|
||||
<th>📉 Current Stock 📊</th>
|
||||
<th>🎯 Desired Stock 🏆</th>
|
||||
<th>⚠️ Difference ⚠️</th>
|
||||
<th>🛠️ Actions 🛠️</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in lowStockItems %}
|
||||
<tr>
|
||||
<td>🥤 {{ item.drinkType.name }} 🥤</td>
|
||||
<td>📉 {{ item.currentStock }} 📉</td>
|
||||
<td>🎯 {{ item.desiredStock }} 🎯</td>
|
||||
<td class="text-danger">⚠️ {{ item.currentStock - item.desiredStock }} ⚠️</td>
|
||||
<td>
|
||||
<a href="/inventory/update" class="btn btn-sm btn-primary">🔄 Update Stock NOW!!! 🔄</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<a href="/orders/create-from-stock" class="btn btn-success btn-lg">🚚 CREATE EMERGENCY ORDER NOW!!! 🚨</a>
|
||||
</div>
|
||||
<div class="mt-2">
|
||||
<p class="text-danger">⏰ Don't delay! Your customers are THIRSTY!!! 🥵 Act NOW before it's TOO LATE!!! ⏰</p>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
48
templates/dashboard/quick-update-form.twig
Normal file
48
templates/dashboard/quick-update-form.twig
Normal file
|
@ -0,0 +1,48 @@
|
|||
{# ⚡⚡⚡ SUPER FAST Quick Update Form Component ⚡⚡⚡ #}
|
||||
{% import 'components/form.twig' as form %}
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="card-title mb-0">⚡ LIGHTNING FAST STOCK UPDATE!!! ⚡</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="/inventory/update" method="post">
|
||||
{{ form.errors(error) }}
|
||||
|
||||
<div class="alert alert-info">
|
||||
<h6>🏎️ ZOOM ZOOM!!! 🏎️</h6>
|
||||
<p>⏱️ Update your stock in SECONDS!!! ⏱️ SO FAST!!! SO EASY!!! 🤩</p>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="drink_type_id">🍹 CHOOSE YOUR DRINK!!! 🍸🥤</label>
|
||||
<select id="drink_type_id" name="drink_type_id" class="form-control" required>
|
||||
<option value="">-- 🔍 SELECT A SUPER AWESOME DRINK 🔎 --</option>
|
||||
{% for drinkType in drinkTypes %}
|
||||
<option value="{{ drinkType.id }}">🥂 {{ drinkType.name }} 🍾</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label for="quantity">🔢 HOW MANY DO YOU HAVE??? 📊📈</label>
|
||||
<input type="number" id="quantity" name="quantity" class="form-control" min="0" required>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mt-3">
|
||||
<button type="submit" class="btn btn-primary btn-lg">🚀 UPDATE STOCK NOW!!! 🚀</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<small class="text-muted">⚡ This AMAZING form updates the current stock level for the selected drink type IN SECONDS!!! ⚡ WOW!!! 😲</small>
|
||||
</div>
|
||||
<div class="card-footer bg-light">
|
||||
<p class="mb-0 text-center">💯 PRODUCTIVITY TIP: Update your stock DAILY for MAXIMUM EFFICIENCY!!! 💯</p>
|
||||
</div>
|
||||
</div>
|
36
templates/drink-types/create.twig
Normal file
36
templates/drink-types/create.twig
Normal file
|
@ -0,0 +1,36 @@
|
|||
{% extends 'layout.twig' %}
|
||||
{% import 'components/form.twig' as form %}
|
||||
|
||||
{% block title %}Add New Drink Type - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Add New Drink Type</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="/drink-types" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to List
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{{ form.errors(error) }}
|
||||
|
||||
<form action="/drink-types" method="post">
|
||||
{{ form.input('name', 'Name', data.name|default(''), 'text', true) }}
|
||||
|
||||
{{ form.textarea('description', 'Description', data.description|default(''), false, 3, 'Enter a description of the drink type') }}
|
||||
|
||||
{{ form.input('desired_stock', 'Desired Stock', data.desired_stock|default('10'), 'number', true, '', 'form-control-sm') }}
|
||||
|
||||
<div class="form-group mt-4">
|
||||
{{ form.submit('Create Drink Type') }}
|
||||
<a href="/drink-types" class="btn btn-link">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
36
templates/drink-types/edit.twig
Normal file
36
templates/drink-types/edit.twig
Normal file
|
@ -0,0 +1,36 @@
|
|||
{% extends 'layout.twig' %}
|
||||
{% import 'components/form.twig' as form %}
|
||||
|
||||
{% block title %}Edit Drink Type - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Edit Drink Type</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="/drink-types" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to List
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{{ form.errors(error) }}
|
||||
|
||||
<form action="/drink-types/{{ drinkType.id }}" method="post">
|
||||
{{ form.input('name', 'Name', drinkType.name, 'text', true) }}
|
||||
|
||||
{{ form.textarea('description', 'Description', drinkType.description, false, 3, 'Enter a description of the drink type') }}
|
||||
|
||||
{{ form.input('desired_stock', 'Desired Stock', drinkType.desiredStock, 'number', true, '', 'form-control-sm') }}
|
||||
|
||||
<div class="form-group mt-4">
|
||||
{{ form.submit('Update Drink Type') }}
|
||||
<a href="/drink-types/{{ drinkType.id }}" class="btn btn-link">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
73
templates/drink-types/index.twig
Normal file
73
templates/drink-types/index.twig
Normal file
|
@ -0,0 +1,73 @@
|
|||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block title %}Drink Types - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Drink Types</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="/drink-types/create" class="btn btn-success">
|
||||
<i class="fas fa-plus"></i> Add New Drink Type
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if drinkTypes is empty %}
|
||||
<div class="alert alert-info">
|
||||
No drink types found. Click the button above to add your first drink type.
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Name</th>
|
||||
<th>Description</th>
|
||||
<th>Desired Stock</th>
|
||||
<th>Current Stock</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for drinkType in drinkTypes %}
|
||||
<tr>
|
||||
<td>{{ drinkType.id }}</td>
|
||||
<td>{{ drinkType.name }}</td>
|
||||
<td>{{ drinkType.description|default('-') }}</td>
|
||||
<td>{{ drinkType.desiredStock }}</td>
|
||||
<td>
|
||||
{% if drinkType.currentStock is defined %}
|
||||
{% if drinkType.currentStock < drinkType.desiredStock %}
|
||||
<span class="text-danger">{{ drinkType.currentStock }}</span>
|
||||
{% else %}
|
||||
<span class="text-success">{{ drinkType.currentStock }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
-
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group" role="group">
|
||||
<a href="/drink-types/{{ drinkType.id }}" class="btn btn-sm btn-info">
|
||||
<i class="fas fa-eye"></i> View
|
||||
</a>
|
||||
<a href="/drink-types/{{ drinkType.id }}/edit" class="btn btn-sm btn-primary">
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</a>
|
||||
<form action="/drink-types/{{ drinkType.id }}/delete" method="post" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this drink type?');">
|
||||
<button type="submit" class="btn btn-sm btn-danger">
|
||||
<i class="fas fa-trash"></i> Delete
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
141
templates/drink-types/show.twig
Normal file
141
templates/drink-types/show.twig
Normal file
|
@ -0,0 +1,141 @@
|
|||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block title %}{{ drinkType.name }} - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Drink Type Details</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="/drink-types" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to List
|
||||
</a>
|
||||
<a href="/drink-types/{{ drinkType.id }}/edit" class="btn btn-primary">
|
||||
<i class="fas fa-edit"></i> Edit
|
||||
</a>
|
||||
<form action="/drink-types/{{ drinkType.id }}/delete" method="post" class="d-inline" onsubmit="return confirm('Are you sure you want to delete this drink type?');">
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<i class="fas fa-trash"></i> Delete
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Basic Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th style="width: 30%">ID</th>
|
||||
<td>{{ drinkType.id }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<td>{{ drinkType.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<td>{{ drinkType.description|default('No description provided') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Desired Stock</th>
|
||||
<td>{{ drinkType.desiredStock }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Current Stock</th>
|
||||
<td>
|
||||
{% if currentStock is defined %}
|
||||
{% if currentStock < drinkType.desiredStock %}
|
||||
<span class="text-danger">{{ currentStock }}</span>
|
||||
{% else %}
|
||||
<span class="text-success">{{ currentStock }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
Not available
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Created At</th>
|
||||
<td>{{ drinkType.createdAt|date('Y-m-d H:i:s') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Updated At</th>
|
||||
<td>{{ drinkType.updatedAt|date('Y-m-d H:i:s') }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Stock History</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if inventoryRecords is defined and inventoryRecords is not empty %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-sm">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
<th>Quantity</th>
|
||||
<th>Change</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for record in inventoryRecords %}
|
||||
<tr>
|
||||
<td>{{ record.createdAt|date('Y-m-d H:i:s') }}</td>
|
||||
<td>{{ record.quantity }}</td>
|
||||
<td>
|
||||
{% if record.change > 0 %}
|
||||
<span class="text-success">+{{ record.change }}</span>
|
||||
{% elseif record.change < 0 %}
|
||||
<span class="text-danger">{{ record.change }}</span>
|
||||
{% else %}
|
||||
<span class="text-muted">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
<a href="/inventory/history/{{ drinkType.id }}" class="btn btn-sm btn-info">
|
||||
View Full History
|
||||
</a>
|
||||
</div>
|
||||
{% else %}
|
||||
<p class="text-muted">No inventory records found for this drink type.</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Quick Actions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="/inventory/update" class="btn btn-primary">
|
||||
<i class="fas fa-sync"></i> Update Stock
|
||||
</a>
|
||||
<a href="/orders/create" class="btn btn-success">
|
||||
<i class="fas fa-shopping-cart"></i> Create Order
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
25
templates/error.twig
Normal file
25
templates/error.twig
Normal file
|
@ -0,0 +1,25 @@
|
|||
{% extends "layout.twig" %}
|
||||
|
||||
{% block title %}Error {{ status_code }} - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="error-container">
|
||||
<h2>Error {{ status_code }}</h2>
|
||||
|
||||
<div class="error-message">
|
||||
<p>{{ error.message }}</p>
|
||||
</div>
|
||||
|
||||
{% if error.trace is defined %}
|
||||
<div class="error-details">
|
||||
<h3>Error Details</h3>
|
||||
<p>File: {{ error.file }} (line {{ error.line }})</p>
|
||||
<pre>{{ error.trace }}</pre>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="error-actions">
|
||||
<a href="/" class="btn">Return to Dashboard</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
172
templates/inventory/history.twig
Normal file
172
templates/inventory/history.twig
Normal file
|
@ -0,0 +1,172 @@
|
|||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block title %}Inventory History - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>
|
||||
{% if drinkType is defined %}
|
||||
Inventory History for {{ drinkType.name }}
|
||||
{% else %}
|
||||
Inventory History
|
||||
{% endif %}
|
||||
</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="/inventory" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Inventory
|
||||
</a>
|
||||
{% if drinkType is defined %}
|
||||
<a href="/inventory/history" class="btn btn-info">
|
||||
<i class="fas fa-list"></i> All History
|
||||
</a>
|
||||
{% endif %}
|
||||
<a href="/inventory/update" class="btn btn-primary">
|
||||
<i class="fas fa-sync"></i> Update Stock
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if inventoryRecords is empty %}
|
||||
<div class="alert alert-info">
|
||||
No inventory records found.
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h5 class="card-title mb-0">Stock Change History</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" id="filterDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Filter
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="filterDropdown">
|
||||
<li><a class="dropdown-item" href="/inventory/history">All Records</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><h6 class="dropdown-header">By Drink Type</h6></li>
|
||||
{% for type in drinkTypes|default([]) %}
|
||||
<li><a class="dropdown-item" href="/inventory/history/{{ type.id }}">{{ type.name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Date</th>
|
||||
{% if drinkType is not defined %}
|
||||
<th>Drink Type</th>
|
||||
{% endif %}
|
||||
<th>Previous Quantity</th>
|
||||
<th>New Quantity</th>
|
||||
<th>Change</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for record in inventoryRecords %}
|
||||
<tr>
|
||||
<td>{{ record.createdAt|date('Y-m-d H:i:s') }}</td>
|
||||
{% if drinkType is not defined %}
|
||||
<td>
|
||||
<a href="/drink-types/{{ record.drinkType.id }}">
|
||||
{{ record.drinkType.name }}
|
||||
</a>
|
||||
</td>
|
||||
{% endif %}
|
||||
<td>{{ record.previousQuantity|default('-') }}</td>
|
||||
<td>{{ record.quantity }}</td>
|
||||
<td>
|
||||
{% if record.change is defined %}
|
||||
{% if record.change > 0 %}
|
||||
<span class="text-success">+{{ record.change }}</span>
|
||||
{% elseif record.change < 0 %}
|
||||
<span class="text-danger">{{ record.change }}</span>
|
||||
{% else %}
|
||||
<span class="text-muted">0</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<span class="text-muted">Initial</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<small class="text-muted">Showing {{ inventoryRecords|length }} records</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if drinkType is defined %}
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Stock Level Trend</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="text-muted">Stock level trend visualization would be displayed here.</p>
|
||||
<div class="alert alert-info">
|
||||
<i class="fas fa-info-circle"></i> This is a placeholder for a chart showing the stock level trend over time.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Drink Type Information</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th style="width: 30%">Name</th>
|
||||
<td>{{ drinkType.name }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Description</th>
|
||||
<td>{{ drinkType.description|default('No description provided') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Desired Stock</th>
|
||||
<td>{{ drinkType.desiredStock }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Current Stock</th>
|
||||
<td>
|
||||
{% if drinkType.currentStock is defined %}
|
||||
{% if drinkType.currentStock < drinkType.desiredStock %}
|
||||
<span class="text-danger">{{ drinkType.currentStock }}</span>
|
||||
{% else %}
|
||||
<span class="text-success">{{ drinkType.currentStock }}</span>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
Not available
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div class="mt-3">
|
||||
<a href="/drink-types/{{ drinkType.id }}" class="btn btn-sm btn-info">
|
||||
<i class="fas fa-eye"></i> View Full Details
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% endblock %}
|
175
templates/inventory/index.twig
Normal file
175
templates/inventory/index.twig
Normal file
|
@ -0,0 +1,175 @@
|
|||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block title %}Inventory Overview - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Inventory Overview</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="/inventory/update" class="btn btn-primary">
|
||||
<i class="fas fa-sync"></i> Update Stock
|
||||
</a>
|
||||
<a href="/inventory/history" class="btn btn-info">
|
||||
<i class="fas fa-history"></i> View History
|
||||
</a>
|
||||
<a href="/orders/create-from-stock" class="btn btn-success">
|
||||
<i class="fas fa-shopping-cart"></i> Create Order
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if stockLevels is empty %}
|
||||
<div class="alert alert-info">
|
||||
No drink types found in inventory. <a href="/drink-types/create">Add a drink type</a> to get started.
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Current Stock Levels</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Drink Type</th>
|
||||
<th>Current Stock</th>
|
||||
<th>Desired Stock</th>
|
||||
<th>Difference</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in stockLevels %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/drink-types/{{ item.drinkType.id }}">
|
||||
{{ item.drinkType.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ item.currentStock }}</td>
|
||||
<td>{{ item.drinkType.desiredStock }}</td>
|
||||
<td>
|
||||
{% set difference = item.currentStock - item.drinkType.desiredStock %}
|
||||
{% if difference < 0 %}
|
||||
<span class="text-danger">{{ difference }}</span>
|
||||
{% elseif difference > 0 %}
|
||||
<span class="text-success">+{{ difference }}</span>
|
||||
{% else %}
|
||||
<span class="text-muted">0</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
{% if item.currentStock < item.drinkType.desiredStock %}
|
||||
<span class="badge bg-danger">Low Stock</span>
|
||||
{% elseif item.currentStock == item.drinkType.desiredStock %}
|
||||
<span class="badge bg-success">Optimal</span>
|
||||
{% else %}
|
||||
<span class="badge bg-info">Excess</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<a href="/inventory/history/{{ item.drinkType.id }}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-history"></i>
|
||||
</a>
|
||||
<a href="/inventory/update" class="btn btn-outline-primary">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="card-title mb-0">Inventory Summary</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% set totalItems = stockLevels|length %}
|
||||
{% set lowStockCount = 0 %}
|
||||
{% set optimalCount = 0 %}
|
||||
{% set excessCount = 0 %}
|
||||
|
||||
{% for item in stockLevels %}
|
||||
{% if item.currentStock < item.drinkType.desiredStock %}
|
||||
{% set lowStockCount = lowStockCount + 1 %}
|
||||
{% elseif item.currentStock == item.drinkType.desiredStock %}
|
||||
{% set optimalCount = optimalCount + 1 %}
|
||||
{% else %}
|
||||
{% set excessCount = excessCount + 1 %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<div class="mb-3">
|
||||
<h6>Total Drink Types: {{ totalItems }}</h6>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>Low Stock:</span>
|
||||
<span class="text-danger">{{ lowStockCount }}</span>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-danger" role="progressbar" style="width: {{ (lowStockCount / totalItems * 100)|round }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>Optimal Stock:</span>
|
||||
<span class="text-success">{{ optimalCount }}</span>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-success" role="progressbar" style="width: {{ (optimalCount / totalItems * 100)|round }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between">
|
||||
<span>Excess Stock:</span>
|
||||
<span class="text-info">{{ excessCount }}</span>
|
||||
</div>
|
||||
<div class="progress">
|
||||
<div class="progress-bar bg-info" role="progressbar" style="width: {{ (excessCount / totalItems * 100)|round }}%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header bg-success text-white">
|
||||
<h5 class="card-title mb-0">Quick Actions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="/inventory/update" class="btn btn-primary">
|
||||
<i class="fas fa-sync"></i> Update Stock
|
||||
</a>
|
||||
<a href="/orders/create-from-stock" class="btn btn-success">
|
||||
<i class="fas fa-shopping-cart"></i> Create Order from Stock Levels
|
||||
</a>
|
||||
<a href="/inventory/low-stock" class="btn btn-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i> View Low Stock Items
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
159
templates/inventory/low-stock.twig
Normal file
159
templates/inventory/low-stock.twig
Normal file
|
@ -0,0 +1,159 @@
|
|||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block title %}Low Stock Items - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Low Stock Items</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="/inventory" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Inventory
|
||||
</a>
|
||||
<a href="/inventory/update" class="btn btn-primary">
|
||||
<i class="fas fa-sync"></i> Update Stock
|
||||
</a>
|
||||
<a href="/orders/create-from-stock" class="btn btn-success">
|
||||
<i class="fas fa-shopping-cart"></i> Create Order
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if lowStockItems is empty %}
|
||||
<div class="alert alert-success">
|
||||
<i class="fas fa-check-circle"></i> All items are at or above their desired stock levels.
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<div class="card-header bg-warning text-white">
|
||||
<h5 class="card-title mb-0">Items Below Desired Stock Levels</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Drink Type</th>
|
||||
<th>Current Stock</th>
|
||||
<th>Desired Stock</th>
|
||||
<th>Difference</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in lowStockItems %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/drink-types/{{ item.drinkType.id }}">
|
||||
{{ item.drinkType.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ item.currentStock }}</td>
|
||||
<td>{{ item.drinkType.desiredStock }}</td>
|
||||
<td class="text-danger">{{ item.currentStock - item.drinkType.desiredStock }}</td>
|
||||
<td>
|
||||
{% set percentage = (item.currentStock / item.drinkType.desiredStock * 100)|round %}
|
||||
{% if percentage < 25 %}
|
||||
<span class="badge bg-danger">Critical ({{ percentage }}%)</span>
|
||||
{% elseif percentage < 50 %}
|
||||
<span class="badge bg-warning">Low ({{ percentage }}%)</span>
|
||||
{% else %}
|
||||
<span class="badge bg-info">Below Target ({{ percentage }}%)</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<a href="/inventory/history/{{ item.drinkType.id }}" class="btn btn-outline-secondary">
|
||||
<i class="fas fa-history"></i>
|
||||
</a>
|
||||
<a href="/inventory/update" class="btn btn-outline-primary">
|
||||
<i class="fas fa-edit"></i>
|
||||
</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<div class="d-flex justify-content-between align-items-center">
|
||||
<small class="text-muted">Showing {{ lowStockItems|length }} items below desired stock levels</small>
|
||||
<a href="/orders/create-from-stock" class="btn btn-success">
|
||||
<i class="fas fa-shopping-cart"></i> Create Order from Stock Levels
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Recommended Actions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="list-group">
|
||||
<li class="list-group-item">
|
||||
<i class="fas fa-shopping-cart text-success"></i> Create an order to replenish stock
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<i class="fas fa-chart-line text-info"></i> Review consumption patterns
|
||||
</li>
|
||||
<li class="list-group-item">
|
||||
<i class="fas fa-cog text-primary"></i> Adjust desired stock levels if needed
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Stock Level Summary</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i> {{ lowStockItems|length }} items are below their desired stock levels.
|
||||
</div>
|
||||
|
||||
{% set criticalCount = 0 %}
|
||||
{% set lowCount = 0 %}
|
||||
{% set belowTargetCount = 0 %}
|
||||
|
||||
{% for item in lowStockItems %}
|
||||
{% set percentage = (item.currentStock / item.drinkType.desiredStock * 100)|round %}
|
||||
{% if percentage < 25 %}
|
||||
{% set criticalCount = criticalCount + 1 %}
|
||||
{% elseif percentage < 50 %}
|
||||
{% set lowCount = lowCount + 1 %}
|
||||
{% else %}
|
||||
{% set belowTargetCount = belowTargetCount + 1 %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<div class="mt-3">
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span>Critical (< 25%):</span>
|
||||
<span class="text-danger">{{ criticalCount }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span>Low (25-50%):</span>
|
||||
<span class="text-warning">{{ lowCount }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span>Below Target (> 50%):</span>
|
||||
<span class="text-info">{{ belowTargetCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
88
templates/inventory/update.twig
Normal file
88
templates/inventory/update.twig
Normal file
|
@ -0,0 +1,88 @@
|
|||
{% extends 'layout.twig' %}
|
||||
{% import 'components/form.twig' as form %}
|
||||
|
||||
{% block title %}Update Stock - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Update Stock Levels</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<a href="/inventory" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Inventory
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Update Single Item</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{{ form.errors(error) }}
|
||||
|
||||
<form action="/inventory/update" method="post">
|
||||
<div class="form-group mb-3">
|
||||
<label for="drink_type_id">Drink Type</label>
|
||||
<select id="drink_type_id" name="drink_type_id" class="form-control" required>
|
||||
<option value="">-- Select Drink Type --</option>
|
||||
{% for drinkType in drinkTypes %}
|
||||
<option value="{{ drinkType.id }}">{{ drinkType.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="quantity">New Quantity</label>
|
||||
<input type="number" id="quantity" name="quantity" class="form-control" min="0" required>
|
||||
<small class="form-text text-muted">Enter the new total quantity, not the amount to add/remove.</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
{{ form.submit('Update Stock') }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header bg-info text-white">
|
||||
<h5 class="card-title mb-0">Stock Update Tips</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<h6 class="alert-heading">How to update stock:</h6>
|
||||
<ol>
|
||||
<li>Select the drink type you want to update</li>
|
||||
<li>Enter the <strong>total</strong> current quantity (not the difference)</li>
|
||||
<li>Click "Update Stock" to save the changes</li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-warning">
|
||||
<h6 class="alert-heading">Important Notes:</h6>
|
||||
<ul>
|
||||
<li>Stock updates are logged in the inventory history</li>
|
||||
<li>The system will calculate the difference from the previous stock level</li>
|
||||
<li>If stock falls below desired levels, it will appear in low stock alerts</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<h6>Quick Links:</h6>
|
||||
<ul>
|
||||
<li><a href="/inventory/history">View inventory history</a></li>
|
||||
<li><a href="/inventory/low-stock">View low stock items</a></li>
|
||||
<li><a href="/orders/create-from-stock">Create order from stock levels</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
70
templates/layout.twig
Normal file
70
templates/layout.twig
Normal file
|
@ -0,0 +1,70 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>🍻🥂 {% block title %}Saufen{% endblock %} 🍹🥤</title>
|
||||
<!-- Bootstrap CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
|
||||
<!-- Font Awesome -->
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.2/css/all.min.css" integrity="sha512-z3gLpd7yknf1YoNbCzqRKc4qyor8gaKU1qmn+CShxbuBusANI9QpRohGBreCFkKxLhei6S9CQXFEbbKuqLg0DA==" crossorigin="anonymous" referrerpolicy="no-referrer" />
|
||||
<!-- Custom styles -->
|
||||
<link rel="stylesheet" href="/assets/css/styles.css">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/">🍺🍷 {{ appName }} 🍸🥃</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarMain" aria-controls="navbarMain" aria-expanded="false" aria-label="Toggle navigation">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarMain">
|
||||
<ul class="navbar-nav ms-auto mb-2 mb-lg-0">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/">🏠 Dashboard 📊</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/drink-types">🍹 Drink Types 🍸🥤</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/inventory">📦 Inventory 🗃️🧮</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/orders">🛒 Orders 📝🚚</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/settings">⚙️ Settings 🔧🛠️</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
</header>
|
||||
|
||||
<main class="container py-4">
|
||||
{% include 'components/flash-messages.twig' %}
|
||||
|
||||
{% block content %}{% endblock %}
|
||||
</main>
|
||||
|
||||
<footer class="bg-dark text-white py-4 mt-5">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<p class="mb-0">🌟✨ © {{ "now"|date("Y") }} 🎉 {{ appName }} 🎊</p>
|
||||
</div>
|
||||
<div class="col-md-6 text-md-end">
|
||||
<p class="mb-0">Made with 💖 and 🍺 ✨🌟</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<!-- Bootstrap Bundle with Popper -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
|
||||
<!-- Custom JavaScript -->
|
||||
<script src="/assets/js/app.js"></script>
|
||||
{% block scripts %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
204
templates/orders/create.twig
Normal file
204
templates/orders/create.twig
Normal file
|
@ -0,0 +1,204 @@
|
|||
{% extends 'layout.twig' %}
|
||||
{% import 'components/form.twig' as form %}
|
||||
|
||||
{% block title %}Create Order - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Create New Order</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="/orders" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Orders
|
||||
</a>
|
||||
<a href="/orders/create-from-stock" class="btn btn-primary">
|
||||
<i class="fas fa-magic"></i> Create from Stock Levels
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{{ form.errors(error) }}
|
||||
|
||||
<form action="/orders" method="post" id="orderForm">
|
||||
<div class="order-items">
|
||||
<div class="row mb-3">
|
||||
<div class="col">
|
||||
<h5>Order Items</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-sm btn-success add-item-btn">
|
||||
<i class="fas fa-plus"></i> Add Item
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="order-item-template d-none">
|
||||
<div class="card mb-3 order-item">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>Drink Type</label>
|
||||
<select name="items[{index}][drink_type_id]" class="form-control drink-type-select" required>
|
||||
<option value="">-- Select Drink Type --</option>
|
||||
{% for drinkType in drinkTypes %}
|
||||
<option value="{{ drinkType.id }}" data-desired-stock="{{ drinkType.desiredStock }}" data-current-stock="{{ drinkType.currentStock|default(0) }}">
|
||||
{{ drinkType.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="form-group">
|
||||
<label>Quantity</label>
|
||||
<input type="number" name="items[{index}][quantity]" class="form-control quantity-input" min="1" required>
|
||||
<small class="form-text text-muted stock-info"></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1 d-flex align-items-end">
|
||||
<button type="button" class="btn btn-sm btn-danger remove-item-btn mb-2">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="order-items-container">
|
||||
<!-- Order items will be added here -->
|
||||
<div class="card mb-3 order-item">
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<div class="form-group">
|
||||
<label>Drink Type</label>
|
||||
<select name="items[0][drink_type_id]" class="form-control drink-type-select" required>
|
||||
<option value="">-- Select Drink Type --</option>
|
||||
{% for drinkType in drinkTypes %}
|
||||
<option value="{{ drinkType.id }}" data-desired-stock="{{ drinkType.desiredStock }}" data-current-stock="{{ drinkType.currentStock|default(0) }}">
|
||||
{{ drinkType.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-5">
|
||||
<div class="form-group">
|
||||
<label>Quantity</label>
|
||||
<input type="number" name="items[0][quantity]" class="form-control quantity-input" min="1" required>
|
||||
<small class="form-text text-muted stock-info"></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-1 d-flex align-items-end">
|
||||
<button type="button" class="btn btn-sm btn-danger remove-item-btn mb-2" disabled>
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info d-none no-items-alert">
|
||||
<i class="fas fa-info-circle"></i> Please add at least one item to the order.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mt-4">
|
||||
{{ form.submit('Create Order') }}
|
||||
<a href="/orders" class="btn btn-link">Cancel</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const orderItemsContainer = document.querySelector('.order-items-container');
|
||||
const orderItemTemplate = document.querySelector('.order-item-template');
|
||||
const addItemBtn = document.querySelector('.add-item-btn');
|
||||
const noItemsAlert = document.querySelector('.no-items-alert');
|
||||
|
||||
// Add item button click handler
|
||||
addItemBtn.addEventListener('click', function() {
|
||||
const newIndex = document.querySelectorAll('.order-item').length;
|
||||
const newItem = orderItemTemplate.querySelector('.order-item').cloneNode(true);
|
||||
|
||||
// Update name attributes with the correct index
|
||||
newItem.querySelectorAll('[name]').forEach(input => {
|
||||
input.name = input.name.replace('{index}', newIndex);
|
||||
});
|
||||
|
||||
// Enable the remove button
|
||||
newItem.querySelector('.remove-item-btn').disabled = false;
|
||||
|
||||
// Add the new item to the container
|
||||
orderItemsContainer.appendChild(newItem);
|
||||
|
||||
// Hide the no items alert
|
||||
noItemsAlert.classList.add('d-none');
|
||||
|
||||
// Add event listeners to the new item
|
||||
addItemEventListeners(newItem);
|
||||
});
|
||||
|
||||
// Add event listeners to initial item
|
||||
addItemEventListeners(orderItemsContainer.querySelector('.order-item'));
|
||||
|
||||
// Form submission handler
|
||||
document.getElementById('orderForm').addEventListener('submit', function(e) {
|
||||
const items = document.querySelectorAll('.order-item');
|
||||
|
||||
if (items.length === 0) {
|
||||
e.preventDefault();
|
||||
noItemsAlert.classList.remove('d-none');
|
||||
}
|
||||
});
|
||||
|
||||
// Function to add event listeners to an item
|
||||
function addItemEventListeners(item) {
|
||||
// Remove item button click handler
|
||||
item.querySelector('.remove-item-btn').addEventListener('click', function() {
|
||||
if (document.querySelectorAll('.order-item').length > 1) {
|
||||
item.remove();
|
||||
} else {
|
||||
noItemsAlert.classList.remove('d-none');
|
||||
}
|
||||
});
|
||||
|
||||
// Drink type select change handler
|
||||
item.querySelector('.drink-type-select').addEventListener('change', function() {
|
||||
const option = this.options[this.selectedIndex];
|
||||
const stockInfo = item.querySelector('.stock-info');
|
||||
const quantityInput = item.querySelector('.quantity-input');
|
||||
|
||||
if (option.value) {
|
||||
const desiredStock = parseInt(option.dataset.desiredStock);
|
||||
const currentStock = parseInt(option.dataset.currentStock);
|
||||
const difference = desiredStock - currentStock;
|
||||
|
||||
if (difference > 0) {
|
||||
stockInfo.textContent = `Current: ${currentStock}, Desired: ${desiredStock}, Suggested order: ${difference}`;
|
||||
quantityInput.value = difference;
|
||||
} else {
|
||||
stockInfo.textContent = `Current: ${currentStock}, Desired: ${desiredStock}, Stock is adequate`;
|
||||
quantityInput.value = '';
|
||||
}
|
||||
} else {
|
||||
stockInfo.textContent = '';
|
||||
quantityInput.value = '';
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
179
templates/orders/index.twig
Normal file
179
templates/orders/index.twig
Normal file
|
@ -0,0 +1,179 @@
|
|||
{% extends 'layout.twig' %}
|
||||
|
||||
{% block title %}Orders - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Orders</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="/orders/create" class="btn btn-success">
|
||||
<i class="fas fa-plus"></i> Create Order
|
||||
</a>
|
||||
<a href="/orders/create-from-stock" class="btn btn-primary">
|
||||
<i class="fas fa-magic"></i> Create from Stock Levels
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if orders is empty %}
|
||||
<div class="alert alert-info">
|
||||
No orders found. Click one of the buttons above to create your first order.
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<h5 class="card-title mb-0">Order List</h5>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-sm btn-outline-secondary dropdown-toggle" type="button" id="filterDropdown" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
Filter
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="filterDropdown">
|
||||
<li><a class="dropdown-item" href="/orders">All Orders</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li><h6 class="dropdown-header">By Status</h6></li>
|
||||
<li><a class="dropdown-item" href="/orders?status=pending">Pending</a></li>
|
||||
<li><a class="dropdown-item" href="/orders?status=completed">Completed</a></li>
|
||||
<li><a class="dropdown-item" href="/orders?status=cancelled">Cancelled</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>Date</th>
|
||||
<th>Items</th>
|
||||
<th>Total Quantity</th>
|
||||
<th>Status</th>
|
||||
<th>Actions</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for order in orders %}
|
||||
<tr>
|
||||
<td>{{ order.id }}</td>
|
||||
<td>{{ order.createdAt|date('Y-m-d H:i:s') }}</td>
|
||||
<td>{{ order.orderItems|length }}</td>
|
||||
<td>
|
||||
{% set totalQuantity = 0 %}
|
||||
{% for item in order.orderItems %}
|
||||
{% set totalQuantity = totalQuantity + item.quantity %}
|
||||
{% endfor %}
|
||||
{{ totalQuantity }}
|
||||
</td>
|
||||
<td>
|
||||
{% if order.status == 'pending' %}
|
||||
<span class="badge bg-warning">Pending</span>
|
||||
{% elseif order.status == 'completed' %}
|
||||
<span class="badge bg-success">Completed</span>
|
||||
{% elseif order.status == 'cancelled' %}
|
||||
<span class="badge bg-danger">Cancelled</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ order.status }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-sm" role="group">
|
||||
<a href="/orders/{{ order.id }}" class="btn btn-outline-info">
|
||||
<i class="fas fa-eye"></i>
|
||||
</a>
|
||||
{% if order.status == 'pending' %}
|
||||
<form action="/orders/{{ order.id }}/status" method="post" class="d-inline">
|
||||
<input type="hidden" name="status" value="completed">
|
||||
<button type="submit" class="btn btn-outline-success" title="Mark as Completed">
|
||||
<i class="fas fa-check"></i>
|
||||
</button>
|
||||
</form>
|
||||
<form action="/orders/{{ order.id }}/status" method="post" class="d-inline">
|
||||
<input type="hidden" name="status" value="cancelled">
|
||||
<button type="submit" class="btn btn-outline-danger" title="Cancel Order">
|
||||
<i class="fas fa-times"></i>
|
||||
</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-footer">
|
||||
<small class="text-muted">Showing {{ orders|length }} orders</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Order Statistics</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% set pendingCount = 0 %}
|
||||
{% set completedCount = 0 %}
|
||||
{% set cancelledCount = 0 %}
|
||||
|
||||
{% for order in orders %}
|
||||
{% if order.status == 'pending' %}
|
||||
{% set pendingCount = pendingCount + 1 %}
|
||||
{% elseif order.status == 'completed' %}
|
||||
{% set completedCount = completedCount + 1 %}
|
||||
{% elseif order.status == 'cancelled' %}
|
||||
{% set cancelledCount = cancelledCount + 1 %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
<div class="mb-3">
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span>Pending Orders:</span>
|
||||
<span class="text-warning">{{ pendingCount }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span>Completed Orders:</span>
|
||||
<span class="text-success">{{ completedCount }}</span>
|
||||
</div>
|
||||
<div class="d-flex justify-content-between mb-1">
|
||||
<span>Cancelled Orders:</span>
|
||||
<span class="text-danger">{{ cancelledCount }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h5 class="card-title mb-0">Quick Actions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="/orders/create" class="btn btn-success">
|
||||
<i class="fas fa-plus"></i> Create New Order
|
||||
</a>
|
||||
<a href="/orders/create-from-stock" class="btn btn-primary">
|
||||
<i class="fas fa-magic"></i> Create Order from Stock Levels
|
||||
</a>
|
||||
<a href="/inventory" class="btn btn-info">
|
||||
<i class="fas fa-boxes"></i> View Inventory
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
252
templates/orders/show.twig
Normal file
252
templates/orders/show.twig
Normal file
|
@ -0,0 +1,252 @@
|
|||
{% extends 'layout.twig' %}
|
||||
{% import 'components/form.twig' as form %}
|
||||
|
||||
{% block title %}Order #{{ order.id }} - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>Order #{{ order.id }}</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<div class="btn-group" role="group">
|
||||
<a href="/orders" class="btn btn-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Back to Orders
|
||||
</a>
|
||||
{% if order.status == 'pending' %}
|
||||
<button type="button" class="btn btn-success" data-bs-toggle="modal" data-bs-target="#completeOrderModal">
|
||||
<i class="fas fa-check"></i> Complete Order
|
||||
</button>
|
||||
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#cancelOrderModal">
|
||||
<i class="fas fa-times"></i> Cancel Order
|
||||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-8">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Order Items</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if order.orderItems is empty %}
|
||||
<div class="alert alert-warning">
|
||||
<i class="fas fa-exclamation-triangle"></i> This order has no items.
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Drink Type</th>
|
||||
<th>Quantity</th>
|
||||
{% if order.status == 'pending' %}
|
||||
<th>Actions</th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for item in order.orderItems %}
|
||||
<tr>
|
||||
<td>
|
||||
<a href="/drink-types/{{ item.drinkType.id }}">
|
||||
{{ item.drinkType.name }}
|
||||
</a>
|
||||
</td>
|
||||
<td>{{ item.quantity }}</td>
|
||||
{% if order.status == 'pending' %}
|
||||
<td>
|
||||
<form action="/orders/{{ order.id }}/items/{{ item.id }}/remove" method="post" class="d-inline" onsubmit="return confirm('Are you sure you want to remove this item?');">
|
||||
<button type="submit" class="btn btn-sm btn-danger">
|
||||
<i class="fas fa-trash"></i> Remove
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
{% endif %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr class="table-active">
|
||||
<th>Total</th>
|
||||
<th>
|
||||
{% set totalQuantity = 0 %}
|
||||
{% for item in order.orderItems %}
|
||||
{% set totalQuantity = totalQuantity + item.quantity %}
|
||||
{% endfor %}
|
||||
{{ totalQuantity }}
|
||||
</th>
|
||||
{% if order.status == 'pending' %}
|
||||
<th></th>
|
||||
{% endif %}
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if order.status == 'pending' %}
|
||||
<div class="mt-4">
|
||||
<button type="button" class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#addItemModal">
|
||||
<i class="fas fa-plus"></i> Add Item
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="card-title mb-0">Order Details</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<table class="table table-bordered">
|
||||
<tr>
|
||||
<th style="width: 40%">Order ID</th>
|
||||
<td>{{ order.id }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<td>
|
||||
{% if order.status == 'pending' %}
|
||||
<span class="badge bg-warning">Pending</span>
|
||||
{% elseif order.status == 'completed' %}
|
||||
<span class="badge bg-success">Completed</span>
|
||||
{% elseif order.status == 'cancelled' %}
|
||||
<span class="badge bg-danger">Cancelled</span>
|
||||
{% else %}
|
||||
<span class="badge bg-secondary">{{ order.status }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Created At</th>
|
||||
<td>{{ order.createdAt|date('Y-m-d H:i:s') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Updated At</th>
|
||||
<td>{{ order.updatedAt|date('Y-m-d H:i:s') }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total Items</th>
|
||||
<td>{{ order.orderItems|length }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total Quantity</th>
|
||||
<td>
|
||||
{% set totalQuantity = 0 %}
|
||||
{% for item in order.orderItems %}
|
||||
{% set totalQuantity = totalQuantity + item.quantity %}
|
||||
{% endfor %}
|
||||
{{ totalQuantity }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if order.status == 'pending' %}
|
||||
<div class="card">
|
||||
<div class="card-header bg-danger text-white">
|
||||
<h5 class="card-title mb-0">Danger Zone</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="/orders/{{ order.id }}/delete" method="post" onsubmit="return confirm('Are you sure you want to delete this order? This action cannot be undone.');">
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-danger">
|
||||
<i class="fas fa-trash"></i> Delete Order
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% if order.status == 'pending' %}
|
||||
<!-- Add Item Modal -->
|
||||
<div class="modal fade" id="addItemModal" tabindex="-1" aria-labelledby="addItemModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="addItemModalLabel">Add Item to Order</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<form action="/orders/{{ order.id }}/items" method="post">
|
||||
<div class="modal-body">
|
||||
<div class="form-group mb-3">
|
||||
<label for="drink_type_id">Drink Type</label>
|
||||
<select id="drink_type_id" name="drink_type_id" class="form-control" required>
|
||||
<option value="">-- Select Drink Type --</option>
|
||||
{% for drinkType in drinkTypes|default([]) %}
|
||||
<option value="{{ drinkType.id }}">{{ drinkType.name }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="quantity">Quantity</label>
|
||||
<input type="number" id="quantity" name="quantity" class="form-control" min="1" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<button type="submit" class="btn btn-primary">Add Item</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Complete Order Modal -->
|
||||
<div class="modal fade" id="completeOrderModal" tabindex="-1" aria-labelledby="completeOrderModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="completeOrderModalLabel">Complete Order</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to mark this order as completed?</p>
|
||||
<p class="text-muted">This will update the inventory levels accordingly.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
|
||||
<form action="/orders/{{ order.id }}/status" method="post">
|
||||
<input type="hidden" name="status" value="completed">
|
||||
<button type="submit" class="btn btn-success">Complete Order</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Cancel Order Modal -->
|
||||
<div class="modal fade" id="cancelOrderModal" tabindex="-1" aria-labelledby="cancelOrderModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="cancelOrderModalLabel">Cancel Order</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>Are you sure you want to cancel this order?</p>
|
||||
<p class="text-muted">This action cannot be undone.</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">No, Keep Order</button>
|
||||
<form action="/orders/{{ order.id }}/status" method="post">
|
||||
<input type="hidden" name="status" value="cancelled">
|
||||
<button type="submit" class="btn btn-danger">Yes, Cancel Order</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endblock %}
|
169
templates/settings/index.twig
Normal file
169
templates/settings/index.twig
Normal file
|
@ -0,0 +1,169 @@
|
|||
{% extends 'layout.twig' %}
|
||||
{% import 'components/form.twig' as form %}
|
||||
|
||||
{% block title %}⚙️🔧 System Settings 🛠️🔩 - {{ parent() }}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="row mb-4">
|
||||
<div class="col">
|
||||
<h2>⚙️✨ SUPER AWESOME SYSTEM SETTINGS 🔧🌟</h2>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#resetSettingsModal">
|
||||
<i class="fas fa-undo"></i> 💣 RESET TO DEFAULTS 💥
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
{{ form.errors(error) }}
|
||||
|
||||
<form action="/settings" method="post">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h5 class="mb-3">📊 Stock Adjustment Settings 📈🔄</h5>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="stock_adjustment_lookback_orders">🔍 Orders to Analyze 🧐🔎</label>
|
||||
<input type="number" id="stock_adjustment_lookback_orders" name="configs[stock_adjustment_lookback_orders]"
|
||||
class="form-control" min="1" max="20"
|
||||
value="{{ configs['stock_adjustment_lookback_orders']|default(5) }}">
|
||||
<small class="form-text text-muted">🧮 Number of recent orders to analyze for consumption patterns (default: 5) 📝</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="stock_adjustment_magnitude">📏 Adjustment Magnitude (%) 📐💯</label>
|
||||
<input type="number" id="stock_adjustment_magnitude" name="configs[stock_adjustment_magnitude]"
|
||||
class="form-control" min="1" max="100"
|
||||
value="{{ configs['stock_adjustment_magnitude']|default(20) }}">
|
||||
<small class="form-text text-muted">🔼 Maximum percentage to adjust stock levels by (default: 20%) 🔽</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="stock_adjustment_threshold">🚧 Adjustment Threshold (%) 🚨🔔</label>
|
||||
<input type="number" id="stock_adjustment_threshold" name="configs[stock_adjustment_threshold]"
|
||||
class="form-control" min="1" max="100"
|
||||
value="{{ configs['stock_adjustment_threshold']|default(10) }}">
|
||||
<small class="form-text text-muted">⚠️ Minimum difference required to trigger an adjustment (default: 10%) 🚦</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h5 class="mb-3">🏪 Inventory Settings 📦🗃️</h5>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="low_stock_threshold">📉 Low Stock Threshold (%) 😰🆘</label>
|
||||
<input type="number" id="low_stock_threshold" name="configs[low_stock_threshold]"
|
||||
class="form-control" min="1" max="100"
|
||||
value="{{ configs['low_stock_threshold']|default(50) }}">
|
||||
<small class="form-text text-muted">⚠️ Percentage of desired stock below which items are considered low stock (default: 50%) 😱</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="critical_stock_threshold">🔥 Critical Stock Threshold (%) 🚨💀</label>
|
||||
<input type="number" id="critical_stock_threshold" name="configs[critical_stock_threshold]"
|
||||
class="form-control" min="1" max="100"
|
||||
value="{{ configs['critical_stock_threshold']|default(25) }}">
|
||||
<small class="form-text text-muted">🆘 Percentage of desired stock below which items are considered critical (default: 25%) 😭</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="default_desired_stock">🎯 Default Desired Stock 🥅🏆</label>
|
||||
<input type="number" id="default_desired_stock" name="configs[default_desired_stock]"
|
||||
class="form-control" min="0"
|
||||
value="{{ configs['default_desired_stock']|default(10) }}">
|
||||
<small class="form-text text-muted">🌟 Default desired stock level for new drink types (default: 10) 🍹</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row mt-4">
|
||||
<div class="col-md-6">
|
||||
<h5 class="mb-3">🖥️ System Settings 💻🌐</h5>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="system_name">✨ System Name 🏷️🔤</label>
|
||||
<input type="text" id="system_name" name="configs[system_name]"
|
||||
class="form-control"
|
||||
value="{{ configs['system_name']|default('Drinks Inventory System') }}">
|
||||
<small class="form-text text-muted">👑 Name of the system displayed in the header 📛</small>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="enable_auto_adjustment"
|
||||
name="configs[enable_auto_adjustment]" value="1"
|
||||
{% if configs['enable_auto_adjustment']|default(1) == 1 %}checked{% endif %}>
|
||||
<label class="form-check-label" for="enable_auto_adjustment">
|
||||
🤖 Enable Automatic Stock Adjustment 🔄✨
|
||||
</label>
|
||||
</div>
|
||||
<small class="form-text text-muted">🧠 Automatically adjust desired stock levels based on consumption patterns 📊</small>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<h5 class="mb-3">🎨 Display Settings 📱💅</h5>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="show_low_stock_alerts"
|
||||
name="configs[show_low_stock_alerts]" value="1"
|
||||
{% if configs['show_low_stock_alerts']|default(1) == 1 %}checked{% endif %}>
|
||||
<label class="form-check-label" for="show_low_stock_alerts">
|
||||
🚨 Show Low Stock Alerts on Dashboard 📉😱
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<div class="form-check form-switch">
|
||||
<input class="form-check-input" type="checkbox" id="show_quick_update_form"
|
||||
name="configs[show_quick_update_form]" value="1"
|
||||
{% if configs['show_quick_update_form']|default(1) == 1 %}checked{% endif %}>
|
||||
<label class="form-check-label" for="show_quick_update_form">
|
||||
⚡ Show Quick Update Form on Dashboard 🔄💨
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mb-3">
|
||||
<label for="items_per_page">📋 Items Per Page 📑📄</label>
|
||||
<input type="number" id="items_per_page" name="configs[items_per_page]"
|
||||
class="form-control" min="5" max="100"
|
||||
value="{{ configs['items_per_page']|default(20) }}">
|
||||
<small class="form-text text-muted">🔢 Number of items to display per page in lists 📊</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group mt-4">
|
||||
{{ form.submit('💾 SAVE AWESOME SETTINGS 🚀') }}
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Reset Settings Modal -->
|
||||
<div class="modal fade" id="resetSettingsModal" tabindex="-1" aria-labelledby="resetSettingsModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="resetSettingsModalLabel">⚠️ RESET SETTINGS ⚠️</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p>🤔 Are you SUPER DUPER sure you want to reset ALL settings to their default values? 🧐</p>
|
||||
<p class="text-danger">💣 This action CANNOT be undone! 💥 Say bye-bye to your settings! 👋</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">❌ Cancel ❌</button>
|
||||
<form action="/settings/reset" method="post">
|
||||
<button type="submit" class="btn btn-danger">💥 RESET TO DEFAULTS 💣</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
Loading…
Add table
Add a link
Reference in a new issue