diff --git a/apps/mobile/app/(app)/(tabs)/settings.tsx b/apps/mobile/app/(app)/(tabs)/settings.tsx
index 9915f4ad..6c926b33 100644
--- a/apps/mobile/app/(app)/(tabs)/settings.tsx
+++ b/apps/mobile/app/(app)/(tabs)/settings.tsx
@@ -91,7 +91,7 @@ export default function SettingsScreen() {
}
/>
-
+
(
-
+
@@ -107,12 +107,19 @@ export default function AuthenticatedLayout() {
}}
/>
+
)
}
diff --git a/apps/mobile/app/(app)/category/[categoryId].tsx b/apps/mobile/app/(app)/category/[categoryId].tsx
new file mode 100644
index 00000000..d74f4ff4
--- /dev/null
+++ b/apps/mobile/app/(app)/category/[categoryId].tsx
@@ -0,0 +1,54 @@
+import { CategoryForm } from '@/components/category/category-form'
+import { Text } from '@/components/ui/text'
+import { updateCategory } from '@/mutations/category'
+import { categoryQueries, useCategories } from '@/queries/category'
+import { useMutation, useQueryClient } from '@tanstack/react-query'
+import { useLocalSearchParams, useRouter } from 'expo-router'
+import { Alert, ScrollView, View } from 'react-native'
+
+export default function EditCategoryScreen() {
+ const { categoryId } = useLocalSearchParams<{ categoryId: string }>()
+ const { data: categories = [] } = useCategories()
+ const queryClient = useQueryClient()
+ const router = useRouter()
+
+ const { mutateAsync: mutateUpdate } = useMutation({
+ mutationFn: updateCategory,
+ onError(error) {
+ Alert.alert(error.message)
+ },
+ onSuccess() {
+ router.back()
+ },
+ async onSettled() {
+ await queryClient.invalidateQueries({
+ queryKey: categoryQueries.list._def,
+ })
+ },
+ throwOnError: true,
+ })
+
+ const category = categories.find((category) => category.id === categoryId)
+
+ if (!category) {
+ return (
+
+ Category not found
+
+ )
+ }
+
+ return (
+
+ mutateUpdate({ id: category.id, data: values })}
+ hiddenFields={['type']}
+ defaultValues={{
+ name: category?.name,
+ icon: category?.icon!,
+ type: category?.type,
+ }}
+ />
+
+ )
+}
diff --git a/apps/mobile/app/(app)/categories/index.tsx b/apps/mobile/app/(app)/category/index.tsx
similarity index 97%
rename from apps/mobile/app/(app)/categories/index.tsx
rename to apps/mobile/app/(app)/category/index.tsx
index 37dfacb0..e0bc5cb0 100644
--- a/apps/mobile/app/(app)/categories/index.tsx
+++ b/apps/mobile/app/(app)/category/index.tsx
@@ -54,7 +54,7 @@ export default function CategoriesScreen() {
label={t(i18n)`New ${section.key.toLowerCase()}`}
onPress={() =>
router.push({
- pathname: '/categories/new-category',
+ pathname: '/category/new-category',
params: { type: section.key },
})
}
diff --git a/apps/mobile/app/(app)/categories/new-category.tsx b/apps/mobile/app/(app)/category/new-category.tsx
similarity index 91%
rename from apps/mobile/app/(app)/categories/new-category.tsx
rename to apps/mobile/app/(app)/category/new-category.tsx
index 514f07e6..5287d117 100644
--- a/apps/mobile/app/(app)/categories/new-category.tsx
+++ b/apps/mobile/app/(app)/category/new-category.tsx
@@ -8,7 +8,9 @@ import { Alert, View } from 'react-native'
export default function CreateCategoryScreen() {
const router = useRouter()
- const { type } = useLocalSearchParams<{ type?: CategoryTypeType }>()
+ const { type = 'EXPENSE' } = useLocalSearchParams<{
+ type?: CategoryTypeType
+ }>()
const queryClient = useQueryClient()
const { mutateAsync } = useMutation({
mutationFn: createCategory,
diff --git a/apps/mobile/components/category/category-form.tsx b/apps/mobile/components/category/category-form.tsx
index 04f2602e..bd1c5776 100644
--- a/apps/mobile/components/category/category-form.tsx
+++ b/apps/mobile/components/category/category-form.tsx
@@ -15,11 +15,13 @@ import { SelectCategoryIconField } from './select-category-icon-field'
type CategoryFormProps = {
onSubmit: (data: CategoryFormValues) => void
defaultValues?: Partial
+ hiddenFields?: Array<'type'>
}
export const CategoryForm = ({
onSubmit,
defaultValues,
+ hiddenFields = [],
}: CategoryFormProps) => {
const { i18n } = useLingui()
const nameInputRef = useRef(null)
@@ -28,11 +30,14 @@ export const CategoryForm = ({
resolver: zodResolver(zCategoryFormValues),
defaultValues: {
name: '',
- type: 'EXPENSE',
icon: 'CreditCard',
...defaultValues,
+ type: defaultValues?.type || 'EXPENSE',
},
})
+ const type = categoryForm.watch('type')
+
+ const isTypeHidden = hiddenFields.includes('type')
return (
@@ -45,34 +50,47 @@ export const CategoryForm = ({
autoCapitalize="none"
autoFocus={!defaultValues}
className="!pl-[62px]"
+ disabled={categoryForm.formState.isLoading}
leftSection={
nameInputRef.current?.focus()}
/>
}
/>
- {t(i18n)`Type`}
- (
-
-
-
- {t(i18n)`Expense`}
-
-
- {t(i18n)`Income`}
-
-
-
- )}
- />
+ {!isTypeHidden && (
+ <>
+ {t(i18n)`Type`}
+ (
+
+
+
+ {t(i18n)`Expense`}
+
+
+ {t(i18n)`Income`}
+
+
+
+ )}
+ />
+ >
+ )}
= ({ category }) => {
asChild
push
href={{
- pathname: '/categories/[categoryId]',
+ pathname: '/category/[categoryId]',
params: { categoryId: category.id },
}}
>
diff --git a/apps/mobile/components/category/select-category-icon-field.tsx b/apps/mobile/components/category/select-category-icon-field.tsx
index 80e59533..652aa817 100644
--- a/apps/mobile/components/category/select-category-icon-field.tsx
+++ b/apps/mobile/components/category/select-category-icon-field.tsx
@@ -1,17 +1,27 @@
import { BottomSheetBackdrop, BottomSheetModal } from '@gorhom/bottom-sheet'
-import { useRef } from 'react'
+import { useMemo, useRef } from 'react'
-import { WALLET_ICONS } from '@/lib/icons/wallet-icons'
+import {
+ CATEGORY_EXPENSE_ICONS,
+ CATEGORY_INCOME_ICONS,
+} from '@/lib/icons/category-icons'
+import { sleep } from '@/lib/utils'
+import type { CategoryTypeType } from '@6pm/validation'
import { useController } from 'react-hook-form'
import { Keyboard } from 'react-native'
+import { FullWindowOverlay } from 'react-native-screens'
import GenericIcon from '../common/generic-icon'
import { IconGridSheet } from '../common/icon-grid-sheet'
import { Button } from '../ui/button'
export function SelectCategoryIconField({
onSelect,
+ disabled,
+ type,
}: {
onSelect?: (currency: string) => void
+ disabled?: boolean
+ type: CategoryTypeType
}) {
const sheetRef = useRef(null)
const {
@@ -19,10 +29,16 @@ export function SelectCategoryIconField({
// fieldState,
} = useController({ name: 'icon' })
+ const icons = useMemo(
+ () => (type === 'EXPENSE' ? CATEGORY_EXPENSE_ICONS : CATEGORY_INCOME_ICONS),
+ [type],
+ )
+
return (
<>