Fase 3 feedback round 3: sidebar icons, thumbnail preview, location search

- Sidebar submenu bullets (List all/Add new/Batch create) swapped from
  far fa-circle to fas fa-angle-right - the outlined circle read as an
  unchecked radio button, per feedback.
- List-page qr thumbnails now sit in a fixed 100x100 box with
  object-fit:contain instead of width/height attrs, so taller images
  (e.g. icon-above-qr) no longer get squashed into a square. Thumbnails
  are now clickable, opening a shared Bootstrap modal with the full-size
  image (same data-toggle/data-target pattern already used for the
  delete-confirmation modal).
- Location QR form gets an address search box backed by OpenStreetMap
  Nominatim (dist/js/location-search.js): free-text query, pick a result,
  it fills in latitude/longitude. No API key needed; the browser talks to
  nominatim.openstreetmap.org directly, called out in the field's help
  text since queries leave the self-hosted server.
This commit is contained in:
2026-07-11 02:18:34 +02:00
parent 7276744f32
commit 89bf472867
6 changed files with 178 additions and 17 deletions
+17
View File
@@ -6,4 +6,21 @@
#err-msg { #err-msg {
display: none; display: none;
color: red; color: red;
}
/* List-page qr code thumbnails: fixed box, but object-fit:contain instead of
width/height attrs so taller images (e.g. icon-above-qr) don't get squashed. */
.qr-thumb-link {
display: inline-block;
padding: 0;
border: 0;
background: none;
cursor: pointer;
}
.qr-thumb-img {
width: 100px;
height: 100px;
object-fit: contain;
background: #fff;
border: 1px solid #dee2e6;
} }
+69
View File
@@ -0,0 +1,69 @@
(function () {
document.addEventListener('DOMContentLoaded', function () {
var searchInput = document.getElementById('location_search');
var searchBtn = document.getElementById('location_search_btn');
var resultsBox = document.getElementById('location_search_results');
var latInput = document.getElementById('latitude');
var lngInput = document.getElementById('longitude');
if (!searchInput || !searchBtn || !resultsBox || !latInput || !lngInput) {
return;
}
function clearResults() {
resultsBox.innerHTML = '';
}
function doSearch() {
var query = searchInput.value.trim();
if (!query) {
return;
}
clearResults();
resultsBox.innerHTML = '<div class="list-group-item">Searching...</div>';
fetch('https://nominatim.openstreetmap.org/search?format=json&limit=5&q=' + encodeURIComponent(query))
.then(function (response) { return response.json(); })
.then(function (results) {
clearResults();
if (!results.length) {
resultsBox.innerHTML = '<div class="list-group-item">No results found.</div>';
return;
}
results.forEach(function (place) {
var item = document.createElement('button');
item.type = 'button';
item.className = 'list-group-item list-group-item-action';
item.textContent = place.display_name;
item.addEventListener('click', function () {
latInput.value = place.lat;
lngInput.value = place.lon;
searchInput.value = place.display_name;
clearResults();
});
resultsBox.appendChild(item);
});
})
.catch(function () {
resultsBox.innerHTML = '<div class="list-group-item text-danger">Search failed (network error).</div>';
});
}
searchBtn.addEventListener('click', doSearch);
searchInput.addEventListener('keydown', function (e) {
if (e.key === 'Enter') {
e.preventDefault();
doSearch();
}
});
document.addEventListener('click', function (e) {
if (e.target !== searchInput && !resultsBox.contains(e.target)) {
clearResults();
}
});
});
})();
+25 -10
View File
@@ -2,26 +2,41 @@
<?php echo csrf_field(); ?> <?php echo csrf_field(); ?>
<?php include BASE_PATH.'/forms/qrcode_options.php'; ?> <?php include BASE_PATH.'/forms/qrcode_options.php'; ?>
<!-- Input forms --> <!-- Input forms -->
<div class="col-sm-4"> <div class="col-sm-8">
<div class="form-group"> <div class="form-group" style="position: relative;">
<label>Latitude *</label> <label for="location_search">Search address</label>
<input type="text" name="latitude" value="" placeholder="40.7127753" class="form-control"> <div class="input-group">
<input type="text" id="location_search" class="form-control" placeholder="Search for an address or place...">
<div class="input-group-append">
<button type="button" id="location_search_btn" class="btn btn-outline-secondary"><i class="fa fa-search"></i></button>
</div>
</div>
<div id="location_search_results" class="list-group" style="position:absolute;z-index:1000;width:100%;"></div>
<small class="form-text text-muted">Looks up coordinates via OpenStreetMap Nominatim (your search leaves this server and goes to nominatim.openstreetmap.org). Or enter coordinates directly below.</small>
</div> </div>
</div> </div>
<div class="col-sm-4"> <div class="col-sm-4">
<div class="form-group"> <div class="form-group">
<label>Longitude *</label> <label for="latitude">Latitude *</label>
<input type="text" name="longitude" value="" placeholder="-74.0059728" class="form-control"> <input type="text" name="latitude" id="latitude" value="" placeholder="40.7127753" class="form-control">
</div> </div>
</div> </div>
<div class="col-sm-4">
<div class="form-group">
<label for="longitude">Longitude *</label>
<input type="text" name="longitude" id="longitude" value="" placeholder="-74.0059728" class="form-control">
</div>
</div>
<div class="col-sm-12 mb-2"> <div class="col-sm-12 mb-2">
<div class="row"> <div class="row">
<div class="col-6 col-md-3"> <div class="col-6 col-md-3">
<button type="submit" class="btn btn-primary">Submit</button> <button type="submit" class="btn btn-primary">Submit</button>
</div> </div>
</div> </div>
</div> </div>
<script src="dist/js/location-search.js"></script>
</form> </form>
+31 -1
View File
@@ -70,7 +70,11 @@
<td><?php echo htmlspecialchars($row['identifier']); ?></td> <td><?php echo htmlspecialchars($row['identifier']); ?></td>
<td><?php echo htmlspecialchars($row['link']); ?></td> <td><?php echo htmlspecialchars($row['link']); ?></td>
<td> <td>
<?php echo '<img src="qrcode_image.php?type=dynamic&id='.$row['id'].'" width="100" height="100">'; ?> <a href="#" class="qr-thumb-link" data-toggle="modal" data-target="#preview-modal"
data-qr-src="qrcode_image.php?type=dynamic&id=<?php echo $row['id']; ?>"
data-qr-name="<?php echo htmlspecialchars($row['filename']); ?>">
<img src="qrcode_image.php?type=dynamic&id=<?php echo $row['id']; ?>" class="qr-thumb-img" alt="QR code for <?php echo htmlspecialchars($row['filename']); ?>">
</a>
</td> </td>
<td><?php echo htmlspecialchars($row['scan']); ?></td> <td><?php echo htmlspecialchars($row['scan']); ?></td>
<td><?php echo htmlspecialchars($row['state']); ?></td> <td><?php echo htmlspecialchars($row['state']); ?></td>
@@ -135,6 +139,32 @@
<!-- /.Delete Confirmation Modal --> <!-- /.Delete Confirmation Modal -->
<?php endif; ?> <?php endif; ?>
<!-- Qr Code Preview Modal -->
<div class="modal fade" id="preview-modal" role="dialog">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="preview-modal-title">QR code preview</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body text-center">
<img id="preview-modal-img" src="" alt="" style="max-width:100%;max-height:70vh;">
</div>
</div>
</div>
</div>
<!-- /.Qr Code Preview Modal -->
<script>
document.querySelectorAll('.qr-thumb-link').forEach(function (link) {
link.addEventListener('click', function (e) {
e.preventDefault();
document.getElementById('preview-modal-img').src = link.getAttribute('data-qr-src');
document.getElementById('preview-modal-title').textContent = link.getAttribute('data-qr-name');
});
});
</script>
<script> <script>
const deleteButtons = document.querySelectorAll('.delete_btn'); const deleteButtons = document.querySelectorAll('.delete_btn');
+31 -1
View File
@@ -68,7 +68,11 @@
<td><?php echo htmlspecialchars($row['type']); ?></td> <td><?php echo htmlspecialchars($row['type']); ?></td>
<td><?php echo htmlspecialchars_decode($row['content']); ?></td> <td><?php echo htmlspecialchars_decode($row['content']); ?></td>
<td> <td>
<?php echo '<img src="qrcode_image.php?type=static&id='.$row['id'].'" width="100" height="100">'; ?> <a href="#" class="qr-thumb-link" data-toggle="modal" data-target="#preview-modal"
data-qr-src="qrcode_image.php?type=static&id=<?php echo $row['id']; ?>"
data-qr-name="<?php echo htmlspecialchars($row['filename']); ?>">
<img src="qrcode_image.php?type=static&id=<?php echo $row['id']; ?>" class="qr-thumb-img" alt="QR code for <?php echo htmlspecialchars($row['filename']); ?>">
</a>
</td> </td>
<td> <td>
<?php if (!$is_readonly_user): ?> <?php if (!$is_readonly_user): ?>
@@ -131,6 +135,32 @@
<!-- /.Delete Confirmation Modal --> <!-- /.Delete Confirmation Modal -->
<?php endif; ?> <?php endif; ?>
<!-- Qr Code Preview Modal -->
<div class="modal fade" id="preview-modal" role="dialog">
<div class="modal-dialog modal-dialog-centered">
<div class="modal-content">
<div class="modal-header">
<h4 class="modal-title" id="preview-modal-title">QR code preview</h4>
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>
</div>
<div class="modal-body text-center">
<img id="preview-modal-img" src="" alt="" style="max-width:100%;max-height:70vh;">
</div>
</div>
</div>
</div>
<!-- /.Qr Code Preview Modal -->
<script>
document.querySelectorAll('.qr-thumb-link').forEach(function (link) {
link.addEventListener('click', function (e) {
e.preventDefault();
document.getElementById('preview-modal-img').src = link.getAttribute('data-qr-src');
document.getElementById('preview-modal-title').textContent = link.getAttribute('data-qr-name');
});
});
</script>
<script> <script>
const deleteButtons = document.querySelectorAll('.delete_btn'); const deleteButtons = document.querySelectorAll('.delete_btn');
+5 -5
View File
@@ -53,20 +53,20 @@
<ul class="nav nav-treeview"> <ul class="nav nav-treeview">
<li class="nav-item"> <li class="nav-item">
<a href="./dynamic_qrcodes.php" <?php echo ((substr(CURRENT_PAGE, 0, 19) == 'dynamic_qrcodes.php')) ? ' class="nav-link active"' : ' class="nav-link"'; ?>> <a href="./dynamic_qrcodes.php" <?php echo ((substr(CURRENT_PAGE, 0, 19) == 'dynamic_qrcodes.php')) ? ' class="nav-link active"' : ' class="nav-link"'; ?>>
<i class="far fa-circle nav-icon"></i> <i class="fas fa-angle-right nav-icon"></i>
<p>List all</p> <p>List all</p>
</a> </a>
</li> </li>
<?php if ($_SESSION['type'] !== 'user'): ?> <?php if ($_SESSION['type'] !== 'user'): ?>
<li class="nav-item"> <li class="nav-item">
<a href="./dynamic_qrcode.php" <?php echo (CURRENT_PAGE == 'dynamic_qrcode.php') ? ' class="nav-link active"' : ' class="nav-link"'; ?>> <a href="./dynamic_qrcode.php" <?php echo (CURRENT_PAGE == 'dynamic_qrcode.php') ? ' class="nav-link active"' : ' class="nav-link"'; ?>>
<i class="far fa-circle nav-icon"></i> <i class="fas fa-angle-right nav-icon"></i>
<p>Add new</p> <p>Add new</p>
</a> </a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a href="./batch_qrcode.php" <?php echo (CURRENT_PAGE == 'batch_qrcode.php') ? ' class="nav-link active"' : ' class="nav-link"'; ?>> <a href="./batch_qrcode.php" <?php echo (CURRENT_PAGE == 'batch_qrcode.php') ? ' class="nav-link active"' : ' class="nav-link"'; ?>>
<i class="far fa-circle nav-icon"></i> <i class="fas fa-angle-right nav-icon"></i>
<p>Batch create (CSV)</p> <p>Batch create (CSV)</p>
</a> </a>
</li> </li>
@@ -86,14 +86,14 @@
<ul class="nav nav-treeview"> <ul class="nav nav-treeview">
<li class="nav-item"> <li class="nav-item">
<a href="./static_qrcodes.php" <?php echo ((substr(CURRENT_PAGE, 0, 18) == 'static_qrcodes.php')) ? ' class="nav-link active"' : ' class="nav-link"'; ?>> <a href="./static_qrcodes.php" <?php echo ((substr(CURRENT_PAGE, 0, 18) == 'static_qrcodes.php')) ? ' class="nav-link active"' : ' class="nav-link"'; ?>>
<i class="far fa-circle nav-icon"></i> <i class="fas fa-angle-right nav-icon"></i>
<p>List all</p> <p>List all</p>
</a> </a>
</li> </li>
<?php if ($_SESSION['type'] !== 'user'): ?> <?php if ($_SESSION['type'] !== 'user'): ?>
<li class="nav-item"> <li class="nav-item">
<a href="./static_qrcode.php" <?php echo (CURRENT_PAGE == 'static_qrcode.php') ? ' class="nav-link active"' : ' class="nav-link"'; ?>> <a href="./static_qrcode.php" <?php echo (CURRENT_PAGE == 'static_qrcode.php') ? ' class="nav-link active"' : ' class="nav-link"'; ?>>
<i class="far fa-circle nav-icon"></i> <i class="fas fa-angle-right nav-icon"></i>
<p>Add new</p> <p>Add new</p>
</a> </a>
</li> </li>