whisper-money/resources/js/components/accounts/account-form.tsx

221 lines
7.5 KiB
TypeScript

import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from '@/components/ui/select';
import {
ACCOUNT_TYPES,
CURRENCY_OPTIONS,
formatAccountType,
type AccountType,
type Bank,
type CurrencyCode,
} from '@/types/account';
import { __ } from '@/utils/i18n';
import { useCallback, useEffect, useState } from 'react';
import { BankCombobox } from './bank-combobox';
import { CustomBankData, CustomBankForm } from './custom-bank-form';
export interface AccountFormData {
displayName: string;
bankId: number | null;
type: AccountType | null;
currencyCode: CurrencyCode | null;
customBank: CustomBankData | null;
}
interface AccountFormProps {
initialValues?: {
displayName: string;
bank: Bank;
type: AccountType;
currencyCode: CurrencyCode;
};
forceAccountType?: AccountType;
onChange: (data: AccountFormData) => void;
}
const initialCustomBankData: CustomBankData = {
name: '',
logo: null,
logoPreview: null,
};
export function AccountForm({
initialValues,
forceAccountType,
onChange,
}: AccountFormProps) {
const [displayName, setDisplayName] = useState(
initialValues?.displayName ?? '',
);
const [selectedBankId, setSelectedBankId] = useState<number | null>(
initialValues?.bank.id ?? null,
);
const [selectedType, setSelectedType] = useState<AccountType | null>(
initialValues?.type ?? forceAccountType ?? null,
);
const [selectedCurrency, setSelectedCurrency] =
useState<CurrencyCode | null>(initialValues?.currencyCode ?? null);
const [isCreatingCustomBank, setIsCreatingCustomBank] = useState(false);
const [customBankData, setCustomBankData] = useState<CustomBankData>(
initialCustomBankData,
);
useEffect(() => {
onChange({
displayName,
bankId: isCreatingCustomBank ? null : selectedBankId,
type: selectedType,
currencyCode: selectedCurrency,
customBank: isCreatingCustomBank ? customBankData : null,
});
}, [
displayName,
selectedBankId,
selectedType,
selectedCurrency,
isCreatingCustomBank,
customBankData,
onChange,
]);
useEffect(() => {
if (initialValues) {
setDisplayName(initialValues.displayName);
setSelectedBankId(initialValues.bank.id);
setSelectedType(initialValues.type);
setSelectedCurrency(initialValues.currencyCode);
setIsCreatingCustomBank(false);
setCustomBankData(initialCustomBankData);
}
}, [initialValues]);
const handleCreateCustomBank = useCallback((searchQuery: string) => {
setIsCreatingCustomBank(true);
setCustomBankData({
name: searchQuery,
logo: null,
logoPreview: null,
});
setSelectedBankId(null);
}, []);
const handleCancelCustomBank = useCallback(() => {
setIsCreatingCustomBank(false);
setCustomBankData(initialCustomBankData);
}, []);
return (
<>
<div className="space-y-2">
<Label htmlFor="display_name">{__('Name')}</Label>
<Input
id="display_name"
className="mt-1"
name="display_name"
value={displayName}
onChange={(e) => setDisplayName(e.target.value)}
placeholder={__('Account name')}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="bank_id">{__('Bank')}</Label>
<div className="mt-1">
{isCreatingCustomBank ? (
<CustomBankForm
defaultName={customBankData.name}
value={customBankData}
onChange={setCustomBankData}
onCancel={handleCancelCustomBank}
/>
) : (
<>
<input
type="hidden"
name="bank_id"
value={selectedBankId ?? ''}
required
/>
<BankCombobox
value={selectedBankId}
onValueChange={setSelectedBankId}
defaultBank={initialValues?.bank}
onCreateCustomBank={handleCreateCustomBank}
/>
</>
)}
</div>
</div>
<div className="space-y-2">
<Label htmlFor="type">{__('Account Type')}</Label>
<div className="mt-1">
<Select
name="type"
value={selectedType ?? undefined}
disabled={!!forceAccountType}
onValueChange={(value) =>
setSelectedType(value as AccountType)
}
required
>
<SelectTrigger name="type">
<SelectValue
placeholder={__('Select account type')}
/>
</SelectTrigger>
<SelectContent>
{ACCOUNT_TYPES.map((type) => (
<SelectItem key={type} value={type}>
{formatAccountType(type)}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
{(selectedType === 'investment' ||
selectedType === 'retirement') && (
<p className="pl-1 text-xs text-muted-foreground">
{__(
"This account type is for balance tracking only and\n doesn't support transactions.",
)}
</p>
)}
</div>
<div className="space-y-2">
<Label htmlFor="currency_code">{__('Currency')}</Label>
<div className="mt-1">
<Select
name="currency_code"
value={selectedCurrency ?? undefined}
onValueChange={(value) =>
setSelectedCurrency(value as CurrencyCode)
}
required
>
<SelectTrigger name="currency_code">
<SelectValue placeholder={__('Select currency')} />
</SelectTrigger>
<SelectContent>
{CURRENCY_OPTIONS.map((currency) => (
<SelectItem key={currency} value={currency}>
{currency}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
</div>
</>
);
}