diff --git a/app/models.py b/app/models.py
index eba7e1e7ecbc221bf12a35e1f42039dab294f1e9..40125e8fac935ba7e1482e075ff773fa643c1374 100644
--- a/app/models.py
+++ b/app/models.py
@@ -132,6 +132,8 @@ class StepPart(db.Model):
name="uq_step_part",
),
)
+
+
class StationInventory(db.Model):
__tablename__ = "station_inventory"
@@ -147,6 +149,12 @@ class StationInventory(db.Model):
nullable=False,
)
quantity = db.Column(db.Integer, nullable=False, default=0)
+ updated_at = db.Column(
+ db.DateTime,
+ nullable=False,
+ default=lambda: datetime.now(timezone.utc),
+ onupdate=lambda: datetime.now(timezone.utc),
+ )
station = db.relationship("Station")
part = db.relationship("Part")
@@ -207,7 +215,6 @@ class ProductUnit(db.Model):
)
unit_number = db.Column(db.Integer, nullable=False)
-
status = db.Column(db.String(20), nullable=False, default="Pending")
job = db.relationship("Job")
@@ -255,4 +262,4 @@ class UnitStepProgress(db.Model):
"job_step_id",
name="uq_unit_step_progress",
),
- )
\ No newline at end of file
+ )
diff --git a/app/routes.py b/app/routes.py
index ae9af9edb070281c56540562d8b431880e9979bf..20ce6bb0945aa137e223cff33bcb4d1afcaee9fa 100644
--- a/app/routes.py
+++ b/app/routes.py
@@ -323,7 +323,7 @@ def admin_photos():
)
photos = InstructionPhoto.query.order_by(
- InstructionPhoto.name
+ InstructionPhoto.uploaded_at.desc()
).all()
return render_template(
@@ -449,8 +449,7 @@ def admin_inventory():
parts = Part.query.order_by(Part.name).all()
inventory_rows = StationInventory.query.order_by(
- StationInventory.station_id,
- StationInventory.part_id,
+ StationInventory.updated_at.desc()
).all()
return render_template(
diff --git a/app/static/list-controls.js b/app/static/list-controls.js
new file mode 100644
index 0000000000000000000000000000000000000000..238d626e0be2786a43e1645ab624ccf1d82e770a
--- /dev/null
+++ b/app/static/list-controls.js
@@ -0,0 +1,107 @@
+document.addEventListener("DOMContentLoaded", function () {
+ document.querySelectorAll(".list-search-block").forEach(function (block) {
+ var limit = parseInt(block.dataset.limit, 10) || 5;
+ var increment = parseInt(block.dataset.increment, 10) || 10;
+ var rows = Array.prototype.slice.call(
+ block.querySelectorAll(".list-row")
+ );
+ var searchInput = block.querySelector(".list-search-input");
+ var toggleButton = block.querySelector(".list-toggle-button");
+ var emptyMessage = block.querySelector(".list-empty-message");
+ var visibleLimit = limit;
+
+ function applyView() {
+ var query = (
+ (searchInput && searchInput.value) || ""
+ ).trim().toLowerCase();
+
+ var visibleCount = 0;
+
+ rows.forEach(function (row) {
+ var matches = (
+ !query
+ || (row.dataset.search || "").indexOf(query) !== -1
+ );
+
+ if (!matches) {
+ row.hidden = true;
+ return;
+ }
+
+ var withinLimit = (
+ Boolean(query) || visibleCount < visibleLimit
+ );
+
+ row.hidden = !withinLimit;
+
+ if (withinLimit) {
+ visibleCount += 1;
+ }
+ });
+
+ if (emptyMessage) {
+ emptyMessage.hidden = visibleCount !== 0;
+ }
+
+ if (toggleButton) {
+ if (query || rows.length <= limit) {
+ toggleButton.hidden = true;
+ return;
+ }
+
+ var remaining = rows.length - visibleLimit;
+
+ toggleButton.hidden = false;
+
+ if (remaining > 0) {
+ var nextBatch = Math.min(increment, remaining);
+
+ toggleButton.textContent = (
+ "Show " + nextBatch + " more ("
+ + remaining + " remaining)"
+ );
+
+ toggleButton.dataset.mode = "more";
+ } else {
+ toggleButton.textContent = "Show fewer";
+ toggleButton.dataset.mode = "less";
+ }
+ }
+ }
+
+ if (searchInput) {
+ searchInput.addEventListener("input", applyView);
+ }
+
+ if (toggleButton) {
+ toggleButton.addEventListener("click", function () {
+ if (toggleButton.dataset.mode === "less") {
+ visibleLimit = limit;
+ } else {
+ visibleLimit = Math.min(
+ visibleLimit + increment,
+ rows.length
+ );
+ }
+
+ applyView();
+ });
+ }
+
+ applyView();
+ });
+
+ document.querySelectorAll(".photo-preview-toggle").forEach(
+ function (button) {
+ button.addEventListener("click", function () {
+ var target = document.getElementById(
+ button.dataset.photoTarget
+ );
+
+ if (target) {
+ target.hidden = !target.hidden;
+ }
+ });
+ }
+ );
+});
diff --git a/app/static/style.css b/app/static/style.css
index 5e7d271253cb0ee958d98eb8d94d36b8c178bfab..0ec418213412407c538e0dfa647de7ffe41a4dfe 100644
--- a/app/static/style.css
+++ b/app/static/style.css
@@ -195,4 +195,80 @@ hr {
border-radius: 6px;
object-fit: contain;
background: #ffffff;
-}
\ No newline at end of file
+}
+
+.list-search-block {
+ margin: 16px 0;
+}
+
+.list-search-input {
+ display: block;
+ width: 100%;
+ max-width: 420px;
+ margin-bottom: 10px;
+ padding: 10px;
+ border: 1px solid var(--border);
+ border-radius: 5px;
+ background: #ffffff;
+ color: var(--text);
+}
+
+.list-toggle-button {
+ margin-top: 10px;
+ padding: 8px 14px;
+ border: 1px solid var(--primary);
+ border-radius: 5px;
+ background: #ffffff;
+ color: var(--primary);
+ font-weight: 600;
+ cursor: pointer;
+}
+
+.list-toggle-button:hover,
+.list-toggle-button:focus {
+ background: var(--primary);
+ color: #ffffff;
+}
+
+.list-empty-message {
+ padding: 10px 0;
+ color: var(--text);
+ font-style: italic;
+}
+
+.photo-name-list {
+ list-style: none;
+ margin: 16px 0;
+ padding: 0;
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ background: var(--surface);
+}
+
+.photo-name-list .list-row {
+ border-bottom: 1px solid var(--border);
+}
+
+.photo-name-list .list-row:last-child {
+ border-bottom: 0;
+}
+
+.photo-preview-toggle {
+ width: 100%;
+ padding: 12px 14px;
+ border: 0;
+ background: transparent;
+ color: var(--primary);
+ font-weight: 600;
+ text-align: left;
+ cursor: pointer;
+}
+
+.photo-preview-toggle:hover,
+.photo-preview-toggle:focus {
+ text-decoration: underline;
+}
+
+.photo-preview {
+ padding: 0 14px 16px;
+}
diff --git a/app/templates/admin_inventory.html b/app/templates/admin_inventory.html
index bf604aa6513ecb1578171c4e03180c38226aac9c..31f9fb3d0c5ed3c2cbced668b1ad9e0c6c2cc042 100644
--- a/app/templates/admin_inventory.html
+++ b/app/templates/admin_inventory.html
@@ -11,6 +11,10 @@
rel="stylesheet"
href="{{ url_for('static', filename='style.css') }}"
>
+
The Digital Factory Game
@@ -165,23 +169,43 @@
Current Station Inventory
{% if inventory_rows %}
-
-
- | Station |
- Part |
- Unit |
- Quantity |
-
-
- {% for inventory in inventory_rows %}
+
+
+
+
- | {{ inventory.station.name }} |
- {{ inventory.part.name }} |
- {{ inventory.part.unit }} |
- {{ inventory.quantity }} |
+ Station |
+ Part |
+ Unit |
+ Quantity |
- {% endfor %}
-
+
+ {% for inventory in inventory_rows %}
+
+ | {{ inventory.station.name }} |
+ {{ inventory.part.name }} |
+ {{ inventory.part.unit }} |
+ {{ inventory.quantity }} |
+
+ {% endfor %}
+
+
+
+ No inventory rows match your search.
+
+
+
+
{% else %}
No station inventory entered yet.
{% endif %}
diff --git a/app/templates/admin_jobs.html b/app/templates/admin_jobs.html
index efc3b525c4683b260a6a631af01152659216f032..72b5bdda51b5a6b6fdfdf18ae384c082531b6185 100644
--- a/app/templates/admin_jobs.html
+++ b/app/templates/admin_jobs.html
@@ -11,6 +11,10 @@
rel="stylesheet"
href="{{ url_for('static', filename='style.css') }}"
>
+
The Digital Factory Game
@@ -69,20 +73,49 @@
+
Existing Jobs
{% if jobs %}
-
- {% for job in jobs %}
- -
-
- {{ job.name }}
-
- - Quantity: {{ job.quantity }}
- - Status: {{ job.status }}
-
- {% endfor %}
-
+
+
+
+
+
+ | Name |
+ Quantity |
+ Status |
+
+
+ {% for job in jobs %}
+
+ |
+
+ {{ job.name }}
+
+ |
+ {{ job.quantity }} |
+ {{ job.status }} |
+
+ {% endfor %}
+
+
+
+ No jobs match your search.
+
+
+
+
{% else %}
No production jobs created yet.
{% endif %}
diff --git a/app/templates/admin_photos.html b/app/templates/admin_photos.html
index 20f099d3789428e0d61df43329b0952807b56af5..721644a4ee2d2cd864ff84fe501b9bbf167fb3e0 100644
--- a/app/templates/admin_photos.html
+++ b/app/templates/admin_photos.html
@@ -11,6 +11,10 @@
rel="stylesheet"
href="{{ url_for('static', filename='style.css') }}"
>
+
The Digital Factory Game
@@ -73,32 +77,64 @@
Stored Instruction Photos
{% if photos %}
-
- {% for photo in photos %}
-
- {{ photo.name }}
+
+
-
 }})
