Files
KCAPDemoServer/src/app/templates/index.html
Matt Hills 03b4264239 Fix foreign key cascades and add validation for missing AR fields
- Enable SQLite foreign key constraints to properly cascade deletes
- Add validation to prevent adding products when no AR fields configured
- Display helpful warnings on product pages when fields are missing
- Redirect users to AR field configuration when needed
2025-10-20 21:36:01 -04:00

216 lines
11 KiB
HTML

{% extends "layout.html" %}
{% block title %}Home - KCAP Demo Server{% endblock %}
{% block content %}
<div class="row mt-4">
<div class="col-md-12">
<div class="jumbotron position-relative">
<div class="position-absolute top-0 end-0 p-3">
<a href="/{{ tenant.id }}/settings" class="btn btn-secondary" title="Settings">
<i class="bi bi-gear-fill"></i> Settings
</a>
</div>
<h1 class="display-4">KCAP Demo Server{% if tenant %} - {{ tenant.name }}{% endif %}</h1>
<p class="lead">Manage your product catalog and AR content for barcode scanning applications.</p>
<hr class="my-4">
<div class="mt-4">
{% if not custom_fields %}
<div class="alert alert-warning" role="alert">
<h4 class="alert-heading"><i class="bi bi-exclamation-triangle-fill me-2"></i>No AR Fields Configured</h4>
<p>Before you can add products, you need to configure custom AR fields that define what information is returned for each product.</p>
<hr>
<p class="mb-0">
<a href="/{{ tenant.id }}/ar_fields" class="btn btn-primary">
<i class="bi bi-gear-fill me-1"></i>Configure AR Fields
</a>
</p>
</div>
{% else %}
<div class="d-flex justify-content-between align-items-center mb-3">
<h2>Products</h2>
<div>
<a href="/{{ tenant.id }}/barcodes" class="btn btn-success me-2">
<i class="bi bi-upc-scan"></i> View All Barcodes
</a>
<a href="/{{ tenant.id }}/add" class="btn btn-primary">
<i class="bi bi-plus-circle"></i> Add New Product
</a>
</div>
</div>
{% if products %}
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>Product ID</th>
{% for custom_field in custom_fields %}
{% if custom_field.fieldName != '_id' %}
<th>{{ custom_field.label }}</th>
{% endif %}
{% endfor %}
<th>Actions</th>
</tr>
</thead>
<tbody>
{% for product_id, product_data in products.items() %}
<tr>
<td>{{ product_id }}</td>
{% for custom_field in custom_fields %}
{% if custom_field.fieldName != '_id' %}
<td>
{% set matching_fields = product_data|selectattr('fieldName', 'equalto', custom_field.fieldName)|list %}
{% if custom_field.fieldType == 'IMAGE_URI' %}
{% if matching_fields and matching_fields[0].value %}
<div class="product-image-container">
<img src="/{{ tenant.id }}{{ matching_fields[0].value }}" alt="{{ custom_field.label }}" class="product-image">
</div>
{% endif %}
{% else %}
{{ matching_fields[0].value if matching_fields else '' }}
{% endif %}
</td>
{% endif %}
{% endfor %}
<td>
<a href="/{{ tenant.id }}/edit/{{ product_id }}" class="btn btn-sm btn-outline-primary">
<i class="bi bi-pencil"></i> Edit
</a>
<button type="button" class="btn btn-sm btn-outline-success"
data-bs-toggle="modal" data-bs-target="#barcodeModal"
data-product-id="{{ product_id }}"
data-product-name="{% set matching_fields = product_data|selectattr('fieldName', 'equalto', '_name')|list %}{{ matching_fields[0].value if matching_fields else product_id }}">
<i class="bi bi-upc-scan"></i> Barcode
</button>
<button type="button" class="btn btn-sm btn-outline-danger"
data-bs-toggle="modal" data-bs-target="#deleteModal"
data-product-id="{{ product_id }}"
data-product-name="{% set matching_fields = product_data|selectattr('fieldName', 'equalto', '_name')|list %}{{ matching_fields[0].value if matching_fields else product_id }}">
<i class="bi bi-trash"></i> Delete
</button>
<form id="delete-form-{{ product_id }}" action="/{{ tenant.id }}/delete/{{ product_id }}" method="POST" style="display: none;">
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="alert alert-info">
<i class="bi bi-info-circle"></i> No products available yet. Click "Add New Product" to get started.
</div>
{% endif %}
{% endif %}
</div>
<div class="mt-4">
<h2>Getting Started</h2>
<ol>
<li>Configure your AR fields to define what information is returned for products</li>
<li>Add products to your catalog with the configured fields</li>
<li>Generate barcodes for your products</li>
<li>Use the AR endpoints in your scanning application</li>
</ol>
</div>
</div>
</div>
</div>
<!-- Barcode Modal -->
<div class="modal fade" id="barcodeModal" tabindex="-1" aria-labelledby="barcodeModalLabel" aria-hidden="true">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="barcodeModalLabel">Product Barcode</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body text-center">
<h6 id="barcode-product-name" class="mb-3"></h6>
<div class="mb-3">
<img id="barcode-image" src="" alt="Product Barcode" class="img-fluid" style="max-width: 400px;">
</div>
<p class="text-muted"><small>Product ID: <code id="barcode-product-id"></code></small></p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
<!-- Delete Confirmation Modal -->
<div class="modal fade" id="deleteModal" tabindex="-1" aria-labelledby="deleteModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header bg-danger text-white">
<h5 class="modal-title" id="deleteModalLabel">Confirm Deletion</h5>
<button type="button" class="btn-close btn-close-white" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure you want to delete the product <strong id="delete-product-name"></strong>?</p>
<p class="text-danger"><i class="bi bi-exclamation-triangle-fill"></i> This action cannot be undone.</p>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="button" class="btn btn-danger" id="confirm-delete-btn">Delete Product</button>
</div>
</div>
</div>
</div>
{% endblock %}
{% block extra_js %}
<script>
// Barcode modal functionality
const barcodeModal = document.getElementById('barcodeModal');
if (barcodeModal) {
barcodeModal.addEventListener('show.bs.modal', function (event) {
// Button that triggered the modal
const button = event.relatedTarget;
// Extract product info from data attributes
const productId = button.getAttribute('data-product-id');
const productName = button.getAttribute('data-product-name');
// Update modal content
const modalProductName = document.getElementById('barcode-product-name');
const modalProductId = document.getElementById('barcode-product-id');
const barcodeImage = document.getElementById('barcode-image');
modalProductName.textContent = productName;
modalProductId.textContent = productId;
// Set barcode image source using tenant's barcode type setting
const barcodeType = '{{ tenant.barcode_type or "code128" }}';
barcodeImage.src = '/{{ tenant.id }}/barcodes/' + productId + '_' + barcodeType + '.png';
});
}
// Delete confirmation modal functionality
const deleteModal = document.getElementById('deleteModal');
if (deleteModal) {
deleteModal.addEventListener('show.bs.modal', function (event) {
// Button that triggered the modal
const button = event.relatedTarget;
// Extract product info from data attributes
const productId = button.getAttribute('data-product-id');
const productName = button.getAttribute('data-product-name');
// Update modal content
const modalProductName = document.getElementById('delete-product-name');
modalProductName.textContent = productName + " (ID: " + productId + ")";
// Setup the confirm button action
const confirmDeleteBtn = document.getElementById('confirm-delete-btn');
confirmDeleteBtn.onclick = function() {
document.getElementById('delete-form-' + productId).submit();
};
});
}
</script>
{% endblock %}