chore: add Biome for lint and fix all lint errors
- Install @biomejs/biome as devDependency at root - Configure biome.json with 2-space indent, double quotes, Tailwind CSS support - Add lint/lint:fix/format/format:fix scripts to root and app package.json - Fix noNonNullAssertion: env vars extracted to variables with suppression, <div role=button> replaced with <button> - Fix noUnusedVariables: remove unused destructured vars - Fix useIterableCallbackReturn: arrow functions with block body - Fix noExplicitAny: recharts Tooltip formatters - Fix noLabelWithoutControl: add htmlFor+id or use <span> for non-input labels - Fix noStaticElementInteractions/useKeyWithClickEvents: role+keyboard events for overlays - Fix noArrayIndexKey: use error string as key - Fix CSS parse: enable tailwindDirectives parser - Normalize formatting across 80 files with biome check --write
This commit is contained in:
@@ -1,8 +1,8 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { useForm } from "react-hook-form";
|
||||
import { z } from "zod";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import type { PeriodicExpense } from "@/lib/api";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import type { PeriodicExpense } from "@/lib/api";
|
||||
|
||||
const months = [
|
||||
{ value: 1, label: "Ene" },
|
||||
@@ -20,8 +20,15 @@ const months = [
|
||||
];
|
||||
|
||||
const periodicExpenseFormSchema = z.object({
|
||||
description: z.string().min(1, "La descripción es obligatoria").max(50, "Máximo 50 caracteres"),
|
||||
defaultDueDay: z.number().int().min(1, "Día entre 1 y 31").max(31, "Día entre 1 y 31"),
|
||||
description: z
|
||||
.string()
|
||||
.min(1, "La descripción es obligatoria")
|
||||
.max(50, "Máximo 50 caracteres"),
|
||||
defaultDueDay: z
|
||||
.number()
|
||||
.int()
|
||||
.min(1, "Día entre 1 y 31")
|
||||
.max(31, "Día entre 1 y 31"),
|
||||
defaultAmount: z.number().positive("El monto debe ser mayor a 0"),
|
||||
periods: z.array(z.number()).min(1, "Seleccioná al menos un mes"),
|
||||
});
|
||||
@@ -34,7 +41,11 @@ interface PeriodicExpenseFormProps {
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
export function PeriodicExpenseForm({ initialData, onSubmit, onCancel }: PeriodicExpenseFormProps) {
|
||||
export function PeriodicExpenseForm({
|
||||
initialData,
|
||||
onSubmit,
|
||||
onCancel,
|
||||
}: PeriodicExpenseFormProps) {
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
@@ -62,9 +73,15 @@ export function PeriodicExpenseForm({ initialData, onSubmit, onCancel }: Periodi
|
||||
|
||||
function toggleMonth(month: number) {
|
||||
if (selectedPeriods.includes(month)) {
|
||||
setValue("periods", selectedPeriods.filter((m) => m !== month), { shouldValidate: true });
|
||||
setValue(
|
||||
"periods",
|
||||
selectedPeriods.filter((m) => m !== month),
|
||||
{ shouldValidate: true },
|
||||
);
|
||||
} else {
|
||||
setValue("periods", [...selectedPeriods, month], { shouldValidate: true });
|
||||
setValue("periods", [...selectedPeriods, month], {
|
||||
shouldValidate: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +98,9 @@ export function PeriodicExpenseForm({ initialData, onSubmit, onCancel }: Periodi
|
||||
className="h-8 rounded-lg border border-input bg-background px-2.5 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
|
||||
/>
|
||||
{errors.description && (
|
||||
<span className="text-xs text-destructive">{errors.description.message}</span>
|
||||
<span className="text-xs text-destructive">
|
||||
{errors.description.message}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -100,7 +119,9 @@ export function PeriodicExpenseForm({ initialData, onSubmit, onCancel }: Periodi
|
||||
className="h-8 rounded-lg border border-input bg-background px-2.5 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
|
||||
/>
|
||||
{errors.defaultAmount && (
|
||||
<span className="text-xs text-destructive">{errors.defaultAmount.message}</span>
|
||||
<span className="text-xs text-destructive">
|
||||
{errors.defaultAmount.message}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -117,14 +138,16 @@ export function PeriodicExpenseForm({ initialData, onSubmit, onCancel }: Periodi
|
||||
className="h-8 rounded-lg border border-input bg-background px-2.5 text-sm outline-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50"
|
||||
/>
|
||||
{errors.defaultDueDay && (
|
||||
<span className="text-xs text-destructive">{errors.defaultDueDay.message}</span>
|
||||
<span className="text-xs text-destructive">
|
||||
{errors.defaultDueDay.message}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<div className="flex items-center justify-between">
|
||||
<label className="text-sm font-medium">Meses de aplicación</label>
|
||||
<span className="text-sm font-medium">Meses de aplicación</span>
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
type="button"
|
||||
@@ -162,13 +185,20 @@ export function PeriodicExpenseForm({ initialData, onSubmit, onCancel }: Periodi
|
||||
))}
|
||||
</div>
|
||||
{errors.periods && (
|
||||
<span className="text-xs text-destructive">{errors.periods.message}</span>
|
||||
<span className="text-xs text-destructive">
|
||||
{errors.periods.message}
|
||||
</span>
|
||||
)}
|
||||
<input type="hidden" {...register("periods")} />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col-reverse sm:flex-row sm:justify-end gap-2 mt-2">
|
||||
<Button type="button" variant="outline" onClick={onCancel} disabled={isSubmitting}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
onClick={onCancel}
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
Cancelar
|
||||
</Button>
|
||||
<Button type="submit" disabled={isSubmitting}>
|
||||
|
||||
Reference in New Issue
Block a user