This commit is contained in:
lubiana 2025-05-31 21:43:13 +02:00
parent e958163a4a
commit b8a5a1ff58
Signed by: lubiana
SSH key fingerprint: SHA256:vW1EA0fRR3Fw+dD/sM0K+x3Il2gSry6YRYHqOeQwrfk
79 changed files with 15113 additions and 0 deletions

View 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 %}

View 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 %}

View 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>

View 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 %}

View 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>

View 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>

View 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 %}

View 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 %}

View 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 %}

View 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
View 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 %}

View 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 %}

View 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 %}

View 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 %}

View 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
View 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">🌟✨ &copy; {{ "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>

View 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
View 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
View 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 %}

View 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 %}