From 227b4b1518b9b1451a3cad5f5ac12796519cbe21 Mon Sep 17 00:00:00 2001 From: Dustin Do Date: Sun, 8 Sep 2024 17:37:43 +0700 Subject: [PATCH] feat(mobile): add account wallet store --- .vscode/settings.json | 3 +- apps/api/package.json | 12 +- apps/mobile/app/(app)/wallet/[walletId].tsx | 88 ++++++-------- apps/mobile/app/(app)/wallet/accounts.tsx | 4 +- apps/mobile/stores/transaction/hooks.tsx | 16 ++- apps/mobile/stores/wallet/hooks.tsx | 128 ++++++++++++++++++++ apps/mobile/stores/wallet/queries.ts | 40 ++++++ apps/mobile/stores/wallet/store.ts | 46 +++++++ pnpm-lock.yaml | 81 +++++++++++-- 9 files changed, 346 insertions(+), 72 deletions(-) create mode 100644 apps/mobile/stores/wallet/hooks.tsx create mode 100644 apps/mobile/stores/wallet/queries.ts create mode 100644 apps/mobile/stores/wallet/store.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index db6c0734..9e837fb4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -16,6 +16,7 @@ "cSpell.words": [ "EXCHANGERATES", "hono", - "openai" + "openai", + "tanstack" ] } \ No newline at end of file diff --git a/apps/api/package.json b/apps/api/package.json index 8674d88d..6008180e 100644 --- a/apps/api/package.json +++ b/apps/api/package.json @@ -22,24 +22,24 @@ "@hono/clerk-auth": "^2.0.0", "@hono/node-server": "^1.11.4", "@hono/zod-validator": "^0.2.2", - "@prisma/client": "^5.16.0", + "@prisma/client": "5.19.0", "@vercel/blob": "^0.23.4", "got": "^14.4.1", "hono": "^4.4.8", "next": "^14.2.4", "openai": "^4.52.7", - "pino": "^9.2.0", "pino-pretty": "^11.2.1", - "react": "18.3.1", + "pino": "^9.2.0", + "prisma": "5.19.0", "react-dom": "18.3.1", + "react": "18.3.1", + "zod-prisma-types": "^3.1.8", "zod": "^3.23.8" }, "devDependencies": { "@types/node": "18.11.18", "@types/react": "18.3.3", "@types/react-dom": "18.3.0", - "prisma": "^5.16.0", - "typescript": "^5.5.2", - "zod-prisma-types": "^3.1.8" + "typescript": "^5.5.2" } } diff --git a/apps/mobile/app/(app)/wallet/[walletId].tsx b/apps/mobile/app/(app)/wallet/[walletId].tsx index 7c8be6c5..50645a9b 100644 --- a/apps/mobile/app/(app)/wallet/[walletId].tsx +++ b/apps/mobile/app/(app)/wallet/[walletId].tsx @@ -1,9 +1,9 @@ import { Button } from '@/components/ui/button' import { AccountForm } from '@/components/wallet/account-form' -import { deleteWallet, updateWallet } from '@/mutations/wallet' -import { transactionQueries } from '@/queries/transaction' -import { useWallets, walletQueries } from '@/queries/wallet' -import { WalletBalanceState } from '@6pm/validation' +import { deleteWallet } from '@/mutations/wallet' +import { walletQueries } from '@/queries/wallet' +import { useUpdateWallet, useWallet } from '@/stores/wallet/hooks' +import { WalletBalanceState, type WalletFormValues } from '@6pm/validation' import { t } from '@lingui/macro' import { useLingui } from '@lingui/react' import { PortalHost, useModalPortalRoot } from '@rn-primitives/portal' @@ -15,32 +15,15 @@ import { Alert, ScrollView, View } from 'react-native' export default function EditAccountScreen() { const { walletId } = useLocalSearchParams() - const { data: walletAccounts } = useWallets() const { sideOffset, ...rootProps } = useModalPortalRoot() const { i18n } = useLingui() const queryClient = useQueryClient() const router = useRouter() const navigation = useNavigation() - const { mutateAsync: mutateUpdate } = useMutation({ - mutationFn: updateWallet, - onError(error) { - Alert.alert(error.message) - }, - onSuccess() { - router.back() - }, - async onSettled() { - await Promise.all([ - queryClient.invalidateQueries({ - queryKey: walletQueries._def, - }), - queryClient.invalidateQueries({ - queryKey: transactionQueries.all, - }), - ]) - }, - throwOnError: true, - }) + + const { wallet } = useWallet(walletId as string) + const { mutateAsync: mutateUpdate } = useUpdateWallet() + const { mutateAsync: mutateDelete } = useMutation({ mutationFn: deleteWallet, onError(error) { @@ -57,7 +40,30 @@ export default function EditAccountScreen() { throwOnError: true, }) - const walletAccount = walletAccounts?.find((w) => w.id === walletId) + const handleUpdateWallet = async ({ balance, ...data }: WalletFormValues) => { + if (!wallet) { + return + } + + const statedBalance = + data.balanceState === WalletBalanceState.Positive + ? balance + : (balance ?? 0) * -1 + const adjustedBalance = + (statedBalance ?? 0) - ((wallet.balance as number) ?? 0) + + mutateUpdate({ + id: walletId as string, + data: { + ...data, + balance: adjustedBalance, + }, + }).catch((error) => { + Alert.alert(error.message) + }) + + router.back() + } // biome-ignore lint/correctness/useExhaustiveDependencies: useEffect(() => { @@ -92,7 +98,7 @@ export default function EditAccountScreen() { }) }, []) - if (!walletAccount) { + if (!wallet) { return null } @@ -105,28 +111,14 @@ export default function EditAccountScreen() { keyboardShouldPersistTaps="handled" > { - const statedBalance = - data.balanceState === WalletBalanceState.Positive - ? balance - : (balance ?? 0) * -1 - const adjustedBalance = - (statedBalance ?? 0) - ((walletAccount.balance as number) ?? 0) - mutateUpdate({ - id: walletId as string, - data: { - ...data, - balance: adjustedBalance, - }, - }) - }} + onSubmit={handleUpdateWallet} defaultValues={{ - name: walletAccount.name, - preferredCurrency: walletAccount.preferredCurrency, - balance: walletAccount.balance, - icon: walletAccount.icon ?? 'CreditCard', - description: walletAccount.description ?? '', - lastDigits: walletAccount.lastDigits ?? '', + name: wallet.name, + preferredCurrency: wallet.preferredCurrency, + balance: wallet.balance, + icon: wallet.icon ?? 'CreditCard', + description: wallet.description ?? '', + lastDigits: wallet.lastDigits ?? '', }} sideOffset={sideOffset} /> diff --git a/apps/mobile/app/(app)/wallet/accounts.tsx b/apps/mobile/app/(app)/wallet/accounts.tsx index 4a50dc6f..da6e4795 100644 --- a/apps/mobile/app/(app)/wallet/accounts.tsx +++ b/apps/mobile/app/(app)/wallet/accounts.tsx @@ -5,7 +5,7 @@ import { Text } from '@/components/ui/text' import { WalletAccountItem } from '@/components/wallet/wallet-account-item' import { useUserEntitlements } from '@/hooks/use-purchases' import { ENTILEMENT_LIMIT } from '@/lib/constaints' -import { useWallets } from '@/queries/wallet' +import { useWalletList } from '@/stores/wallet/hooks' import { t } from '@lingui/macro' import { useLingui } from '@lingui/react' import { Link } from 'expo-router' @@ -16,7 +16,7 @@ import { FlatList } from 'react-native' export default function WalletAccountsScreen() { const { i18n } = useLingui() - const { data: walletAccounts, isLoading, refetch } = useWallets() + const { wallets: walletAccounts, isLoading, refetch } = useWalletList() const router = useRouter() const navigation = useNavigation() const { entilement } = useUserEntitlements() diff --git a/apps/mobile/stores/transaction/hooks.tsx b/apps/mobile/stores/transaction/hooks.tsx index 31eda494..2a2cfb80 100644 --- a/apps/mobile/stores/transaction/hooks.tsx +++ b/apps/mobile/stores/transaction/hooks.tsx @@ -174,11 +174,20 @@ export function useCreateTransaction() { data, }: { id: string; data: TransactionFormValues }) => { const hc = await getHonoClient() + const category = data.categoryId ? categoriesDict[data.categoryId] : null + const categoryType = category?.type + + const amount = category + ? categoryType === 'INCOME' + ? Math.abs(data.amount) + : -Math.abs(data.amount) + : data.amount + const result = await hc.v1.transactions.$post({ json: { ...data, id, - amount: -data.amount, + amount, }, }) @@ -203,10 +212,11 @@ export function useCreateTransaction() { const category = data.categoryId ? categoriesDict[data.categoryId] : null const categoryType = category?.type - const amount = - categoryType === 'INCOME' + const amount = category + ? categoryType === 'INCOME' ? Math.abs(data.amount) : -Math.abs(data.amount) + : data.amount const transaction: TransactionPopulated = { id, diff --git a/apps/mobile/stores/wallet/hooks.tsx b/apps/mobile/stores/wallet/hooks.tsx new file mode 100644 index 00000000..a634d38e --- /dev/null +++ b/apps/mobile/stores/wallet/hooks.tsx @@ -0,0 +1,128 @@ +import { getHonoClient } from '@/lib/client' +import type { WalletFormValues } from '@6pm/validation' +import { createId } from '@paralleldrive/cuid2' +import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' +import { keyBy } from 'lodash-es' +import { useMemo } from 'react' +import type { StoreHookQueryOptions } from '../core/stores' +import { useCreateTransaction } from '../transaction/hooks' +import { walletQueries } from './queries' +import { useWalletStore } from './store' + +export const useWalletListQueryOptions = ( + queryOptions?: StoreHookQueryOptions, +) => { + const wallets = useWalletStore().wallets + const setWalletsState = useWalletStore((state) => state.setWallets) + return { + ...walletQueries.all({ setWalletsState }), + initialData: wallets?.length > 0 ? wallets : undefined, + ...queryOptions, + } +} + +export const useWalletList = (queryOptions?: StoreHookQueryOptions) => { + const wallets = useWalletStore().wallets + const queryOpts = useWalletListQueryOptions(queryOptions) + + const query = useQuery(queryOpts) + + const walletsDict = useMemo(() => keyBy(wallets, 'id'), [wallets]) + + return { + ...query, + wallets, + walletsDict, + } +} + +export const useWallet = (walletId: string) => { + const wallets = useWalletStore().wallets + const wallet = useMemo( + () => wallets.find((wallet) => wallet.id === walletId) || null, + [wallets, walletId], + ) + + return { wallet } +} + +export const useUpdateWallet = () => { + const updateWalletInStore = useWalletStore((state) => state.updateWallet) + const { walletsDict } = useWalletList() + const { mutateAsync: mutateCreateTransaction } = useCreateTransaction() + const queryClient = useQueryClient() + + const mutation = useMutation( + { + mutationFn: async ({ + id: walletId, + data, + }: { + id: string + data: WalletFormValues + }) => { + const { balance, ...walletData } = data + const hc = await getHonoClient() + const wallet = walletsDict[walletId] + + if (!wallet) { + return + } + + // Update wallet info and create adjust balance transaction + const promises = [ + hc.v1.wallets[':walletId'].$put({ + param: { walletId }, + json: walletData, + }), + ] + + if (balance) { + promises.push( + mutateCreateTransaction({ + id: createId(), + data: { + amount: balance ?? 0, + walletAccountId: wallet.id, + currency: wallet.preferredCurrency, + date: new Date(), + note: 'Adjust balance', + }, + // biome-ignore lint/suspicious/noExplicitAny: + }) as any, + ) + } + + const [result] = await Promise.all(promises) + + return result + }, + onMutate: async ({ id, data: { balance, ...data } }) => { + let wallet = walletsDict[id] + if (!wallet) { + return + } + + const newBallance = (balance ?? 0) + (wallet.balance ?? 0) + + wallet = { + ...wallet, + ...data, + balance: newBallance, + updatedAt: new Date(), + } + + updateWalletInStore(wallet) + + return wallet + }, + }, + queryClient, + ) + + return mutation +} + +// export const useCreateWallet = () => {} + +// export const useDeleteWallet = () => {} diff --git a/apps/mobile/stores/wallet/queries.ts b/apps/mobile/stores/wallet/queries.ts new file mode 100644 index 00000000..924dc6e5 --- /dev/null +++ b/apps/mobile/stores/wallet/queries.ts @@ -0,0 +1,40 @@ +import { getHonoClient } from '@/lib/client' +import { + type WalletAccountWithBalance, + WalletAccountWithBalanceSchema, +} from '@6pm/validation' +import { createQueryKeys } from '@lukemorales/query-key-factory' +import { z } from 'zod' + +export const walletQueries = createQueryKeys('wallet', { + all: ({ + setWalletsState, + }: { + setWalletsState: (wallets: WalletAccountWithBalance[]) => void + }) => ({ + queryKey: [{}], + queryFn: async () => { + const hc = await getHonoClient() + const res = await hc.v1.wallets.$get() + + if (!res.ok) { + throw new Error(await res.text()) + } + + try { + const items = await res.json() + const wallets = items.map((item) => + WalletAccountWithBalanceSchema.extend({ + id: z.string().cuid2(), + }).parse(item), + ) + + setWalletsState(wallets) + return wallets + } catch (error) { + // biome-ignore lint/suspicious/noConsoleLog: + console.log(error) + } + }, + }), +}) diff --git a/apps/mobile/stores/wallet/store.ts b/apps/mobile/stores/wallet/store.ts new file mode 100644 index 00000000..d6a450eb --- /dev/null +++ b/apps/mobile/stores/wallet/store.ts @@ -0,0 +1,46 @@ +import type { WalletAccountWithBalance } from '@6pm/validation' +import AsyncStorage from '@react-native-async-storage/async-storage' +import { create } from 'zustand' +import { createJSONStorage, persist } from 'zustand/middleware' + +interface WalletStore { + wallets: WalletAccountWithBalance[] + _reset: () => void + setWallets: (wallets: WalletAccountWithBalance[]) => void + updateWallet: (wallet: WalletAccountWithBalance) => void + removeWallet: (walletId: string) => void +} + +export const useWalletStore = create()( + persist( + (set) => ({ + wallets: [], + // biome-ignore lint/style/useNamingConvention: + _reset: () => set({ wallets: [] }), + setWallets: (wallets: WalletAccountWithBalance[]) => set({ wallets }), + updateWallet: (wallet: WalletAccountWithBalance) => + set((state) => { + const index = state.wallets.findIndex((c) => c.id === wallet.id) + if (index === -1) { + return { + wallets: [...state.wallets, wallet], + } + } + + return { + wallets: state.wallets.map((c) => + c.id === wallet.id ? wallet : c, + ), + } + }), + removeWallet: (walletId) => + set((state) => { + return { wallets: state.wallets.filter((c) => c.id !== walletId) } + }), + }), + { + name: 'wallet-storage', + storage: createJSONStorage(() => AsyncStorage), + }, + ), +) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 92c1db4c..d006ee84 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,7 +45,7 @@ importers: specifier: ^0.2.2 version: 0.2.2(hono@4.5.9)(zod@3.23.8) '@prisma/client': - specifier: ^5.16.0 + specifier: 5.19.0 version: 5.19.0(prisma@5.19.0) '@vercel/blob': specifier: ^0.23.4 @@ -68,6 +68,9 @@ importers: pino-pretty: specifier: ^11.2.1 version: 11.2.2 + prisma: + specifier: 5.19.0 + version: 5.19.0 react: specifier: 18.3.1 version: 18.3.1 @@ -77,6 +80,9 @@ importers: zod: specifier: ^3.23.8 version: 3.23.8 + zod-prisma-types: + specifier: ^3.1.8 + version: 3.1.8 devDependencies: '@types/node': specifier: 18.11.18 @@ -87,15 +93,9 @@ importers: '@types/react-dom': specifier: 18.3.0 version: 18.3.0 - prisma: - specifier: ^5.16.0 - version: 5.19.0 typescript: specifier: ^5.5.2 version: 5.5.4 - zod-prisma-types: - specifier: ^3.1.8 - version: 3.1.8 apps/mobile: dependencies: @@ -287,7 +287,7 @@ importers: version: 0.28.16(expo@51.0.31(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))) expo-router: specifier: ~3.5.15 - version: 3.5.23(expo-constants@16.0.2(expo@51.0.31(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))))(expo-linking@6.3.1(expo@51.0.31(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))))(expo-modules-autolinking@1.11.2)(expo-status-bar@1.12.1)(expo@51.0.31(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2)))(react-native-reanimated@3.10.1(@babel/core@7.25.2)(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native-screens@3.31.1(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1)(typescript@5.3.3) + version: 3.5.23(wdmb7b7dywngkq3iaob6t4cnwq) expo-secure-store: specifier: ^13.0.1 version: 13.0.2(expo@51.0.31(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))) @@ -419,7 +419,7 @@ importers: version: link:../validation '@prisma/client': specifier: ^5.16.0 - version: 5.19.0(prisma@5.19.0) + version: 5.19.0(prisma@5.19.1) dayjs: specifier: ^1.11.11 version: 1.11.13 @@ -428,7 +428,7 @@ importers: dependencies: '@prisma/client': specifier: ^5.16.0 - version: 5.19.0(prisma@5.19.0) + version: 5.19.0(prisma@5.19.1) zod: specifier: ^3.23.8 version: 3.23.8 @@ -1957,21 +1957,36 @@ packages: '@prisma/debug@5.19.0': resolution: {integrity: sha512-+b/G0ubAZlrS+JSiDhXnYV5DF/aTJ3pinktkiV/L4TtLRLZO6SVGyFELgxBsicCTWJ2ZMu5vEV/jTtYCdjFTRA==} + '@prisma/debug@5.19.1': + resolution: {integrity: sha512-lAG6A6QnG2AskAukIEucYJZxxcSqKsMK74ZFVfCTOM/7UiyJQi48v6TQ47d6qKG3LbMslqOvnTX25dj/qvclGg==} + '@prisma/engines-version@5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f': resolution: {integrity: sha512-GimI9aZIFy/yvvR11KfXRn3pliFn1QAkdebVlsXlnoh5uk0YhLblVmeYiHfsu+wDA7BeKqYT4sFfzg8mutzuWw==} + '@prisma/engines-version@5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3': + resolution: {integrity: sha512-xR6rt+z5LnNqTP5BBc+8+ySgf4WNMimOKXRn6xfNRDSpHvbOEmd7+qAOmzCrddEc4Cp8nFC0txU14dstjH7FXA==} + '@prisma/engines@5.19.0': resolution: {integrity: sha512-UtW+0m4HYoRSSR3LoDGKF3Ud4BSMWYlLEt4slTnuP1mI+vrV3zaDoiAPmejdAT76vCN5UqnWURbkXxf66nSylQ==} + '@prisma/engines@5.19.1': + resolution: {integrity: sha512-kR/PoxZDrfUmbbXqqb8SlBBgCjvGaJYMCOe189PEYzq9rKqitQ2fvT/VJ8PDSe8tTNxhc2KzsCfCAL+Iwm/7Cg==} + '@prisma/fetch-engine@5.19.0': resolution: {integrity: sha512-oOiPNtmJX0cP/ebu7BBEouJvCw8T84/MFD/Hf2zlqjxkK4ojl38bB9i9J5LAxotL6WlYVThKdxc7HqoWnPOhqQ==} + '@prisma/fetch-engine@5.19.1': + resolution: {integrity: sha512-pCq74rtlOVJfn4pLmdJj+eI4P7w2dugOnnTXpRilP/6n5b2aZiA4ulJlE0ddCbTPkfHmOL9BfaRgA8o+1rfdHw==} + '@prisma/generator-helper@5.19.0': resolution: {integrity: sha512-qZDgnq/dHVHYUNRG8ETuIvoiZzWxwKHhG9Jb4WWoQFXXuTY+1km0L5QAPOJ0U7Qo8ookUf25B88n1Z9Az7l/UQ==} '@prisma/get-platform@5.19.0': resolution: {integrity: sha512-s9DWkZKnuP4Y8uy6yZfvqQ/9X3/+2KYf3IZUVZz5OstJdGBJrBlbmIuMl81917wp5TuK/1k2TpHNCEdpYLPKmg==} + '@prisma/get-platform@5.19.1': + resolution: {integrity: sha512-sCeoJ+7yt0UjnR+AXZL7vXlg5eNxaFOwC23h0KvW1YIXUoa7+W2ZcAUhoEQBmJTW4GrFqCuZ8YSP0mkDa4k3Zg==} + '@radix-ui/number@1.1.0': resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} @@ -6114,6 +6129,11 @@ packages: engines: {node: '>=16.13'} hasBin: true + prisma@5.19.1: + resolution: {integrity: sha512-c5K9MiDaa+VAAyh1OiYk76PXOme9s3E992D7kvvIOhCrNsBQfy2mP2QAQtX0WNj140IgG++12kwZpYB9iIydNQ==} + engines: {node: '>=16.13'} + hasBin: true + process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} @@ -9609,10 +9629,20 @@ snapshots: optionalDependencies: prisma: 5.19.0 + '@prisma/client@5.19.0(prisma@5.19.1)': + optionalDependencies: + prisma: 5.19.1 + '@prisma/debug@5.19.0': {} + '@prisma/debug@5.19.1': + optional: true + '@prisma/engines-version@5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f': {} + '@prisma/engines-version@5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3': + optional: true + '@prisma/engines@5.19.0': dependencies: '@prisma/debug': 5.19.0 @@ -9620,12 +9650,27 @@ snapshots: '@prisma/fetch-engine': 5.19.0 '@prisma/get-platform': 5.19.0 + '@prisma/engines@5.19.1': + dependencies: + '@prisma/debug': 5.19.1 + '@prisma/engines-version': 5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3 + '@prisma/fetch-engine': 5.19.1 + '@prisma/get-platform': 5.19.1 + optional: true + '@prisma/fetch-engine@5.19.0': dependencies: '@prisma/debug': 5.19.0 '@prisma/engines-version': 5.19.0-31.5fe21811a6ba0b952a3bc71400666511fe3b902f '@prisma/get-platform': 5.19.0 + '@prisma/fetch-engine@5.19.1': + dependencies: + '@prisma/debug': 5.19.1 + '@prisma/engines-version': 5.19.1-2.69d742ee20b815d88e17e54db4a2a7a3b30324e3 + '@prisma/get-platform': 5.19.1 + optional: true + '@prisma/generator-helper@5.19.0': dependencies: '@prisma/debug': 5.19.0 @@ -9634,6 +9679,11 @@ snapshots: dependencies: '@prisma/debug': 5.19.0 + '@prisma/get-platform@5.19.1': + dependencies: + '@prisma/debug': 5.19.1 + optional: true + '@radix-ui/number@1.1.0': {} '@radix-ui/primitive@1.1.0': {} @@ -12315,8 +12365,8 @@ snapshots: - encoding - supports-color - ? expo-router@3.5.23(expo-constants@16.0.2(expo@51.0.31(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))))(expo-linking@6.3.1(expo@51.0.31(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))))(expo-modules-autolinking@1.11.2)(expo-status-bar@1.12.1)(expo@51.0.31(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2)))(react-native-reanimated@3.10.1(@babel/core@7.25.2)(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native-safe-area-context@4.10.1(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native-screens@3.31.1(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1))(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.3.1))(react@18.3.1)(typescript@5.3.3) - : dependencies: + expo-router@3.5.23(wdmb7b7dywngkq3iaob6t4cnwq): + dependencies: '@expo/metro-runtime': 3.2.3(react-native@0.74.2(@babel/core@7.25.2)(@babel/preset-env@7.25.4(@babel/core@7.25.2))(@types/react@18.2.79)(react@18.3.1)) '@expo/server': 0.4.4(typescript@5.3.3) '@radix-ui/react-slot': 1.0.1(react@18.3.1) @@ -14550,6 +14600,13 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + prisma@5.19.1: + dependencies: + '@prisma/engines': 5.19.1 + optionalDependencies: + fsevents: 2.3.3 + optional: true + process-nextick-args@2.0.1: {} process-warning@4.0.0: {}