573 lines
28 KiB
HTML
573 lines
28 KiB
HTML
{% extends "base.html" %}
|
||
{% load static %}
|
||
|
||
{% block title %}{% if object %}Edit Stock{% else %}New Stock Entry{% endif %}{% endblock %}
|
||
|
||
{% block content %}
|
||
<div class="d-flex align-items-center mb-3">
|
||
<div>
|
||
<ol class="breadcrumb">
|
||
<li class="breadcrumb-item"><a href="{% url 'inventory:dashboard' %}">Inventory</a></li>
|
||
<li class="breadcrumb-item"><a href="{% url 'inventory:stock_list' %}">Stock</a></li>
|
||
<li class="breadcrumb-item active">{% if object %}Edit{% else %}New{% endif %}</li>
|
||
</ol>
|
||
<h1 class="page-header mb-0">
|
||
{% if object %}Edit Stock - {{ object.item.name }}{% else %}Create Stock Entry{% endif %}
|
||
</h1>
|
||
</div>
|
||
<div class="ms-auto">
|
||
<a href="{% url 'inventory:stock_list' %}" class="btn btn-secondary">
|
||
<i class="fas fa-arrow-left me-2"></i>Back to Stock List
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-xl-8">
|
||
<div class="panel panel-inverse" data-sortable-id="index-1">
|
||
<div class="panel-heading">
|
||
<h4 class="panel-title"><i class="fas fa-boxes me-2"></i> {{ _("Stock Information")}}</h4>
|
||
<div class="panel-heading-btn">
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>#}
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-success" data-toggle="panel-reload"><i class="fa fa-redo"></i></a>#}
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-warning" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>#}
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-danger" data-toggle="panel-remove"><i class="fa fa-times"></i></a>#}
|
||
|
||
</div>
|
||
</div>
|
||
<div class="panel-body">
|
||
{% if messages %}
|
||
{% for message in messages %}
|
||
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
|
||
{{ message }}
|
||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||
</div>
|
||
{% endfor %}
|
||
{% endif %}
|
||
|
||
<form method="post" novalidate>
|
||
{% csrf_token %}
|
||
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label for="{{ form.item.id_for_label }}" class="form-label">Item *</label>
|
||
<select class="form-select {% if form.item.errors %}is-invalid{% endif %}"
|
||
id="{{ form.item.id_for_label }}"
|
||
name="{{ form.item.name }}"
|
||
required>
|
||
<option value="">Select Item</option>
|
||
{% for choice in form.item.field.choices %}
|
||
{% if choice.0 %}
|
||
<option value="{{ choice.0 }}" {% if choice.0 == form.item.value %}selected{% endif %}>
|
||
{{ choice.1 }}
|
||
</option>
|
||
{% endif %}
|
||
{% endfor %}
|
||
</select>
|
||
{% if form.item.errors %}
|
||
<div class="invalid-feedback">
|
||
{{ form.item.errors.0 }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label for="{{ form.location.id_for_label }}" class="form-label">Location *</label>
|
||
<select class="form-select {% if form.location.errors %}is-invalid{% endif %}"
|
||
id="{{ form.location.id_for_label }}"
|
||
name="{{ form.location.name }}"
|
||
required>
|
||
<option value="">Select Location</option>
|
||
{% for choice in form.location.field.choices %}
|
||
{% if choice.0 %}
|
||
<option value="{{ choice.0 }}" {% if choice.0 == form.location.value %}selected{% endif %}>
|
||
{{ choice.1 }}
|
||
</option>
|
||
{% endif %}
|
||
{% endfor %}
|
||
</select>
|
||
{% if form.location.errors %}
|
||
<div class="invalid-feedback">
|
||
{{ form.location.errors.0 }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-md-4">
|
||
<div class="mb-3">
|
||
<label for="{{ form.current_quantity.id_for_label }}" class="form-label">Current Quantity *</label>
|
||
<input type="number"
|
||
class="form-control {% if form.current_quantity.errors %}is-invalid{% endif %}"
|
||
id="{{ form.current_quantity.id_for_label }}"
|
||
name="{{ form.current_quantity.name }}"
|
||
value="{{ form.current_quantity.value|default:'' }}"
|
||
min="0"
|
||
step="0.01"
|
||
placeholder="Enter current quantity"
|
||
required>
|
||
{% if form.current_quantity.errors %}
|
||
<div class="invalid-feedback">
|
||
{{ form.current_quantity.errors.0 }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<div class="mb-3">
|
||
<label for="{{ form.minimum_quantity.id_for_label }}" class="form-label">Minimum Quantity *</label>
|
||
<input type="number"
|
||
class="form-control {% if form.minimum_quantity.errors %}is-invalid{% endif %}"
|
||
id="{{ form.minimum_quantity.id_for_label }}"
|
||
name="{{ form.minimum_quantity.name }}"
|
||
value="{{ form.minimum_quantity.value|default:'' }}"
|
||
min="0"
|
||
step="0.01"
|
||
placeholder="Enter minimum quantity"
|
||
required>
|
||
{% if form.minimum_quantity.errors %}
|
||
<div class="invalid-feedback">
|
||
{{ form.minimum_quantity.errors.0 }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<div class="mb-3">
|
||
<label for="{{ form.reorder_level.id_for_label }}" class="form-label">Reorder Level *</label>
|
||
<input type="number"
|
||
class="form-control {% if form.reorder_level.errors %}is-invalid{% endif %}"
|
||
id="{{ form.reorder_level.id_for_label }}"
|
||
name="{{ form.reorder_level.name }}"
|
||
value="{{ form.reorder_level.value|default:'' }}"
|
||
min="0"
|
||
step="0.01"
|
||
placeholder="Enter reorder level"
|
||
required>
|
||
{% if form.reorder_level.errors %}
|
||
<div class="invalid-feedback">
|
||
{{ form.reorder_level.errors.0 }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-md-4">
|
||
<div class="mb-3">
|
||
<label for="{{ form.maximum_quantity.id_for_label }}" class="form-label">Maximum Quantity</label>
|
||
<input type="number"
|
||
class="form-control {% if form.maximum_quantity.errors %}is-invalid{% endif %}"
|
||
id="{{ form.maximum_quantity.id_for_label }}"
|
||
name="{{ form.maximum_quantity.name }}"
|
||
value="{{ form.maximum_quantity.value|default:'' }}"
|
||
min="0"
|
||
step="0.01"
|
||
placeholder="Enter maximum quantity">
|
||
{% if form.maximum_quantity.errors %}
|
||
<div class="invalid-feedback">
|
||
{{ form.maximum_quantity.errors.0 }}
|
||
</div>
|
||
{% endif %}
|
||
<div class="form-text">
|
||
Optional: Maximum storage capacity for this location
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<div class="mb-3">
|
||
<label for="{{ form.unit_cost.id_for_label }}" class="form-label">Unit Cost *</label>
|
||
<div class="input-group">
|
||
<span class="input-group-text">$</span>
|
||
<input type="number"
|
||
class="form-control {% if form.unit_cost.errors %}is-invalid{% endif %}"
|
||
id="{{ form.unit_cost.id_for_label }}"
|
||
name="{{ form.unit_cost.name }}"
|
||
value="{{ form.unit_cost.value|default:'' }}"
|
||
min="0"
|
||
step="0.01"
|
||
placeholder="0.00"
|
||
required>
|
||
</div>
|
||
{% if form.unit_cost.errors %}
|
||
<div class="invalid-feedback">
|
||
{{ form.unit_cost.errors.0 }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="col-md-4">
|
||
<div class="mb-3">
|
||
<label class="form-label">Total Value</label>
|
||
<div class="input-group">
|
||
<span class="input-group-text">$</span>
|
||
<input type="text"
|
||
class="form-control"
|
||
id="totalValue"
|
||
readonly
|
||
placeholder="0.00">
|
||
</div>
|
||
<div class="form-text">
|
||
Automatically calculated: Quantity × Unit Cost
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="row">
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label for="{{ form.batch_number.id_for_label }}" class="form-label">Batch Number</label>
|
||
<input type="text"
|
||
class="form-control {% if form.batch_number.errors %}is-invalid{% endif %}"
|
||
id="{{ form.batch_number.id_for_label }}"
|
||
name="{{ form.batch_number.name }}"
|
||
value="{{ form.batch_number.value|default:'' }}"
|
||
placeholder="Enter batch number">
|
||
{% if form.batch_number.errors %}
|
||
<div class="invalid-feedback">
|
||
{{ form.batch_number.errors.0 }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
<div class="col-md-6">
|
||
<div class="mb-3">
|
||
<label for="{{ form.expiry_date.id_for_label }}" class="form-label">Expiry Date</label>
|
||
<input type="date"
|
||
class="form-control {% if form.expiry_date.errors %}is-invalid{% endif %}"
|
||
id="{{ form.expiry_date.id_for_label }}"
|
||
name="{{ form.expiry_date.name }}"
|
||
value="{{ form.expiry_date.value|default:'' }}">
|
||
{% if form.expiry_date.errors %}
|
||
<div class="invalid-feedback">
|
||
{{ form.expiry_date.errors.0 }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<label for="{{ form.notes.id_for_label }}" class="form-label">Notes</label>
|
||
<textarea class="form-control {% if form.notes.errors %}is-invalid{% endif %}"
|
||
id="{{ form.notes.id_for_label }}"
|
||
name="{{ form.notes.name }}"
|
||
rows="4"
|
||
placeholder="Enter any additional notes about this stock entry...">{{ form.notes.value|default:'' }}</textarea>
|
||
{% if form.notes.errors %}
|
||
<div class="invalid-feedback">
|
||
{{ form.notes.errors.0 }}
|
||
</div>
|
||
{% endif %}
|
||
</div>
|
||
|
||
<div class="d-flex justify-content-between">
|
||
<div>
|
||
{% if object %}
|
||
<a href="{% url 'inventory:stock_detail' object.pk %}" class="btn btn-secondary">
|
||
<i class="fas fa-times me-2"></i>Cancel
|
||
</a>
|
||
{% else %}
|
||
<a href="{% url 'inventory:stock_list' %}" class="btn btn-secondary">
|
||
<i class="fas fa-times me-2"></i>Cancel
|
||
</a>
|
||
{% endif %}
|
||
</div>
|
||
<div>
|
||
<button type="submit" class="btn btn-primary">
|
||
<i class="fas fa-save me-2"></i>
|
||
{% if object %}Update Stock{% else %}Create Stock Entry{% endif %}
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</form>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="col-xl-4">
|
||
<!-- Stock Level Guidelines -->
|
||
<div class="panel panel-inverse" data-sortable-id="index-2">
|
||
<div class="panel-heading">
|
||
<h4 class="panel-title"><i class="fas fa-info-circle me-2"></i> {{ _("Stock Level Guidelines")}}</h4>
|
||
<div class="panel-heading-btn">
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>#}
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-success" data-toggle="panel-reload"><i class="fa fa-redo"></i></a>#}
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-warning" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>#}
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-danger" data-toggle="panel-remove"><i class="fa fa-times"></i></a>#}
|
||
|
||
</div>
|
||
</div>
|
||
<div class="panel-body">
|
||
<div class="mb-3">
|
||
<h6><i class="fas fa-exclamation-triangle text-danger me-2"></i>Minimum Quantity</h6>
|
||
<p class="small text-muted">
|
||
The lowest acceptable stock level. When stock reaches this level,
|
||
immediate action is required to prevent stockouts.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<h6><i class="fas fa-bell text-warning me-2"></i>Reorder Level</h6>
|
||
<p class="small text-muted">
|
||
The stock level that triggers a reorder. Should be set higher than
|
||
minimum quantity to account for lead times.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="mb-3">
|
||
<h6><i class="fas fa-check-circle text-success me-2"></i>Maximum Quantity</h6>
|
||
<p class="small text-muted">
|
||
The maximum storage capacity for this location. Helps prevent
|
||
overstocking and storage issues.
|
||
</p>
|
||
</div>
|
||
|
||
<div class="alert alert-info">
|
||
<small>
|
||
<strong>Recommended:</strong> Reorder Level should be 2-3 times
|
||
the Minimum Quantity to ensure adequate buffer stock.
|
||
</small>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Item Information -->
|
||
<div class="card mb-4" id="itemInfoCard" style="display: none;">
|
||
<div class="card-header">
|
||
<h5 class="card-title">
|
||
<i class="fas fa-cube me-2"></i>
|
||
Item Information
|
||
</h5>
|
||
</div>
|
||
<div class="card-body" id="itemInfoContent">
|
||
<!-- Item details will be loaded here -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Location Information -->
|
||
<div class="card mb-4" id="locationInfoCard" style="display: none;">
|
||
<div class="card-header">
|
||
<h5 class="card-title">
|
||
<i class="fas fa-map-marker-alt me-2"></i>
|
||
Location Information
|
||
</h5>
|
||
</div>
|
||
<div class="card-body" id="locationInfoContent">
|
||
<!-- Location details will be loaded here -->
|
||
</div>
|
||
</div>
|
||
|
||
<!-- Quick Actions -->
|
||
<div class="panel panel-inverse" data-sortable-id="index-3">
|
||
<div class="panel-heading">
|
||
<h4 class="panel-title"><i class="fas fa-bolt me-2"></i> {{ _("Quick Actions")}}</h4>
|
||
<div class="panel-heading-btn">
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-default" data-toggle="panel-expand"><i class="fa fa-expand"></i></a>#}
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-success" data-toggle="panel-reload"><i class="fa fa-redo"></i></a>#}
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-warning" data-toggle="panel-collapse"><i class="fa fa-minus"></i></a>#}
|
||
{# <a href="javascript:;" class="btn btn-xs btn-icon btn-danger" data-toggle="panel-remove"><i class="fa fa-times"></i></a>#}
|
||
|
||
</div>
|
||
</div>
|
||
<div class="panel-body">
|
||
<div class="d-grid gap-2">
|
||
<button type="button" class="btn btn-outline-primary" onclick="calculateRecommendedLevels()">
|
||
<i class="fas fa-calculator me-2"></i>Calculate Recommended Levels
|
||
</button>
|
||
<button type="button" class="btn btn-outline-secondary" onclick="copyFromExisting()">
|
||
<i class="fas fa-copy me-2"></i>Copy from Existing Stock
|
||
</button>
|
||
<button type="button" class="btn btn-outline-info" onclick="viewItemHistory()">
|
||
<i class="fas fa-history me-2"></i>View Item History
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const itemSelect = document.getElementById('{{ form.item.id_for_label }}');
|
||
const locationSelect = document.getElementById('{{ form.location.id_for_label }}');
|
||
const currentQuantityInput = document.getElementById('{{ form.current_quantity.id_for_label }}');
|
||
const unitCostInput = document.getElementById('{{ form.unit_cost.id_for_label }}');
|
||
const totalValueInput = document.getElementById('totalValue');
|
||
const minimumQuantityInput = document.getElementById('{{ form.minimum_quantity.id_for_label }}');
|
||
const reorderLevelInput = document.getElementById('{{ form.reorder_level.id_for_label }}');
|
||
|
||
// Load item information when item changes
|
||
itemSelect.addEventListener('change', function() {
|
||
const itemId = this.value;
|
||
if (itemId) {
|
||
loadItemInfo(itemId);
|
||
} else {
|
||
document.getElementById('itemInfoCard').style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// Load location information when location changes
|
||
locationSelect.addEventListener('change', function() {
|
||
const locationId = this.value;
|
||
if (locationId) {
|
||
loadLocationInfo(locationId);
|
||
} else {
|
||
document.getElementById('locationInfoCard').style.display = 'none';
|
||
}
|
||
});
|
||
|
||
// Calculate total value when quantity or unit cost changes
|
||
function calculateTotalValue() {
|
||
const quantity = parseFloat(currentQuantityInput.value) || 0;
|
||
const unitCost = parseFloat(unitCostInput.value) || 0;
|
||
const totalValue = quantity * unitCost;
|
||
totalValueInput.value = totalValue.toFixed(2);
|
||
}
|
||
|
||
currentQuantityInput.addEventListener('input', calculateTotalValue);
|
||
unitCostInput.addEventListener('input', calculateTotalValue);
|
||
|
||
// Validate stock levels
|
||
function validateStockLevels() {
|
||
const minimum = parseFloat(minimumQuantityInput.value) || 0;
|
||
const reorder = parseFloat(reorderLevelInput.value) || 0;
|
||
|
||
if (reorder <= minimum && reorder > 0) {
|
||
showWarning('Reorder level should be higher than minimum quantity');
|
||
}
|
||
}
|
||
|
||
minimumQuantityInput.addEventListener('blur', validateStockLevels);
|
||
reorderLevelInput.addEventListener('blur', validateStockLevels);
|
||
|
||
// Initial calculation
|
||
calculateTotalValue();
|
||
});
|
||
|
||
function loadItemInfo(itemId) {
|
||
// Simulate loading item information
|
||
const itemInfoCard = document.getElementById('itemInfoCard');
|
||
const itemInfoContent = document.getElementById('itemInfoContent');
|
||
|
||
itemInfoContent.innerHTML = `
|
||
<div class="text-center">
|
||
<div class="spinner-border spinner-border-sm text-primary mb-2" role="status"></div>
|
||
<div class="small">Loading item information...</div>
|
||
</div>
|
||
`;
|
||
itemInfoCard.style.display = 'block';
|
||
|
||
// Simulate API call
|
||
setTimeout(() => {
|
||
itemInfoContent.innerHTML = `
|
||
<div class="mb-2">
|
||
<strong>SKU:</strong> ITM-001
|
||
</div>
|
||
<div class="mb-2">
|
||
<strong>Category:</strong> Medical Supplies
|
||
</div>
|
||
<div class="mb-2">
|
||
<strong>Unit:</strong> Each
|
||
</div>
|
||
<div class="mb-2">
|
||
<strong>Current Stock:</strong> 150 units
|
||
</div>
|
||
<div>
|
||
<strong>Avg. Monthly Usage:</strong> 45 units
|
||
</div>
|
||
`;
|
||
}, 1000);
|
||
}
|
||
|
||
function loadLocationInfo(locationId) {
|
||
// Simulate loading location information
|
||
const locationInfoCard = document.getElementById('locationInfoCard');
|
||
const locationInfoContent = document.getElementById('locationInfoContent');
|
||
|
||
locationInfoContent.innerHTML = `
|
||
<div class="text-center">
|
||
<div class="spinner-border spinner-border-sm text-primary mb-2" role="status"></div>
|
||
<div class="small">Loading location information...</div>
|
||
</div>
|
||
`;
|
||
locationInfoCard.style.display = 'block';
|
||
|
||
// Simulate API call
|
||
setTimeout(() => {
|
||
locationInfoContent.innerHTML = `
|
||
<div class="mb-2">
|
||
<strong>Zone:</strong> A
|
||
</div>
|
||
<div class="mb-2">
|
||
<strong>Aisle:</strong> 3
|
||
</div>
|
||
<div class="mb-2">
|
||
<strong>Shelf:</strong> B2
|
||
</div>
|
||
<div class="mb-2">
|
||
<strong>Capacity:</strong> 500 units
|
||
</div>
|
||
<div>
|
||
<strong>Temperature:</strong> Room temperature
|
||
</div>
|
||
`;
|
||
}, 1000);
|
||
}
|
||
|
||
function calculateRecommendedLevels() {
|
||
// In a real implementation, this would calculate based on usage patterns
|
||
const avgMonthlyUsage = 45; // This would come from actual data
|
||
const leadTimeDays = 7; // This would be configurable
|
||
|
||
const dailyUsage = avgMonthlyUsage / 30;
|
||
const recommendedMinimum = Math.ceil(dailyUsage * leadTimeDays);
|
||
const recommendedReorder = Math.ceil(recommendedMinimum * 2);
|
||
|
||
document.getElementById('{{ form.minimum_quantity.id_for_label }}').value = recommendedMinimum;
|
||
document.getElementById('{{ form.reorder_level.id_for_label }}').value = recommendedReorder;
|
||
|
||
showSuccess(`Recommended levels calculated: Minimum: ${recommendedMinimum}, Reorder: ${recommendedReorder}`);
|
||
}
|
||
|
||
function copyFromExisting() {
|
||
// In a real implementation, this would open a modal to select existing stock
|
||
alert('Copy from existing stock functionality would be implemented here.');
|
||
}
|
||
|
||
function viewItemHistory() {
|
||
// In a real implementation, this would show item usage history
|
||
alert('Item history functionality would be implemented here.');
|
||
}
|
||
|
||
function showWarning(message) {
|
||
// Simple warning display - in a real implementation, use a proper notification system
|
||
const alertDiv = document.createElement('div');
|
||
alertDiv.className = 'alert alert-warning alert-dismissible fade show';
|
||
alertDiv.innerHTML = `
|
||
${message}
|
||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||
`;
|
||
document.querySelector('.card-body').insertBefore(alertDiv, document.querySelector('form'));
|
||
}
|
||
|
||
function showSuccess(message) {
|
||
// Simple success display - in a real implementation, use a proper notification system
|
||
const alertDiv = document.createElement('div');
|
||
alertDiv.className = 'alert alert-success alert-dismissible fade show';
|
||
alertDiv.innerHTML = `
|
||
${message}
|
||
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
|
||
`;
|
||
document.querySelector('.card-body').insertBefore(alertDiv, document.querySelector('form'));
|
||
}
|
||
</script>
|
||
{% endblock %}
|
||
|