From 0879e913a1d8bf991965fbba3e11bcd9ddb4b005 Mon Sep 17 00:00:00 2001 From: Rohit Kumar Saini Date: Tue, 11 Feb 2025 10:03:28 +0100 Subject: [PATCH 1/2] feat(currency-selector): create new currency selector with search --- .../workspace/currency-selector.tsx | 109 ++++++++++++++++++ app/components/workspace/edit-form.tsx | 38 +----- .../migration.sql | 3 + app/database/schema.prisma | 1 + app/modules/organization/service.server.ts | 2 + app/routes/_layout+/settings.general.tsx | 2 +- 6 files changed, 120 insertions(+), 35 deletions(-) create mode 100644 app/components/workspace/currency-selector.tsx create mode 100644 app/database/migrations/20250210115134_add_pkr_currency/migration.sql diff --git a/app/components/workspace/currency-selector.tsx b/app/components/workspace/currency-selector.tsx new file mode 100644 index 000000000..3fdf7f424 --- /dev/null +++ b/app/components/workspace/currency-selector.tsx @@ -0,0 +1,109 @@ +import { useMemo, useRef, useState } from "react"; +import { + Popover, + PopoverContent, + PopoverPortal, + PopoverTrigger, +} from "@radix-ui/react-popover"; +import { useLoaderData } from "@remix-run/react"; +import { CheckIcon, ChevronDownIcon, SearchIcon } from "lucide-react"; +import type { loader } from "~/routes/_layout+/account-details.workspace.$workspaceId.edit"; +import { tw } from "~/utils/tw"; +import When from "../when/when"; + +type CurrencySelectorProps = { + className?: string; + defaultValue: string; + name?: string; +}; + +export default function CurrencySelector({ + className, + defaultValue, + name, +}: CurrencySelectorProps) { + const triggerRef = useRef(null); + const { curriences } = useLoaderData(); + + const [isOpen, setIsOpen] = useState(false); + const [selectedCurrency, setSelectedCurrency] = useState(defaultValue); + const [searchQuery, setSearchQuery] = useState(""); + + const filteredCurrencies = useMemo(() => { + if (!searchQuery) { + return curriences; + } + + return curriences.filter((currency) => + currency.toLowerCase().includes(searchQuery.toLowerCase()) + ); + }, [curriences, searchQuery]); + + function handleSelect(currency: string) { + setSelectedCurrency(currency); + setIsOpen(false); + } + + return ( + + + + + + +
+ + { + setSearchQuery(event.target.value); + }} + /> +
+ {filteredCurrencies.map((currency) => { + const isSelected = selectedCurrency === currency; + + return ( +
{ + handleSelect(currency); + }} + > + {currency} + + + +
+ ); + })} + {filteredCurrencies.length === 0 && ( +
+ No currency found +
+ )} +
+
+
+ ); +} diff --git a/app/components/workspace/edit-form.tsx b/app/components/workspace/edit-form.tsx index 51fe14b61..00e49c388 100644 --- a/app/components/workspace/edit-form.tsx +++ b/app/components/workspace/edit-form.tsx @@ -10,16 +10,10 @@ import { ACCEPT_SUPPORTED_IMAGES } from "~/utils/constants"; import { isFormProcessing } from "~/utils/form"; import { tw } from "~/utils/tw"; import { zodFieldIsRequired } from "~/utils/zod"; +import CurrencySelector from "./currency-selector"; import FormRow from "../forms/form-row"; import { InnerLabel } from "../forms/inner-label"; import Input from "../forms/input"; -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from "../forms/select"; import { CrispButton } from "../marketing/crisp"; import { Button } from "../shared/button"; import { Card } from "../shared/card"; @@ -61,8 +55,7 @@ export const WorkspaceEditForm = ({ children, className, }: Props) => { - const { curriences, organization, isPersonalWorkspace } = - useLoaderData(); + const { organization, isPersonalWorkspace } = useLoaderData(); const navigation = useNavigation(); let schema = EditWorkspaceFormSchema( @@ -144,33 +137,10 @@ export const WorkspaceEditForm = ({ } > Currency - + /> diff --git a/app/database/migrations/20250210115134_add_pkr_currency/migration.sql b/app/database/migrations/20250210115134_add_pkr_currency/migration.sql new file mode 100644 index 000000000..7aed4fbe5 --- /dev/null +++ b/app/database/migrations/20250210115134_add_pkr_currency/migration.sql @@ -0,0 +1,3 @@ +-- AlterEnum +ALTER TYPE "Currency" ADD VALUE 'PKR'; + diff --git a/app/database/schema.prisma b/app/database/schema.prisma index d7c9f214f..71567f2de 100644 --- a/app/database/schema.prisma +++ b/app/database/schema.prisma @@ -632,6 +632,7 @@ enum Currency { SEK // Swedish Krona SGD // Singapore Dollar ZAR // South African Rand + PKR // Pakistani Rupee } enum InviteStatuses { diff --git a/app/modules/organization/service.server.ts b/app/modules/organization/service.server.ts index dff0e2fb4..ea9e63a44 100644 --- a/app/modules/organization/service.server.ts +++ b/app/modules/organization/service.server.ts @@ -264,6 +264,8 @@ export async function updateOrganization({ }); } + console.log(data); + return await db.organization.update({ where: { id }, data: data, diff --git a/app/routes/_layout+/settings.general.tsx b/app/routes/_layout+/settings.general.tsx index 8ac0d7f65..06379f49f 100644 --- a/app/routes/_layout+/settings.general.tsx +++ b/app/routes/_layout+/settings.general.tsx @@ -224,7 +224,7 @@ export default function GeneralPage() { const { organization, canExportAssets } = useLoaderData(); return (
-
+

General

Manage general workspace settings. From 750598b111873065a0e0f51ccd7b379a3bac45f3 Mon Sep 17 00:00:00 2001 From: Donkoko Date: Tue, 11 Feb 2025 12:08:01 +0200 Subject: [PATCH 2/2] small adjustments: - removed usage of useEffect in keyboard navigation - added the option to navigate with arrows when using the currency selector - removed a forgotten console.log --- .../advanced-filters/field-selector.tsx | 34 ++++++----- .../workspace/currency-selector.tsx | 60 ++++++++++++++++++- app/modules/organization/service.server.ts | 2 - 3 files changed, 77 insertions(+), 19 deletions(-) diff --git a/app/components/assets/assets-index/advanced-filters/field-selector.tsx b/app/components/assets/assets-index/advanced-filters/field-selector.tsx index c4a134173..9f27d0300 100644 --- a/app/components/assets/assets-index/advanced-filters/field-selector.tsx +++ b/app/components/assets/assets-index/advanced-filters/field-selector.tsx @@ -59,17 +59,33 @@ export function FieldSelector({ setSelectedIndex(0); // Reset selection when search changes }; + // Ensure selected item is visible in viewport + const scrollToIndex = (index: number) => { + setTimeout(() => { + const selectedElement = document.getElementById(`column-option-${index}`); + if (selectedElement) { + selectedElement.scrollIntoView({ block: "nearest" }); + } + }, 0); + }; + const handleKeyDown = (event: KeyboardEvent) => { switch (event.key) { case "ArrowDown": event.preventDefault(); - setSelectedIndex((prev) => - prev < filteredColumns.length - 1 ? prev + 1 : prev - ); + setSelectedIndex((prev) => { + const newIndex = prev < filteredColumns.length - 1 ? prev + 1 : prev; + scrollToIndex(newIndex); + return newIndex; + }); break; case "ArrowUp": event.preventDefault(); - setSelectedIndex((prev) => (prev > 0 ? prev - 1 : prev)); + setSelectedIndex((prev) => { + const newIndex = prev > 0 ? prev - 1 : prev; + scrollToIndex(newIndex); + return newIndex; + }); break; case "Enter": event.preventDefault(); @@ -80,16 +96,6 @@ export function FieldSelector({ } }; - // Ensure selected item is visible in viewport - useEffect(() => { - const selectedElement = document.getElementById( - `column-option-${selectedIndex}` - ); - if (selectedElement) { - selectedElement.scrollIntoView({ block: "nearest" }); - } - }, [selectedIndex]); - const displayText = filter.isNew ? "Select column" : parseColumnName(fieldName); diff --git a/app/components/workspace/currency-selector.tsx b/app/components/workspace/currency-selector.tsx index 3fdf7f424..18cf4b376 100644 --- a/app/components/workspace/currency-selector.tsx +++ b/app/components/workspace/currency-selector.tsx @@ -1,4 +1,6 @@ import { useMemo, useRef, useState } from "react"; +import type { KeyboardEvent } from "react"; + import { Popover, PopoverContent, @@ -27,6 +29,8 @@ export default function CurrencySelector({ const [isOpen, setIsOpen] = useState(false); const [selectedCurrency, setSelectedCurrency] = useState(defaultValue); + const [selectedIndex, setSelectedIndex] = useState(0); + const [searchQuery, setSearchQuery] = useState(""); const filteredCurrencies = useMemo(() => { @@ -44,8 +48,55 @@ export default function CurrencySelector({ setIsOpen(false); } + // Ensure selected item is visible in viewport + const scrollToIndex = (index: number) => { + setTimeout(() => { + const selectedElement = document.getElementById( + `currency-option-${index}` + ); + if (selectedElement) { + selectedElement.scrollIntoView({ block: "nearest" }); + } + }, 0); + }; + + const handleKeyDown = (event: KeyboardEvent) => { + switch (event.key) { + case "ArrowDown": + event.preventDefault(); + setSelectedIndex((prev) => { + const newIndex = + prev < filteredCurrencies.length - 1 ? prev + 1 : prev; + scrollToIndex(newIndex); + return newIndex; + }); + break; + case "ArrowUp": + event.preventDefault(); + setSelectedIndex((prev) => { + const newIndex = prev > 0 ? prev - 1 : prev; + scrollToIndex(newIndex); + return newIndex; + }); + break; + case "Enter": + event.preventDefault(); + if (filteredCurrencies[selectedIndex]) { + setSelectedCurrency(filteredCurrencies[selectedIndex]); + setIsOpen(false); + } + break; + } + }; + return ( - + { + scrollToIndex(selectedIndex); + setIsOpen(v); + }} + >

- {filteredCurrencies.map((currency) => { + {filteredCurrencies.map((currency, index) => { const isSelected = selectedCurrency === currency; + const isHovered = selectedIndex === index; return (
{ handleSelect(currency); diff --git a/app/modules/organization/service.server.ts b/app/modules/organization/service.server.ts index ea9e63a44..dff0e2fb4 100644 --- a/app/modules/organization/service.server.ts +++ b/app/modules/organization/service.server.ts @@ -264,8 +264,6 @@ export async function updateOrganization({ }); } - console.log(data); - return await db.organization.update({ where: { id }, data: data,