Add new front page with boxes and thing types tree
All checks were successful
Build containers when image tags change / build-if-image-changed (., web, containers, main container, git.baumann.gr/adebaumann/labhelper) (push) Successful in 29s
Build containers when image tags change / build-if-image-changed (data-loader, loader, initContainers, init-container, git.baumann.gr/adebaumann/labhelper-data-loader) (push) Successful in 6s
All checks were successful
Build containers when image tags change / build-if-image-changed (., web, containers, main container, git.baumann.gr/adebaumann/labhelper) (push) Successful in 29s
Build containers when image tags change / build-if-image-changed (data-loader, loader, initContainers, init-container, git.baumann.gr/adebaumann/labhelper-data-loader) (push) Successful in 6s
- Replace simple HTML index with full template - Add grid of all boxes with details and item counts - Add expandable tree view of thing types using MPTT - Add 'mptt' to INSTALLED_APPS for recursetree tag - Add jQuery for tree toggle functionality Bump container version to 0.027
This commit is contained in:
@@ -27,7 +27,7 @@ spec:
|
|||||||
mountPath: /data
|
mountPath: /data
|
||||||
containers:
|
containers:
|
||||||
- name: web
|
- name: web
|
||||||
image: git.baumann.gr/adebaumann/labhelper:0.026
|
image: git.baumann.gr/adebaumann/labhelper:0.027
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
ports:
|
ports:
|
||||||
- containerPort: 8000
|
- containerPort: 8000
|
||||||
|
|||||||
205
boxes/templates/boxes/index.html
Normal file
205
boxes/templates/boxes/index.html
Normal file
@@ -0,0 +1,205 @@
|
|||||||
|
{% load mptt_tags %}
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>LabHelper</title>
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
||||||
|
margin: 20px;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
}
|
||||||
|
h1 {
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
.nav-links {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
.nav-links a {
|
||||||
|
color: #4a90a4;
|
||||||
|
text-decoration: none;
|
||||||
|
margin-left: 20px;
|
||||||
|
}
|
||||||
|
.nav-links a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.section {
|
||||||
|
background: white;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0 1px 3px rgba(0,0,0,0.1);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
.section h2 {
|
||||||
|
color: #333;
|
||||||
|
margin-top: 0;
|
||||||
|
font-size: 20px;
|
||||||
|
border-bottom: 2px solid #4a90a4;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
}
|
||||||
|
.box-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
.box-card {
|
||||||
|
background: #f8f9fa;
|
||||||
|
padding: 15px;
|
||||||
|
border-radius: 6px;
|
||||||
|
border: 1px solid #e0e0e0;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
.box-card:hover {
|
||||||
|
border-color: #4a90a4;
|
||||||
|
box-shadow: 0 2px 5px rgba(74, 144, 164, 0.2);
|
||||||
|
}
|
||||||
|
.box-card a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #333;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.box-card .box-id {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #4a90a4;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
.box-card .box-type {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
.tree {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
.tree ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 20px;
|
||||||
|
}
|
||||||
|
.tree li {
|
||||||
|
padding: 5px 0;
|
||||||
|
}
|
||||||
|
.tree li a {
|
||||||
|
color: #4a90a4;
|
||||||
|
text-decoration: none;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
.tree li a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
.tree-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
.tree-toggle {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #999;
|
||||||
|
font-size: 14px;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.tree ul {
|
||||||
|
list-style: none;
|
||||||
|
padding-left: 20px;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.tree > li > ul {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
.tree li {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.tree li a {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
.toggle-handle {
|
||||||
|
display: inline-block;
|
||||||
|
width: 20px;
|
||||||
|
color: #999;
|
||||||
|
font-weight: bold;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>
|
||||||
|
<script>
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('.toggle-handle').click(function(e) {
|
||||||
|
e.stopPropagation();
|
||||||
|
var $ul = $(this).closest('li').children('ul');
|
||||||
|
if ($ul.length) {
|
||||||
|
$ul.toggle();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
<h1>LabHelper</h1>
|
||||||
|
|
||||||
|
<div class="nav-links">
|
||||||
|
<a href="/search/">Search Things</a>
|
||||||
|
<a href="/admin/">Admin</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Boxes</h2>
|
||||||
|
{% if boxes %}
|
||||||
|
<div class="box-grid">
|
||||||
|
{% for box in boxes %}
|
||||||
|
<div class="box-card">
|
||||||
|
<a href="{% url 'box_detail' box.id %}">
|
||||||
|
<div class="box-id">Box {{ box.id }}</div>
|
||||||
|
<div class="box-type">{{ box.box_type.name }}</div>
|
||||||
|
<div class="box-type" style="font-size: 12px; margin-top: 3px;">
|
||||||
|
{{ box.box_type.width }} x {{ box.box_type.height }} x {{ box.box_type.length }} mm
|
||||||
|
</div>
|
||||||
|
<div class="box-type" style="font-size: 12px; margin-top: 3px;">
|
||||||
|
{{ box.things.count }} item{{ box.things.count|pluralize }}
|
||||||
|
</div>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% else %}
|
||||||
|
<p>No boxes found.</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section">
|
||||||
|
<h2>Thing Types</h2>
|
||||||
|
{% if thing_types %}
|
||||||
|
<ul class="tree">
|
||||||
|
{% recursetree thing_types %}
|
||||||
|
<li>
|
||||||
|
{% if children %}
|
||||||
|
<span class="toggle-handle">[-]</span>
|
||||||
|
{% else %}
|
||||||
|
<span class="toggle-handle"> </span>
|
||||||
|
{% endif %}
|
||||||
|
<a href="{% url 'thing_type_detail' node.pk %}">{{ node.name }}</a>
|
||||||
|
{% if node.things.exists %}
|
||||||
|
<span style="color: #999; font-size: 13px;">({{ node.things.count }})</span>
|
||||||
|
{% endif %}
|
||||||
|
{% if children %}
|
||||||
|
<ul>
|
||||||
|
{{ children }}
|
||||||
|
</ul>
|
||||||
|
{% endif %}
|
||||||
|
</li>
|
||||||
|
{% endrecursetree %}
|
||||||
|
</ul>
|
||||||
|
{% else %}
|
||||||
|
<p>No thing types found.</p>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -6,9 +6,13 @@ from .models import Box, Thing, ThingType
|
|||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
"""Simple index page."""
|
"""Home page with boxes and thing types."""
|
||||||
html = '<h1>LabHelper</h1><p><a href="/search/">Search Things</a> | <a href="/admin/">Admin</a></p>'
|
boxes = Box.objects.select_related('box_type').all().order_by('id')
|
||||||
return HttpResponse(html)
|
thing_types = ThingType.objects.all()
|
||||||
|
return render(request, 'boxes/index.html', {
|
||||||
|
'boxes': boxes,
|
||||||
|
'thing_types': thing_types,
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
def box_detail(request, box_id):
|
def box_detail(request, box_id):
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ INSTALLED_APPS = [
|
|||||||
'django.contrib.sessions',
|
'django.contrib.sessions',
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
|
'mptt',
|
||||||
'django_mptt_admin',
|
'django_mptt_admin',
|
||||||
'sorl.thumbnail',
|
'sorl.thumbnail',
|
||||||
'boxes',
|
'boxes',
|
||||||
|
|||||||
Reference in New Issue
Block a user