mirror of
https://github.com/sysadminsmedia/homebox.git
synced 2025-12-21 13:23:14 +01:00
ProductBarcode: apply linting and fixes on frontend
This commit is contained in:
@@ -15,13 +15,14 @@
|
||||
</div>
|
||||
<div
|
||||
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"
|
||||
>
|
||||
<div class="flex">
|
||||
<MdiBarcode class="text-default mr-2" />
|
||||
<MdiBarcode class="mr-2" />
|
||||
<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>
|
||||
</div>
|
||||
|
||||
@@ -74,7 +75,6 @@
|
||||
const errorMessage = ref<string | null>(null);
|
||||
const detectedBarcode = ref<string>("");
|
||||
const detectedBarcodeType = ref<string>("");
|
||||
const api = useUserApi();
|
||||
|
||||
const handleError = (error: unknown) => {
|
||||
console.error("Scanner error:", error);
|
||||
@@ -170,9 +170,9 @@
|
||||
case BarcodeFormat.UPC_EAN_EXTENSION:
|
||||
console.info("Barcode detected");
|
||||
detectedBarcode.value = result.getText();
|
||||
detectedBarcodeType.value = BarcodeFormat[bcfmt].replaceAll("_","-");
|
||||
detectedBarcodeType.value = BarcodeFormat[bcfmt].replaceAll("_", "-");
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
handleError(err);
|
||||
}
|
||||
@@ -194,4 +194,3 @@
|
||||
stopScanner();
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
<template>
|
||||
<Dialog dialog-id="product-import">
|
||||
<DialogContent :class="'w-full md:max-w-xl lg:max-w-4xl'" >
|
||||
<Dialog dialog-id="product-import">
|
||||
<DialogContent :class="'w-full md:max-w-xl lg:max-w-4xl'">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{{ $t("components.item.product_import.title") }}</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div class="flex items-center space-x-4">
|
||||
<FormTextField :disabled=searching class="w-[30%]" :modelValue="barcode" :label="$t('components.item.product_import.barcode')" />
|
||||
<Button :variant="searching ? 'destructive' : 'default'" @click="retrieveProductInfo(barcode)" style="margin-top: auto">
|
||||
<FormTextField
|
||||
: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="absolute inset-0 flex items-center justify-center">
|
||||
<MdiBarcode class="size-5 group-hover:hidden" />
|
||||
@@ -16,7 +25,7 @@
|
||||
{{ searching ? "Cancel" : "Search product" }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="divide-y border-t" />
|
||||
|
||||
<BaseCard>
|
||||
@@ -43,31 +52,34 @@
|
||||
</TableHeader>
|
||||
|
||||
<TableBody>
|
||||
<TableRow
|
||||
<TableRow
|
||||
v-for="(p, index) in products"
|
||||
:key="index"
|
||||
class='cursor-pointer'
|
||||
:class="{ selected: selectedRow === index }"
|
||||
@click="selectProduct(index)">
|
||||
<TableCell v-for="h in headers"
|
||||
:class="{
|
||||
class="cursor-pointer"
|
||||
:class="{ selected: selectedRow === index }"
|
||||
@click="selectProduct(index)"
|
||||
>
|
||||
<TableCell
|
||||
v-for="h in headers"
|
||||
:key="h.value"
|
||||
:class="{
|
||||
'text-center': h.align === 'center',
|
||||
'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'">
|
||||
<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>
|
||||
|
||||
<slot v-else :name="cell(h)">
|
||||
{{ extractValue(p, h.value) }}
|
||||
</slot>
|
||||
</TableCell>
|
||||
<slot v-else :name="cell(h)">
|
||||
{{ extractValue(p, h.value) }}
|
||||
</slot>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
</TableBody>
|
||||
</Table>
|
||||
@@ -81,20 +93,19 @@
|
||||
</template>
|
||||
|
||||
<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 { useDialog } from "~/components/ui/dialog-provider";
|
||||
import MdiBarcode from "~icons/mdi/barcode";
|
||||
import type { TableData } from "~/components/Item/View/Table.types";
|
||||
const { openDialog, activeDialog, closeDialog } = useDialog();
|
||||
|
||||
const { openDialog, activeDialog } = useDialog();
|
||||
|
||||
const searching = ref(false);
|
||||
const barcode = ref<string>("");
|
||||
const products = ref<BarcodeProduct[] | null>(null);
|
||||
const selectedRow = ref(-1);
|
||||
|
||||
import type { ItemSummary } from "~~/lib/api/types/data-contracts";
|
||||
|
||||
type BarcodeTableHeader = {
|
||||
text: string;
|
||||
value: string;
|
||||
@@ -109,9 +120,9 @@
|
||||
align: "left",
|
||||
type: "name",
|
||||
},
|
||||
{ text: "items.manufacturer", value: "manufacturer", align: "center"},
|
||||
{ text: "items.model_number", value: "modelNumber", align: "center"},
|
||||
{ text: "DB source", value: "search_engine_name", align: "center"},
|
||||
{ text: "items.manufacturer", value: "manufacturer", align: "center" },
|
||||
{ text: "items.model_number", value: "modelNumber", align: "center" },
|
||||
{ text: "DB source", value: "search_engine_name", align: "center" },
|
||||
] satisfies BarcodeTableHeader[];
|
||||
|
||||
// Need for later filtering
|
||||
@@ -123,21 +134,16 @@
|
||||
if (active && active.id === "product-import") {
|
||||
selectedRow.value = -1;
|
||||
|
||||
if(active.params)
|
||||
{
|
||||
if (active.params) {
|
||||
// Reset if the barcode is different
|
||||
if(active.params != barcode.value)
|
||||
{
|
||||
if (active.params !== barcode.value) {
|
||||
barcode.value = active.params;
|
||||
|
||||
retrieveProductInfo(barcode.value).then(() =>
|
||||
{
|
||||
retrieveProductInfo(barcode.value).then(() => {
|
||||
console.log("Processing finished");
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
barcode.value = "";
|
||||
products.value = null;
|
||||
}
|
||||
@@ -147,11 +153,10 @@
|
||||
|
||||
const api = useUserApi();
|
||||
|
||||
async function createItem(close = true) {
|
||||
if (products !== null)
|
||||
{
|
||||
var p = products.value![selectedRow.value];
|
||||
openDialog("create-item", p);
|
||||
function createItem() {
|
||||
if (products !== null) {
|
||||
const p = products.value![selectedRow.value];
|
||||
openDialog("create-item", p);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,23 +165,19 @@
|
||||
searching.value = true;
|
||||
|
||||
if (!barcode || barcode.trim().length === 0) {
|
||||
console.error('Invalid barcode provided');
|
||||
console.error("Invalid barcode provided");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const result = await api.products.searchFromBarcode(barcode.trim());
|
||||
if(result.error)
|
||||
{
|
||||
console.error('API Error:', result.error);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (result.error) {
|
||||
console.error("API Error:", result.error);
|
||||
} else {
|
||||
products.value = result.data;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to retrieve product info:', error);
|
||||
console.error("Failed to retrieve product info:", error);
|
||||
} finally {
|
||||
searching.value = false;
|
||||
}
|
||||
@@ -197,26 +198,22 @@
|
||||
|
||||
function selectProduct(index: number) {
|
||||
// Unselect if already selected
|
||||
if(selectedRow.value == index)
|
||||
{
|
||||
if (selectedRow.value === index) {
|
||||
selectedRow.value = -1;
|
||||
return;
|
||||
}
|
||||
|
||||
selectedRow.value = index;
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
tr.selected {
|
||||
background-color: hsl(var(--primary));
|
||||
color: hsl(var(--background));
|
||||
}
|
||||
|
||||
tr:hover.selected {
|
||||
tr.selected {
|
||||
background-color: hsl(var(--primary));
|
||||
}
|
||||
color: hsl(var(--background));
|
||||
}
|
||||
|
||||
</style>
|
||||
tr:hover.selected {
|
||||
background-color: hsl(var(--primary));
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,28 +10,28 @@
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{{ $t('components.item.create_modal.product_tooltip_input_barcode') }}</p>
|
||||
<p>{{ $t("components.item.create_modal.product_tooltip_input_barcode") }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
<Tooltip>
|
||||
<TooltipTrigger>
|
||||
<Button variant="outline" :disabled="loading" data-pos="end" @click="openQrScannerPage()">
|
||||
<MdiBarcodeScan class="size-5" />
|
||||
<MdiBarcodeScan class="size-5" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>
|
||||
<p>{{ $t('components.item.create_modal.product_tooltip_scan_barcode') }}</p>
|
||||
<p>{{ $t("components.item.create_modal.product_tooltip_scan_barcode") }}</p>
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</ButtonGroup>
|
||||
</TooltipProvider >
|
||||
<div class= "items-center justify-center flex mx-2">
|
||||
{{ $t('components.item.create_modal.product_autofill') }}
|
||||
</TooltipProvider>
|
||||
<div class="mx-2 flex items-center justify-center">
|
||||
{{ $t("components.item.create_modal.product_autofill") }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class=" border-t" />
|
||||
|
||||
<div class="border-t" />
|
||||
|
||||
<form class="flex flex-col gap-2" @submit.prevent="create()">
|
||||
<LocationSelector v-model="form.location" />
|
||||
<ItemSelector
|
||||
@@ -345,19 +345,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
if(active.params)
|
||||
{
|
||||
if (active.params) {
|
||||
form.name = active.params.item.name;
|
||||
form.description = active.params.item.description;
|
||||
|
||||
if(active.params.imageURL)
|
||||
{
|
||||
if (active.params.imageURL) {
|
||||
form.photos.push({
|
||||
photoName: "product_view.jpg",
|
||||
fileBase64: active.params.imageBase64,
|
||||
primary: form.photos.length === 0,
|
||||
file: dataURLtoFile(active.params.imageBase64 ,"product_view.jpg")
|
||||
});
|
||||
photoName: "product_view.jpg",
|
||||
fileBase64: active.params.imageBase64,
|
||||
primary: form.photos.length === 0,
|
||||
file: dataURLtoFile(active.params.imageBase64, "product_view.jpg"),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -517,13 +515,13 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function openQrScannerPage() {
|
||||
function openQrScannerPage() {
|
||||
closeDialog("create-item");
|
||||
openDialog("scanner")
|
||||
openDialog("scanner");
|
||||
}
|
||||
|
||||
async function openBarcodeDialog() {
|
||||
function openBarcodeDialog() {
|
||||
closeDialog("create-item");
|
||||
openDialog("product-import")
|
||||
openDialog("product-import");
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
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 {
|
||||
ensureAssetIDs() {
|
||||
|
||||
@@ -5,4 +5,4 @@ export class ProductAPI extends BaseAPI {
|
||||
searchFromBarcode(productEAN: string) {
|
||||
return this.http.get<BarcodeProduct[]>({ url: route(`/products/search-from-barcode`, { productEAN }) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user