Initial commit: FPT Financial Planning Tool
Deploy App / deploy (push) Successful in 3m3s

This commit is contained in:
2026-07-08 19:19:39 +02:00
commit 4f990c9686
60 changed files with 12436 additions and 0 deletions
+106
View File
@@ -0,0 +1,106 @@
"use client";
import { InfoBubble } from "@/components/InfoBubble";
const baseInputClass =
"w-full rounded-md border border-zinc-300 bg-white px-2 py-1.5 text-sm text-zinc-900 focus:border-zinc-500 focus:outline-none dark:border-zinc-600 dark:bg-zinc-900 dark:text-zinc-100";
export function FieldLabel({ label, help }: { label: string; help?: string }) {
return (
<label className="mb-1 flex items-center text-xs font-medium text-zinc-600 dark:text-zinc-400">
{label}
{help && <InfoBubble text={help} />}
</label>
);
}
export function NumberField({
label,
help,
value,
onChange,
step,
min,
max,
}: {
label: string;
help?: string;
value: number;
onChange: (value: number) => void;
step?: number;
min?: number;
max?: number;
}) {
return (
<div>
<FieldLabel label={label} help={help} />
<input
type="number"
className={baseInputClass}
value={Number.isFinite(value) ? value : 0}
step={step ?? "any"}
min={min}
max={max}
onChange={(e) => onChange(e.target.valueAsNumber || 0)}
/>
</div>
);
}
export function TextField({
label,
help,
value,
onChange,
placeholder,
}: {
label: string;
help?: string;
value: string;
onChange: (value: string) => void;
placeholder?: string;
}) {
return (
<div>
<FieldLabel label={label} help={help} />
<input
type="text"
className={baseInputClass}
value={value}
placeholder={placeholder}
onChange={(e) => onChange(e.target.value)}
/>
</div>
);
}
export function SelectField<T extends string>({
label,
help,
value,
onChange,
options,
}: {
label: string;
help?: string;
value: T;
onChange: (value: T) => void;
options: { value: T; label: string }[];
}) {
return (
<div>
<FieldLabel label={label} help={help} />
<select
className={baseInputClass}
value={value}
onChange={(e) => onChange(e.target.value as T)}
>
{options.map((o) => (
<option key={o.value} value={o.value}>
{o.label}
</option>
))}
</select>
</div>
);
}