ProductBarcode: apply linting and fixes on frontend

This commit is contained in:
Crumb Owl
2025-07-07 13:07:14 +00:00
parent 68b6d58ab4
commit 18149a5c9a
5 changed files with 91 additions and 97 deletions

View File

@@ -15,13 +15,14 @@
</div> </div>
<div <div
v-if="detectedBarcode" v-if="detectedBarcode"
class="border-accent-foreground bg-accent text-accent-foreground mb-5 flex flex-col items-center gap-2 rounded-md border p-4" class="mb-5 flex flex-col items-center gap-2 rounded-md border border-accent-foreground bg-accent p-4 text-accent-foreground"
role="alert" role="alert"
> >
<div class="flex"> <div class="flex">
<MdiBarcode class="text-default mr-2" /> <MdiBarcode class="mr-2" />
<span class="flex-1 text-center text-sm font-medium"> <span class="flex-1 text-center text-sm font-medium">
{{ detectedBarcodeType }} {{ $t("scanner.barcode_detected_message") }}: <strong>{{ detectedBarcode }}</strong> {{ detectedBarcodeType }} {{ $t("scanner.barcode_detected_message") }}:
<strong>{{ detectedBarcode }}</strong>
</span> </span>
</div> </div>
@@ -74,7 +75,6 @@
const errorMessage = ref<string | null>(null); const errorMessage = ref<string | null>(null);
const detectedBarcode = ref<string>(""); const detectedBarcode = ref<string>("");
const detectedBarcodeType = ref<string>(""); const detectedBarcodeType = ref<string>("");
const api = useUserApi();
const handleError = (error: unknown) => { const handleError = (error: unknown) => {
console.error("Scanner error:", error); console.error("Scanner error:", error);
@@ -170,9 +170,9 @@
case BarcodeFormat.UPC_EAN_EXTENSION: case BarcodeFormat.UPC_EAN_EXTENSION:
console.info("Barcode detected"); console.info("Barcode detected");
detectedBarcode.value = result.getText(); detectedBarcode.value = result.getText();
detectedBarcodeType.value = BarcodeFormat[bcfmt].replaceAll("_","-"); detectedBarcodeType.value = BarcodeFormat[bcfmt].replaceAll("_", "-");
break; break;
default: default:
handleError(err); handleError(err);
} }
@@ -194,4 +194,3 @@
stopScanner(); stopScanner();
}); });
</script> </script>

View File

