first commit
This commit is contained in:
132
src/components/income/IncomeForm.tsx
Normal file
132
src/components/income/IncomeForm.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useState } from "react";
|
||||
import { DateField } from "@/components/shared/DateField";
|
||||
import { Field, inputClass } from "@/components/shared/Field";
|
||||
import { Select } from "@/components/shared/Select";
|
||||
import {
|
||||
SimpleFormActions,
|
||||
SimpleFormReview,
|
||||
SimpleFormStepIndicator,
|
||||
} from "@/components/shared/SimpleFormSteps";
|
||||
import { TitleCombobox } from "@/components/shared/TitleCombobox";
|
||||
import { formatItalianDate, todayIso } from "@/lib/date/formatDate";
|
||||
import { incomeTypeLabel } from "@/lib/finance/labels";
|
||||
import {
|
||||
euroFormatter,
|
||||
formatMoneyInput,
|
||||
parseMoneyInput,
|
||||
} from "@/lib/finance/money";
|
||||
import type { Income, PaymentTitle } from "@/types/finance";
|
||||
|
||||
export function IncomeForm({
|
||||
titles,
|
||||
editing,
|
||||
defaultDate = todayIso(),
|
||||
onSubmit,
|
||||
onCreateTitle,
|
||||
onCancel,
|
||||
saving = false,
|
||||
}: {
|
||||
titles: PaymentTitle[];
|
||||
editing?: Income;
|
||||
defaultDate?: string;
|
||||
onSubmit: (data: object) => Promise<void>;
|
||||
onCreateTitle?: (name: string) => Promise<void>;
|
||||
onCancel?: () => void;
|
||||
saving?: boolean;
|
||||
}) {
|
||||
const [review, setReview] = useState(false);
|
||||
const [title, setTitle] = useState(editing?.title ?? "");
|
||||
const [amount, setAmount] = useState(formatMoneyInput(editing?.amount));
|
||||
const [type, setType] = useState(editing?.type ?? "salary");
|
||||
const [date, setDate] = useState(editing?.date ?? defaultDate);
|
||||
const [error, setError] = useState("");
|
||||
|
||||
useEffect(() => {
|
||||
setReview(false);
|
||||
setTitle(editing?.title ?? "");
|
||||
setAmount(formatMoneyInput(editing?.amount));
|
||||
setType(editing?.type ?? "salary");
|
||||
setDate(editing?.date ?? defaultDate);
|
||||
setError("");
|
||||
}, [defaultDate, editing]);
|
||||
|
||||
function payload() {
|
||||
if (!title.trim()) throw new Error("Inserisci il titolo dell'entrata");
|
||||
if (!date) throw new Error("Inserisci la data dell'entrata");
|
||||
return { title, amount: parseMoneyInput(amount), type, date };
|
||||
}
|
||||
|
||||
return (
|
||||
<form
|
||||
onSubmit={async (event) => {
|
||||
event.preventDefault();
|
||||
try {
|
||||
setError("");
|
||||
const data = payload();
|
||||
if (!review) {
|
||||
setReview(true);
|
||||
return;
|
||||
}
|
||||
await onSubmit(data);
|
||||
} catch (err) {
|
||||
setError(
|
||||
err instanceof Error ? err.message : "Salvataggio non riuscito",
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<SimpleFormStepIndicator review={review} />
|
||||
{review ? (
|
||||
<SimpleFormReview
|
||||
rows={[
|
||||
{
|
||||
label: "Importo",
|
||||
value: euroFormatter.format(parseMoneyInput(amount)),
|
||||
},
|
||||
{ label: "Tipo", value: incomeTypeLabel(type) },
|
||||
{ label: "Data", value: formatItalianDate(date) },
|
||||
]}
|
||||
title={title}
|
||||
/>
|
||||
) : (
|
||||
<div className="grid gap-3 md:grid-cols-4">
|
||||
<TitleCombobox
|
||||
titles={titles}
|
||||
type="income"
|
||||
value={title}
|
||||
onChange={setTitle}
|
||||
onCreate={onCreateTitle}
|
||||
/>
|
||||
<Field label="Importo">
|
||||
<input
|
||||
className={inputClass}
|
||||
value={amount}
|
||||
onChange={(event) => setAmount(event.target.value)}
|
||||
/>
|
||||
</Field>
|
||||
<Select
|
||||
label="Tipo"
|
||||
value={type}
|
||||
onChange={(value) => setType(value as typeof type)}
|
||||
options={[
|
||||
{ value: "salary", label: "Stipendio" },
|
||||
{ value: "recurring", label: "Ricorrente" },
|
||||
{ value: "one_time", label: "Una tantum" },
|
||||
{ value: "other", label: "Altro" },
|
||||
]}
|
||||
/>
|
||||
<DateField label="Data" name="date" value={date} onChange={setDate} />
|
||||
</div>
|
||||
)}
|
||||
{error ? <p className="mt-4 text-sm text-rose-300">{error}</p> : null}
|
||||
<SimpleFormActions
|
||||
onBack={() => setReview(false)}
|
||||
onCancel={onCancel}
|
||||
review={review}
|
||||
saving={saving}
|
||||
/>
|
||||
</form>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user