Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add search filter for items with no photo #383

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,8 @@
"editor.quickSuggestions": {
"strings": true
},
"tailwindCSS.experimental.configFile": "./frontend/tailwind.config.js"
"tailwindCSS.experimental.configFile": "./frontend/tailwind.config.js",
"[go]": {
"editor.defaultFormatter": "golang.go"
},
}
21 changes: 11 additions & 10 deletions backend/app/api/handlers/v1/v1_ctrl_items.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,17 @@ func (ctrl *V1Controller) HandleItemsGetAll() errchain.HandlerFunc {
}

v := repo.ItemQuery{
Page: queryIntOrNegativeOne(params.Get("page")),
PageSize: queryIntOrNegativeOne(params.Get("pageSize")),
Search: params.Get("q"),
LocationIDs: queryUUIDList(params, "locations"),
LabelIDs: queryUUIDList(params, "labels"),
NegateLabels: queryBool(params.Get("negateLabels")),
ParentItemIDs: queryUUIDList(params, "parentIds"),
IncludeArchived: queryBool(params.Get("includeArchived")),
Fields: filterFieldItems(params["fields"]),
OrderBy: params.Get("orderBy"),
Page: queryIntOrNegativeOne(params.Get("page")),
PageSize: queryIntOrNegativeOne(params.Get("pageSize")),
Search: params.Get("q"),
LocationIDs: queryUUIDList(params, "locations"),
LabelIDs: queryUUIDList(params, "labels"),
NegateLabels: queryBool(params.Get("negateLabels")),
OnlyWithoutPhoto: queryBool(params.Get("onlyWithoutPhoto")),
ParentItemIDs: queryUUIDList(params, "parentIds"),
IncludeArchived: queryBool(params.Get("includeArchived")),
Fields: filterFieldItems(params["fields"]),
OrderBy: params.Get("orderBy"),
}

