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:
Felipe Coutinho
2026-01-27 13:15:37 +00:00
parent 8ffe61c59b
commit a7f63fb77a
442 changed files with 66141 additions and 69292 deletions

View File

@@ -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>
);
}