feat(portal): real-time search + filter pills on 4 FP list pages
Replaces the tab nav / portal.portal_searchbar on the 4 FP list
pages with the new fp_portal_list_controls macro (filter pills +
search input + sort dropdown) and drops portal_pager in favour of
client-side filtering of up to 500 records:
- Quote Requests (/my/quote_requests):
filters: All / Active / Converted / Declined
sorts: Newest / Reference / Status
extra search fields: contact_name, contact_email, line.part_number,
line.description, line.product_id.default_code
- Work Orders (/my/jobs, cards layout):
filters: All / Active / Ready to Ship / Complete
sorts: Newest / Reference / Status
extra search fields per card: part_catalog.part_number, part_catalog.name,
sale_order.name, sale_order.client_order_ref,
job.notes
- Certifications (/my/certifications):
no filters (all rows are terminal CoC jobs)
sorts: Newest / Reference
extra search fields: part name, processes (already in card text)
- Packing Slips / Deliveries (/my/deliveries):
no filters (all rows are state=done)
sorts: Newest / Reference
adds a visible Origin column (sale order ref) so customers can
locate a slip by the SO it came from
Each route accepts ?filter_state=... and ?sortby=... query params,
returns up to 500 records, and passes result_total + clipped to the
template so the macro can render a "showing latest 500 of N" notice
when the cap is hit.
Hidden <td class="d-none"> cells inside each row carry extra terms
that aren't displayed but are matched by the JS textContent scan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
<odoo>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- QUOTE REQUESTS — list with tabs (Active / Converted / Declined) -->
|
||||
<!-- QUOTE REQUESTS — list with filter pills + real-time search -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_quote_requests" name="My Quote Requests">
|
||||
<t t-call="portal.portal_layout">
|
||||
@@ -15,15 +15,19 @@
|
||||
<t t-set="title">Quote Requests</t>
|
||||
</t>
|
||||
|
||||
<!-- Tab navigation -->
|
||||
<ul class="nav nav-tabs mb-3">
|
||||
<li class="nav-item" t-foreach="searchbar_filters" t-as="f">
|
||||
<a t-attf-class="nav-link #{'active' if filterby == f else ''}"
|
||||
t-attf-href="/my/quote_requests?filterby=#{f}&sortby=#{sortby}">
|
||||
<t t-out="searchbar_filters[f]['label']"/>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Filter pills + search + sort strip -->
|
||||
<t t-call="fusion_plating_portal.fp_portal_list_controls">
|
||||
<t t-set="filters" t-value="filters"/>
|
||||
<t t-set="active_filter" t-value="filter_state"/>
|
||||
<t t-set="sorts" t-value="sorts"/>
|
||||
<t t-set="active_sort" t-value="sortby"/>
|
||||
<t t-set="search" t-value="search"/>
|
||||
<t t-set="url" t-value="url"/>
|
||||
<t t-set="extra_qs" t-value="extra_qs"/>
|
||||
<t t-set="target" t-value="target"/>
|
||||
<t t-set="result_total" t-value="result_total"/>
|
||||
<t t-set="clipped" t-value="clipped"/>
|
||||
</t>
|
||||
|
||||
<div class="d-flex justify-content-end mb-3">
|
||||
<a href="/my/quote_requests/new" class="o_fp_btn_primary">
|
||||
@@ -49,7 +53,7 @@
|
||||
<th class="text-end">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="o_fp_qr_filterable">
|
||||
<tr t-foreach="quote_requests" t-as="qr">
|
||||
<td>
|
||||
<a t-att-href="'/my/quote_requests/%s' % qr.id"
|
||||
@@ -73,6 +77,16 @@
|
||||
<t t-set="label" t-value="dict(qr._fields['state']._description_selection(qr.env)).get(qr.state)"/>
|
||||
</t>
|
||||
</td>
|
||||
<!-- Hidden search fields: contact, part numbers, descriptions -->
|
||||
<td class="d-none" aria-hidden="true">
|
||||
<span t-out="qr.contact_name or ''"/>
|
||||
<span t-out="qr.contact_email or ''"/>
|
||||
<t t-foreach="qr.line_ids" t-as="ln">
|
||||
<span t-out="ln.part_number or ''"/>
|
||||
<span t-out="ln.description or ''"/>
|
||||
<span t-if="ln.product_id" t-out="ln.product_id.default_code or ''"/>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</t>
|
||||
@@ -417,7 +431,7 @@
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- JOBS — list with segmented progress bars -->
|
||||
<!-- JOBS — list with filter pills + real-time search (cards layout) -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_jobs" name="My Work Orders">
|
||||
<t t-call="portal.portal_layout">
|
||||
@@ -425,6 +439,20 @@
|
||||
<t t-set="title">Work Orders</t>
|
||||
</t>
|
||||
|
||||
<!-- Filter pills + search + sort strip -->
|
||||
<t t-call="fusion_plating_portal.fp_portal_list_controls">
|
||||
<t t-set="filters" t-value="filters"/>
|
||||
<t t-set="active_filter" t-value="filter_state"/>
|
||||
<t t-set="sorts" t-value="sorts"/>
|
||||
<t t-set="active_sort" t-value="sortby"/>
|
||||
<t t-set="search" t-value="search"/>
|
||||
<t t-set="url" t-value="url"/>
|
||||
<t t-set="extra_qs" t-value="extra_qs"/>
|
||||
<t t-set="target" t-value="target"/>
|
||||
<t t-set="result_total" t-value="result_total"/>
|
||||
<t t-set="clipped" t-value="clipped"/>
|
||||
</t>
|
||||
|
||||
<t t-if="not jobs">
|
||||
<div class="o_fp_card text-center text-muted">
|
||||
<p class="mb-2">You have no plating jobs yet.</p>
|
||||
@@ -432,11 +460,32 @@
|
||||
</div>
|
||||
</t>
|
||||
<t t-if="jobs">
|
||||
<div class="o_fp_dashboard">
|
||||
<!-- id="fp_jobs_list" is the data-fp-target for the search JS -->
|
||||
<div class="o_fp_dashboard" id="fp_jobs_list">
|
||||
<t t-foreach="jobs" t-as="job">
|
||||
<t t-call="fusion_plating_portal.fp_portal_job_card">
|
||||
<t t-set="job" t-value="job"/>
|
||||
</t>
|
||||
<!-- Wrapper div is the filterable row unit.
|
||||
Hidden span carries extra search terms that
|
||||
are not visible in the card UI. -->
|
||||
<div class="o_fp_job_card_wrap">
|
||||
<t t-call="fusion_plating_portal.fp_portal_job_card">
|
||||
<t t-set="job" t-value="job"/>
|
||||
</t>
|
||||
<!-- Extra hidden search terms for this card -->
|
||||
<t t-set="_backend_job" t-value="job.x_fc_job_id if 'x_fc_job_id' in job._fields else False"/>
|
||||
<t t-set="_so" t-value="_backend_job.sale_order_id if _backend_job and 'sale_order_id' in _backend_job._fields else False"/>
|
||||
<t t-set="_part" t-value="_backend_job.part_catalog_id if _backend_job and 'part_catalog_id' in _backend_job._fields else False"/>
|
||||
<span class="d-none" aria-hidden="true">
|
||||
<t t-if="_part">
|
||||
<t t-out="_part.part_number or ''"/>
|
||||
<t t-out="_part.name or ''"/>
|
||||
</t>
|
||||
<t t-if="_so">
|
||||
<t t-out="_so.name or ''"/>
|
||||
<t t-out="_so.client_order_ref or ''"/>
|
||||
</t>
|
||||
<t t-out="job.notes or ''"/>
|
||||
</span>
|
||||
</div>
|
||||
</t>
|
||||
</div>
|
||||
</t>
|
||||
@@ -574,7 +623,7 @@
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- DELIVERIES / PACKING SLIPS — list -->
|
||||
<!-- DELIVERIES / PACKING SLIPS — list with search + sort -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_deliveries" name="My Deliveries">
|
||||
<t t-call="portal.portal_layout">
|
||||
@@ -582,6 +631,20 @@
|
||||
<t t-set="title">Packing Slips / Deliveries</t>
|
||||
</t>
|
||||
|
||||
<!-- Search + sort strip (no filter pills — all rows are delivered) -->
|
||||
<t t-call="fusion_plating_portal.fp_portal_list_controls">
|
||||
<t t-set="filters" t-value="filters"/>
|
||||
<t t-set="active_filter" t-value="filter_state"/>
|
||||
<t t-set="sorts" t-value="sorts"/>
|
||||
<t t-set="active_sort" t-value="sortby"/>
|
||||
<t t-set="search" t-value="search"/>
|
||||
<t t-set="url" t-value="url"/>
|
||||
<t t-set="extra_qs" t-value="extra_qs"/>
|
||||
<t t-set="target" t-value="target"/>
|
||||
<t t-set="result_total" t-value="result_total"/>
|
||||
<t t-set="clipped" t-value="clipped"/>
|
||||
</t>
|
||||
|
||||
<t t-if="not deliveries">
|
||||
<div class="o_fp_portal_card card bg-body-tertiary border-0 p-4 text-center">
|
||||
<p class="text-muted mb-0">No deliveries found.</p>
|
||||
@@ -591,13 +654,17 @@
|
||||
<thead>
|
||||
<tr class="active">
|
||||
<th>Reference</th>
|
||||
<th>Origin</th>
|
||||
<th>Date</th>
|
||||
<th class="text-end">Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="o_fp_deliveries_filterable">
|
||||
<tr t-foreach="deliveries" t-as="dlv">
|
||||
<td t-out="dlv.name"/>
|
||||
<td>
|
||||
<span t-out="dlv.origin or ''"/>
|
||||
</td>
|
||||
<td>
|
||||
<span t-if="dlv.date_done"
|
||||
t-field="dlv.date_done"
|
||||
@@ -608,6 +675,13 @@
|
||||
<span class="o_fp_badge_dot"/>Delivered
|
||||
</span>
|
||||
</td>
|
||||
<!-- Hidden: partner ref / customer PO on the origin SO -->
|
||||
<td class="d-none" aria-hidden="true">
|
||||
<t t-if="dlv.sale_id">
|
||||
<span t-out="dlv.sale_id.name or ''"/>
|
||||
<span t-out="dlv.sale_id.client_order_ref or ''"/>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</t>
|
||||
@@ -615,7 +689,7 @@
|
||||
</template>
|
||||
|
||||
<!-- ================================================================== -->
|
||||
<!-- CERTIFICATIONS — list -->
|
||||
<!-- CERTIFICATIONS — list with search + sort -->
|
||||
<!-- ================================================================== -->
|
||||
<template id="portal_my_certifications" name="My Certifications">
|
||||
<t t-call="portal.portal_layout">
|
||||
@@ -623,6 +697,20 @@
|
||||
<t t-set="title">Certifications & Quality</t>
|
||||
</t>
|
||||
|
||||
<!-- Search + sort strip (no filter pills — all certs are terminal) -->
|
||||
<t t-call="fusion_plating_portal.fp_portal_list_controls">
|
||||
<t t-set="filters" t-value="filters"/>
|
||||
<t t-set="active_filter" t-value="filter_state"/>
|
||||
<t t-set="sorts" t-value="sorts"/>
|
||||
<t t-set="active_sort" t-value="sortby"/>
|
||||
<t t-set="search" t-value="search"/>
|
||||
<t t-set="url" t-value="url"/>
|
||||
<t t-set="extra_qs" t-value="extra_qs"/>
|
||||
<t t-set="target" t-value="target"/>
|
||||
<t t-set="result_total" t-value="result_total"/>
|
||||
<t t-set="clipped" t-value="clipped"/>
|
||||
</t>
|
||||
|
||||
<t t-if="not cert_jobs">
|
||||
<div class="o_fp_portal_card card bg-body-tertiary border-0 p-4 text-center">
|
||||
<p class="text-muted mb-0">No certificates available yet.</p>
|
||||
@@ -637,7 +725,7 @@
|
||||
<th class="text-end">Download</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tbody class="o_fp_certs_filterable">
|
||||
<tr t-foreach="cert_jobs" t-as="cj">
|
||||
<td>
|
||||
<a t-att-href="'/my/jobs/%s' % cj.id" t-out="cj.name"/>
|
||||
@@ -658,6 +746,19 @@
|
||||
<i class="fa fa-download"/> CoC
|
||||
</a>
|
||||
</td>
|
||||
<!-- Hidden: part name, customer PO from the backend job -->
|
||||
<td class="d-none" aria-hidden="true">
|
||||
<t t-set="_bj" t-value="cj.x_fc_job_id if 'x_fc_job_id' in cj._fields else False"/>
|
||||
<t t-set="_so" t-value="_bj.sale_order_id if _bj and 'sale_order_id' in _bj._fields else False"/>
|
||||
<t t-set="_part" t-value="_bj.part_catalog_id if _bj and 'part_catalog_id' in _bj._fields else False"/>
|
||||
<t t-if="_part">
|
||||
<span t-out="_part.part_number or ''"/>
|
||||
<span t-out="_part.name or ''"/>
|
||||
</t>
|
||||
<t t-if="_so">
|
||||
<span t-out="_so.client_order_ref or ''"/>
|
||||
</t>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</t>
|
||||
|
||||
Reference in New Issue
Block a user