if strings.HasPrefix(v.Search, "#") {
Expand Down
36 changes: 24 additions & 12 deletions backend/internal/data/repo/repo_items.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,18 +30,19 @@ type (
}

ItemQuery struct {
Page int
PageSize int
Search string `json:"search"`
AssetID AssetID `json:"assetId"`
LocationIDs []uuid.UUID `json:"locationIds"`
LabelIDs []uuid.UUID `json:"labelIds"`
NegateLabels bool `json:"negateLabels"`
ParentItemIDs []uuid.UUID `json:"parentIds"`
SortBy string `json:"sortBy"`
IncludeArchived bool `json:"includeArchived"`
Fields []FieldQuery `json:"fields"`
OrderBy string `json:"orderBy"`
Page int
PageSize int
Search string `json:"search"`
AssetID AssetID `json:"assetId"`
LocationIDs []uuid.UUID `json:"locationIds"`
LabelIDs []uuid.UUID `json:"labelIds"`
NegateLabels bool `json:"negateLabels"`
OnlyWithoutPhoto bool `json:"onlyWithoutPhoto"`
ParentItemIDs []uuid.UUID `json:"parentIds"`
SortBy string `json:"sortBy"`
IncludeArchived bool `json:"includeArchived"`
Fields []FieldQuery `json:"fields"`
OrderBy string `json:"orderBy"`
}

ItemField struct {
Expand Down Expand Up @@ -385,6 +386,17 @@ func (e *ItemsRepository) QueryByGroup(ctx context.Context, gid uuid.UUID, q Ite
}
}

if q.OnlyWithoutPhoto {
andPredicates = append(andPredicates, item.Not(
item.HasAttachmentsWith(
attachment.And(
attachment.Primary(true),
attachment.TypeEQ(attachment.TypePhoto),
),
)),
)
}

if len(q.LocationIDs) > 0 {
locationPredicates := make([]predicate.Item, 0, len(q.LocationIDs))
for _, l := range q.LocationIDs {
Expand Down
1 change: 1 addition & 0 deletions frontend/lib/api/classes/items.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type ItemsQuery = {
locations?: string[];
labels?: string[];
negateLabels?: boolean;
onlyWithoutPhoto?: boolean;
parentIds?: string[];
q?: string;
fields?: string[];
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/ca.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@
"next_page": "Pàgina següent",
"no_results": "No s'ha trobat cap element",
"notes": "Notes",
"only_without_photo": "Només articles sense foto",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How are these being translated? Machine based translations?

If they are machine based translations, we ask that only the English translations, and other languages you natively speak be included in the PR. And our community can handle translating this string to all the other supported languages.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I used a translator service for those yes, but if you prefer I can remove them and only leave the English ones for now.

"options": "Opcions",
"order_by": "Ordena per",
"pages": "Pàgina { page } de { totalPages }",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
"model_number": "Modelnummer",
"name": "Name",
"negate_labels": "Ausgewählte Etiketten negieren",
"only_without_photo": "Nur Artikel ohne Foto",
"next_page": "Nächste Seite",
"no_results": "Keine Elemente gefunden",
"notes": "Anmerkungen",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
"model_number": "Model Number",
"name": "Name",
"negate_labels": "Negate Selected Labels",
"only_without_photo": "Only items without photo",
"next_page": "Next Page",
"no_results": "No Items Found",
"notes": "Notes",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
"model_number": "Número de Modelo",
"name": "Nombre",
"negate_labels": "Negar Etiquetas Seleccionadas",
"only_without_photo": "Solo artículos sin foto",
"next_page": "Siguiente Página",
"no_results": "No se Encontraron Elementos",
"notes": "Notas",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@
"include_archive": "Inclure les éléments archivés",
"last": "Dernier",
"negate_labels": "Négliger les étiquettes sélectionnées",
"only_without_photo": "Seulement des articles sans photo",
"next_page": "Page suivante",
"no_results": "Aucun élément trouvé",
"options": "Options",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
"model_number": "Numero modello",
"name": "Nome",
"negate_labels": "Negare Etichette Selezionate",
"only_without_photo": "Solo articoli senza foto",
"next_page": "Pagina Successiva",
"no_results": "Nessun Articolo Trovato",
"notes": "Note",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/nl.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@
"next_page": "Volgende pagina",
"no_results": "Geen Items Gevonden",
"notes": "Opmerkingen",
"only_without_photo": "Alleen items zonder foto",
"options": "Opties",
"order_by": "Sorteren op",
"pages": "Pagina { page } van { totalPages }",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
"next_page": "Następna strona",
"no_results": "Nie znaleziono przedmiotów",
"notes": "Notatki",
"only_without_photo": "Tylko przedmioty bez zdjęcia",
"options": "Opcje",
"order_by": "Ułóż według",
"pages": "Strona {page} z {totalPages}",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
"model_number": "Номер модели",
"name": "Название",
"negate_labels": "Снять выбранные ярлыки",
"only_without_photo": "Только товары без фото",
"next_page": "Следующая страница",
"no_results": "Элементы не найдены",
"notes": "Заметки",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/sl.json
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@
"next_page": "Naslednja stran",
"no_results": "Ni najdenih predmetov",
"notes": "Opombe",
"only_without_photo": "Samo izdelki brez fotografije",
"options": "Možnosti",
"order_by": "Razvrsti po",
"pages": "Stran { page } od { totalPages }",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/sv.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
"model_number": "Modellnummer",
"name": "Namn",
"negate_labels": "Negera valda etiketter",
"only_without_photo": "Endast objekt utan foto",
"next_page": "Nästa sida",
"no_results": "Inga föremål hittades",
"notes": "Anteckningar",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/tr.json
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,7 @@
"model_number": "Model Numarası",
"name": "İsim",
"negate_labels": "Seçili Etiketleri Yoksay",
"only_without_photo": "Fotoğrafsız ürünler",
"next_page": "Sonraki Sayfa",
"no_results": "Öğe Bulunamadı",
"notes": "Notlar",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/zh-CN.json
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@
"model_number": "型号",
"name": "名称",
"negate_labels": "取消选中的标签",
"only_without_photo": "仅无照片的商品",
"next_page": "下一页",
"no_results": "没有可显示的物品",
"notes": "笔记",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/zh-HK.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"created_at": "創建於",
"last": "最後一項",
"negate_labels": "取消選定的標籤",
"only_without_photo": "僅無相片的商品",
"next_page": "下一頁",
"no_results": "沒有找到項目",
"options": "選項",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/zh-MO.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"created_at": "創建於",
"last": "最後一項",
"negate_labels": "取消選定的標籤",
"only_without_photo": "僅無相片的商品",
"next_page": "下一頁",
"no_results": "沒有找到項目",
"options": "選項",
Expand Down
1 change: 1 addition & 0 deletions frontend/locales/zh-TW.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
"created_at": "創建於",
"last": "最後一項",
"negate_labels": "取消選定的標籤",
"only_without_photo": "僅無照片的商品",
"next_page": "下一頁",
"no_results": "沒有找到項目",
"options": "選項",
Expand Down
24 changes: 19 additions & 5 deletions frontend/pages/items.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
const includeArchived = useRouteQuery("archived", false);
const fieldSelector = useRouteQuery("fieldSelector", false);
const negateLabels = useRouteQuery("negateLabels", false);
const onlyWithoutPhoto = useRouteQuery("onlyWithoutPhoto", false);
const orderBy = useRouteQuery("orderBy", "name");

const totalPages = computed(() => Math.ceil(total.value / pageSize.value));
Expand Down Expand Up @@ -177,6 +178,12 @@
}
});

watch(onlyWithoutPhoto, (newV, oldV) => {
if (newV !== oldV) {
search();
}
});

watch(orderBy, (newV, oldV) => {
if (newV !== oldV) {
search();
Expand Down Expand Up @@ -215,6 +222,7 @@
pageSize: pageSize.value,
includeArchived: includeArchived.value ? "true" : "false",
negateLabels: negateLabels.value ? "true" : "false",
onlyWithoutPhoto: onlyWithoutPhoto.value ? "true" : "false",
orderBy: orderBy.value,
},
});
Expand Down Expand Up @@ -243,6 +251,7 @@
locations: locIDs.value,
labels: labIDs.value,
negateLabels: negateLabels.value,
onlyWithoutPhoto: onlyWithoutPhoto.value,
includeArchived: includeArchived.value,
page: page.value,
pageSize: pageSize.value,
Expand Down Expand Up @@ -294,6 +303,7 @@
archived: includeArchived.value ? "true" : "false",
fieldSelector: fieldSelector.value ? "true" : "false",
negateLabels: negateLabels.value ? "true" : "false",
onlyWithoutPhoto: onlyWithoutPhoto.value ? "true" : "false",
orderBy: orderBy.value,
pageSize: pageSize.value,
page: page.value,
Expand Down Expand Up @@ -377,27 +387,31 @@
<label tabindex="0" class="btn btn-xs">{{ $t("items.options") }}</label>
<div
tabindex="0"
class="dropdown-content mt-1 max-h-72 w-64 -translate-x-24 overflow-auto rounded-md bg-base-100 p-4 shadow"
class="dropdown-content mt-1 max-h-[80vh] w-72 -translate-x-24 overflow-auto rounded-md bg-base-100 p-4 shadow"
>
<label class="label mr-auto cursor-pointer">
<input v-model="includeArchived" type="checkbox" class="toggle toggle-primary toggle-sm" />
<span class="label-text ml-4"> {{ $t("items.include_archive") }} </span>
<span class="label-text ml-4 text-right"> {{ $t("items.include_archive") }} </span>
</label>
<label class="label mr-auto cursor-pointer">
<input v-model="fieldSelector" type="checkbox" class="toggle toggle-primary toggle-sm" />
<span class="label-text ml-4"> {{ $t("items.field_selector") }} </span>
<span class="label-text ml-4 text-right"> {{ $t("items.field_selector") }} </span>
</label>
<label class="label mr-auto cursor-pointer">
<input v-model="negateLabels" type="checkbox" class="toggle toggle-primary toggle-sm" />
<span class="label-text ml-4"> {{ $t("items.negate_labels") }} </span>
<span class="label-text ml-4 text-right"> {{ $t("items.negate_labels") }} </span>
</label>
<label class="label mr-auto cursor-pointer">
<input v-model="onlyWithoutPhoto" type="checkbox" class="toggle toggle-primary toggle-sm" />
<span class="label-text ml-4 text-right"> {{ $t("items.only_without_photo") }} </span>
</label>
<label class="label mr-auto cursor-pointer">
<select v-model="orderBy" class="select select-bordered select-sm">
<option value="name" selected>{{ $t("global.name") }}</option>
<option value="createdAt">{{ $t("items.created_at") }}</option>
<option value="updatedAt">{{ $t("items.updated_at") }}</option>
</select>
<span class="label-text ml-4"> {{ $t("items.order_by") }} </span>
<span class="label-text ml-4 text-right"> {{ $t("items.order_by") }} </span>
</label>
<hr class="my-2" />
<BaseButton class="btn-sm btn-block" @click="reset"> {{ $t("items.reset_search") }} </BaseButton>
Expand Down
Loading