mirror of
https://github.com/ae-utbm/sith.git
synced 2024-12-23 00:01:16 +00:00
Make products filterable by product type
This commit is contained in:
parent
6953eaa9d0
commit
accf1befce
@ -107,7 +107,7 @@ form {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
label {
|
label, legend {
|
||||||
display: block;
|
display: block;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
@ -429,6 +429,16 @@ body {
|
|||||||
.row {
|
.row {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
|
||||||
|
$col-gap: 1rem;
|
||||||
|
&.gap {
|
||||||
|
column-gap: var($col-gap);
|
||||||
|
}
|
||||||
|
@for $i from 2 through 5 {
|
||||||
|
&.gap-#{$i}x {
|
||||||
|
column-gap: $i * $col-gap;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*---------------------------------NEWS--------------------------------*/
|
/*---------------------------------NEWS--------------------------------*/
|
||||||
|
@ -4,9 +4,11 @@ import type { TomOption } from "tom-select/dist/types/types";
|
|||||||
import type { escape_html } from "tom-select/dist/types/utils";
|
import type { escape_html } from "tom-select/dist/types/utils";
|
||||||
import {
|
import {
|
||||||
type CounterSchema,
|
type CounterSchema,
|
||||||
|
type ProductTypeSchema,
|
||||||
type SimpleProductSchema,
|
type SimpleProductSchema,
|
||||||
counterSearchCounter,
|
counterSearchCounter,
|
||||||
productSearchProducts,
|
productSearchProducts,
|
||||||
|
producttypeFetchAll,
|
||||||
} from "#openapi";
|
} from "#openapi";
|
||||||
|
|
||||||
@registerComponent("product-ajax-select")
|
@registerComponent("product-ajax-select")
|
||||||
@ -34,6 +36,37 @@ export class ProductAjaxSelect extends AjaxSelect {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@registerComponent("product-type-ajax-select")
|
||||||
|
export class ProductTypeAjaxSelect extends AjaxSelect {
|
||||||
|
protected valueField = "id";
|
||||||
|
protected labelField = "name";
|
||||||
|
protected searchField = ["name"];
|
||||||
|
private productTypes = null as ProductTypeSchema[];
|
||||||
|
|
||||||
|
protected async search(query: string): Promise<TomOption[]> {
|
||||||
|
// The production database has a grand total of 26 product types
|
||||||
|
// and the filter logic is really simple.
|
||||||
|
// Thus, it's appropriate to fetch all product types during first use,
|
||||||
|
// then to reuse the result again and again.
|
||||||
|
if (this.productTypes === null) {
|
||||||
|
this.productTypes = (await producttypeFetchAll()).data || null;
|
||||||
|
}
|
||||||
|
return this.productTypes.filter((t) =>
|
||||||
|
t.name.toLowerCase().includes(query.toLowerCase()),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderOption(item: ProductTypeSchema, sanitize: typeof escape_html) {
|
||||||
|
return `<div class="select-item">
|
||||||
|
<span class="select-item-text">${sanitize(item.name)}</span>
|
||||||
|
</div>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected renderItem(item: ProductTypeSchema, sanitize: typeof escape_html) {
|
||||||
|
return `<span>${sanitize(item.name)}</span>`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@registerComponent("counter-ajax-select")
|
@registerComponent("counter-ajax-select")
|
||||||
export class CounterAjaxSelect extends AjaxSelect {
|
export class CounterAjaxSelect extends AjaxSelect {
|
||||||
protected valueField = "id";
|
protected valueField = "id";
|
||||||
|
@ -3,6 +3,7 @@ import { csv } from "#core:utils/csv";
|
|||||||
import { History, getCurrentUrlParams, updateQueryString } from "#core:utils/history";
|
import { History, getCurrentUrlParams, updateQueryString } from "#core:utils/history";
|
||||||
import type { NestedKeyOf } from "#core:utils/types";
|
import type { NestedKeyOf } from "#core:utils/types";
|
||||||
import { showSaveFilePicker } from "native-file-system-adapter";
|
import { showSaveFilePicker } from "native-file-system-adapter";
|
||||||
|
import type TomSelect from "tom-select";
|
||||||
import {
|
import {
|
||||||
type ProductSchema,
|
type ProductSchema,
|
||||||
type ProductSearchProductsDetailedData,
|
type ProductSearchProductsDetailedData,
|
||||||
@ -58,6 +59,7 @@ document.addEventListener("alpine:init", () => {
|
|||||||
|
|
||||||
productStatus: "" as "active" | "archived" | "both",
|
productStatus: "" as "active" | "archived" | "both",
|
||||||
search: "",
|
search: "",
|
||||||
|
productTypes: [] as string[],
|
||||||
pageSize: defaultPageSize,
|
pageSize: defaultPageSize,
|
||||||
page: defaultPage,
|
page: defaultPage,
|
||||||
|
|
||||||
@ -65,17 +67,22 @@ document.addEventListener("alpine:init", () => {
|
|||||||
const url = getCurrentUrlParams();
|
const url = getCurrentUrlParams();
|
||||||
this.search = url.get("search") || "";
|
this.search = url.get("search") || "";
|
||||||
this.productStatus = url.get("productStatus") ?? "active";
|
this.productStatus = url.get("productStatus") ?? "active";
|
||||||
|
const widget = this.$refs.productTypesInput.widget as TomSelect;
|
||||||
|
widget.on("change", (items: string[]) => {
|
||||||
|
this.productTypes = [...items];
|
||||||
|
});
|
||||||
|
|
||||||
await this.load();
|
await this.load();
|
||||||
for (const param of ["search", "productStatus"]) {
|
const searchParams = ["search", "productStatus", "productTypes"];
|
||||||
|
for (const param of searchParams) {
|
||||||
this.$watch(param, () => {
|
this.$watch(param, () => {
|
||||||
this.page = defaultPage;
|
this.page = defaultPage;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
for (const param of ["search", "productStatus", "page"]) {
|
for (const param of [...searchParams, "page"]) {
|
||||||
this.$watch(param, async (value: string) => {
|
this.$watch(param, async (value: string) => {
|
||||||
updateQueryString(param, value, History.Replace);
|
updateQueryString(param, value, History.Replace);
|
||||||
this.nbPages = 0;
|
this.nbPages = 0;
|
||||||
this.products = {};
|
|
||||||
await this.load();
|
await this.load();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -100,6 +107,8 @@ document.addEventListener("alpine:init", () => {
|
|||||||
search: search,
|
search: search,
|
||||||
// biome-ignore lint/style/useNamingConvention: api is in snake_case
|
// biome-ignore lint/style/useNamingConvention: api is in snake_case
|
||||||
is_archived: isArchived,
|
is_archived: isArchived,
|
||||||
|
// biome-ignore lint/style/useNamingConvention: api is in snake_case
|
||||||
|
product_type: this.productTypes,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
@ -6,18 +6,23 @@
|
|||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block additional_js %}
|
{% block additional_js %}
|
||||||
|
<script type="module" src="{{ static("bundled/counter/components/ajax-select-index.ts") }}"></script>
|
||||||
<script type="module" src="{{ static("bundled/counter/product-list-index.ts") }}"></script>
|
<script type="module" src="{{ static("bundled/counter/product-list-index.ts") }}"></script>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block additional_css %}
|
{% block additional_css %}
|
||||||
<link rel="stylesheet" href="{{ static("core/components/card.scss") }}">
|
<link rel="stylesheet" href="{{ static("core/components/card.scss") }}">
|
||||||
<link rel="stylesheet" href="{{ static("counter/css/admin.scss") }}">
|
<link rel="stylesheet" href="{{ static("counter/css/admin.scss") }}">
|
||||||
|
<link rel="stylesheet" href="{{ static("bundled/core/components/ajax-select-index.css") }}">
|
||||||
|
<link rel="stylesheet" href="{{ static("core/components/ajax-select.scss") }}">
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
<main x-data="productList">
|
<main x-data="productList">
|
||||||
<form id="search-form" class="margin-bottom">
|
|
||||||
<h4 class="margin-bottom">{% trans %}Filter products{% endtrans %}</h4>
|
<h4 class="margin-bottom">{% trans %}Filter products{% endtrans %}</h4>
|
||||||
|
<form id="search-form" class="margin-bottom">
|
||||||
|
<div class="row gap-4x">
|
||||||
|
|
||||||
<fieldset>
|
<fieldset>
|
||||||
<label for="search-input">{% trans %}Product name{% endtrans %}</label>
|
<label for="search-input">{% trans %}Product name{% endtrans %}</label>
|
||||||
<input
|
<input
|
||||||
@ -28,6 +33,7 @@
|
|||||||
/>
|
/>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<fieldset>
|
<fieldset>
|
||||||
|
<legend>{% trans %}Product state{% endtrans %}</legend>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<input type="radio" id="filter-active-products" x-model="productStatus" value="active">
|
<input type="radio" id="filter-active-products" x-model="productStatus" value="active">
|
||||||
<label for="filter-active-products">{% trans %}Active products{% endtrans %}</label>
|
<label for="filter-active-products">{% trans %}Active products{% endtrans %}</label>
|
||||||
@ -41,10 +47,19 @@
|
|||||||
<label for="filter-all-products">{% trans %}All products{% endtrans %}</label>
|
<label for="filter-all-products">{% trans %}All products{% endtrans %}</label>
|
||||||
</div>
|
</div>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
|
</div>
|
||||||
|
<fieldset>
|
||||||
|
<label for="type-search-input">{% trans %}Product type{% endtrans %}</label>
|
||||||
|
<product-type-ajax-select
|
||||||
|
id="type-search-input"
|
||||||
|
name="product-type"
|
||||||
|
x-ref="productTypesInput"
|
||||||
|
multiple
|
||||||
|
>
|
||||||
|
</product-type-ajax-select>
|
||||||
|
</fieldset>
|
||||||
</form>
|
</form>
|
||||||
<h3 @click="console.log(totalCount, nbPages())" class="margin-bottom">
|
<h3 class="margin-bottom">{% trans %}Product list{% endtrans %}</h3>
|
||||||
{% trans %}Product list{% endtrans %}
|
|
||||||
</h3>
|
|
||||||
|
|
||||||
<div class="row margin-bottom">
|
<div class="row margin-bottom">
|
||||||
<a href="{{ url('counter:new_product') }}" class="btn btn-blue">
|
<a href="{{ url('counter:new_product') }}" class="btn btn-blue">
|
||||||
|
@ -1,8 +1,12 @@
|
|||||||
from pydantic import TypeAdapter
|
from pydantic import TypeAdapter
|
||||||
|
|
||||||
from core.views.widgets.select import AutoCompleteSelect, AutoCompleteSelectMultiple
|
from core.views.widgets.select import AutoCompleteSelect, AutoCompleteSelectMultiple
|
||||||
from counter.models import Counter, Product
|
from counter.models import Counter, Product, ProductType
|
||||||
from counter.schemas import SimpleProductSchema, SimplifiedCounterSchema
|
from counter.schemas import (
|
||||||
|
ProductTypeSchema,
|
||||||
|
SimpleProductSchema,
|
||||||
|
SimplifiedCounterSchema,
|
||||||
|
)
|
||||||
|
|
||||||
_js = ["bundled/counter/components/ajax-select-index.ts"]
|
_js = ["bundled/counter/components/ajax-select-index.ts"]
|
||||||
|
|
||||||
@ -33,3 +37,17 @@ class AutoCompleteSelectMultipleProduct(AutoCompleteSelectMultiple):
|
|||||||
model = Product
|
model = Product
|
||||||
adapter = TypeAdapter(list[SimpleProductSchema])
|
adapter = TypeAdapter(list[SimpleProductSchema])
|
||||||
js = _js
|
js = _js
|
||||||
|
|
||||||
|
|
||||||
|
class AutoCompleteSelectProductType(AutoCompleteSelect):
|
||||||
|
component_name = "product-type-ajax-select"
|
||||||
|
model = ProductType
|
||||||
|
adapter = TypeAdapter(list[ProductTypeSchema])
|
||||||
|
js = _js
|
||||||
|
|
||||||
|
|
||||||
|
class AutoCompleteSelectMultipleProductType(AutoCompleteSelectMultiple):
|
||||||
|
component_name = "product-type-ajax-select"
|
||||||
|
model = ProductType
|
||||||
|
adapter = TypeAdapter(list[ProductTypeSchema])
|
||||||
|
js = _js
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
msgid ""
|
msgid ""
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Report-Msgid-Bugs-To: \n"
|
"Report-Msgid-Bugs-To: \n"
|
||||||
"POT-Creation-Date: 2024-12-19 10:43+0100\n"
|
"POT-Creation-Date: 2024-12-21 02:15+0100\n"
|
||||||
"PO-Revision-Date: 2016-07-18\n"
|
"PO-Revision-Date: 2016-07-18\n"
|
||||||
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
"Last-Translator: Maréchal <thomas.girod@utbm.fr\n"
|
||||||
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
"Language-Team: AE info <ae.info@utbm.fr>\n"
|
||||||
@ -765,7 +765,7 @@ msgstr "Opération liée : "
|
|||||||
#: core/templates/core/create.jinja:12 core/templates/core/edit.jinja:7
|
#: core/templates/core/create.jinja:12 core/templates/core/edit.jinja:7
|
||||||
#: core/templates/core/edit.jinja:15 core/templates/core/edit.jinja:20
|
#: core/templates/core/edit.jinja:15 core/templates/core/edit.jinja:20
|
||||||
#: core/templates/core/file_edit.jinja:8
|
#: core/templates/core/file_edit.jinja:8
|
||||||
#: core/templates/core/macros_pages.jinja:25
|
#: core/templates/core/macros_pages.jinja:26
|
||||||
#: core/templates/core/page_prop.jinja:11
|
#: core/templates/core/page_prop.jinja:11
|
||||||
#: core/templates/core/user_godfathers.jinja:61
|
#: core/templates/core/user_godfathers.jinja:61
|
||||||
#: core/templates/core/user_godfathers_tree.jinja:85
|
#: core/templates/core/user_godfathers_tree.jinja:85
|
||||||
@ -971,7 +971,7 @@ msgstr "Comptoir"
|
|||||||
msgid "Products"
|
msgid "Products"
|
||||||
msgstr "Produits"
|
msgstr "Produits"
|
||||||
|
|
||||||
#: club/forms.py:168 counter/templates/counter/product_list.jinja:37
|
#: club/forms.py:168 counter/templates/counter/product_list.jinja:43
|
||||||
msgid "Archived products"
|
msgid "Archived products"
|
||||||
msgstr "Produits archivés"
|
msgstr "Produits archivés"
|
||||||
|
|
||||||
@ -1146,7 +1146,7 @@ msgid "There are no members in this club."
|
|||||||
msgstr "Il n'y a pas de membres dans ce club."
|
msgstr "Il n'y a pas de membres dans ce club."
|
||||||
|
|
||||||
#: club/templates/club/club_members.jinja:80
|
#: club/templates/club/club_members.jinja:80
|
||||||
#: core/templates/core/file_detail.jinja:19 core/views/forms.py:305
|
#: core/templates/core/file_detail.jinja:19 core/views/forms.py:309
|
||||||
#: launderette/views.py:208 trombi/templates/trombi/detail.jinja:19
|
#: launderette/views.py:208 trombi/templates/trombi/detail.jinja:19
|
||||||
msgid "Add"
|
msgid "Add"
|
||||||
msgstr "Ajouter"
|
msgstr "Ajouter"
|
||||||
@ -1192,7 +1192,7 @@ msgid "Show"
|
|||||||
msgstr "Montrer"
|
msgstr "Montrer"
|
||||||
|
|
||||||
#: club/templates/club/club_sellings.jinja:38
|
#: club/templates/club/club_sellings.jinja:38
|
||||||
#: counter/templates/counter/product_list.jinja:59
|
#: counter/templates/counter/product_list.jinja:74
|
||||||
msgid "Download as cvs"
|
msgid "Download as cvs"
|
||||||
msgstr "Télécharger en CSV"
|
msgstr "Télécharger en CSV"
|
||||||
|
|
||||||
@ -2038,8 +2038,8 @@ msgid ""
|
|||||||
"The groups this user belongs to. A user will get all permissions granted to "
|
"The groups this user belongs to. A user will get all permissions granted to "
|
||||||
"each of their groups."
|
"each of their groups."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
"Les groupes auxquels cet utilisateur appartient. Un utilisateur aura toutes les "
|
"Les groupes auxquels cet utilisateur appartient. Un utilisateur aura toutes "
|
||||||
"permissions de chacun de ses groupes."
|
"les permissions de chacun de ses groupes."
|
||||||
|
|
||||||
#: core/models.py:277
|
#: core/models.py:277
|
||||||
msgid "profile"
|
msgid "profile"
|
||||||
@ -2764,11 +2764,11 @@ msgstr "Tout désélectionner"
|
|||||||
msgid "You're seeing the history of page \"%(page_name)s\""
|
msgid "You're seeing the history of page \"%(page_name)s\""
|
||||||
msgstr "Vous consultez l'historique de la page \"%(page_name)s\""
|
msgstr "Vous consultez l'historique de la page \"%(page_name)s\""
|
||||||
|
|
||||||
#: core/templates/core/macros_pages.jinja:8
|
#: core/templates/core/macros_pages.jinja:10
|
||||||
msgid "last"
|
msgid "last"
|
||||||
msgstr "actuel"
|
msgstr "actuel"
|
||||||
|
|
||||||
#: core/templates/core/macros_pages.jinja:21
|
#: core/templates/core/macros_pages.jinja:22
|
||||||
msgid "Edit page"
|
msgid "Edit page"
|
||||||
msgstr "Éditer la page"
|
msgstr "Éditer la page"
|
||||||
|
|
||||||
@ -3026,7 +3026,7 @@ msgstr "Facture eboutic"
|
|||||||
msgid "Etickets"
|
msgid "Etickets"
|
||||||
msgstr "Etickets"
|
msgstr "Etickets"
|
||||||
|
|
||||||
#: core/templates/core/user_account.jinja:69 core/views/user.py:633
|
#: core/templates/core/user_account.jinja:69 core/views/user.py:631
|
||||||
msgid "User has no account"
|
msgid "User has no account"
|
||||||
msgstr "L'utilisateur n'a pas de compte"
|
msgstr "L'utilisateur n'a pas de compte"
|
||||||
|
|
||||||
@ -3326,7 +3326,8 @@ msgstr "Outils utilisateurs"
|
|||||||
msgid "Sith management"
|
msgid "Sith management"
|
||||||
msgstr "Gestion de Sith"
|
msgstr "Gestion de Sith"
|
||||||
|
|
||||||
#: core/templates/core/user_tools.jinja:21 core/views/user.py:254
|
#: core/templates/core/user_tools.jinja:21 core/views/forms.py:295
|
||||||
|
#: core/views/user.py:254
|
||||||
msgid "Groups"
|
msgid "Groups"
|
||||||
msgstr "Groupes"
|
msgstr "Groupes"
|
||||||
|
|
||||||
@ -3518,32 +3519,32 @@ msgstr "Blouse : montrez aux autres à quoi ressemble votre blouse !"
|
|||||||
msgid "Bad image format, only jpeg, png, webp and gif are accepted"
|
msgid "Bad image format, only jpeg, png, webp and gif are accepted"
|
||||||
msgstr "Mauvais format d'image, seuls les jpeg, png, webp et gif sont acceptés"
|
msgstr "Mauvais format d'image, seuls les jpeg, png, webp et gif sont acceptés"
|
||||||
|
|
||||||
#: core/views/forms.py:302
|
#: core/views/forms.py:306
|
||||||
msgid "Godfather / Godmother"
|
msgid "Godfather / Godmother"
|
||||||
msgstr "Parrain / Marraine"
|
msgstr "Parrain / Marraine"
|
||||||
|
|
||||||
#: core/views/forms.py:303
|
#: core/views/forms.py:307
|
||||||
msgid "Godchild"
|
msgid "Godchild"
|
||||||
msgstr "Fillot / Fillote"
|
msgstr "Fillot / Fillote"
|
||||||
|
|
||||||
#: core/views/forms.py:308 counter/forms.py:78 trombi/views.py:151
|
#: core/views/forms.py:312 counter/forms.py:78 trombi/views.py:151
|
||||||
msgid "Select user"
|
msgid "Select user"
|
||||||
msgstr "Choisir un utilisateur"
|
msgstr "Choisir un utilisateur"
|
||||||
|
|
||||||
#: core/views/forms.py:322
|
#: core/views/forms.py:326
|
||||||
msgid "This user does not exist"
|
msgid "This user does not exist"
|
||||||
msgstr "Cet utilisateur n'existe pas"
|
msgstr "Cet utilisateur n'existe pas"
|
||||||
|
|
||||||
#: core/views/forms.py:324
|
#: core/views/forms.py:328
|
||||||
msgid "You cannot be related to yourself"
|
msgid "You cannot be related to yourself"
|
||||||
msgstr "Vous ne pouvez pas être relié à vous-même"
|
msgstr "Vous ne pouvez pas être relié à vous-même"
|
||||||
|
|
||||||
#: core/views/forms.py:336
|
#: core/views/forms.py:340
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%s is already your godfather"
|
msgid "%s is already your godfather"
|
||||||
msgstr "%s est déjà votre parrain/marraine"
|
msgstr "%s est déjà votre parrain/marraine"
|
||||||
|
|
||||||
#: core/views/forms.py:342
|
#: core/views/forms.py:346
|
||||||
#, python-format
|
#, python-format
|
||||||
msgid "%s is already your godchild"
|
msgid "%s is already your godchild"
|
||||||
msgstr "%s est déjà votre fillot/fillote"
|
msgstr "%s est déjà votre fillot/fillote"
|
||||||
@ -4152,31 +4153,35 @@ msgstr ""
|
|||||||
"aucune conséquence autre que le retrait de l'argent de votre compte."
|
"aucune conséquence autre que le retrait de l'argent de votre compte."
|
||||||
|
|
||||||
#: counter/templates/counter/product_list.jinja:5
|
#: counter/templates/counter/product_list.jinja:5
|
||||||
#: counter/templates/counter/product_list.jinja:46
|
#: counter/templates/counter/product_list.jinja:62
|
||||||
msgid "Product list"
|
msgid "Product list"
|
||||||
msgstr "Liste des produits"
|
msgstr "Liste des produits"
|
||||||
|
|
||||||
#: counter/templates/counter/product_list.jinja:20
|
#: counter/templates/counter/product_list.jinja:22
|
||||||
msgid "Filter products"
|
msgid "Filter products"
|
||||||
msgstr "Filtrer les produits"
|
msgstr "Filtrer les produits"
|
||||||
|
|
||||||
#: counter/templates/counter/product_list.jinja:22
|
#: counter/templates/counter/product_list.jinja:27
|
||||||
msgid "Product name"
|
msgid "Product name"
|
||||||
msgstr "Nom du produit"
|
msgstr "Nom du produit"
|
||||||
|
|
||||||
#: counter/templates/counter/product_list.jinja:33
|
#: counter/templates/counter/product_list.jinja:36
|
||||||
#, fuzzy
|
msgid "Product state"
|
||||||
#| msgid "Archived products"
|
msgstr "Etat du produit"
|
||||||
|
|
||||||
|
#: counter/templates/counter/product_list.jinja:39
|
||||||
msgid "Active products"
|
msgid "Active products"
|
||||||
msgstr "Produits archivés"
|
msgstr "Produits actifs"
|
||||||
|
|
||||||
#: counter/templates/counter/product_list.jinja:41
|
#: counter/templates/counter/product_list.jinja:47
|
||||||
#, fuzzy
|
|
||||||
#| msgid "products"
|
|
||||||
msgid "All products"
|
msgid "All products"
|
||||||
msgstr "produits"
|
msgstr "Tous les produits"
|
||||||
|
|
||||||
#: counter/templates/counter/product_list.jinja:51
|
#: counter/templates/counter/product_list.jinja:52
|
||||||
|
msgid "Product type"
|
||||||
|
msgstr "Type de produit"
|
||||||
|
|
||||||
|
#: counter/templates/counter/product_list.jinja:66
|
||||||
msgid "New product"
|
msgid "New product"
|
||||||
msgstr "Nouveau produit"
|
msgstr "Nouveau produit"
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user