From 5ac402330899d496509c1cfbfa40cb1b5fdf6b70 Mon Sep 17 00:00:00 2001 From: Nikolay Bonev Date: Tue, 21 Jan 2025 14:37:05 +0200 Subject: [PATCH 1/2] making qrId filter work with barcode scanner --- .../assets-index/advanced-filters/helpers.ts | 32 +++++++++ .../advanced-filters/value-field.tsx | 72 ++++++++++++++++++- 2 files changed, 103 insertions(+), 1 deletion(-) diff --git a/app/components/assets/assets-index/advanced-filters/helpers.ts b/app/components/assets/assets-index/advanced-filters/helpers.ts index 3cbc92321..2413bf1b7 100644 --- a/app/components/assets/assets-index/advanced-filters/helpers.ts +++ b/app/components/assets/assets-index/advanced-filters/helpers.ts @@ -231,3 +231,35 @@ export function getAvailableColumns( return true; }); } + +/** + * Extracts the QR ID from a URL or returns the original value if it's not a URL + * Removes any query parameters and returns the last path segment + * + * @example + * localhost:3000/qr/abc123?hello=world -> abc123 + * https://example.com/abc123 -> abc123 + * abc123 -> abc123 + * + * @param value - The input value (URL or QR ID) + * @returns The extracted QR ID or original value + */ +export function extractQrIdFromValue(value: string): string { + try { + // Try to parse as URL first + const url = new URL(value); + + // Remove leading and trailing slashes and split path + const pathParts = url.pathname.split("/").filter(Boolean); + + // Get the last part of the path (if exists) + if (pathParts.length > 0) { + return pathParts[pathParts.length - 1]; + } + + return value; + } catch (e) { + // If URL parsing fails, return original value + return value; + } +} diff --git a/app/components/assets/assets-index/advanced-filters/value-field.tsx b/app/components/assets/assets-index/advanced-filters/value-field.tsx index 9aa362570..024f0b2d8 100644 --- a/app/components/assets/assets-index/advanced-filters/value-field.tsx +++ b/app/components/assets/assets-index/advanced-filters/value-field.tsx @@ -13,15 +13,28 @@ import DynamicDropdown from "~/components/dynamic-dropdown/dynamic-dropdown"; import DynamicSelect from "~/components/dynamic-select/dynamic-select"; import Input from "~/components/forms/input"; -import { CheckIcon, ChevronRight, PlusIcon } from "~/components/icons/library"; +import { + CheckIcon, + ChevronRight, + HelpIcon, + PlusIcon, +} from "~/components/icons/library"; import { Button } from "~/components/shared/button"; import type { AssetIndexLoaderData } from "~/routes/_layout+/assets._index"; import { useHints } from "~/utils/client-hints"; import { adjustDateToUTC, isDateString } from "~/utils/date-fns"; import { tw } from "~/utils/tw"; import { resolveTeamMemberName } from "~/utils/user"; +import { extractQrIdFromValue } from "./helpers"; import type { Filter } from "./schema"; import { userFriendlyAssetStatus } from "../../asset-status-badge"; +import { SearchFieldTooltip } from "~/components/list/filters/search-field-tooltip"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "~/components/shared/tooltip"; export function ValueField({ filter, @@ -156,6 +169,63 @@ export function ValueField({ switch (filter.type) { case "string": case "text": + if (filter.name === "qrId") { + return ( +
+
+ { + setFilter(e.target.value); + }} + placeholder={placeholder(filter.operator)} + onKeyUp={(e: React.KeyboardEvent) => { + if (e.key === "Enter") { + setTimeout(() => { + // Assert the target as HTMLInputElement to access value + const input = e.target as HTMLInputElement; + const cleanValue = extractQrIdFromValue(input.value); + setFilter(cleanValue); + // Create a new keyboard event for submitOnEnter + submitOnEnter(e as React.KeyboardEvent); + }, 10); + } + }} + error={error} + name={fieldName} + /> + {!["contains", "containsAny", "matchesAny"].includes( + filter.operator + ) ? ( + + + + + + + + +
+
+ Barcode scanner ready +
+

+ This fields supports barcode scanners. Simply place + your cursor in the field and scan a Shelf QR code with + your barcode scanner. The value will be automatically + filled in for you. +

+
+
+
+
+ ) : null} +
+
+ ); + } return ( Date: Tue, 21 Jan 2025 15:13:46 +0200 Subject: [PATCH 2/2] cleanup --- .../assets-index/advanced-filters/value-field.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/app/components/assets/assets-index/advanced-filters/value-field.tsx b/app/components/assets/assets-index/advanced-filters/value-field.tsx index 024f0b2d8..5f22c0149 100644 --- a/app/components/assets/assets-index/advanced-filters/value-field.tsx +++ b/app/components/assets/assets-index/advanced-filters/value-field.tsx @@ -20,6 +20,12 @@ import { PlusIcon, } from "~/components/icons/library"; import { Button } from "~/components/shared/button"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "~/components/shared/tooltip"; import type { AssetIndexLoaderData } from "~/routes/_layout+/assets._index"; import { useHints } from "~/utils/client-hints"; import { adjustDateToUTC, isDateString } from "~/utils/date-fns"; @@ -28,13 +34,6 @@ import { resolveTeamMemberName } from "~/utils/user"; import { extractQrIdFromValue } from "./helpers"; import type { Filter } from "./schema"; import { userFriendlyAssetStatus } from "../../asset-status-badge"; -import { SearchFieldTooltip } from "~/components/list/filters/search-field-tooltip"; -import { - Tooltip, - TooltipContent, - TooltipProvider, - TooltipTrigger, -} from "~/components/shared/tooltip"; export function ValueField({ filter,