forked from git.gladyson/openmonetis
refactor: migrate from ESLint to Biome and extract SQL queries to data.ts
- Replace ESLint with Biome for linting and formatting - Configure Biome with tabs, double quotes, and organized imports - Move all SQL/Drizzle queries from page.tsx files to data.ts files - Create new data.ts files for: ajustes, dashboard, relatorios/categorias - Update existing data.ts files: extrato, fatura (add lancamentos queries) - Remove all drizzle-orm imports from page.tsx files - Update README.md with new tooling info Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,126 +4,126 @@ import { RiCheckLine, RiSearchLine } from "@remixicon/react";
|
||||
import * as React from "react";
|
||||
|
||||
import {
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
Command,
|
||||
CommandEmpty,
|
||||
CommandGroup,
|
||||
CommandItem,
|
||||
CommandList,
|
||||
} from "@/components/ui/command";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import { cn } from "@/lib/utils/ui";
|
||||
|
||||
export interface EstabelecimentoInputProps {
|
||||
id?: string;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
estabelecimentos: string[];
|
||||
placeholder?: string;
|
||||
required?: boolean;
|
||||
maxLength?: number;
|
||||
id?: string;
|
||||
value: string;
|
||||
onChange: (value: string) => void;
|
||||
estabelecimentos: string[];
|
||||
placeholder?: string;
|
||||
required?: boolean;
|
||||
maxLength?: number;
|
||||
}
|
||||
|
||||
export function EstabelecimentoInput({
|
||||
id,
|
||||
value,
|
||||
onChange,
|
||||
estabelecimentos = [],
|
||||
placeholder = "Ex.: Padaria",
|
||||
required = false,
|
||||
maxLength = 20,
|
||||
id,
|
||||
value,
|
||||
onChange,
|
||||
estabelecimentos = [],
|
||||
placeholder = "Ex.: Padaria",
|
||||
required = false,
|
||||
maxLength = 20,
|
||||
}: EstabelecimentoInputProps) {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [searchValue, setSearchValue] = React.useState("");
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const [searchValue, setSearchValue] = React.useState("");
|
||||
|
||||
const handleSelect = (selectedValue: string) => {
|
||||
onChange(selectedValue);
|
||||
setOpen(false);
|
||||
setSearchValue("");
|
||||
};
|
||||
const handleSelect = (selectedValue: string) => {
|
||||
onChange(selectedValue);
|
||||
setOpen(false);
|
||||
setSearchValue("");
|
||||
};
|
||||
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = e.target.value;
|
||||
onChange(newValue);
|
||||
setSearchValue(newValue);
|
||||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const newValue = e.target.value;
|
||||
onChange(newValue);
|
||||
setSearchValue(newValue);
|
||||
|
||||
// Open popover when user types and there are suggestions
|
||||
if (newValue.length > 0 && estabelecimentos.length > 0) {
|
||||
setOpen(true);
|
||||
}
|
||||
};
|
||||
// Open popover when user types and there are suggestions
|
||||
if (newValue.length > 0 && estabelecimentos.length > 0) {
|
||||
setOpen(true);
|
||||
}
|
||||
};
|
||||
|
||||
const filteredEstabelecimentos = React.useMemo(() => {
|
||||
if (!searchValue) return estabelecimentos;
|
||||
const filteredEstabelecimentos = React.useMemo(() => {
|
||||
if (!searchValue) return estabelecimentos;
|
||||
|
||||
const lowerSearch = searchValue.toLowerCase();
|
||||
return estabelecimentos.filter((item) =>
|
||||
item.toLowerCase().includes(lowerSearch)
|
||||
);
|
||||
}, [estabelecimentos, searchValue]);
|
||||
const lowerSearch = searchValue.toLowerCase();
|
||||
return estabelecimentos.filter((item) =>
|
||||
item.toLowerCase().includes(lowerSearch),
|
||||
);
|
||||
}, [estabelecimentos, searchValue]);
|
||||
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id={id}
|
||||
value={value}
|
||||
onChange={handleInputChange}
|
||||
placeholder={placeholder}
|
||||
required={required}
|
||||
maxLength={maxLength}
|
||||
autoComplete="off"
|
||||
onFocus={() => {
|
||||
if (estabelecimentos.length > 0) {
|
||||
setOpen(true);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{estabelecimentos.length > 0 && (
|
||||
<RiSearchLine className="absolute right-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground pointer-events-none" />
|
||||
)}
|
||||
</div>
|
||||
</PopoverTrigger>
|
||||
{estabelecimentos.length > 0 && (
|
||||
<PopoverContent
|
||||
className="p-0 w-[--radix-popover-trigger-width]"
|
||||
align="start"
|
||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||
>
|
||||
<Command>
|
||||
<CommandList className="max-h-[300px] overflow-y-auto">
|
||||
<CommandEmpty className="p-6">
|
||||
Nenhum estabelecimento encontrado.
|
||||
</CommandEmpty>
|
||||
<CommandGroup className="p-1">
|
||||
{filteredEstabelecimentos.map((item) => (
|
||||
<CommandItem
|
||||
key={item}
|
||||
value={item}
|
||||
onSelect={() => handleSelect(item)}
|
||||
className="cursor-pointer gap-1"
|
||||
>
|
||||
<RiCheckLine
|
||||
className={cn(
|
||||
"size-4 shrink-0",
|
||||
value === item
|
||||
? "opacity-100 text-green-500"
|
||||
: "opacity-5"
|
||||
)}
|
||||
/>
|
||||
<span className="truncate flex-1">{item}</span>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
)}
|
||||
</Popover>
|
||||
);
|
||||
return (
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<div className="relative">
|
||||
<Input
|
||||
id={id}
|
||||
value={value}
|
||||
onChange={handleInputChange}
|
||||
placeholder={placeholder}
|
||||
required={required}
|
||||
maxLength={maxLength}
|
||||
autoComplete="off"
|
||||
onFocus={() => {
|
||||
if (estabelecimentos.length > 0) {
|
||||
setOpen(true);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{estabelecimentos.length > 0 && (
|
||||
<RiSearchLine className="absolute right-3 top-1/2 -translate-y-1/2 size-4 text-muted-foreground pointer-events-none" />
|
||||
)}
|
||||
</div>
|
||||
</PopoverTrigger>
|
||||
{estabelecimentos.length > 0 && (
|
||||
<PopoverContent
|
||||
className="p-0 w-[--radix-popover-trigger-width]"
|
||||
align="start"
|
||||
onOpenAutoFocus={(e) => e.preventDefault()}
|
||||
>
|
||||
<Command>
|
||||
<CommandList className="max-h-[300px] overflow-y-auto">
|
||||
<CommandEmpty className="p-6">
|
||||
Nenhum estabelecimento encontrado.
|
||||
</CommandEmpty>
|
||||
<CommandGroup className="p-1">
|
||||
{filteredEstabelecimentos.map((item) => (
|
||||
<CommandItem
|
||||
key={item}
|
||||
value={item}
|
||||
onSelect={() => handleSelect(item)}
|
||||
className="cursor-pointer gap-1"
|
||||
>
|
||||
<RiCheckLine
|
||||
className={cn(
|
||||
"size-4 shrink-0",
|
||||
value === item
|
||||
? "opacity-100 text-green-500"
|
||||
: "opacity-5",
|
||||
)}
|
||||
/>
|
||||
<span className="truncate flex-1">{item}</span>
|
||||
</CommandItem>
|
||||
))}
|
||||
</CommandGroup>
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
)}
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user