615 lines
27 KiB
HTML
615 lines
27 KiB
HTML
{% extends "base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %}Inventory Dashboard - {{ block.super }}{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="container-fluid">
|
|
<!-- Page Header -->
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<div>
|
|
<h1 class="h3 mb-1">
|
|
<i class="fas fa-warehouse me-2"></i>Inventory Dashboard
|
|
</h1>
|
|
<nav aria-label="breadcrumb">
|
|
<ol class="breadcrumb mb-0">
|
|
<li class="breadcrumb-item"><a href="{% url 'core:dashboard' %}">Dashboard</a></li>
|
|
<li class="breadcrumb-item active">Inventory</li>
|
|
</ol>
|
|
</nav>
|
|
</div>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-outline-secondary" onclick="refreshDashboard()">
|
|
<i class="fas fa-sync-alt me-2"></i>Refresh
|
|
</button>
|
|
<div class="btn-group">
|
|
<button type="button" class="btn btn-primary dropdown-toggle" data-bs-toggle="dropdown">
|
|
<i class="fas fa-plus me-2"></i>Quick Add
|
|
</button>
|
|
<ul class="dropdown-menu">
|
|
<li><a class="dropdown-item" href="{% url 'inventory:item_create' %}">
|
|
<i class="fas fa-box me-2"></i>New Item
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="{% url 'inventory:stock_create' %}">
|
|
<i class="fas fa-plus me-2"></i>Add Stock
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="{% url 'inventory:purchase_order_create' %}">
|
|
<i class="fas fa-shopping-cart me-2"></i>Purchase Order
|
|
</a></li>
|
|
<li><a class="dropdown-item" href="{% url 'inventory:supplier_create' %}">
|
|
<i class="fas fa-truck me-2"></i>New Supplier
|
|
</a></li>
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Key Performance Indicators -->
|
|
<div class="row mb-4" hx-get="{% url 'inventory:inventory_stats' %}" hx-trigger="load, every 30s">
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Total Items</h6>
|
|
<h3 class="mb-0 text-primary">{{ total_items }}</h3>
|
|
<small class="text-muted">{{ active_items }} active</small>
|
|
</div>
|
|
<div class="bg-primary bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-boxes fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Locations</h6>
|
|
<h3 class="mb-0 text-success">{{ total_locations }}</h3>
|
|
<small class="text-muted">Storage areas</small>
|
|
</div>
|
|
<div class="bg-success bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-map-marker-alt fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Suppliers</h6>
|
|
<h3 class="mb-0 text-info">{{ total_suppliers }}</h3>
|
|
<small class="text-muted">Active partners</small>
|
|
</div>
|
|
<div class="bg-info bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-truck fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Total Value</h6>
|
|
<h3 class="mb-0 text-warning">${{ total_inventory_value|floatformat:0 }}</h3>
|
|
<small class="text-muted">Inventory worth</small>
|
|
</div>
|
|
<div class="bg-warning bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-dollar-sign fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Alert Cards -->
|
|
<div class="row mb-4">
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100 {% if low_stock_items > 0 %}border-warning{% endif %}">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Low Stock</h6>
|
|
<h3 class="mb-0 {% if low_stock_items > 0 %}text-warning{% else %}text-success{% endif %}">{{ low_stock_items }}</h3>
|
|
<small class="text-muted">Items below minimum</small>
|
|
</div>
|
|
<div class="bg-{% if low_stock_items > 0 %}warning{% else %}success{% endif %} bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-exclamation-triangle fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100 {% if expired_items > 0 %}border-danger{% endif %}">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Expired</h6>
|
|
<h3 class="mb-0 {% if expired_items > 0 %}text-danger{% else %}text-success{% endif %}">{{ expired_items }}</h3>
|
|
<small class="text-muted">Past expiry date</small>
|
|
</div>
|
|
<div class="bg-{% if expired_items > 0 %}danger{% else %}success{% endif %} bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-calendar-times fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100 {% if expiring_soon_items > 0 %}border-warning{% endif %}">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Expiring Soon</h6>
|
|
<h3 class="mb-0 {% if expiring_soon_items > 0 %}text-warning{% else %}text-success{% endif %}">{{ expiring_soon_items }}</h3>
|
|
<small class="text-muted">Within 30 days</small>
|
|
</div>
|
|
<div class="bg-{% if expiring_soon_items > 0 %}warning{% else %}success{% endif %} bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-clock fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-3 col-md-6 mb-3">
|
|
<div class="card border-0 shadow-sm h-100">
|
|
<div class="card-body">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<div>
|
|
<h6 class="card-title text-muted mb-1">Active Orders</h6>
|
|
<h3 class="mb-0 text-primary">{{ active_orders }}</h3>
|
|
<small class="text-muted">In progress</small>
|
|
</div>
|
|
<div class="bg-primary bg-gradient rounded-circle d-flex align-items-center justify-content-center" style="width: 50px; height: 50px;">
|
|
<i class="fas fa-shopping-cart fa-lg text-white"></i>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Recent Purchase Orders -->
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-shopping-cart me-2"></i>Recent Purchase Orders
|
|
</h5>
|
|
<a href="{% url 'inventory:purchase_order_list' %}" class="btn btn-outline-primary btn-sm">
|
|
<i class="fas fa-list me-1"></i>View All
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
{% if recent_orders %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Order #</th>
|
|
<th>Supplier</th>
|
|
<th>Status</th>
|
|
<th>Amount</th>
|
|
<th>Date</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for order in recent_orders %}
|
|
<tr>
|
|
<td>
|
|
<a href="{% url 'inventory:purchase_order_detail' order.pk %}"
|
|
class="text-decoration-none fw-semibold">
|
|
{{ order.order_number }}
|
|
</a>
|
|
</td>
|
|
<td>{{ order.supplier.name|truncatechars:20 }}</td>
|
|
<td>
|
|
<span class="badge bg-{% if order.status == 'RECEIVED' %}success{% elif order.status == 'APPROVED' %}info{% elif order.status == 'ORDERED' %}warning{% else %}secondary{% endif %}">
|
|
{{ order.get_status_display }}
|
|
</span>
|
|
</td>
|
|
<td>${{ order.total_amount|floatformat:2 }}</td>
|
|
<td>{{ order.order_date|date:"M d" }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-shopping-cart fa-2x text-muted mb-3"></i>
|
|
<h6 class="text-muted">No recent orders</h6>
|
|
<p class="text-muted">Create your first purchase order</p>
|
|
<a href="{% url 'inventory:purchase_order_create' %}" class="btn btn-primary">
|
|
<i class="fas fa-plus me-2"></i>Create Order
|
|
</a>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Low Stock Alerts -->
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-exclamation-triangle me-2"></i>Low Stock Alerts
|
|
</h5>
|
|
<a href="{% url 'inventory:stock_list' %}?stock_status=low" class="btn btn-outline-warning btn-sm">
|
|
<i class="fas fa-list me-1"></i>View All
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body">
|
|
{% if low_stock_alerts %}
|
|
{% for alert in low_stock_alerts %}
|
|
<div class="d-flex align-items-center mb-3 p-3 bg-light rounded">
|
|
<div class="bg-warning bg-gradient rounded-circle d-flex align-items-center justify-content-center me-3" style="width: 32px; height: 32px;">
|
|
<i class="fas fa-exclamation-triangle text-white small"></i>
|
|
</div>
|
|
<div class="flex-grow-1">
|
|
<div class="fw-semibold">{{ alert.item.item_name }}</div>
|
|
<small class="text-muted">{{ alert.location.location_name }}</small>
|
|
</div>
|
|
<div class="text-end">
|
|
<div class="fw-semibold text-warning">{{ alert.quantity }}</div>
|
|
<small class="text-muted">Min: {{ alert.minimum_stock_level }}</small>
|
|
</div>
|
|
</div>
|
|
{% endfor %}
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-check-circle fa-2x text-success mb-3"></i>
|
|
<h6 class="text-muted">All stock levels are good</h6>
|
|
<p class="text-muted">No low stock alerts at this time</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row">
|
|
<!-- Recent Stock Movements -->
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header">
|
|
<div class="d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-exchange-alt me-2"></i>Recent Stock Movements
|
|
</h5>
|
|
<a href="{% url 'inventory:stock_list' %}" class="btn btn-outline-primary btn-sm">
|
|
<i class="fas fa-list me-1"></i>View All
|
|
</a>
|
|
</div>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
{% if recent_stock_movements %}
|
|
<div class="table-responsive">
|
|
<table class="table table-hover mb-0">
|
|
<thead class="table-light">
|
|
<tr>
|
|
<th>Item</th>
|
|
<th>Location</th>
|
|
<th>Quantity</th>
|
|
<th>Updated</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% for stock in recent_stock_movements %}
|
|
<tr>
|
|
<td>
|
|
<a href="{% url 'inventory:stock_detail' stock.pk %}"
|
|
class="text-decoration-none">
|
|
{{ stock.item.item_name|truncatechars:25 }}
|
|
</a>
|
|
</td>
|
|
<td>{{ stock.location.location_name|truncatechars:15 }}</td>
|
|
<td>
|
|
<span class="{% if stock.quantity <= stock.minimum_stock_level %}text-warning{% else %}text-success{% endif %} fw-semibold">
|
|
{{ stock.quantity }}
|
|
</span>
|
|
</td>
|
|
<td>{{ stock.updated_at|date:"M d, H:i" }}</td>
|
|
</tr>
|
|
{% endfor %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
{% else %}
|
|
<div class="text-center py-4">
|
|
<i class="fas fa-exchange-alt fa-2x text-muted mb-3"></i>
|
|
<h6 class="text-muted">No recent movements</h6>
|
|
<p class="text-muted">Stock movements will appear here</p>
|
|
</div>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick Actions -->
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card h-100">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-bolt me-2"></i>Quick Actions
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<div class="row g-3">
|
|
<div class="col-6">
|
|
<a href="{% url 'inventory:item_list' %}" class="btn btn-outline-primary w-100 h-100 d-flex flex-column align-items-center justify-content-center">
|
|
<i class="fas fa-boxes fa-2x mb-2"></i>
|
|
<span>Items</span>
|
|
</a>
|
|
</div>
|
|
<div class="col-6">
|
|
<a href="{% url 'inventory:stock_list' %}" class="btn btn-outline-success w-100 h-100 d-flex flex-column align-items-center justify-content-center">
|
|
<i class="fas fa-warehouse fa-2x mb-2"></i>
|
|
<span>Stock</span>
|
|
</a>
|
|
</div>
|
|
<div class="col-6">
|
|
<a href="{% url 'inventory:purchase_order_list' %}" class="btn btn-outline-info w-100 h-100 d-flex flex-column align-items-center justify-content-center">
|
|
<i class="fas fa-shopping-cart fa-2x mb-2"></i>
|
|
<span>Orders</span>
|
|
</a>
|
|
</div>
|
|
<div class="col-6">
|
|
<a href="{% url 'inventory:supplier_list' %}" class="btn btn-outline-warning w-100 h-100 d-flex flex-column align-items-center justify-content-center">
|
|
<i class="fas fa-truck fa-2x mb-2"></i>
|
|
<span>Suppliers</span>
|
|
</a>
|
|
</div>
|
|
<div class="col-6">
|
|
<a href="{% url 'inventory:location_list' %}" class="btn btn-outline-secondary w-100 h-100 d-flex flex-column align-items-center justify-content-center">
|
|
<i class="fas fa-map-marker-alt fa-2x mb-2"></i>
|
|
<span>Locations</span>
|
|
</a>
|
|
</div>
|
|
<div class="col-6">
|
|
<a href="{% url 'inventory:stock_list' %}?stock_status=low" class="btn btn-outline-danger w-100 h-100 d-flex flex-column align-items-center justify-content-center">
|
|
<i class="fas fa-exclamation-triangle fa-2x mb-2"></i>
|
|
<span>Alerts</span>
|
|
</a>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Inventory Charts -->
|
|
<div class="row">
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-chart-pie me-2"></i>Inventory by Category
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<canvas id="categoryChart" width="400" height="200"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-6 mb-4">
|
|
<div class="card">
|
|
<div class="card-header">
|
|
<h5 class="mb-0">
|
|
<i class="fas fa-chart-bar me-2"></i>Stock Status Overview
|
|
</h5>
|
|
</div>
|
|
<div class="card-body">
|
|
<canvas id="stockStatusChart" width="400" height="200"></canvas>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
// Dashboard functionality
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
// Initialize charts
|
|
initializeCharts();
|
|
|
|
// Auto-refresh dashboard every 5 minutes
|
|
setInterval(refreshDashboard, 300000);
|
|
});
|
|
|
|
function refreshDashboard() {
|
|
// Trigger HTMX refresh for stats
|
|
htmx.trigger(document.querySelector('[hx-get]'), 'refresh');
|
|
|
|
// Refresh charts
|
|
updateCharts();
|
|
|
|
// Show refresh feedback
|
|
const refreshBtn = document.querySelector('[onclick="refreshDashboard()"]');
|
|
const originalText = refreshBtn.innerHTML;
|
|
refreshBtn.innerHTML = '<i class="fas fa-spinner fa-spin me-2"></i>Refreshing...';
|
|
refreshBtn.disabled = true;
|
|
|
|
setTimeout(() => {
|
|
refreshBtn.innerHTML = originalText;
|
|
refreshBtn.disabled = false;
|
|
}, 2000);
|
|
}
|
|
|
|
function initializeCharts() {
|
|
// Category Chart
|
|
const categoryCtx = document.getElementById('categoryChart');
|
|
if (categoryCtx) {
|
|
new Chart(categoryCtx, {
|
|
type: 'doughnut',
|
|
data: {
|
|
labels: {{ category_labels|safe }},
|
|
datasets: [{
|
|
data: {{ category_data|safe }},
|
|
backgroundColor: [
|
|
'#0d6efd', '#198754', '#ffc107', '#dc3545',
|
|
'#6f42c1', '#fd7e14', '#20c997', '#6c757d'
|
|
]
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
position: 'bottom'
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
// Stock Status Chart
|
|
const stockCtx = document.getElementById('stockStatusChart');
|
|
if (stockCtx) {
|
|
new Chart(stockCtx, {
|
|
type: 'bar',
|
|
data: {
|
|
labels: ['In Stock', 'Low Stock', 'Out of Stock', 'Expired'],
|
|
datasets: [{
|
|
data: [
|
|
{{ in_stock_count }},
|
|
{{ low_stock_items }},
|
|
{{ out_of_stock_count }},
|
|
{{ expired_items }}
|
|
],
|
|
backgroundColor: ['#198754', '#ffc107', '#dc3545', '#6c757d']
|
|
}]
|
|
},
|
|
options: {
|
|
responsive: true,
|
|
maintainAspectRatio: false,
|
|
plugins: {
|
|
legend: {
|
|
display: false
|
|
}
|
|
},
|
|
scales: {
|
|
y: {
|
|
beginAtZero: true
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
{#function updateCharts() {#}
|
|
{# // Fetch updated chart data and refresh charts#}
|
|
{# fetch('{% url "inventory:chart_data" %}')#}
|
|
{# .then(response => response.json())#}
|
|
{# .then(data => {#}
|
|
{# // Update chart data#}
|
|
{# console.log('Chart data updated');#}
|
|
{# })#}
|
|
{# .catch(error => {#}
|
|
{# console.error('Error updating charts:', error);#}
|
|
{# });#}
|
|
{# } #}
|
|
|
|
// Real-time notifications for critical alerts
|
|
{#function checkCriticalAlerts() {#}
|
|
{# if ({{ expired_items }} > 0 || {{ low_stock_items }} > 5) {#}
|
|
{# // Show notification#}
|
|
{# if ('Notification' in window && Notification.permission === 'granted') {#}
|
|
{# new Notification('Inventory Alert', {#}
|
|
{# body: `${{{ expired_items }}} expired items, ${{{ low_stock_items }}} low stock items`,#}
|
|
{# icon: '/static/img/inventory-icon.png'#}
|
|
{# });#}
|
|
{# }#}
|
|
{# }#}
|
|
{# }#}
|
|
|
|
// Request notification permission
|
|
if ('Notification' in window && Notification.permission === 'default') {
|
|
Notification.requestPermission();
|
|
}
|
|
|
|
// Check alerts on load
|
|
checkCriticalAlerts();
|
|
</script>
|
|
|
|
<style>
|
|
.bg-gradient {
|
|
background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0));
|
|
}
|
|
|
|
.table th {
|
|
border-top: none;
|
|
font-weight: 600;
|
|
color: #6c757d;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.table td {
|
|
vertical-align: middle;
|
|
font-size: 0.875rem;
|
|
}
|
|
|
|
.badge {
|
|
font-size: 0.75em;
|
|
}
|
|
|
|
.btn-outline-primary:hover,
|
|
.btn-outline-success:hover,
|
|
.btn-outline-info:hover,
|
|
.btn-outline-warning:hover,
|
|
.btn-outline-secondary:hover,
|
|
.btn-outline-danger:hover {
|
|
transform: translateY(-1px);
|
|
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
.d-flex.justify-content-between {
|
|
flex-direction: column;
|
|
gap: 1rem;
|
|
}
|
|
|
|
.btn-group {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.5rem;
|
|
}
|
|
|
|
.col-lg-3, .col-md-6 {
|
|
margin-bottom: 1rem;
|
|
}
|
|
|
|
.row.g-3 .col-6 {
|
|
margin-bottom: 0.5rem;
|
|
}
|
|
}
|
|
</style>
|
|
{% endblock %}
|
|
|