fix: support pagination in UI
Browse files- templates/products.html +58 -1
- web/routes.py +29 -3
templates/products.html
CHANGED
|
@@ -100,6 +100,55 @@
|
|
| 100 |
</tbody>
|
| 101 |
</table>
|
| 102 |
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
</div>
|
| 104 |
</div>
|
| 105 |
</div>
|
|
@@ -115,6 +164,8 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
| 115 |
// Get the base URL for the download
|
| 116 |
const baseUrl = "/products/download-csv";
|
| 117 |
const filter = "{{ filter_query }}";
|
|
|
|
|
|
|
| 118 |
const page = "{{ pagination.page }}";
|
| 119 |
const perPage = "{{ pagination.per_page }}";
|
| 120 |
|
|
@@ -123,6 +174,12 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
| 123 |
if (filter) {
|
| 124 |
downloadUrl += `&filter=${encodeURIComponent(filter)}`;
|
| 125 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 126 |
|
| 127 |
// Create a custom dialog for filename
|
| 128 |
const dialogContainer = document.createElement('div');
|
|
@@ -271,7 +328,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|
| 271 |
console.error('Error in CSV download dialog:', error);
|
| 272 |
|
| 273 |
// Fallback to direct download if the dialog fails
|
| 274 |
-
window.location.href = "/products/download-csv?filter={{ filter_query }}&page={{ pagination.page }}&per_page={{ pagination.per_page }}";
|
| 275 |
}
|
| 276 |
});
|
| 277 |
}
|
|
|
|
| 100 |
</tbody>
|
| 101 |
</table>
|
| 102 |
</div>
|
| 103 |
+
|
| 104 |
+
<!-- Pagination -->
|
| 105 |
+
{% if pagination.pages > 1 %}
|
| 106 |
+
<nav class="mt-4">
|
| 107 |
+
<ul class="pagination justify-content-center">
|
| 108 |
+
{% macro page_url(page_num) -%}
|
| 109 |
+
{{ url_for('web.products') }}?
|
| 110 |
+
{%- for key, value in request.args.items() if key != 'page' and value %}
|
| 111 |
+
{{ key }}={{ value|urlencode }}&
|
| 112 |
+
{%- endfor -%}
|
| 113 |
+
page={{ page_num }}
|
| 114 |
+
{%- endmacro %}
|
| 115 |
+
|
| 116 |
+
{% if pagination.has_prev %}
|
| 117 |
+
<li class="page-item">
|
| 118 |
+
<a class="page-link" href="{{ page_url(pagination.prev_num) }}">
|
| 119 |
+
<i data-feather="chevron-left"></i>
|
| 120 |
+
</a>
|
| 121 |
+
</li>
|
| 122 |
+
{% endif %}
|
| 123 |
+
|
| 124 |
+
{% for page_num in pagination.iter_pages() %}
|
| 125 |
+
{% if page_num %}
|
| 126 |
+
{% if page_num != pagination.page %}
|
| 127 |
+
<li class="page-item">
|
| 128 |
+
<a class="page-link" href="{{ page_url(page_num) }}">{{ page_num }}</a>
|
| 129 |
+
</li>
|
| 130 |
+
{% else %}
|
| 131 |
+
<li class="page-item active">
|
| 132 |
+
<span class="page-link">{{ page_num }}</span>
|
| 133 |
+
</li>
|
| 134 |
+
{% endif %}
|
| 135 |
+
{% else %}
|
| 136 |
+
<li class="page-item disabled">
|
| 137 |
+
<span class="page-link">…</span>
|
| 138 |
+
</li>
|
| 139 |
+
{% endif %}
|
| 140 |
+
{% endfor %}
|
| 141 |
+
|
| 142 |
+
{% if pagination.has_next %}
|
| 143 |
+
<li class="page-item">
|
| 144 |
+
<a class="page-link" href="{{ page_url(pagination.next_num) }}">
|
| 145 |
+
<i data-feather="chevron-right"></i>
|
| 146 |
+
</a>
|
| 147 |
+
</li>
|
| 148 |
+
{% endif %}
|
| 149 |
+
</ul>
|
| 150 |
+
</nav>
|
| 151 |
+
{% endif %}
|
| 152 |
</div>
|
| 153 |
</div>
|
| 154 |
</div>
|
|
|
|
| 164 |
// Get the base URL for the download
|
| 165 |
const baseUrl = "/products/download-csv";
|
| 166 |
const filter = "{{ filter_query }}";
|
| 167 |
+
const version = "{{ version_filter }}";
|
| 168 |
+
const showAll = "{{ show_all }}" === "True" ? "1" : "";
|
| 169 |
const page = "{{ pagination.page }}";
|
| 170 |
const perPage = "{{ pagination.per_page }}";
|
| 171 |
|
|
|
|
| 174 |
if (filter) {
|
| 175 |
downloadUrl += `&filter=${encodeURIComponent(filter)}`;
|
| 176 |
}
|
| 177 |
+
if (version) {
|
| 178 |
+
downloadUrl += `&version=${encodeURIComponent(version)}`;
|
| 179 |
+
}
|
| 180 |
+
if (showAll) {
|
| 181 |
+
downloadUrl += `&show_all=${showAll}`;
|
| 182 |
+
}
|
| 183 |
|
| 184 |
// Create a custom dialog for filename
|
| 185 |
const dialogContainer = document.createElement('div');
|
|
|
|
| 328 |
console.error('Error in CSV download dialog:', error);
|
| 329 |
|
| 330 |
// Fallback to direct download if the dialog fails
|
| 331 |
+
window.location.href = "/products/download-csv?filter={{ filter_query }}&version={{ version_filter }}&page={{ pagination.page }}&per_page={{ pagination.per_page }}{% if show_all %}&show_all=1{% endif %}";
|
| 332 |
}
|
| 333 |
});
|
| 334 |
}
|
web/routes.py
CHANGED
|
@@ -248,7 +248,15 @@ def download_products_csv():
|
|
| 248 |
page = request.args.get('page', 1, type=int)
|
| 249 |
per_page = request.args.get('per_page', 20, type=int)
|
| 250 |
custom_filename = request.args.get('filename', '')
|
| 251 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 252 |
from models import Badge
|
| 253 |
all_badges = Badge.query.order_by(Badge.name).all()
|
| 254 |
output = io.StringIO()
|
|
@@ -2417,8 +2425,26 @@ def test_webhook_ui(webhook_id):
|
|
| 2417 |
@web_bp.route('/products')
|
| 2418 |
def products():
|
| 2419 |
filter_query = request.args.get('filter', '')
|
| 2420 |
-
|
| 2421 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2422 |
|
| 2423 |
# Product update API endpoint
|
| 2424 |
@web_bp.route('/products/update', methods=['POST'])
|
|
|
|
| 248 |
page = request.args.get('page', 1, type=int)
|
| 249 |
per_page = request.args.get('per_page', 20, type=int)
|
| 250 |
custom_filename = request.args.get('filename', '')
|
| 251 |
+
user_id = session.get('user_id')
|
| 252 |
+
show_all = request.args.get('show_all') and session.get('is_admin', False)
|
| 253 |
+
|
| 254 |
+
products, pagination = get_products_with_stats(
|
| 255 |
+
filter_query=filter_query,
|
| 256 |
+
version_filter=version_filter,
|
| 257 |
+
user_id=user_id,
|
| 258 |
+
show_all=show_all
|
| 259 |
+
)
|
| 260 |
from models import Badge
|
| 261 |
all_badges = Badge.query.order_by(Badge.name).all()
|
| 262 |
output = io.StringIO()
|
|
|
|
| 2425 |
@web_bp.route('/products')
|
| 2426 |
def products():
|
| 2427 |
filter_query = request.args.get('filter', '')
|
| 2428 |
+
version_filter = request.args.get('version', '')
|
| 2429 |
+
user_id = session.get('user_id')
|
| 2430 |
+
show_all = request.args.get('show_all') and session.get('is_admin', False)
|
| 2431 |
+
|
| 2432 |
+
products, pagination = get_products_with_stats(
|
| 2433 |
+
filter_query=filter_query,
|
| 2434 |
+
version_filter=version_filter,
|
| 2435 |
+
user_id=user_id,
|
| 2436 |
+
show_all=show_all
|
| 2437 |
+
)
|
| 2438 |
+
|
| 2439 |
+
return render_template(
|
| 2440 |
+
'products.html',
|
| 2441 |
+
products=products,
|
| 2442 |
+
pagination=pagination,
|
| 2443 |
+
filter_query=filter_query,
|
| 2444 |
+
version_filter=version_filter,
|
| 2445 |
+
show_all=show_all,
|
| 2446 |
+
is_admin=session.get('is_admin', False)
|
| 2447 |
+
)
|
| 2448 |
|
| 2449 |
# Product update API endpoint
|
| 2450 |
@web_bp.route('/products/update', methods=['POST'])
|