From 1ae3beeaecc849e16faaa7b511e221dc03f2a097 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Qu=E1=BB=91c=20Kh=C3=A1nh?= Date: Tue, 9 Jul 2024 21:30:08 +0700 Subject: [PATCH] feat(mobile): add transaction form layout --- apps/mobile/app/(app)/new-record.tsx | 30 ++-- .../components/form-fields/input-field.tsx | 4 +- .../components/numeric-pad/numeric-pad.tsx | 11 +- .../transaction/transaction-form.tsx | 138 ++++++++++++++++++ packages/validation/src/transaction.zod.ts | 3 + 5 files changed, 164 insertions(+), 22 deletions(-) create mode 100644 apps/mobile/components/transaction/transaction-form.tsx diff --git a/apps/mobile/app/(app)/new-record.tsx b/apps/mobile/app/(app)/new-record.tsx index 3543c84f..415ec63a 100644 --- a/apps/mobile/app/(app)/new-record.tsx +++ b/apps/mobile/app/(app)/new-record.tsx @@ -1,21 +1,19 @@ -import { NumericPad } from '@/components/numeric-pad' -import { TextTicker } from '@/components/text-ticker' -import { useState } from 'react' -import { View } from 'react-native' +import { TransactionForm } from '@/components/transaction/transaction-form' +import { useWallets } from '@/queries/wallet' +import { useRouter } from 'expo-router' export default function NewRecordScreen() { - const [value, setValue] = useState(0) + const router = useRouter() + const { data: walletAccounts } = useWallets() + return ( - - - - - - + console.log('submit', values)} + onCancel={router.back} + defaultValues={{ + walletAccountId: walletAccounts?.[0].id, + currency: walletAccounts?.[0].preferredCurrency, + }} + /> ) } diff --git a/apps/mobile/components/form-fields/input-field.tsx b/apps/mobile/components/form-fields/input-field.tsx index 4b8a2db9..c62185db 100644 --- a/apps/mobile/components/form-fields/input-field.tsx +++ b/apps/mobile/components/form-fields/input-field.tsx @@ -11,6 +11,7 @@ type InputFieldProps = TextInputProps & { leftSection?: React.ReactNode rightSection?: React.ReactNode disabled?: boolean + wrapperClassName?: string } export const InputField = forwardRef( @@ -21,6 +22,7 @@ export const InputField = forwardRef( leftSection, rightSection, className, + wrapperClassName, disabled, ...props }: InputFieldProps, @@ -31,7 +33,7 @@ export const InputField = forwardRef( fieldState, } = useController({ name }) return ( - + {!!label && } {leftSection && ( diff --git a/apps/mobile/components/numeric-pad/numeric-pad.tsx b/apps/mobile/components/numeric-pad/numeric-pad.tsx index 2120d5a2..eee60192 100644 --- a/apps/mobile/components/numeric-pad/numeric-pad.tsx +++ b/apps/mobile/components/numeric-pad/numeric-pad.tsx @@ -1,6 +1,7 @@ import { cn } from '@/lib/utils' import { DeleteIcon } from 'lucide-react-native' import { View } from 'react-native' +import Animated from 'react-native-reanimated' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { Button } from '../ui/button' import { Text } from '../ui/text' @@ -50,15 +51,15 @@ export function NumericPad({ } return ( - {buttonKeys.map((buttonKey) => ( - + ))} - + - + ) } diff --git a/apps/mobile/components/transaction/transaction-form.tsx b/apps/mobile/components/transaction/transaction-form.tsx new file mode 100644 index 00000000..089866cd --- /dev/null +++ b/apps/mobile/components/transaction/transaction-form.tsx @@ -0,0 +1,138 @@ +import { + type TransactionFormValues, + zTransactionFormValues, +} from '@6pm/validation' +import { zodResolver } from '@hookform/resolvers/zod' +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { + Calendar, + CreditCard, + LandPlot, + ShapesIcon, + XIcon, +} from 'lucide-react-native' +import { Controller, FormProvider, useForm } from 'react-hook-form' +import { ScrollView, View } from 'react-native' +import Animated, { + useAnimatedKeyboard, + useAnimatedStyle, +} from 'react-native-reanimated' +import { InputField } from '../form-fields/input-field' +import { SubmitButton } from '../form-fields/submit-button' +import { NumericPad } from '../numeric-pad' +import { TextTicker } from '../text-ticker' +import { Button } from '../ui/button' +import { Text } from '../ui/text' + +type TransactionFormProps = { + onSubmit: (data: TransactionFormValues) => void + defaultValues?: Partial + onCancel?: () => void +} + +export const TransactionForm = ({ + onSubmit, + defaultValues, + onCancel, +}: TransactionFormProps) => { + const { i18n } = useLingui() + + const keyboard = useAnimatedKeyboard() + const translateStyle = useAnimatedStyle(() => { + return { + transform: [{ translateY: keyboard.height.value }], + } + }) + + const transactionForm = useForm({ + resolver: zodResolver(zTransactionFormValues), + defaultValues: { + date: new Date(), + amount: 0, + currency: 'VND', + note: '', + ...defaultValues, + }, + }) + + const amount = transactionForm.watch('amount') + const currency = transactionForm.watch('currency') + + return ( + + + + + + + + + + + + + + + + + + + + + {t(i18n)`Save`} + + + ( + + )} + /> + + + + ) +} diff --git a/packages/validation/src/transaction.zod.ts b/packages/validation/src/transaction.zod.ts index 526559ec..afee882c 100644 --- a/packages/validation/src/transaction.zod.ts +++ b/packages/validation/src/transaction.zod.ts @@ -19,3 +19,6 @@ export const zUpdateTransaction = z.object({ walletAccountId: z.string().optional(), }) export type UpdateTransaction = z.infer + +export const zTransactionFormValues = zCreateTransaction +export type TransactionFormValues = z.infer