Adding filtering to tables
This commit is contained in:
1 parent
d67b4ceb65
commit
a50fccd56d
10 files changed
+329
-81
No files matched your search
@@ -24,6 +24,55 @@ async def list_all(
|
|||||||
return list(result.scalars().all())
|
return list(result.scalars().all())
|
||||||
|
|
||||||
|
|
||||||
|
async def list_filtered(
|
||||||
|
db: AsyncSession,
|
||||||
|
description: str = "",
|
||||||
|
colour_id: int | None = None,
|
||||||
|
manufacturer_id: int | None = None,
|
||||||
|
scope_model_id: int | None = None,
|
||||||
|
part_type_id: int | None = None,
|
||||||
|
sub_part_type_id: int | None = None,
|
||||||
|
skip: int = 0,
|
||||||
|
limit: int = 100,
|
||||||
|
) -> list[AugmentedPart]:
|
||||||
|
q = select(AugmentedPart)
|
||||||
|
if description.strip():
|
||||||
|
q = q.where(AugmentedPart.description.ilike(f"%{description.strip()}%"))
|
||||||
|
if colour_id is not None:
|
||||||
|
q = q.where(AugmentedPart.colour_id == colour_id)
|
||||||
|
if manufacturer_id is not None:
|
||||||
|
q = q.where(AugmentedPart.manufacturer_id == manufacturer_id)
|
||||||
|
if scope_model_id is not None:
|
||||||
|
q = q.where(AugmentedPart.scope_model_id == scope_model_id)
|
||||||
|
if part_type_id is not None:
|
||||||
|
q = q.where(AugmentedPart.part_type_id == part_type_id)
|
||||||
|
if sub_part_type_id is not None:
|
||||||
|
q = q.where(AugmentedPart.sub_part_type_id == sub_part_type_id)
|
||||||
|
result = await db.execute(q.order_by(AugmentedPart.description).offset(skip).limit(limit))
|
||||||
|
return list(result.scalars().all())
|
||||||
|
|
||||||
|
|
||||||
|
async def get_unlinked_imported_parts_filtered(
|
||||||
|
db: AsyncSession,
|
||||||
|
description: str = "",
|
||||||
|
colour: str = "",
|
||||||
|
) -> list[ImportedPart]:
|
||||||
|
q = (
|
||||||
|
select(ImportedPart)
|
||||||
|
.where(
|
||||||
|
ImportedPart.InventoryID.not_in(
|
||||||
|
select(augmented_part_imported_parts.c.imported_part_id)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if description.strip():
|
||||||
|
q = q.where(ImportedPart.Description.ilike(f"%{description.strip()}%"))
|
||||||
|
if colour.strip():
|
||||||
|
q = q.where(ImportedPart.TBXColour.ilike(f"%{colour.strip()}%"))
|
||||||
|
result = await db.execute(q.order_by(ImportedPart.InventoryID))
|
||||||
|
return list(result.scalars().all())
|
||||||
|
|
||||||
|
|
||||||
async def create(
|
async def create(
|
||||||
db: AsyncSession,
|
db: AsyncSession,
|
||||||
*,
|
*,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from sqlalchemy import insert, select, update
|
from sqlalchemy import distinct, insert, select, update
|
||||||
from sqlalchemy.ext.asyncio import AsyncSession
|
from sqlalchemy.ext.asyncio import AsyncSession
|
||||||
|
|
||||||
from app.models.imported_part import ImportedPart
|
from app.models.imported_part import ImportedPart
|
||||||
@@ -23,6 +23,28 @@ async def list_all(
|
|||||||
return list(result.scalars().all())
|
return list(result.scalars().all())
|
||||||
|
|
||||||
|
|
||||||
|
async def list_filtered(
|
||||||
|
db: AsyncSession,
|
||||||
|
description: str = "",
|
||||||
|
skip: int = 0,
|
||||||
|
limit: int = 500,
|
||||||
|
) -> list[ImportedPart]:
|
||||||
|
q = select(ImportedPart)
|
||||||
|
if description.strip():
|
||||||
|
q = q.where(ImportedPart.Description.ilike(f"%{description.strip()}%"))
|
||||||
|
result = await db.execute(q.order_by(ImportedPart.InventoryID).offset(skip).limit(limit))
|
||||||
|
return list(result.scalars().all())
|
||||||
|
|
||||||
|
|
||||||
|
async def distinct_colours(db: AsyncSession) -> list[str]:
|
||||||
|
result = await db.execute(
|
||||||
|
select(distinct(ImportedPart.TBXColour))
|
||||||
|
.where(ImportedPart.TBXColour.isnot(None))
|
||||||
|
.order_by(ImportedPart.TBXColour)
|
||||||
|
)
|
||||||
|
return [row[0] for row in result.all()]
|
||||||
|
|
||||||
|
|
||||||
async def count(db: AsyncSession) -> int:
|
async def count(db: AsyncSession) -> int:
|
||||||
from sqlalchemy import func
|
from sqlalchemy import func
|
||||||
result = await db.execute(select(func.count()).select_from(ImportedPart))
|
result = await db.execute(select(func.count()).select_from(ImportedPart))
|
||||||
|
|||||||
@@ -400,6 +400,39 @@ input[type=password] {
|
|||||||
.ap-item-conn { flex: 1; }
|
.ap-item-conn { flex: 1; }
|
||||||
.ap-item .row-actions { margin-left: auto; }
|
.ap-item .row-actions { margin-left: auto; }
|
||||||
|
|
||||||
|
/* Filter bar */
|
||||||
|
.filter-bar {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar form {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar input[type="text"] {
|
||||||
|
flex: 1 1 180px;
|
||||||
|
min-width: 0;
|
||||||
|
padding: 0.35rem 0.6rem;
|
||||||
|
background: var(--surface-2);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: var(--text);
|
||||||
|
border-radius: 6px;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter-bar select {
|
||||||
|
flex: 0 1 auto;
|
||||||
|
padding: 0.35rem 0.6rem;
|
||||||
|
background: var(--surface-2);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: var(--text);
|
||||||
|
border-radius: 6px;
|
||||||
|
font: inherit;
|
||||||
|
}
|
||||||
|
|
||||||
/* Buttons */
|
/* Buttons */
|
||||||
.btn {
|
.btn {
|
||||||
font: inherit;
|
font: inherit;
|
||||||
|
|||||||
@@ -5,28 +5,44 @@
|
|||||||
{% if sync_error %}
|
{% if sync_error %}
|
||||||
<p class="alert alert-error">{{ sync_error }}</p>
|
<p class="alert alert-error">{{ sync_error }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
<div class="card">
|
<div class="filter-bar">
|
||||||
<table class="table">
|
<form hx-get="/admin/augmented-parts/filter"
|
||||||
<thead>
|
hx-target="#augmented-parts-table-card"
|
||||||
<tr>
|
hx-trigger="input delay:300ms, change"
|
||||||
<th>ID</th>
|
hx-swap="outerHTML">
|
||||||
<th>SKU</th>
|
<input type="text" name="description" placeholder="Filter by description…" value="{{ filter_description or '' }}">
|
||||||
<th>Description</th>
|
<select name="colour_id">
|
||||||
<th>Colour</th>
|
<option value="">All colours</option>
|
||||||
<th>Manufacturer</th>
|
{% for pc in part_colours %}
|
||||||
<th>Model</th>
|
<option value="{{ pc.id }}" {{ "selected" if filter_colour_id and filter_colour_id|int == pc.id }}>{{ pc.name }}</option>
|
||||||
<th>Type</th>
|
|
||||||
<th>Sub Type</th>
|
|
||||||
<th></th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for ap in augmented_parts %}
|
|
||||||
{% include "admin/_augmented_part_row.html" %}
|
|
||||||
{% else %}
|
|
||||||
<tr><td colspan="9" class="muted">No augmented parts recorded.</td></tr>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</select>
|
||||||
</table>
|
<select name="manufacturer_id">
|
||||||
|
<option value="">All manufacturers</option>
|
||||||
|
{% for m in manufacturers %}
|
||||||
|
<option value="{{ m.id }}" {{ "selected" if filter_manufacturer_id and filter_manufacturer_id|int == m.id }}>{{ m.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<select name="scope_model_id">
|
||||||
|
<option value="">All models</option>
|
||||||
|
{% for sm in scope_models %}
|
||||||
|
<option value="{{ sm.id }}" {{ "selected" if filter_scope_model_id and filter_scope_model_id|int == sm.id }}>{{ sm.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<select name="part_type_id">
|
||||||
|
<option value="">All types</option>
|
||||||
|
{% for pt in part_types %}
|
||||||
|
<option value="{{ pt.id }}" {{ "selected" if filter_part_type_id and filter_part_type_id|int == pt.id }}>{{ pt.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
<select name="sub_part_type_id">
|
||||||
|
<option value="">All sub types</option>
|
||||||
|
{% for spt in sub_part_types %}
|
||||||
|
<option value="{{ spt.id }}" {{ "selected" if filter_sub_part_type_id and filter_sub_part_type_id|int == spt.id }}>{{ spt.name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% include "admin/_augmented_parts_table_card.html" %}
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<div id="augmented-parts-table-card" class="card">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>ID</th>
|
||||||
|
<th>SKU</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Colour</th>
|
||||||
|
<th>Manufacturer</th>
|
||||||
|
<th>Model</th>
|
||||||
|
<th>Type</th>
|
||||||
|
<th>Sub Type</th>
|
||||||
|
<th></th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for ap in augmented_parts %}
|
||||||
|
{% include "admin/_augmented_part_row.html" %}
|
||||||
|
{% else %}
|
||||||
|
<tr><td colspan="9" class="muted">No augmented parts recorded.</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@@ -6,39 +6,14 @@
|
|||||||
<p style="color:var(--color-danger,#c00)">{{ sync_error }}</p>
|
<p style="color:var(--color-danger,#c00)">{{ sync_error }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<div class="card" style="overflow-x:auto">
|
<div class="filter-bar">
|
||||||
<p class="muted" style="margin-bottom:.5rem">
|
<form hx-get="/admin/imported-parts/filter"
|
||||||
Showing {{ parts|length }} record{{ '' if parts|length == 1 else 's' }}{% if total and total > parts|length %} of {{ total }} total{% endif %}.
|
hx-target="#imported-parts-table-card"
|
||||||
</p>
|
hx-trigger="input delay:300ms, change"
|
||||||
<table class="table">
|
hx-swap="outerHTML">
|
||||||
<thead>
|
<input type="text" name="description" placeholder="Filter by description…" value="{{ filter_description or '' }}">
|
||||||
<tr>
|
</form>
|
||||||
<th>InventoryID</th>
|
|
||||||
<th>Description</th>
|
|
||||||
<th>TBX Colour</th>
|
|
||||||
<th>Max Thread Depth (mm)</th>
|
|
||||||
<th>Max Thread Depth (in)</th>
|
|
||||||
<th>OD (mm)</th>
|
|
||||||
<th>OD (in)</th>
|
|
||||||
<th>Thread Attribute</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{% for p in parts %}
|
|
||||||
<tr>
|
|
||||||
<td>{{ p.InventoryID }}</td>
|
|
||||||
<td>{{ p.Description or '—' }}</td>
|
|
||||||
<td>{{ p.TBXColour or '—' }}</td>
|
|
||||||
<td>{{ p.ProductMaxThreadDepthmm or '—' }}</td>
|
|
||||||
<td>{{ p.ProductMaxThreadDepthin or '—' }}</td>
|
|
||||||
<td>{{ p.ProductODmm or '—' }}</td>
|
|
||||||
<td>{{ p.ProductODin or '—' }}</td>
|
|
||||||
<td>{{ p.TBXThreadAttribute or '—' }}</td>
|
|
||||||
</tr>
|
|
||||||
{% else %}
|
|
||||||
<tr><td colspan="8" class="muted">No imported parts. Use Refresh to sync from OData.</td></tr>
|
|
||||||
{% endfor %}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% include "admin/_imported_parts_table_card.html" %}
|
||||||
</div>
|
</div>
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
<div id="imported-parts-table-card" class="card" style="overflow-x:auto">
|
||||||
|
<p class="muted" style="margin-bottom:.5rem">
|
||||||
|
Showing {{ parts|length }} record{{ '' if parts|length == 1 else 's' }}{% if total and total > parts|length %} of {{ total }} total{% endif %}.
|
||||||
|
</p>
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>InventoryID</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>TBX Colour</th>
|
||||||
|
<th>Max Thread Depth (mm)</th>
|
||||||
|
<th>Max Thread Depth (in)</th>
|
||||||
|
<th>OD (mm)</th>
|
||||||
|
<th>OD (in)</th>
|
||||||
|
<th>Thread Attribute</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for p in parts %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ p.InventoryID }}</td>
|
||||||
|
<td>{{ p.Description or '—' }}</td>
|
||||||
|
<td>{{ p.TBXColour or '—' }}</td>
|
||||||
|
<td>{{ p.ProductMaxThreadDepthmm or '—' }}</td>
|
||||||
|
<td>{{ p.ProductMaxThreadDepthin or '—' }}</td>
|
||||||
|
<td>{{ p.ProductODmm or '—' }}</td>
|
||||||
|
<td>{{ p.ProductODin or '—' }}</td>
|
||||||
|
<td>{{ p.TBXThreadAttribute or '—' }}</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr><td colspan="8" class="muted">No imported parts. Use Refresh to sync from OData.</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
{% if sync_result %}<p class="alert alert-success">{{ sync_result }}</p>{% endif %}
|
{% if sync_result %}<p class="alert alert-success">{{ sync_result }}</p>{% endif %}
|
||||||
{% if sync_error %}<p class="alert alert-error">{{ sync_error }}</p>{% endif %}
|
{% if sync_error %}<p class="alert alert-error">{{ sync_error }}</p>{% endif %}
|
||||||
|
|
||||||
{% if unlinked_parts %}
|
{% if any_unlinked %}
|
||||||
<form hx-post="/admin/augmented-parts/skipped"
|
<form hx-post="/admin/augmented-parts/skipped"
|
||||||
hx-target="#skipped-parts-list"
|
hx-target="#skipped-parts-list"
|
||||||
hx-swap="outerHTML">
|
hx-swap="outerHTML">
|
||||||
@@ -29,29 +29,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card" style="overflow-x:auto">
|
<div class="filter-bar">
|
||||||
<table class="table">
|
<form hx-get="/admin/augmented-parts/skipped/filter"
|
||||||
<thead>
|
hx-target="#skipped-parts-table-card"
|
||||||
<tr>
|
hx-trigger="input delay:300ms, change"
|
||||||
<th><input type="checkbox" id="select-all" title="Select all"></th>
|
hx-swap="outerHTML">
|
||||||
<th>Inventory ID</th>
|
<input type="text" name="description" placeholder="Filter by description…" value="{{ filter_description or '' }}">
|
||||||
<th>Description</th>
|
<select name="colour">
|
||||||
<th>Colour</th>
|
<option value="">All colours</option>
|
||||||
</tr>
|
{% for c in skipped_colours %}
|
||||||
</thead>
|
<option value="{{ c }}" {{ "selected" if filter_colour == c }}>{{ c }}</option>
|
||||||
<tbody>
|
|
||||||
{% for ip in unlinked_parts %}
|
|
||||||
<tr>
|
|
||||||
<td><input type="checkbox" name="inventory_ids" value="{{ ip.InventoryID }}"></td>
|
|
||||||
<td><code>{{ ip.InventoryID }}</code></td>
|
|
||||||
<td>{{ ip.Description | truncate(80) if ip.Description else '—' }}</td>
|
|
||||||
<td>{{ ip.TBXColour or '—' }}</td>
|
|
||||||
</tr>
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</select>
|
||||||
</table>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{% include "admin/_skipped_parts_table_card.html" %}
|
||||||
|
|
||||||
</form>
|
</form>
|
||||||
{% else %}
|
{% else %}
|
||||||
<div class="card">
|
<div class="card">
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
<div id="skipped-parts-table-card" class="card" style="overflow-x:auto">
|
||||||
|
<table class="table">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th><input type="checkbox" id="select-all" title="Select all"></th>
|
||||||
|
<th>Inventory ID</th>
|
||||||
|
<th>Description</th>
|
||||||
|
<th>Colour</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for ip in unlinked_parts %}
|
||||||
|
<tr>
|
||||||
|
<td><input type="checkbox" name="inventory_ids" value="{{ ip.InventoryID }}"></td>
|
||||||
|
<td><code>{{ ip.InventoryID }}</code></td>
|
||||||
|
<td>{{ ip.Description | truncate(80) if ip.Description else '—' }}</td>
|
||||||
|
<td>{{ ip.TBXColour or '—' }}</td>
|
||||||
|
</tr>
|
||||||
|
{% else %}
|
||||||
|
<tr><td colspan="4" class="muted">No parts match the current filter.</td></tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
+79
-3
@@ -1124,6 +1124,21 @@ async def refresh_imported_parts(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/imported-parts/filter", response_class=HTMLResponse)
|
||||||
|
async def filter_imported_parts(
|
||||||
|
request: Request,
|
||||||
|
description: str = "",
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
_: User = Depends(web_require("imported_parts:read")),
|
||||||
|
):
|
||||||
|
parts = await imported_part_crud.list_filtered(db, description=description)
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
request,
|
||||||
|
"admin/_imported_parts_table_card.html",
|
||||||
|
{"parts": parts},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
# --- Augmented Parts ---------------------------------------------------------
|
# --- Augmented Parts ---------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
@@ -1179,10 +1194,11 @@ async def create_augmented_part(
|
|||||||
"AugmentedPart", created.id, created.sku or created.description
|
"AugmentedPart", created.id, created.sku or created.description
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
form_ctx = await _augmented_part_form_ctx(db)
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"admin/_augmented_parts_table.html",
|
"admin/_augmented_parts_table.html",
|
||||||
{"augmented_parts": await augmented_part_crud.list_all(db)},
|
{**form_ctx, "augmented_parts": await augmented_part_crud.list_all(db)},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -1211,10 +1227,12 @@ async def sync_augmented_parts(
|
|||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
sync_result = None
|
sync_result = None
|
||||||
sync_error = f"Sync failed: {exc}"
|
sync_error = f"Sync failed: {exc}"
|
||||||
|
form_ctx = await _augmented_part_form_ctx(db)
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"admin/_augmented_parts_table.html",
|
"admin/_augmented_parts_table.html",
|
||||||
{
|
{
|
||||||
|
**form_ctx,
|
||||||
"augmented_parts": await augmented_part_crud.list_all(db),
|
"augmented_parts": await augmented_part_crud.list_all(db),
|
||||||
"sync_result": sync_result,
|
"sync_result": sync_result,
|
||||||
"sync_error": sync_error,
|
"sync_error": sync_error,
|
||||||
@@ -1222,6 +1240,34 @@ async def sync_augmented_parts(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/augmented-parts/filter", response_class=HTMLResponse)
|
||||||
|
async def filter_augmented_parts(
|
||||||
|
request: Request,
|
||||||
|
description: str = "",
|
||||||
|
colour_id: str = "",
|
||||||
|
manufacturer_id: str = "",
|
||||||
|
scope_model_id: str = "",
|
||||||
|
part_type_id: str = "",
|
||||||
|
sub_part_type_id: str = "",
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
_: User = Depends(web_require("augmented_parts:read")),
|
||||||
|
):
|
||||||
|
parts = await augmented_part_crud.list_filtered(
|
||||||
|
db,
|
||||||
|
description=description,
|
||||||
|
colour_id=int(colour_id) if colour_id else None,
|
||||||
|
manufacturer_id=int(manufacturer_id) if manufacturer_id else None,
|
||||||
|
scope_model_id=int(scope_model_id) if scope_model_id else None,
|
||||||
|
part_type_id=int(part_type_id) if part_type_id else None,
|
||||||
|
sub_part_type_id=int(sub_part_type_id) if sub_part_type_id else None,
|
||||||
|
)
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
request,
|
||||||
|
"admin/_augmented_parts_table_card.html",
|
||||||
|
{"augmented_parts": parts},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/augmented-parts/skipped", response_class=HTMLResponse)
|
@router.get("/augmented-parts/skipped", response_class=HTMLResponse)
|
||||||
async def skipped_parts_page(
|
async def skipped_parts_page(
|
||||||
request: Request,
|
request: Request,
|
||||||
@@ -1229,13 +1275,19 @@ async def skipped_parts_page(
|
|||||||
user: User = Depends(web_require("augmented_parts:read")),
|
user: User = Depends(web_require("augmented_parts:read")),
|
||||||
):
|
):
|
||||||
form_ctx = await _augmented_part_form_ctx(db)
|
form_ctx = await _augmented_part_form_ctx(db)
|
||||||
|
unlinked = await augmented_part_crud.get_unlinked_imported_parts(db)
|
||||||
|
skipped_colours = sorted({p.TBXColour for p in unlinked if p.TBXColour})
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"admin/augmented_parts_skipped.html",
|
"admin/augmented_parts_skipped.html",
|
||||||
{
|
{
|
||||||
"user": user,
|
"user": user,
|
||||||
**form_ctx,
|
**form_ctx,
|
||||||
"unlinked_parts": await augmented_part_crud.get_unlinked_imported_parts(db),
|
"unlinked_parts": unlinked,
|
||||||
|
"any_unlinked": bool(unlinked),
|
||||||
|
"skipped_colours": skipped_colours,
|
||||||
|
"filter_description": "",
|
||||||
|
"filter_colour": "",
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -1288,18 +1340,42 @@ async def sync_skipped_parts(
|
|||||||
sync_result = f"{synced} part(s) synced successfully."
|
sync_result = f"{synced} part(s) synced successfully."
|
||||||
|
|
||||||
form_ctx = await _augmented_part_form_ctx(db)
|
form_ctx = await _augmented_part_form_ctx(db)
|
||||||
|
unlinked = await augmented_part_crud.get_unlinked_imported_parts(db)
|
||||||
|
skipped_colours = sorted({p.TBXColour for p in unlinked if p.TBXColour})
|
||||||
return templates.TemplateResponse(
|
return templates.TemplateResponse(
|
||||||
request,
|
request,
|
||||||
"admin/_skipped_parts_table.html",
|
"admin/_skipped_parts_table.html",
|
||||||
{
|
{
|
||||||
**form_ctx,
|
**form_ctx,
|
||||||
"unlinked_parts": await augmented_part_crud.get_unlinked_imported_parts(db),
|
"unlinked_parts": unlinked,
|
||||||
|
"any_unlinked": bool(unlinked),
|
||||||
|
"skipped_colours": skipped_colours,
|
||||||
|
"filter_description": "",
|
||||||
|
"filter_colour": "",
|
||||||
"sync_result": sync_result,
|
"sync_result": sync_result,
|
||||||
"sync_error": sync_error,
|
"sync_error": sync_error,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@router.get("/augmented-parts/skipped/filter", response_class=HTMLResponse)
|
||||||
|
async def filter_skipped_parts(
|
||||||
|
request: Request,
|
||||||
|
description: str = "",
|
||||||
|
colour: str = "",
|
||||||
|
db: AsyncSession = Depends(get_db),
|
||||||
|
_: User = Depends(web_require("augmented_parts:read")),
|
||||||
|
):
|
||||||
|
unlinked = await augmented_part_crud.get_unlinked_imported_parts_filtered(
|
||||||
|
db, description=description, colour=colour
|
||||||
|
)
|
||||||
|
return templates.TemplateResponse(
|
||||||
|
request,
|
||||||
|
"admin/_skipped_parts_table_card.html",
|
||||||
|
{"unlinked_parts": unlinked},
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@router.get("/augmented-parts/{augmented_part_id}/edit", response_class=HTMLResponse)
|
@router.get("/augmented-parts/{augmented_part_id}/edit", response_class=HTMLResponse)
|
||||||
async def edit_augmented_part_row(
|
async def edit_augmented_part_row(
|
||||||
request: Request,
|
request: Request,
|
||||||
|
|||||||
Reference in new issue
Block a user