@@ -1,13 +1,22 @@
<template> <template>
<Dialog dialog-id="product-import"> <Dialog dialog-id="product-import">
<DialogContent :class="'w-full md:max-w-xl lg:max-w-4xl'" > <DialogContent :class="'w-full md:max-w-xl lg:max-w-4xl'">
<DialogHeader> <DialogHeader>
<DialogTitle>{{ $t("components.item.product_import.title") }}</DialogTitle> <DialogTitle>{{ $t("components.item.product_import.title") }}</DialogTitle>
</DialogHeader> </DialogHeader>
<div class="flex items-center space-x-4"> <div class="flex items-center space-x-4">
<FormTextField :disabled=searching class="w-[30%]" :modelValue="barcode" :label="$t('components.item.product_import.barcode')" /> <FormTextField
<Button :variant="searching ? 'destructive' : 'default'" @click="retrieveProductInfo(barcode)" style="margin-top: auto"> :disabled="searching"
class="w-[30%]"
:model-value="barcode"
:label="$t('components.item.product_import.barcode')"
/>
<Button
:variant="searching ? 'destructive' : 'default'"
style="margin-top: auto"
@click="retrieveProductInfo(barcode)"
>
<div class="relative mx-2"> <div class="relative mx-2">
<div class="absolute inset-0 flex items-center justify-center"> <div class="absolute inset-0 flex items-center justify-center">
<MdiBarcode class="size-5 group-hover:hidden" /> <MdiBarcode class="size-5 group-hover:hidden" />
@@ -16,7 +25,7 @@
{{ searching ? "Cancel" : "Search product" }} {{ searching ? "Cancel" : "Search product" }}
</Button> </Button>
</div> </div>
<div class="divide-y border-t" /> <div class="divide-y border-t" />
<BaseCard> <BaseCard>
@@ -43,31 +52,34 @@
</TableHeader> </TableHeader>
<TableBody> <TableBody>
<TableRow <TableRow
v-for="(p, index) in products" v-for="(p, index) in products"
:key="index" :key="index"
class='cursor-pointer' class="cursor-pointer"
:class="{ selected: selectedRow === index }" :class="{ selected: selectedRow === index }"
@click="selectProduct(index)"> @click="selectProduct(index)"
<TableCell v-for="h in headers" >
:class="{ <TableCell
v-for="h in headers"
:key="h.value"
:class="{
'text-center': h.align === 'center', 'text-center': h.align === 'center',
'text-left': h.align === 'left', 'text-left': h.align === 'left',
}"> }"
>
<template v-if="h.type === 'name'">
<div class="flex items-center space-x-4">
<img :src="p.imageBase64" class="w-16 rounded object-fill shadow-sm" alt="Product's photo" />
<span class="text-sm font-medium">
{{ p.item.name }}
</span>
</div>
</template>
<template v-if="h.type === 'name'"> <slot v-else :name="cell(h)">
<div class="flex items-center space-x-4"> {{ extractValue(p, h.value) }}
<img :src="p.imageBase64" class="w-16 rounded object-fill shadow-sm" alt="Product's photo" /> </slot>
<span class="text-sm font-medium"> </TableCell>
{{ p.item.name }}
</span>
</div>
</template>
<slot v-else :name="cell(h)">
{{ extractValue(p, h.value) }}
</slot>
</TableCell>
</TableRow> </TableRow>
</TableBody> </TableBody>
</Table> </Table>
@@ -81,20 +93,19 @@
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
import { Button, ButtonGroup } from "~/components/ui/button"; import { Button } from "~/components/ui/button";
import type { BarcodeProduct } from "~~/lib/api/types/data-contracts"; import type { BarcodeProduct } from "~~/lib/api/types/data-contracts";
import { useDialog } from "~/components/ui/dialog-provider"; import { useDialog } from "~/components/ui/dialog-provider";
import MdiBarcode from "~icons/mdi/barcode"; import MdiBarcode from "~icons/mdi/barcode";
import type { TableData } from "~/components/Item/View/Table.types"; import type { TableData } from "~/components/Item/View/Table.types";
const { openDialog, activeDialog, closeDialog } = useDialog();
const { openDialog, activeDialog } = useDialog();
const searching = ref(false); const searching = ref(false);
const barcode = ref<string>(""); const barcode = ref<string>("");
const products = ref<BarcodeProduct[] | null>(null); const products = ref<BarcodeProduct[] | null>(null);
const selectedRow = ref(-1); const selectedRow = ref(-1);
import type { ItemSummary } from "~~/lib/api/types/data-contracts";
type BarcodeTableHeader = { type BarcodeTableHeader = {
text: string; text: string;
value: string; value: string;
@@ -109,9 +120,9 @@
align: "left", align: "left",
type: "name", type: "name",
}, },
{ text: "items.manufacturer", value: "manufacturer", align: "center"}, { text: "items.manufacturer", value: "manufacturer", align: "center" },
{ text: "items.model_number", value: "modelNumber", align: "center"}, { text: "items.model_number", value: "modelNumber", align: "center" },
{ text: "DB source", value: "search_engine_name", align: "center"}, { text: "DB source", value: "search_engine_name", align: "center" },
] satisfies BarcodeTableHeader[]; ] satisfies BarcodeTableHeader[];
// Need for later filtering // Need for later filtering
@@ -123,21 +134,16 @@
if (active && active.id === "product-import") { if (active && active.id === "product-import") {
selectedRow.value = -1; selectedRow.value = -1;
if(active.params) if (active.params) {
{
// Reset if the barcode is different // Reset if the barcode is different
if(active.params != barcode.value) if (active.params !== barcode.value) {
{
barcode.value = active.params; barcode.value = active.params;
retrieveProductInfo(barcode.value).then(() => retrieveProductInfo(barcode.value).then(() => {
{
console.log("Processing finished"); console.log("Processing finished");
}); });
} }
} } else {
else
{
barcode.value = ""; barcode.value = "";
products.value = null; products.value = null;
} }
@@ -147,11 +153,10 @@
const api = useUserApi(); const api = useUserApi();
async function createItem(close = true) { function createItem() {
if (products !== null) if (products !== null) {
{ const p = products.value![selectedRow.value];
var p = products.value![selectedRow.value]; openDialog("create-item", p);
openDialog("create-item", p);
} }
} }
@@ -160,23 +165,19 @@
searching.value = true; searching.value = true;
if (!barcode || barcode.trim().length === 0) { if (!barcode || barcode.trim().length === 0) {
console.error('Invalid barcode provided'); console.error("Invalid barcode provided");
return; return;
} }
try { try {
const result = await api.products.searchFromBarcode(barcode.trim()); const result = await api.products.searchFromBarcode(barcode.trim());
if(result.error) if (result.error) {
{ console.error("API Error:", result.error);
console.error('API Error:', result.error); } else {
return;
}
else
{
products.value = result.data; products.value = result.data;
} }
} catch (error) { } catch (error) {
console.error('Failed to retrieve product info:', error); console.error("Failed to retrieve product info:", error);
} finally { } finally {
searching.value = false; searching.value = false;
} }
@@ -197,26 +198,22 @@
function selectProduct(index: number) { function selectProduct(index: number) {
// Unselect if already selected // Unselect if already selected
if(selectedRow.value == index) if (selectedRow.value === index) {
{
selectedRow.value = -1; selectedRow.value = -1;
return; return;
} }
selectedRow.value = index; selectedRow.value = index;
} }
</script> </script>
<style> <style>
tr.selected {
tr.selected {
background-color: hsl(var(--primary));
color: hsl(var(--background));
}
tr:hover.selected {
background-color: hsl(var(--primary)); background-color: hsl(var(--primary));
} color: hsl(var(--background));
}
</style> tr:hover.selected {
background-color: hsl(var(--primary));
}
</style>

View File

@@ -10,28 +10,28 @@
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p>{{ $t('components.item.create_modal.product_tooltip_input_barcode') }}</p> <p>{{ $t("components.item.create_modal.product_tooltip_input_barcode") }}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
<Tooltip> <Tooltip>
<TooltipTrigger> <TooltipTrigger>
<Button variant="outline" :disabled="loading" data-pos="end" @click="openQrScannerPage()"> <Button variant="outline" :disabled="loading" data-pos="end" @click="openQrScannerPage()">
<MdiBarcodeScan class="size-5" /> <MdiBarcodeScan class="size-5" />
</Button> </Button>
</TooltipTrigger> </TooltipTrigger>
<TooltipContent> <TooltipContent>
<p>{{ $t('components.item.create_modal.product_tooltip_scan_barcode') }}</p> <p>{{ $t("components.item.create_modal.product_tooltip_scan_barcode") }}</p>
</TooltipContent> </TooltipContent>
</Tooltip> </Tooltip>
</ButtonGroup> </ButtonGroup>
</TooltipProvider > </TooltipProvider>
<div class= "items-center justify-center flex mx-2"> <div class="mx-2 flex items-center justify-center">
{{ $t('components.item.create_modal.product_autofill') }} {{ $t("components.item.create_modal.product_autofill") }}
</div> </div>
</div> </div>
<div class=" border-t" /> <div class="border-t" />
<form class="flex flex-col gap-2" @submit.prevent="create()"> <form class="flex flex-col gap-2" @submit.prevent="create()">
<LocationSelector v-model="form.location" /> <LocationSelector v-model="form.location" />
<ItemSelector <ItemSelector
@@ -345,19 +345,17 @@
} }
} }
if(active.params) if (active.params) {
{
form.name = active.params.item.name; form.name = active.params.item.name;
form.description = active.params.item.description; form.description = active.params.item.description;
if(active.params.imageURL) if (active.params.imageURL) {
{
form.photos.push({ form.photos.push({
photoName: "product_view.jpg", photoName: "product_view.jpg",
fileBase64: active.params.imageBase64, fileBase64: active.params.imageBase64,
primary: form.photos.length === 0, primary: form.photos.length === 0,
file: dataURLtoFile(active.params.imageBase64 ,"product_view.jpg") file: dataURLtoFile(active.params.imageBase64, "product_view.jpg"),
}); });
} }
} }
@@ -517,13 +515,13 @@
} }
} }
async function openQrScannerPage() { function openQrScannerPage() {
closeDialog("create-item"); closeDialog("create-item");
openDialog("scanner") openDialog("scanner");
} }
async function openBarcodeDialog() { function openBarcodeDialog() {
closeDialog("create-item"); closeDialog("create-item");
openDialog("product-import") openDialog("product-import");
} }
</script> </script>

View File

@@ -1,5 +1,5 @@
import { BaseAPI, route } from "../base"; import { BaseAPI, route } from "../base";
import type { ActionAmountResult, ItemCreate } from "../types/data-contracts"; import type { ActionAmountResult } from "../types/data-contracts";
export class ActionsAPI extends BaseAPI { export class ActionsAPI extends BaseAPI {
ensureAssetIDs() { ensureAssetIDs() {

View File

@@ -5,4 +5,4 @@ export class ProductAPI extends BaseAPI {
searchFromBarcode(productEAN: string) { searchFromBarcode(productEAN: string) {
return this.http.get<BarcodeProduct[]>({ url: route(`/products/search-from-barcode`, { productEAN }) }); return this.http.get<BarcodeProduct[]>({ url: route(`/products/search-from-barcode`, { productEAN }) });
} }
} }