Skip to content

Commit

Permalink
Vat input
Browse files Browse the repository at this point in the history
  • Loading branch information
pontusab committed May 13, 2024
1 parent 72d2fbb commit 093acde
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 67 deletions.
36 changes: 15 additions & 21 deletions apps/dashboard/src/components/modals/create-categories-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import {
createCategoriesSchema,
} from "@/actions/schema";
import { InputColor } from "@/components/input-color";
import { VatAssistant } from "@/components/vat-assistant";
import { zodResolver } from "@hookform/resolvers/zod";
import { Button } from "@midday/ui/button";
import {
Expand All @@ -22,6 +21,7 @@ import { Loader2 } from "lucide-react";
import { useAction } from "next-safe-action/hooks";
import { useEffect } from "react";
import { useFieldArray, useForm } from "react-hook-form";
import { VatInput } from "../vat-input";

type Props = {
onOpenChange: (isOpen: boolean) => void;
Expand Down Expand Up @@ -132,31 +132,25 @@ export function CreateCategoriesModal({ onOpenChange, isOpen }: Props) {
render={({ field }) => (
<FormItem className="flex-1">
<FormControl>
<Input
{...field}
autoFocus={false}
placeholder="VAT"
className="remove-arrow"
type="number"
min={0}
max={100}
<VatInput
value={field.value}
name={form.watch(`categories.${index}.name`)}
onChange={(evt) => {
field.onChange(evt.target.value);
}}
onSelect={(vat) => {
if (vat) {
form.setValue(
`categories.${index}.vat`,
vat.toString()
);
}
}}
/>
</FormControl>
</FormItem>
)}
/>

<VatAssistant
name={form.watch(`categories.${index}.name`)}
onSelect={(vat) => {
if (vat) {
form.setValue(
`categories.${index}.vat`,
vat.toString()
);
}
}}
/>
</div>
</div>

Expand Down
31 changes: 12 additions & 19 deletions apps/dashboard/src/components/modals/edit-category-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { Loader2 } from "lucide-react";
import { useAction } from "next-safe-action/hooks";
import { useForm } from "react-hook-form";
import { InputColor } from "../input-color";
import { VatInput } from "../vat-input";

type Props = {
id: string;
Expand Down Expand Up @@ -117,30 +118,22 @@ export function EditCategoryModal({
render={({ field }) => (
<FormItem className="flex-1">
<FormControl>
<Input
{...field}
autoFocus={false}
placeholder="VAT"
className="remove-arrow"
type="number"
min={0}
max={100}
<VatInput
value={field.value}
name={form.watch("name")}
onChange={(evt) => {
field.onChange(evt.target.value);
}}
onSelect={(vat) => {
if (vat) {
form.setValue("vat", vat.toString());
}
}}
/>
</FormControl>
</FormItem>
)}
/>

{defaultValue.name !== form.watch("name") && (
<VatAssistant
name={form.watch("name")}
onSelect={(vat) => {
if (vat) {
form.setValue("vat", vat.toString());
}
}}
/>
)}
</div>
</div>

Expand Down
40 changes: 13 additions & 27 deletions apps/dashboard/src/components/vat-assistant.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,21 @@ import {
TooltipProvider,
TooltipTrigger,
} from "@midday/ui/tooltip";
import { useDebounce } from "@uidotdev/usehooks";
import { Loader2 } from "lucide-react";
import { useAction } from "next-safe-action/hooks";
import { useEffect, useState } from "react";

type Props = {
name?: string;
value?: string | null;
onSelect: (value: number) => void;
isFocused: boolean;
};

export function VatAssistant({ name, onSelect }: Props) {
export function VatAssistant({ name, onSelect, isFocused, value }: Props) {
const [result, setResult] = useState<
{ vat: number; country: string } | undefined
>();
const [isLoading, setLoading] = useState(false);
const debouncedName = useDebounce(name, 300);

const getVatRate = useAction(getVatRateAction, {
onSuccess: (data) => {
Expand All @@ -46,37 +45,24 @@ export function VatAssistant({ name, onSelect }: Props) {
};

useEffect(() => {
if (name) {
if (isFocused && name && name.length > 2 && !value) {
setLoading(true);
getVatRate.execute({ name });
}

if (!name) {
setLoading(false);
setResult(undefined);
}
}, [name]);

useEffect(() => {
if (debouncedName && debouncedName.length > 2) {
getVatRate.execute({ name: debouncedName });
}
}, [debouncedName]);
}, [isFocused, name]);

return (
<TooltipProvider delayDuration={0}>
<Tooltip>
<TooltipTrigger asChild>
<div className="absolute right-2 top-3">
{isLoading ? (
<Loader2 className="h-3.5 w-3.5 animate-spin" />
) : (
<Icons.AIOutline
className={cn(
"pointer-events-none transition-all opacity-0",
result?.vat && "opacity-100"
)}
/>
)}
<Icons.AIOutline
className={cn(
"pointer-events-none opacity-50 transition-colors",
result?.vat && "opacity-100",
isLoading && "animate-pulse opacity-100"
)}
/>
</div>
</TooltipTrigger>
{result?.vat && (
Expand Down
50 changes: 50 additions & 0 deletions apps/dashboard/src/components/vat-input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { Input } from "@midday/ui/input";
import { type ChangeEventHandler, useState } from "react";
import { VatAssistant } from "./vat-assistant";

type Props = {
name: string;
onChange: (value: ChangeEventHandler<HTMLInputElement>) => void;
onSelect: (vat: number) => void;
value?: string | null;
};

export function VatInput({
name,
onChange,
onSelect,
value: defaultValue,
}: Props) {
const [isFocused, setFocused] = useState(false);
const [value, setValue] = useState(defaultValue);

const handleOnSelect = (vat: number) => {
setValue(vat.toString());
onSelect(vat);
};

return (
<div className="relative">
<Input
key={value}
onChange={onChange}
autoFocus={false}
placeholder="VAT"
className="remove-arrow"
type="number"
min={0}
max={100}
onFocus={() => setFocused(true)}
onBlur={() => setFocused(false)}
defaultValue={value ?? ""}
/>

<VatAssistant
name={name}
value={value}
onSelect={handleOnSelect}
isFocused={isFocused}
/>
</div>
);
}

0 comments on commit 093acde

Please sign in to comment.