+ {% for photo in photos %}
+
+
+
+
+
 }})
+
+
+ Original file:
+ {{ photo.original_filename }}
+
+
+
+ Uploaded:
+ {{ photo.uploaded_at }}
+
+
+
+ {% endfor %}
+
-
- Original file:
- {{ photo.original_filename }}
-
+
+ No photos match your search.
+
-
- Uploaded:
- {{ photo.uploaded_at }}
-
-
- {% endfor %}
+
{% else %}
No instruction photos uploaded yet.
diff --git a/app/templates/transfer_requests.html b/app/templates/transfer_requests.html
index c1f1b39ea2ffe484aadd78cf043962782ed49b61..e987731b547b38796cf9d1866207cf88047af866 100644
--- a/app/templates/transfer_requests.html
+++ b/app/templates/transfer_requests.html
@@ -11,6 +11,10 @@
rel="stylesheet"
href="{{ url_for('static', filename='style.css') }}"
>
+
The Digital Factory Game
@@ -19,55 +23,75 @@
Manage station replenishment requests.
{% if transfer_requests %}
-
-
- | Request |
- Job |
- Station |
- Part |
- Requested |
- Transferred |
- Status |
- Action |
-
+
+
+
+ No transfer requests match your search.
+
+
+
+
{% else %}
No transfer requests.
{% endif %}