add a button to download products as csv

This commit is contained in:
imperosol 2024-12-14 00:11:25 +01:00
parent 39b36aa509
commit 1a9556f811
2 changed files with 78 additions and 4 deletions

View File

@ -1,8 +1,12 @@
import { paginated } from "#core:utils/api";
import { csv } from "#core:utils/csv";
import { History, getCurrentUrlParams, updateQueryString } from "#core:utils/history";
import type { NestedKeyOf } from "#core:utils/types";
import { showSaveFilePicker } from "native-file-system-adapter";
import {
type ProductSchema,
productSearchProductsDetailed,
type ProductSearchProductsDetailedData,
productSearchProductsDetailed,
} from "#openapi";
type ProductType = string;
@ -11,6 +15,38 @@ type GroupedProducts = Record<ProductType, ProductSchema[]>;
const defaultPageSize = 100;
const defaultPage = 1;
/**
* Keys of the properties to include in the CSV.
*/
const csvColumns = [
"id",
"name",
"code",
"description",
"product_type.name",
"club.name",
"limit_age",
"purchase_price",
"selling_price",
"archived",
] as NestedKeyOf<ProductSchema>[];
/**
* Title of the csv columns.
*/
const csvColumnTitles = [
"id",
gettext("name"),
"code",
"description",
gettext("product type"),
"club",
gettext("limit age"),
gettext("purchase price"),
gettext("selling price"),
gettext("archived"),
];
document.addEventListener("alpine:init", () => {
Alpine.data("productList", () => ({
loading: false,
@ -84,5 +120,33 @@ document.addEventListener("alpine:init", () => {
}, {});
this.loading = false;
},
/**
* Download products corresponding to the current filters as a CSV file.
* If the pagination has multiple pages, all pages are downloaded.
*/
async downloadCsv() {
this.csvLoading = true;
const fileHandle = await showSaveFilePicker({
_preferPolyfill: false,
suggestedName: gettext("products.csv"),
types: [],
excludeAcceptAllOption: false,
});
// if products to download are already in-memory, directly take them.
// If not, fetch them.
const products =
this.nbPages > 1
? await paginated(productSearchProductsDetailed, this.getQueryParams())
: Object.values<ProductSchema[]>(this.products).flat();
const content = csv.stringify(products, {
columns: csvColumns,
titleRow: csvColumnTitles,
});
const file = await fileHandle.createWritable();
await file.write(content);
await file.close();
this.csvLoading = false;
},
}));
});

View File

@ -39,9 +39,19 @@
</form>
<h3 @click="console.log(totalCount, nbPages())">{% trans %}Product list{% endtrans %}</h3>
<div class="row">
<a href="{{ url('counter:new_product') }}" class="btn btn-blue">
{% trans %}New product{% endtrans %} <i class="fa fa-plus"></i>
</a>
<button
class="btn btn-blue"
@click="downloadCsv()"
:disabled="csvLoading"
:aria-busy="csvLoading"
>
{% trans %}Download as cvs{% endtrans %} <i class="fa fa-file-arrow-down"></i>
</button>
</div>
<template x-if="loading">
<section :aria-busy="loading"></section>
</template>