From 23f29456a4d2a40c89602b7fbb5a273cff32b12e Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Mon, 6 Oct 2025 20:16:06 +0200 Subject: [PATCH 01/13] fix header and theming --- src/app/(tabs)/insights/index.tsx | 4 ++-- src/app/(tabs)/settings/index.tsx | 9 +++++---- src/components/Header.tsx | 6 +++--- tailwind.config.js | 2 +- 4 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/app/(tabs)/insights/index.tsx b/src/app/(tabs)/insights/index.tsx index 45e203d..7ae520e 100644 --- a/src/app/(tabs)/insights/index.tsx +++ b/src/app/(tabs)/insights/index.tsx @@ -14,12 +14,12 @@ export default function InsightsScreen() { const { t } = useTranslation(); return ( - + - + {/* Header */}
+ - + {/* Header */}
= ({ name, type }) => { return ( {name} - {t(type)} diff --git a/tailwind.config.js b/tailwind.config.js index 36d6876..aadc89b 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -10,7 +10,7 @@ module.exports = { theme: { // Need to be the same as in Figma, keep these in sync. - // Also keep everything with /src/contexts/ThemeContext.tsx in sync. + // Also, keep everything with /src/contexts/ThemeContext.tsx in sync. colors: { background: "hsl(240, 60%, 99.02%)", primary: "hsl(243.33, 40.91%, 91.37%)", From cf1ec319d240674ed1770e91767cd474bd86bcf7 Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Sun, 12 Oct 2025 14:04:37 +0200 Subject: [PATCH 02/13] feat: build the home page of settings section - add Container component - update settings localization --- localization/ar/settings.json | 9 +++++ localization/de/settings.json | 9 +++++ localization/en/settings.json | 8 ++++- localization/it/settings.json | 9 +++++ src/app/(tabs)/settings/index.tsx | 56 +++++++++++++++++++++++++---- src/components/SectionHeader.tsx | 17 +++++---- src/components/ui/container.tsx | 59 +++++++++++++++++++++++++++++++ src/i18n-resources.ts | 24 +++---------- src/types/images.d.ts | 4 +++ tsconfig.json | 2 +- 10 files changed, 162 insertions(+), 35 deletions(-) create mode 100644 localization/ar/settings.json create mode 100644 localization/de/settings.json create mode 100644 localization/it/settings.json create mode 100644 src/components/ui/container.tsx create mode 100644 src/types/images.d.ts diff --git a/localization/ar/settings.json b/localization/ar/settings.json new file mode 100644 index 0000000..c46c36b --- /dev/null +++ b/localization/ar/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "الملف الشخصي", + "user": "المستخدم", + "notifications": "الإشعارات", + "financial": "المالية", + "limits": "القيود", + "income": "الدخل", + "bankAccounts": "الحسابات المصرفية" +} \ No newline at end of file diff --git a/localization/de/settings.json b/localization/de/settings.json new file mode 100644 index 0000000..47dc7d0 --- /dev/null +++ b/localization/de/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "Profil", + "user": "Benutzer", + "notifications": "Benachrichtigungen", + "financial": "Finanzen", + "limits": "Limits", + "income": "Einkommen", + "bankAccounts": "Bankkonten" +} diff --git a/localization/en/settings.json b/localization/en/settings.json index 320c67f..4cae6a1 100644 --- a/localization/en/settings.json +++ b/localization/en/settings.json @@ -1,3 +1,9 @@ { - "profile": "Profile" + "profile": "Profile", + "user": "User", + "notifications": "Notifications", + "financial": "Financial", + "limits": "Limits", + "income": "Income", + "bankAccounts": "Bank Accounts" } diff --git a/localization/it/settings.json b/localization/it/settings.json new file mode 100644 index 0000000..49f3b5a --- /dev/null +++ b/localization/it/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "Profilo", + "user": "Utente", + "notifications": "Notifiche", + "financial": "Finanziario", + "limits": "Limiti", + "income": "Reddito", + "bankAccounts": "Conti Bancari" +} diff --git a/src/app/(tabs)/settings/index.tsx b/src/app/(tabs)/settings/index.tsx index 27250e9..b3a6465 100644 --- a/src/app/(tabs)/settings/index.tsx +++ b/src/app/(tabs)/settings/index.tsx @@ -1,5 +1,5 @@ import React from "react"; -import { Platform, ScrollView, StatusBar } from "react-native"; +import { ScrollView, StatusBar, View } from "react-native"; import { SafeAreaView } from "react-native-safe-area-context"; import { useTheme } from "~/contexts/ThemeContext"; import { Header } from "~/components/Header"; @@ -7,11 +7,20 @@ import { SectionHeader } from "~/components/SectionHeader"; import { NavigationItems } from "~/types"; import { useUser } from "@clerk/clerk-expo"; import { useTranslation } from "react-i18next"; +import AppText from "~/components/ui/AppText"; +import { Container } from "~/components/ui/container"; +import { + BellRingIcon, + LandmarkIcon, + ShieldXIcon, + UsersRoundIcon, +} from "lucide-react-native"; +import MoneyIcon from "~/assets/Icons/money.png"; export default function SettingsScreen() { const { colors, isDark } = useTheme(); const { user, isLoaded } = useUser(); - const { t } = useTranslation(); + const { t } = useTranslation("settings"); return ( @@ -32,13 +41,46 @@ export default function SettingsScreen() { /> + + + + {t("profile")} + + + + + {t("notifications")} + + + + + + + + {t("limits")} + + + + + {t("income")} + + + + + {t("bankAccounts")} + + + ); diff --git a/src/components/SectionHeader.tsx b/src/components/SectionHeader.tsx index aa48a06..e247d65 100644 --- a/src/components/SectionHeader.tsx +++ b/src/components/SectionHeader.tsx @@ -1,32 +1,37 @@ import type React from "react"; import Animated, { FadeInDown } from "react-native-reanimated"; -import { useTheme } from "../contexts/ThemeContext"; +import { useTheme } from "~/contexts/ThemeContext"; import AppText from "./ui/AppText"; +import { cn } from "~/utils/lib"; interface SectionHeaderProps { title: string; delay?: number; - size?: "large" | "medium" | "small"; + size?: "large" | "medium" | "small" | "xl"; + className?: string; } export const SectionHeader: React.FC = ({ title, delay = 0, size = "large", + className, }) => { const { colors } = useTheme(); const sizeClassMap = { - large: "text-xl mt-6 font-bold", - medium: "text-base mt-4 font-semibold", - small: "text-sm mt-3 font-medium", + xl: "text-2xl mt-8", + large: "text-xl mt-6", + medium: "text-base mt-4", + small: "text-sm mt-3", }; return ( {title} diff --git a/src/components/ui/container.tsx b/src/components/ui/container.tsx new file mode 100644 index 0000000..5b838eb --- /dev/null +++ b/src/components/ui/container.tsx @@ -0,0 +1,59 @@ +import { ImageSourcePropType, View } from "react-native"; +import { cn } from "~/utils/lib"; +import { CircleArrowRight, LucideIcon } from "lucide-react-native"; +import { cssInterop } from "nativewind"; +import AppImage from "~/components/ui/AppImage"; + +const isLucideIcon = (val: unknown): val is LucideIcon => { + if (!val) return false; + if (typeof val === "function") return true; + if (typeof val === "object") { + const t = (val as any).$$typeof; + // Detect React.forwardRef / React.memo component types + return ( + t === Symbol.for("react.forward_ref") || t === Symbol.for("react.memo") + ); + } + return false; +}; + +export const Container = ({ + children, + className, + icon: Icon, +}: { + children: React.ReactNode; + className?: string; + icon: LucideIcon | ImageSourcePropType; +}) => { + const StyledCircleArrowRight = cssInterop(CircleArrowRight, { + className: { target: "style" }, + }); + + let IconNode: React.ReactNode; + if (isLucideIcon(Icon)) { + const StyledIcon = cssInterop(Icon, { className: { target: "style" } }); + IconNode = ; + } else { + IconNode = ( + + ); + } + + return ( + + + + {IconNode} + + {children} + + + + ); +}; diff --git a/src/i18n-resources.ts b/src/i18n-resources.ts index 92c2ee4..ab5c260 100644 --- a/src/i18n-resources.ts +++ b/src/i18n-resources.ts @@ -10,6 +10,7 @@ import fa_common from "../localization/fa/common.json"; import fr_common from "../localization/fr/common.json"; import hi_common from "../localization/hi/common.json"; import it_common from "../localization/it/common.json"; +import it_settings from "../localization/it/settings.json"; import ja_common from "../localization/ja/common.json"; import ko_common from "../localization/ko/common.json"; import ps_common from "../localization/ps/common.json"; @@ -50,7 +51,7 @@ export const resources = { }, it: { common: it_common, - settings: {}, + settings: it_settings, }, ja: { common: ja_common, @@ -86,25 +87,8 @@ export const resources = { }, }; -export const languages = [ - "ar", - "de", - "en", - "es", - "fa", - "fr", - "hi", - "it", - "ja", - "ko", - "ps", - "pt", - "ru", - "tr", - "ur", - "zh", -] as const; -export const namespaces = ["common", "settings"] as const; +export const languages = ["ar","de","en","es","fa","fr","hi","it","ja","ko","ps","pt","ru","tr","ur","zh"] as const; +export const namespaces = ["common","settings"] as const; export type LanguageKey = (typeof languages)[number]; export type Namespace = (typeof namespaces)[number]; diff --git a/src/types/images.d.ts b/src/types/images.d.ts new file mode 100644 index 0000000..76db7f9 --- /dev/null +++ b/src/types/images.d.ts @@ -0,0 +1,4 @@ +declare module "*.png" { + const value: import("react-native").ImageSourcePropType; + export default value; +} diff --git a/tsconfig.json b/tsconfig.json index 9d66740..8268e51 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,6 @@ "**/*.tsx", "nativewind-env.d.ts", ".expo/types/**/*.ts", - "expo-env.d.ts" + "expo-env.d.ts", "src", "**/*.d.ts" ] } From 98aac75e644a6d1b00b4208afeaf6954c55298b5 Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Sun, 12 Oct 2025 14:12:13 +0200 Subject: [PATCH 03/13] fix: added translations --- localization/es/settings.json | 9 +++++++++ localization/fa/settings.json | 9 +++++++++ localization/fr/settings.json | 9 +++++++++ localization/hi/settings.json | 9 +++++++++ localization/ja/settings.json | 9 +++++++++ localization/ko/settings.json | 9 +++++++++ localization/ps/settings.json | 9 +++++++++ localization/pt/settings.json | 9 +++++++++ localization/ru/settings.json | 9 +++++++++ localization/tr/settings.json | 9 +++++++++ localization/ur/settings.json | 9 +++++++++ localization/zh/settings.json | 9 +++++++++ 12 files changed, 108 insertions(+) create mode 100644 localization/es/settings.json create mode 100644 localization/fa/settings.json create mode 100644 localization/fr/settings.json create mode 100644 localization/hi/settings.json create mode 100644 localization/ja/settings.json create mode 100644 localization/ko/settings.json create mode 100644 localization/ps/settings.json create mode 100644 localization/pt/settings.json create mode 100644 localization/ru/settings.json create mode 100644 localization/tr/settings.json create mode 100644 localization/ur/settings.json create mode 100644 localization/zh/settings.json diff --git a/localization/es/settings.json b/localization/es/settings.json new file mode 100644 index 0000000..7455674 --- /dev/null +++ b/localization/es/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "Perfil", + "user": "Usuario", + "notifications": "Notificaciones", + "financial": "Finanzas", + "limits": "Límites", + "income": "Ingresos", + "bankAccounts": "Cuentas bancarias" +} diff --git a/localization/fa/settings.json b/localization/fa/settings.json new file mode 100644 index 0000000..552e528 --- /dev/null +++ b/localization/fa/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "پروفایل", + "user": "کاربر", + "notifications": "اطلاعیه‌ها", + "financial": "مالی", + "limits": "محدودیت‌ها", + "income": "درآمد", + "bankAccounts": "حساب‌های بانکی" +} diff --git a/localization/fr/settings.json b/localization/fr/settings.json new file mode 100644 index 0000000..7f24d93 --- /dev/null +++ b/localization/fr/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "Profil", + "user": "Utilisateur", + "notifications": "Notifications", + "financial": "Financier", + "limits": "Limites", + "income": "Revenu", + "bankAccounts": "Comptes bancaires" +} diff --git a/localization/hi/settings.json b/localization/hi/settings.json new file mode 100644 index 0000000..1baee23 --- /dev/null +++ b/localization/hi/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "प्रोफ़ाइल", + "user": "उपयोगकर्ता", + "notifications": "सूचनाएँ", + "financial": "वित्तीय", + "limits": "सीमाएँ", + "income": "आय", + "bankAccounts": "बैंक खाते" +} diff --git a/localization/ja/settings.json b/localization/ja/settings.json new file mode 100644 index 0000000..edbb104 --- /dev/null +++ b/localization/ja/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "プロフィール", + "user": "ユーザー", + "notifications": "通知", + "financial": "財務", + "limits": "制限", + "income": "収入", + "bankAccounts": "銀行口座" +} \ No newline at end of file diff --git a/localization/ko/settings.json b/localization/ko/settings.json new file mode 100644 index 0000000..99a0440 --- /dev/null +++ b/localization/ko/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "프로필", + "user": "사용자", + "notifications": "알림", + "financial": "재무", + "limits": "한도", + "income": "수입", + "bankAccounts": "은행 계좌" +} diff --git a/localization/ps/settings.json b/localization/ps/settings.json new file mode 100644 index 0000000..59014cc --- /dev/null +++ b/localization/ps/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "پروفایل", + "user": "کارن", + "notifications": "خبرتیاوې", + "financial": "مالي", + "limits": "حدونه", + "income": "عاید", + "bankAccounts": "بانکي حسابونه" +} diff --git a/localization/pt/settings.json b/localization/pt/settings.json new file mode 100644 index 0000000..3bb2ae7 --- /dev/null +++ b/localization/pt/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "Perfil", + "user": "Usuário", + "notifications": "Notificações", + "financial": "Financeiro", + "limits": "Limites", + "income": "Renda", + "bankAccounts": "Contas bancárias" +} \ No newline at end of file diff --git a/localization/ru/settings.json b/localization/ru/settings.json new file mode 100644 index 0000000..e8c020c --- /dev/null +++ b/localization/ru/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "Профиль", + "user": "Пользователь", + "notifications": "Уведомления", + "financial": "Финансы", + "limits": "Ограничения", + "income": "Доход", + "bankAccounts": "Банковские счета" +} diff --git a/localization/tr/settings.json b/localization/tr/settings.json new file mode 100644 index 0000000..3cd8b9e --- /dev/null +++ b/localization/tr/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "Profil", + "user": "Kullanıcı", + "notifications": "Bildirimler", + "financial": "Finans", + "limits": "Limitler", + "income": "Gelir", + "bankAccounts": "Banka Hesapları" +} diff --git a/localization/ur/settings.json b/localization/ur/settings.json new file mode 100644 index 0000000..ca34487 --- /dev/null +++ b/localization/ur/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "پروفائل", + "user": "صارف", + "notifications": "اطلاعات", + "financial": "مالی", + "limits": "حدود", + "income": "آمدنی", + "bankAccounts": "بینک اکاؤنٹس" +} \ No newline at end of file diff --git a/localization/zh/settings.json b/localization/zh/settings.json new file mode 100644 index 0000000..5fa3223 --- /dev/null +++ b/localization/zh/settings.json @@ -0,0 +1,9 @@ +{ + "profile": "个人资料", + "user": "用户", + "notifications": "通知", + "financial": "财务", + "limits": "限额", + "income": "收入", + "bankAccounts": "银行账户" +} From f9813d639f664250d70b9f41e5e864d7b94cb3b2 Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Sun, 12 Oct 2025 14:13:52 +0200 Subject: [PATCH 04/13] fix: format correctly --- localization/ar/settings.json | 2 +- localization/ja/settings.json | 2 +- localization/pt/settings.json | 2 +- localization/ur/settings.json | 2 +- src/i18n-resources.ts | 21 +++++++++++++++++++-- src/server/routers/welcome.ts | 6 +++--- tsconfig.json | 4 +++- 7 files changed, 29 insertions(+), 10 deletions(-) diff --git a/localization/ar/settings.json b/localization/ar/settings.json index c46c36b..f91b997 100644 --- a/localization/ar/settings.json +++ b/localization/ar/settings.json @@ -6,4 +6,4 @@ "limits": "القيود", "income": "الدخل", "bankAccounts": "الحسابات المصرفية" -} \ No newline at end of file +} diff --git a/localization/ja/settings.json b/localization/ja/settings.json index edbb104..44ae238 100644 --- a/localization/ja/settings.json +++ b/localization/ja/settings.json @@ -6,4 +6,4 @@ "limits": "制限", "income": "収入", "bankAccounts": "銀行口座" -} \ No newline at end of file +} diff --git a/localization/pt/settings.json b/localization/pt/settings.json index 3bb2ae7..08f630d 100644 --- a/localization/pt/settings.json +++ b/localization/pt/settings.json @@ -6,4 +6,4 @@ "limits": "Limites", "income": "Renda", "bankAccounts": "Contas bancárias" -} \ No newline at end of file +} diff --git a/localization/ur/settings.json b/localization/ur/settings.json index ca34487..3cce44a 100644 --- a/localization/ur/settings.json +++ b/localization/ur/settings.json @@ -6,4 +6,4 @@ "limits": "حدود", "income": "آمدنی", "bankAccounts": "بینک اکاؤنٹس" -} \ No newline at end of file +} diff --git a/src/i18n-resources.ts b/src/i18n-resources.ts index ab5c260..761f58c 100644 --- a/src/i18n-resources.ts +++ b/src/i18n-resources.ts @@ -87,8 +87,25 @@ export const resources = { }, }; -export const languages = ["ar","de","en","es","fa","fr","hi","it","ja","ko","ps","pt","ru","tr","ur","zh"] as const; -export const namespaces = ["common","settings"] as const; +export const languages = [ + "ar", + "de", + "en", + "es", + "fa", + "fr", + "hi", + "it", + "ja", + "ko", + "ps", + "pt", + "ru", + "tr", + "ur", + "zh", +] as const; +export const namespaces = ["common", "settings"] as const; export type LanguageKey = (typeof languages)[number]; export type Namespace = (typeof namespaces)[number]; diff --git a/src/server/routers/welcome.ts b/src/server/routers/welcome.ts index 0b685b9..a8d6984 100644 --- a/src/server/routers/welcome.ts +++ b/src/server/routers/welcome.ts @@ -14,7 +14,7 @@ export const welcomeRouter = createTRPCRouter({ if (Number.isNaN(amount)) { throw new Error("Provided amount is not a valid number"); } - + /* const result = await db .insert(welcomeTable) .values({ @@ -25,9 +25,9 @@ export const welcomeRouter = createTRPCRouter({ userId, createdAt: new Date(), }) - .returning(); + .returning(); */ - return { success: true, account: result[0] }; + return { success: true /* account: result[0] */ }; }), getByUserId: protectedProcedure.query(async ({ ctx }) => { diff --git a/tsconfig.json b/tsconfig.json index 8268e51..d0b801a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,8 @@ "**/*.tsx", "nativewind-env.d.ts", ".expo/types/**/*.ts", - "expo-env.d.ts", "src", "**/*.d.ts" + "expo-env.d.ts", + "src", + "**/*.d.ts" ] } From 51777ff44cce6f3e418a18ac73febe034b12cfee Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Sun, 12 Oct 2025 14:14:37 +0200 Subject: [PATCH 05/13] Revert unecessary change --- src/server/routers/welcome.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/server/routers/welcome.ts b/src/server/routers/welcome.ts index a8d6984..0b685b9 100644 --- a/src/server/routers/welcome.ts +++ b/src/server/routers/welcome.ts @@ -14,7 +14,7 @@ export const welcomeRouter = createTRPCRouter({ if (Number.isNaN(amount)) { throw new Error("Provided amount is not a valid number"); } - /* + const result = await db .insert(welcomeTable) .values({ @@ -25,9 +25,9 @@ export const welcomeRouter = createTRPCRouter({ userId, createdAt: new Date(), }) - .returning(); */ + .returning(); - return { success: true /* account: result[0] */ }; + return { success: true, account: result[0] }; }), getByUserId: protectedProcedure.query(async ({ ctx }) => { From 212961038beba9a4eaa8909d4672498399eabce2 Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Sun, 12 Oct 2025 16:39:20 +0200 Subject: [PATCH 06/13] feat: added container for appearance screen - added translations for all of that - commented the hell out of the container component --- localization/ar/settings.json | 1 + localization/de/settings.json | 1 + localization/en/settings.json | 1 + localization/es/settings.json | 1 + localization/fa/settings.json | 1 + localization/fr/settings.json | 1 + localization/hi/settings.json | 1 + localization/it/settings.json | 1 + localization/ja/settings.json | 1 + localization/ko/settings.json | 1 + localization/ps/settings.json | 1 + localization/pt/settings.json | 1 + localization/ru/settings.json | 1 + localization/tr/settings.json | 1 + localization/ur/settings.json | 1 + localization/zh/settings.json | 1 + src/app/(tabs)/settings/index.tsx | 6 +++ src/components/ui/container.tsx | 63 ++++++++++++++++++++++++++++++- src/i18n-resources.ts | 63 +++++++++++++++---------------- 19 files changed, 114 insertions(+), 34 deletions(-) diff --git a/localization/ar/settings.json b/localization/ar/settings.json index f91b997..5e86b27 100644 --- a/localization/ar/settings.json +++ b/localization/ar/settings.json @@ -1,6 +1,7 @@ { "profile": "الملف الشخصي", "user": "المستخدم", + "appearance": "المظهر", "notifications": "الإشعارات", "financial": "المالية", "limits": "القيود", diff --git a/localization/de/settings.json b/localization/de/settings.json index 47dc7d0..228ff95 100644 --- a/localization/de/settings.json +++ b/localization/de/settings.json @@ -1,6 +1,7 @@ { "profile": "Profil", "user": "Benutzer", + "appearance": "Erscheinungsbild", "notifications": "Benachrichtigungen", "financial": "Finanzen", "limits": "Limits", diff --git a/localization/en/settings.json b/localization/en/settings.json index 4cae6a1..5b033ad 100644 --- a/localization/en/settings.json +++ b/localization/en/settings.json @@ -1,6 +1,7 @@ { "profile": "Profile", "user": "User", + "appearance": "Appearance", "notifications": "Notifications", "financial": "Financial", "limits": "Limits", diff --git a/localization/es/settings.json b/localization/es/settings.json index 7455674..6726fa5 100644 --- a/localization/es/settings.json +++ b/localization/es/settings.json @@ -1,6 +1,7 @@ { "profile": "Perfil", "user": "Usuario", + "appearance": "Apariencia", "notifications": "Notificaciones", "financial": "Finanzas", "limits": "Límites", diff --git a/localization/fa/settings.json b/localization/fa/settings.json index 552e528..04892e0 100644 --- a/localization/fa/settings.json +++ b/localization/fa/settings.json @@ -1,6 +1,7 @@ { "profile": "پروفایل", "user": "کاربر", + "appearance": "ظاهر", "notifications": "اطلاعیه‌ها", "financial": "مالی", "limits": "محدودیت‌ها", diff --git a/localization/fr/settings.json b/localization/fr/settings.json index 7f24d93..4f314bf 100644 --- a/localization/fr/settings.json +++ b/localization/fr/settings.json @@ -1,6 +1,7 @@ { "profile": "Profil", "user": "Utilisateur", + "appearance": "Apparence", "notifications": "Notifications", "financial": "Financier", "limits": "Limites", diff --git a/localization/hi/settings.json b/localization/hi/settings.json index 1baee23..012171e 100644 --- a/localization/hi/settings.json +++ b/localization/hi/settings.json @@ -1,6 +1,7 @@ { "profile": "प्रोफ़ाइल", "user": "उपयोगकर्ता", + "appearance": "दिखावट", "notifications": "सूचनाएँ", "financial": "वित्तीय", "limits": "सीमाएँ", diff --git a/localization/it/settings.json b/localization/it/settings.json index 49f3b5a..04c78cf 100644 --- a/localization/it/settings.json +++ b/localization/it/settings.json @@ -1,6 +1,7 @@ { "profile": "Profilo", "user": "Utente", + "appearance": "Aspetto", "notifications": "Notifiche", "financial": "Finanziario", "limits": "Limiti", diff --git a/localization/ja/settings.json b/localization/ja/settings.json index 44ae238..529596e 100644 --- a/localization/ja/settings.json +++ b/localization/ja/settings.json @@ -1,6 +1,7 @@ { "profile": "プロフィール", "user": "ユーザー", + "appearance": "外観", "notifications": "通知", "financial": "財務", "limits": "制限", diff --git a/localization/ko/settings.json b/localization/ko/settings.json index 99a0440..cc4095d 100644 --- a/localization/ko/settings.json +++ b/localization/ko/settings.json @@ -1,6 +1,7 @@ { "profile": "프로필", "user": "사용자", + "appearance": "모양", "notifications": "알림", "financial": "재무", "limits": "한도", diff --git a/localization/ps/settings.json b/localization/ps/settings.json index 59014cc..0f4a228 100644 --- a/localization/ps/settings.json +++ b/localization/ps/settings.json @@ -1,6 +1,7 @@ { "profile": "پروفایل", "user": "کارن", + "appearance": "بڼه", "notifications": "خبرتیاوې", "financial": "مالي", "limits": "حدونه", diff --git a/localization/pt/settings.json b/localization/pt/settings.json index 08f630d..f3e6133 100644 --- a/localization/pt/settings.json +++ b/localization/pt/settings.json @@ -1,6 +1,7 @@ { "profile": "Perfil", "user": "Usuário", + "appearance": "Aparência", "notifications": "Notificações", "financial": "Financeiro", "limits": "Limites", diff --git a/localization/ru/settings.json b/localization/ru/settings.json index e8c020c..f94aae6 100644 --- a/localization/ru/settings.json +++ b/localization/ru/settings.json @@ -1,6 +1,7 @@ { "profile": "Профиль", "user": "Пользователь", + "appearance": "Внешний вид", "notifications": "Уведомления", "financial": "Финансы", "limits": "Ограничения", diff --git a/localization/tr/settings.json b/localization/tr/settings.json index 3cd8b9e..12d3e75 100644 --- a/localization/tr/settings.json +++ b/localization/tr/settings.json @@ -1,6 +1,7 @@ { "profile": "Profil", "user": "Kullanıcı", + "appearance": "Görünüm", "notifications": "Bildirimler", "financial": "Finans", "limits": "Limitler", diff --git a/localization/ur/settings.json b/localization/ur/settings.json index 3cce44a..ca0b16d 100644 --- a/localization/ur/settings.json +++ b/localization/ur/settings.json @@ -1,6 +1,7 @@ { "profile": "پروفائل", "user": "صارف", + "appearance": "ظاہری شکل", "notifications": "اطلاعات", "financial": "مالی", "limits": "حدود", diff --git a/localization/zh/settings.json b/localization/zh/settings.json index 5fa3223..6e64b01 100644 --- a/localization/zh/settings.json +++ b/localization/zh/settings.json @@ -1,6 +1,7 @@ { "profile": "个人资料", "user": "用户", + "appearance": "外观", "notifications": "通知", "financial": "财务", "limits": "限额", diff --git a/src/app/(tabs)/settings/index.tsx b/src/app/(tabs)/settings/index.tsx index b3a6465..c089ac2 100644 --- a/src/app/(tabs)/settings/index.tsx +++ b/src/app/(tabs)/settings/index.tsx @@ -13,6 +13,7 @@ import { BellRingIcon, LandmarkIcon, ShieldXIcon, + SunMoonIcon, UsersRoundIcon, } from "lucide-react-native"; import MoneyIcon from "~/assets/Icons/money.png"; @@ -52,6 +53,11 @@ export default function SettingsScreen() { {t("profile")} + + + {t("appearance")} + + {t("notifications")} diff --git a/src/components/ui/container.tsx b/src/components/ui/container.tsx index 5b838eb..06a22d9 100644 --- a/src/components/ui/container.tsx +++ b/src/components/ui/container.tsx @@ -1,12 +1,43 @@ +/** + * Container component + * + * A reusable UI wrapper that: + * - Renders a leading icon within a colored circular badge + * - Shows arbitrary children content next to the icon + * - Displays a trailing chevron/arrow for affordance + * + * Styling is done via Tailwind classes interpreted by NativeWind. + * Icon input can be either: + * - A Lucide icon component (function, forwardRef, or memo) + * - A static image source (ImageSourcePropType) rendered via AppImage + */ + import { ImageSourcePropType, View } from "react-native"; import { cn } from "~/utils/lib"; import { CircleArrowRight, LucideIcon } from "lucide-react-native"; import { cssInterop } from "nativewind"; import AppImage from "~/components/ui/AppImage"; +/** + * Type guard to determine whether a value is a Lucide icon component. + * + * Accepts: + * - Function components (common case for Lucide icons) + * - React.forwardRef(...) wrapped components + * - React.memo(...) wrapped components + * + * Notes: + * - We peek at the internal React $$$typeof symbol to differentiate memo/forwardRef. + * - This avoids false positives when an image source (object) is provided instead. + */ const isLucideIcon = (val: unknown): val is LucideIcon => { + // Fast-fail nullish values if (!val) return false; + + // Plain function component (most Lucide exports) if (typeof val === "function") return true; + + // Object-wrapped components (forwardRef or memo) if (typeof val === "object") { const t = (val as any).$$typeof; // Detect React.forwardRef / React.memo component types @@ -14,9 +45,18 @@ const isLucideIcon = (val: unknown): val is LucideIcon => { t === Symbol.for("react.forward_ref") || t === Symbol.for("react.memo") ); } + + // Anything else (e.g., numbers, strings) is not a component return false; }; +/** + * Container props + * + * children: React content to render next to the icon + * className: Additional Tailwind classes to merge with the default + * icon: Either a Lucide icon component or an image source for AppImage + */ export const Container = ({ children, className, @@ -26,15 +66,22 @@ export const Container = ({ className?: string; icon: LucideIcon | ImageSourcePropType; }) => { + // Create a NativeWind-interoperable version of the trailing arrow icon + // so we can style it with `className`. const StyledCircleArrowRight = cssInterop(CircleArrowRight, { className: { target: "style" }, }); + // Resolve the "icon" prop into a concrete React node: + // - If it's a Lucide component, wrap with cssInterop for className support. + // - Otherwise, treat it as an image source and render via AppImage. let IconNode: React.ReactNode; if (isLucideIcon(Icon)) { + // Adapt the incoming Lucide icon component to accept `className` const StyledIcon = cssInterop(Icon, { className: { target: "style" } }); IconNode = ; } else { + // Render provided an image source with constrained size and containment IconNode = ( ); @@ -42,17 +89,31 @@ export const Container = ({ return ( + {/* Left side: circular icon badge + children content */} + {/* Circular badge that hosts the icon node */} {IconNode} + + {/* Consumer-provided content (text, actions, etc.) */} {children} + + {/* Right side: affordance icon (e.g., navigation or action hint) */} ); diff --git a/src/i18n-resources.ts b/src/i18n-resources.ts index 761f58c..7681a7c 100644 --- a/src/i18n-resources.ts +++ b/src/i18n-resources.ts @@ -2,32 +2,46 @@ // Generated by generate-i18n-resources.js import ar_common from "../localization/ar/common.json"; +import ar_settings from "../localization/ar/settings.json"; import de_common from "../localization/de/common.json"; +import de_settings from "../localization/de/settings.json"; import en_common from "../localization/en/common.json"; import en_settings from "../localization/en/settings.json"; import es_common from "../localization/es/common.json"; +import es_settings from "../localization/es/settings.json"; import fa_common from "../localization/fa/common.json"; +import fa_settings from "../localization/fa/settings.json"; import fr_common from "../localization/fr/common.json"; +import fr_settings from "../localization/fr/settings.json"; import hi_common from "../localization/hi/common.json"; +import hi_settings from "../localization/hi/settings.json"; import it_common from "../localization/it/common.json"; import it_settings from "../localization/it/settings.json"; import ja_common from "../localization/ja/common.json"; +import ja_settings from "../localization/ja/settings.json"; import ko_common from "../localization/ko/common.json"; +import ko_settings from "../localization/ko/settings.json"; import ps_common from "../localization/ps/common.json"; +import ps_settings from "../localization/ps/settings.json"; import pt_common from "../localization/pt/common.json"; +import pt_settings from "../localization/pt/settings.json"; import ru_common from "../localization/ru/common.json"; +import ru_settings from "../localization/ru/settings.json"; import tr_common from "../localization/tr/common.json"; +import tr_settings from "../localization/tr/settings.json"; import ur_common from "../localization/ur/common.json"; +import ur_settings from "../localization/ur/settings.json"; import zh_common from "../localization/zh/common.json"; +import zh_settings from "../localization/zh/settings.json"; export const resources = { ar: { common: ar_common, - settings: {}, + settings: ar_settings, }, de: { common: de_common, - settings: {}, + settings: de_settings, }, en: { common: en_common, @@ -35,19 +49,19 @@ export const resources = { }, es: { common: es_common, - settings: {}, + settings: es_settings, }, fa: { common: fa_common, - settings: {}, + settings: fa_settings, }, fr: { common: fr_common, - settings: {}, + settings: fr_settings, }, hi: { common: hi_common, - settings: {}, + settings: hi_settings, }, it: { common: it_common, @@ -55,57 +69,40 @@ export const resources = { }, ja: { common: ja_common, - settings: {}, + settings: ja_settings, }, ko: { common: ko_common, - settings: {}, + settings: ko_settings, }, ps: { common: ps_common, - settings: {}, + settings: ps_settings, }, pt: { common: pt_common, - settings: {}, + settings: pt_settings, }, ru: { common: ru_common, - settings: {}, + settings: ru_settings, }, tr: { common: tr_common, - settings: {}, + settings: tr_settings, }, ur: { common: ur_common, - settings: {}, + settings: ur_settings, }, zh: { common: zh_common, - settings: {}, + settings: zh_settings, }, }; -export const languages = [ - "ar", - "de", - "en", - "es", - "fa", - "fr", - "hi", - "it", - "ja", - "ko", - "ps", - "pt", - "ru", - "tr", - "ur", - "zh", -] as const; -export const namespaces = ["common", "settings"] as const; +export const languages = ["ar","de","en","es","fa","fr","hi","it","ja","ko","ps","pt","ru","tr","ur","zh"] as const; +export const namespaces = ["common","settings"] as const; export type LanguageKey = (typeof languages)[number]; export type Namespace = (typeof namespaces)[number]; From e68e5872303b76aced521494d7c4b79b82c25dff Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Sun, 12 Oct 2025 17:52:32 +0200 Subject: [PATCH 07/13] feat: add some new screens and their links - simplified Header component - created Input component and moved to it --- src/app/(auth)/sign-in.tsx | 10 +-- src/app/(auth)/sign-up.tsx | 20 ++---- src/app/(tabs)/settings/index.tsx | 103 ++++++++++++++++++----------- src/app/settings/appearance.tsx | 12 ++++ src/app/settings/bank-accounts.tsx | 12 ++++ src/app/settings/income.tsx | 12 ++++ src/app/settings/limits.tsx | 12 ++++ src/app/settings/notifications.tsx | 12 ++++ src/app/settings/profile.tsx | 19 ++++++ src/components/Header.tsx | 10 ++- src/components/ui/container.tsx | 2 +- src/components/ui/text-input.tsx | 59 +++++++++++++++++ src/types/index.ts | 1 + 13 files changed, 220 insertions(+), 64 deletions(-) create mode 100644 src/app/settings/appearance.tsx create mode 100644 src/app/settings/bank-accounts.tsx create mode 100644 src/app/settings/income.tsx create mode 100644 src/app/settings/limits.tsx create mode 100644 src/app/settings/notifications.tsx create mode 100644 src/app/settings/profile.tsx create mode 100644 src/components/ui/text-input.tsx diff --git a/src/app/(auth)/sign-in.tsx b/src/app/(auth)/sign-in.tsx index b9be1f5..cf992c0 100644 --- a/src/app/(auth)/sign-in.tsx +++ b/src/app/(auth)/sign-in.tsx @@ -2,7 +2,6 @@ import { useSignIn } from "@clerk/clerk-expo"; import { useRouter } from "expo-router"; import React, { useCallback, useState, useEffect } from "react"; import { - TextInput, View, TouchableOpacity, KeyboardAvoidingView, @@ -15,6 +14,7 @@ import "~/i18n"; import { languageService } from "~/services/languageService"; import Button from "~/components/ui/button"; import AppImage from "~/components/ui/AppImage"; +import { TextInput } from "~/components/ui/text-input"; export default function Page() { const { signIn, setActive, isLoaded } = useSignIn(); @@ -161,10 +161,8 @@ export default function Page() { Finance.io - - {t("email")} - )} - - {t("password")} - { const initLanguage = async () => { try { @@ -204,10 +204,8 @@ export default function SignUpScreen() { {!pendingVerification ? ( <> - - {t("firstName")} - )} - - {t("lastName")} - - {t("email")} - )} - - {t("password")} - {/* Header */} -
+
- - - {t("profile")} - - - - - {t("appearance")} - - - - - {t("notifications")} - - + + + + {t("profile")} + + + + + + + {t("appearance")} + + + + + + + {t("notifications")} + + + - - - {t("limits")} - - - - - {t("income")} - - - - - {t("bankAccounts")} - - + + + + {t("limits")} + + + + + + + + {t("income")} + + + + + + + + {t("bankAccounts")} + + + diff --git a/src/app/settings/appearance.tsx b/src/app/settings/appearance.tsx new file mode 100644 index 0000000..aeca802 --- /dev/null +++ b/src/app/settings/appearance.tsx @@ -0,0 +1,12 @@ +import AppText from "~/components/ui/AppText"; +import { SafeAreaView } from "react-native-safe-area-context"; + +const ProfilePage = () => { + return ( + + Hiiii + + ); +}; + +export default ProfilePage; diff --git a/src/app/settings/bank-accounts.tsx b/src/app/settings/bank-accounts.tsx new file mode 100644 index 0000000..aeca802 --- /dev/null +++ b/src/app/settings/bank-accounts.tsx @@ -0,0 +1,12 @@ +import AppText from "~/components/ui/AppText"; +import { SafeAreaView } from "react-native-safe-area-context"; + +const ProfilePage = () => { + return ( + + Hiiii + + ); +}; + +export default ProfilePage; diff --git a/src/app/settings/income.tsx b/src/app/settings/income.tsx new file mode 100644 index 0000000..aeca802 --- /dev/null +++ b/src/app/settings/income.tsx @@ -0,0 +1,12 @@ +import AppText from "~/components/ui/AppText"; +import { SafeAreaView } from "react-native-safe-area-context"; + +const ProfilePage = () => { + return ( + + Hiiii + + ); +}; + +export default ProfilePage; diff --git a/src/app/settings/limits.tsx b/src/app/settings/limits.tsx new file mode 100644 index 0000000..aeca802 --- /dev/null +++ b/src/app/settings/limits.tsx @@ -0,0 +1,12 @@ +import AppText from "~/components/ui/AppText"; +import { SafeAreaView } from "react-native-safe-area-context"; + +const ProfilePage = () => { + return ( + + Hiiii + + ); +}; + +export default ProfilePage; diff --git a/src/app/settings/notifications.tsx b/src/app/settings/notifications.tsx new file mode 100644 index 0000000..aeca802 --- /dev/null +++ b/src/app/settings/notifications.tsx @@ -0,0 +1,12 @@ +import AppText from "~/components/ui/AppText"; +import { SafeAreaView } from "react-native-safe-area-context"; + +const ProfilePage = () => { + return ( + + Hiiii + + ); +}; + +export default ProfilePage; diff --git a/src/app/settings/profile.tsx b/src/app/settings/profile.tsx new file mode 100644 index 0000000..1b5935b --- /dev/null +++ b/src/app/settings/profile.tsx @@ -0,0 +1,19 @@ +import AppText from "~/components/ui/AppText"; +import { SafeAreaView } from "react-native-safe-area-context"; +import { Header } from "~/components/Header"; +import { NavigationItems } from "~/types"; +import React from "react"; +import { useTranslation } from "react-i18next"; + +const ProfilePage = () => { + const { t } = useTranslation("settings"); + + return ( + +
+ Hiiii + + ); +}; + +export default ProfilePage; diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 56e4ab3..b75ac23 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -9,18 +9,19 @@ import AppText from "./ui/AppText"; import AppImage from "~/components/ui/AppImage"; import { useTranslation } from "react-i18next"; import { NavigationItems } from "~/types"; +import { useUser } from "@clerk/clerk-expo"; const AnimatedView = Animated.createAnimatedComponent(Animated.View); interface HeaderProps { - name: string; type: NavigationItems; // enum key, not a translated string } -export const Header: React.FC = ({ name, type }) => { +export const Header: React.FC = ({ type }) => { const { colors } = useTheme(); const headerOpacity = useSharedValue(0); const { t } = useTranslation(); + const { user, isLoaded } = useUser(); const headerAnimatedStyle = useAnimatedStyle(() => ({ opacity: withSpring(headerOpacity.value), @@ -44,7 +45,10 @@ export const Header: React.FC = ({ name, type }) => { className="text-center text-xl" style={{ color: colors.text }} > - {name} - {t(type)} + {isLoaded && user + ? user.firstName || t("defaultUser") + : t("defaultUser")}{" "} + - {t(type)} ); diff --git a/src/components/ui/container.tsx b/src/components/ui/container.tsx index 06a22d9..fbb7ec0 100644 --- a/src/components/ui/container.tsx +++ b/src/components/ui/container.tsx @@ -92,7 +92,7 @@ export const Container = ({ // Merge default container styles with any consumer-provided classes className={cn( // Layout: horizontal row, spaced ends, vertical centering - "flex-row items-center justify-between", + "w-full flex-row items-center justify-between", // Card-like visuals "rounded-xl border-2 border-stroke bg-secondary", // Spacing: y padding, asymmetric x padding (extra left for icon badge) diff --git a/src/components/ui/text-input.tsx b/src/components/ui/text-input.tsx new file mode 100644 index 0000000..2477a3f --- /dev/null +++ b/src/components/ui/text-input.tsx @@ -0,0 +1,59 @@ +/** + * TextInput wrapper component for React Native with a labeled header. + * - Uses NativeWind Tailwind classes via `className`. + * - Enforces project rule to use `AppText` instead of `Text`. + * - Provides sensible defaults for accessibility and keyboard behavior. + * - Accepts all React Native `TextInputProps` and a required `name` for the label. + */ + +import AppText from "~/components/ui/AppText"; // Project typography component; use instead of RN +import React from "react"; +import { TextInputProps, TextInput as RNTextInput } from "react-native"; // Alias to avoid name collision +import { cn } from "~/utils/lib"; // Utility to merge/compose class names conditionally + +/** + * Props: + * - `name`: Visible field label and base for accessibility labels. + * - `...TextInputProps`: Forwarded to RN TextInput. Explicit props override defaults. + */ +export const TextInput = ({ + name, + ...props +}: { name: string } & TextInputProps) => { + return ( + <> + {/* Field label — use AppText per project lint rule */} + + {name} + + + {/* Underlying input. Note: `className` is supported via NativeWind. */} + + + ); +}; diff --git a/src/types/index.ts b/src/types/index.ts index a93d057..1a7294d 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -117,6 +117,7 @@ export enum NavigationItems { INSIGHTS = "navigationInsights", /** Settings/preferences area: profile, security, language. */ SETTINGS = "navigationSettings", + PROFILE = "navigationProfile", /** Banking/accounts area: balances, transfers, cards. */ BANKING = "navigationBankAccounts", } From 9267e4b4d2abd88c14a6d04ed8372b37f5a58dc7 Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Sun, 19 Oct 2025 15:38:50 +0200 Subject: [PATCH 08/13] fixed type error --- src/app/(tabs)/insights/index.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/app/(tabs)/insights/index.tsx b/src/app/(tabs)/insights/index.tsx index 7ae520e..697d772 100644 --- a/src/app/(tabs)/insights/index.tsx +++ b/src/app/(tabs)/insights/index.tsx @@ -21,14 +21,7 @@ export default function InsightsScreen() { /> {/* Header */} -
+
From 3238857f4cc243196c8655f8a088f859d2b4f682 Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Sun, 19 Oct 2025 18:08:07 +0200 Subject: [PATCH 09/13] updated stuff --- package.json | 4 ++-- pnpm-lock.yaml | 54 ++++++++++++++++++++++----------------------- pnpm-workspace.yaml | 5 +++-- 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/package.json b/package.json index ef223d9..abc67b5 100644 --- a/package.json +++ b/package.json @@ -74,7 +74,7 @@ "tailwind-merge": "^3.3.1", "tailwind-variants": "^3.1.1", "tailwindcss": "^3.4.18", - "zod": "^4.1.11" + "zod": "^4.1.12" }, "devDependencies": { "@babel/core": "^7.28.4", @@ -96,5 +96,5 @@ "typescript": "^5.9.3" }, "private": true, - "packageManager": "pnpm@10.18.0+sha512.e804f889f1cecc40d572db084eec3e4881739f8dec69c0ff10d2d1beff9a4e309383ba27b5b750059d7f4c149535b6cd0d2cb1ed3aeb739239a4284a68f40cfa" + "packageManager": "pnpm@10.18.3+sha512.bbd16e6d7286fd7e01f6b3c0b3c932cda2965c06a908328f74663f10a9aea51f1129eea615134bf992831b009eabe167ecb7008b597f40ff9bc75946aadfb08d" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 94df5b9..873fcea 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -13,7 +13,7 @@ importers: version: 2.17.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@clerk/clerk-expo': specifier: ^2.15.4 - version: 2.15.4(@types/react@19.1.17)(expo-auth-session@7.0.8(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(expo-secure-store@15.0.7(expo@54.0.12))(expo-web-browser@15.0.8(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0)))(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.11) + version: 2.15.4(@types/react@19.1.17)(expo-auth-session@7.0.8(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(expo-secure-store@15.0.7(expo@54.0.12))(expo-web-browser@15.0.8(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0)))(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.12) '@expo/server': specifier: ^0.7.5 version: 0.7.5 @@ -28,7 +28,7 @@ importers: version: 1.0.1 '@t3-oss/env-core': specifier: ^0.13.6 - version: 0.13.8(typescript@5.9.3)(zod@4.1.11) + version: 0.13.8(typescript@5.9.3)(zod@4.1.12) '@tanstack/react-query': specifier: ^5.90.2 version: 5.90.2(react@19.1.0) @@ -171,8 +171,8 @@ importers: specifier: ^3.4.18 version: 3.4.18(tsx@4.20.6)(yaml@2.8.1) zod: - specifier: ^4.1.11 - version: 4.1.11 + specifier: ^4.1.12 + version: 4.1.12 devDependencies: '@babel/core': specifier: ^7.28.4 @@ -7028,8 +7028,8 @@ packages: zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - zod@4.1.11: - resolution: {integrity: sha512-WPsqwxITS2tzx1bzhIKsEs19ABD5vmCVa4xBo2tq/SrV4RNZtfws1EnCWQXM6yh8bD08a1idvkB5MZSBiZsjwg==} + zod@4.1.12: + resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==} zustand@5.0.3: resolution: {integrity: sha512-14fwWQtU3pH4dE0dOpdMiWjddcH+QzKIgk1cl8epwSE7yag43k/AD/m4L6+K7DytAOr9gGBe3/EXj9g7cdostg==} @@ -7696,15 +7696,15 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 - '@base-org/account@2.0.1(@types/react@19.1.17)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.11)': + '@base-org/account@2.0.1(@types/react@19.1.17)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.12)': dependencies: '@noble/hashes': 1.4.0 clsx: 1.2.1 eventemitter3: 5.0.1 idb-keyval: 6.2.1 - ox: 0.6.9(typescript@5.9.3)(zod@4.1.11) + ox: 0.6.9(typescript@5.9.3)(zod@4.1.12) preact: 10.24.2 - viem: 2.37.12(typescript@5.9.3)(zod@4.1.11) + viem: 2.37.12(typescript@5.9.3)(zod@4.1.12) zustand: 5.0.3(@types/react@19.1.17)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)) transitivePeerDependencies: - '@types/react' @@ -7729,9 +7729,9 @@ snapshots: - react - react-dom - '@clerk/clerk-expo@2.15.4(@types/react@19.1.17)(expo-auth-session@7.0.8(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(expo-secure-store@15.0.7(expo@54.0.12))(expo-web-browser@15.0.8(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0)))(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.11)': + '@clerk/clerk-expo@2.15.4(@types/react@19.1.17)(expo-auth-session@7.0.8(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0))(expo-secure-store@15.0.7(expo@54.0.12))(expo-web-browser@15.0.8(expo@54.0.12)(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0)))(react-dom@19.1.0(react@19.1.0))(react-native@0.81.4(@babel/core@7.28.4)(@types/react@19.1.17)(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.12)': dependencies: - '@clerk/clerk-js': 5.97.0(@types/react@19.1.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.11) + '@clerk/clerk-js': 5.97.0(@types/react@19.1.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.12) '@clerk/clerk-react': 5.49.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@clerk/shared': 3.27.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@clerk/types': 4.90.0 @@ -7755,9 +7755,9 @@ snapshots: - utf-8-validate - zod - '@clerk/clerk-js@5.97.0(@types/react@19.1.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.11)': + '@clerk/clerk-js@5.97.0(@types/react@19.1.17)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.12)': dependencies: - '@base-org/account': 2.0.1(@types/react@19.1.17)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.11) + '@base-org/account': 2.0.1(@types/react@19.1.17)(react@19.1.0)(typescript@5.9.3)(use-sync-external-store@1.5.0(react@19.1.0))(zod@4.1.12) '@clerk/localizations': 3.25.5 '@clerk/shared': 3.27.1(react-dom@19.1.0(react@19.1.0))(react@19.1.0) '@clerk/types': 4.90.0 @@ -9356,10 +9356,10 @@ snapshots: dependencies: '@swc/counter': 0.1.3 - '@t3-oss/env-core@0.13.8(typescript@5.9.3)(zod@4.1.11)': + '@t3-oss/env-core@0.13.8(typescript@5.9.3)(zod@4.1.12)': optionalDependencies: typescript: 5.9.3 - zod: 4.1.11 + zod: 4.1.12 '@tanstack/query-core@5.90.2': {} @@ -9747,15 +9747,15 @@ snapshots: abab@2.0.6: {} - abitype@1.1.0(typescript@5.9.3)(zod@4.1.11): + abitype@1.1.0(typescript@5.9.3)(zod@4.1.12): optionalDependencies: typescript: 5.9.3 - zod: 4.1.11 + zod: 4.1.12 - abitype@1.1.1(typescript@5.9.3)(zod@4.1.11): + abitype@1.1.1(typescript@5.9.3)(zod@4.1.12): optionalDependencies: typescript: 5.9.3 - zod: 4.1.11 + zod: 4.1.12 abort-controller@3.0.0: dependencies: @@ -13292,21 +13292,21 @@ snapshots: object-keys: 1.1.1 safe-push-apply: 1.0.0 - ox@0.6.9(typescript@5.9.3)(zod@4.1.11): + ox@0.6.9(typescript@5.9.3)(zod@4.1.12): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/curves': 1.9.7 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.1(typescript@5.9.3)(zod@4.1.11) + abitype: 1.1.1(typescript@5.9.3)(zod@4.1.12) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 transitivePeerDependencies: - zod - ox@0.9.6(typescript@5.9.3)(zod@4.1.11): + ox@0.9.6(typescript@5.9.3)(zod@4.1.12): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -13314,7 +13314,7 @@ snapshots: '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.3)(zod@4.1.11) + abitype: 1.1.0(typescript@5.9.3)(zod@4.1.12) eventemitter3: 5.0.1 optionalDependencies: typescript: 5.9.3 @@ -14670,15 +14670,15 @@ snapshots: - '@types/react' - '@types/react-dom' - viem@2.37.12(typescript@5.9.3)(zod@4.1.11): + viem@2.37.12(typescript@5.9.3)(zod@4.1.12): dependencies: '@noble/curves': 1.9.1 '@noble/hashes': 1.8.0 '@scure/bip32': 1.7.0 '@scure/bip39': 1.6.0 - abitype: 1.1.0(typescript@5.9.3)(zod@4.1.11) + abitype: 1.1.0(typescript@5.9.3)(zod@4.1.12) isows: 1.0.7(ws@8.18.3) - ox: 0.9.6(typescript@5.9.3)(zod@4.1.11) + ox: 0.9.6(typescript@5.9.3)(zod@4.1.12) ws: 8.18.3 optionalDependencies: typescript: 5.9.3 @@ -14914,7 +14914,7 @@ snapshots: zod@3.25.76: {} - zod@4.1.11: {} + zod@4.1.12: {} zustand@5.0.3(@types/react@19.1.17)(react@19.1.0)(use-sync-external-store@1.5.0(react@19.1.0)): optionalDependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0d110a0..0ef09fa 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,8 +1,9 @@ packages: - - "." + - . onlyBuiltDependencies: - - "@clerk/shared" + - '@clerk/shared' + - '@swc/core' - browser-tabs-lock - core-js - esbuild From 0624ff6cee6b451bc16e37a6a65661ec2a27535c Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Mon, 20 Oct 2025 12:54:36 +0200 Subject: [PATCH 10/13] make routing work --- src/app/(tabs)/_layout.tsx | 6 +-- src/app/(tabs)/banking/_layout.tsx | 15 ++++++++ src/app/(tabs)/insights/_layout.tsx | 15 ++++++++ src/app/(tabs)/settings/_layout.tsx | 15 ++++++++ src/app/{ => (tabs)}/settings/appearance.tsx | 0 .../{ => (tabs)}/settings/bank-accounts.tsx | 0 src/app/{ => (tabs)}/settings/income.tsx | 0 src/app/(tabs)/settings/index.tsx | 37 +++++++++++++++---- src/app/{ => (tabs)}/settings/limits.tsx | 0 .../{ => (tabs)}/settings/notifications.tsx | 0 src/app/{ => (tabs)}/settings/profile.tsx | 0 11 files changed, 78 insertions(+), 10 deletions(-) create mode 100644 src/app/(tabs)/banking/_layout.tsx create mode 100644 src/app/(tabs)/insights/_layout.tsx create mode 100644 src/app/(tabs)/settings/_layout.tsx rename src/app/{ => (tabs)}/settings/appearance.tsx (100%) rename src/app/{ => (tabs)}/settings/bank-accounts.tsx (100%) rename src/app/{ => (tabs)}/settings/income.tsx (100%) rename src/app/{ => (tabs)}/settings/limits.tsx (100%) rename src/app/{ => (tabs)}/settings/notifications.tsx (100%) rename src/app/{ => (tabs)}/settings/profile.tsx (100%) diff --git a/src/app/(tabs)/_layout.tsx b/src/app/(tabs)/_layout.tsx index 365b0a8..15ed1da 100644 --- a/src/app/(tabs)/_layout.tsx +++ b/src/app/(tabs)/_layout.tsx @@ -21,17 +21,17 @@ const DashboardLayout = () => { const { colors } = useTheme(); const navigationOptions: NavigationOption[] = [ { - name: "banking/index", + name: "banking", title: t(NavigationItems.BANKING), icon: CreditCardIcon, }, { - name: "insights/index", + name: "insights", title: t(NavigationItems.INSIGHTS), icon: ChartColumnIcon, }, { - name: "settings/index", + name: "settings", title: t(NavigationItems.SETTINGS), icon: SettingsIcon, }, diff --git a/src/app/(tabs)/banking/_layout.tsx b/src/app/(tabs)/banking/_layout.tsx new file mode 100644 index 0000000..ecc26e3 --- /dev/null +++ b/src/app/(tabs)/banking/_layout.tsx @@ -0,0 +1,15 @@ +import { Stack } from "expo-router"; + +export const unstable_settings = { + initialRouteName: "index", +}; + +export default function BankingLayout() { + return ( + + ); +} diff --git a/src/app/(tabs)/insights/_layout.tsx b/src/app/(tabs)/insights/_layout.tsx new file mode 100644 index 0000000..6af99ac --- /dev/null +++ b/src/app/(tabs)/insights/_layout.tsx @@ -0,0 +1,15 @@ +import { Stack } from "expo-router"; + +export const unstable_settings = { + initialRouteName: "index", +}; + +export default function InsightsLayout() { + return ( + + ); +} diff --git a/src/app/(tabs)/settings/_layout.tsx b/src/app/(tabs)/settings/_layout.tsx new file mode 100644 index 0000000..b6358c7 --- /dev/null +++ b/src/app/(tabs)/settings/_layout.tsx @@ -0,0 +1,15 @@ +import { Stack } from "expo-router"; + +export const unstable_settings = { + initialRouteName: "index", +}; + +export default function SettingsLayout() { + return ( + + ); +} diff --git a/src/app/settings/appearance.tsx b/src/app/(tabs)/settings/appearance.tsx similarity index 100% rename from src/app/settings/appearance.tsx rename to src/app/(tabs)/settings/appearance.tsx diff --git a/src/app/settings/bank-accounts.tsx b/src/app/(tabs)/settings/bank-accounts.tsx similarity index 100% rename from src/app/settings/bank-accounts.tsx rename to src/app/(tabs)/settings/bank-accounts.tsx diff --git a/src/app/settings/income.tsx b/src/app/(tabs)/settings/income.tsx similarity index 100% rename from src/app/settings/income.tsx rename to src/app/(tabs)/settings/income.tsx diff --git a/src/app/(tabs)/settings/index.tsx b/src/app/(tabs)/settings/index.tsx index 0f57377..d0e4c29 100644 --- a/src/app/(tabs)/settings/index.tsx +++ b/src/app/(tabs)/settings/index.tsx @@ -5,7 +5,6 @@ import { useTheme } from "~/contexts/ThemeContext"; import { Header } from "~/components/Header"; import { SectionHeader } from "~/components/SectionHeader"; import { NavigationItems } from "~/types"; -import { useUser } from "@clerk/clerk-expo"; import { useTranslation } from "react-i18next"; import AppText from "~/components/ui/AppText"; import { Container } from "~/components/ui/container"; @@ -41,7 +40,11 @@ export default function SettingsScreen() { delay={400} /> - + - + - + - + - + - + Date: Wed, 22 Oct 2025 18:02:40 +0200 Subject: [PATCH 11/13] update text-input - updated the text input component to work for all cases --- .idea/tailwindcss.xml | 6 + .prettierrc | 3 +- localization/en/settings.json | 4 +- localization/it/settings.json | 4 +- package.json | 4 +- pnpm-lock.yaml | 10 ++ pnpm-workspace.yaml | 4 +- src/app/(tabs)/settings/profile.tsx | 16 ++- src/app/start/index.tsx | 212 +++++++++++----------------- src/assets/Icons/bank.png | Bin 705 -> 0 bytes src/components/ui/button.tsx | 34 ++++- src/components/ui/container.tsx | 34 +---- src/components/ui/text-input.tsx | 101 ++++++++----- src/utils/lib.ts | 33 +++++ 14 files changed, 253 insertions(+), 212 deletions(-) create mode 100644 .idea/tailwindcss.xml delete mode 100644 src/assets/Icons/bank.png diff --git a/.idea/tailwindcss.xml b/.idea/tailwindcss.xml new file mode 100644 index 0000000..78a6bc1 --- /dev/null +++ b/.idea/tailwindcss.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.prettierrc b/.prettierrc index b4bfed3..439e6bd 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,3 +1,4 @@ { - "plugins": ["prettier-plugin-tailwindcss"] + "plugins": ["prettier-plugin-tailwindcss"], + "tailwindFunctions": ["cva", "cx", "cn"] } diff --git a/localization/en/settings.json b/localization/en/settings.json index 5b033ad..a1e3750 100644 --- a/localization/en/settings.json +++ b/localization/en/settings.json @@ -6,5 +6,7 @@ "financial": "Financial", "limits": "Limits", "income": "Income", - "bankAccounts": "Bank Accounts" + "bankAccounts": "Bank Accounts", + "saveChanges": "Save Changes", + "changePassword": "Change Password" } diff --git a/localization/it/settings.json b/localization/it/settings.json index 04c78cf..5421aa5 100644 --- a/localization/it/settings.json +++ b/localization/it/settings.json @@ -6,5 +6,7 @@ "financial": "Finanziario", "limits": "Limiti", "income": "Reddito", - "bankAccounts": "Conti Bancari" + "bankAccounts": "Conti Bancari", + "saveChanges": "Salva Modifiche", + "changePassword": "Cambia Password" } diff --git a/package.json b/package.json index abc67b5..f739fea 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "format:check": "prettier --check .", "format:write": "prettier --write .", "generate:i18n": "node scripts/generate-i18n-resources.js", + "preinstall": "npx only-allow pnpm", "prestart": "pnpm run generate:i18n", "prebuild": "pnpm run generate:i18n" }, @@ -32,6 +33,7 @@ "@trpc/react-query": "^11.6.0", "@trpc/server": "^11.6.0", "clsx": "^2.1.1", + "cva": "npm:class-variance-authority@^0.7.1", "dotenv": "^17.2.3", "drizzle-orm": "^0.44.6", "expo": "~54.0.12", @@ -96,5 +98,5 @@ "typescript": "^5.9.3" }, "private": true, - "packageManager": "pnpm@10.18.3+sha512.bbd16e6d7286fd7e01f6b3c0b3c932cda2965c06a908328f74663f10a9aea51f1129eea615134bf992831b009eabe167ecb7008b597f40ff9bc75946aadfb08d" + "packageManager": "pnpm@10.19.0+sha512.c9fc7236e92adf5c8af42fd5bf1612df99c2ceb62f27047032f4720b33f8eacdde311865e91c411f2774f618d82f320808ecb51718bfa82c060c4ba7c76a32b8" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 873fcea..e8eb9e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -44,6 +44,9 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + cva: + specifier: npm:class-variance-authority@^0.7.1 + version: class-variance-authority@0.7.1 dotenv: specifier: ^17.2.3 version: 17.2.3 @@ -2983,6 +2986,9 @@ packages: cjs-module-lexer@1.4.3: resolution: {integrity: sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==} + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + cli-cursor@2.1.0: resolution: {integrity: sha512-8lgKz8LmCRYZZQDpRyT2m5rKJ08TnU4tR9FFFW2rxpxR1FzWi4PQ/NfyODchAatHaUgnSPVcx/R5w6NuTBzFiw==} engines: {node: '>=4'} @@ -10255,6 +10261,10 @@ snapshots: cjs-module-lexer@1.4.3: {} + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + cli-cursor@2.1.0: dependencies: restore-cursor: 2.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 0ef09fa..8fe03ce 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,8 +2,8 @@ packages: - . onlyBuiltDependencies: - - '@clerk/shared' - - '@swc/core' + - "@clerk/shared" + - "@swc/core" - browser-tabs-lock - core-js - esbuild diff --git a/src/app/(tabs)/settings/profile.tsx b/src/app/(tabs)/settings/profile.tsx index 1b5935b..e79e1ed 100644 --- a/src/app/(tabs)/settings/profile.tsx +++ b/src/app/(tabs)/settings/profile.tsx @@ -1,17 +1,25 @@ -import AppText from "~/components/ui/AppText"; import { SafeAreaView } from "react-native-safe-area-context"; import { Header } from "~/components/Header"; import { NavigationItems } from "~/types"; import React from "react"; import { useTranslation } from "react-i18next"; +import { TextInput } from "~/components/ui/text-input"; +import Button from "~/components/ui/button"; const ProfilePage = () => { - const { t } = useTranslation("settings"); + const { t: tCommon } = useTranslation("common"); + const { t: tSettings } = useTranslation("settings"); return ( - +
- Hiiii + + + + + ); }; diff --git a/src/app/start/index.tsx b/src/app/start/index.tsx index 6fef10f..4776bc2 100644 --- a/src/app/start/index.tsx +++ b/src/app/start/index.tsx @@ -2,7 +2,6 @@ import React, { useEffect, useState } from "react"; import { ScrollView, View, - TextInput, KeyboardAvoidingView, Platform, Alert, @@ -10,7 +9,7 @@ import { } from "react-native"; import AppText from "~/components/ui/AppText"; import { useUser } from "@clerk/clerk-expo"; -import { Link } from "lucide-react-native"; +import { LandmarkIcon, Link } from "lucide-react-native"; import { trpc } from "~/utils/trpc"; import { useForm, Controller } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; @@ -21,6 +20,7 @@ import Button from "~/components/ui/button"; import { useRouter } from "expo-router"; import AppImage from "~/components/ui/AppImage"; import { WelcomeFormValues, welcomeSchema } from "~/schemas/welcomeSchema"; +import { TextInput } from "~/components/ui/text-input"; const Home = () => { const { user, isLoaded } = useUser(); @@ -85,7 +85,7 @@ const Home = () => { behavior={Platform.OS === "ios" ? "padding" : "height"} className="flex-1" > - + { {/*Link Icon*/} - + { {/*Input Fields*/} - - {t("bankName")} - ( - - - - - - + )} /> {errors.bankName && ( @@ -159,60 +147,48 @@ const Home = () => { )} - - {t("currentAmount")} - ( - - - - - { - // Only allow numbers and one decimal point - const sanitized = text.replace(/[^0-9.]/g, ""); - // Ensure only one decimal point is allowed - const parts = sanitized.split("."); - const formattedText = - parts.length > 2 - ? parts[0] + "." + parts.slice(1).join("") - : sanitized; + { + // Only allow numbers and one decimal point + const sanitized = text.replace(/[^0-9.]/g, ""); + // Ensure only one decimal point is allowed + const parts = sanitized.split("."); + const formattedText = + parts.length > 2 + ? parts[0] + "." + parts.slice(1).join("") + : sanitized; - // Limit decimal places to 2 - let finalText = formattedText; - if (parts.length === 2 && parts[1].length > 2) { - finalText = parts[0] + "." + parts[1].slice(0, 2); - } + // Limit decimal places to 2 + let finalText = formattedText; + if (parts.length === 2 && parts[1].length > 2) { + finalText = parts[0] + "." + parts[1].slice(0, 2); + } - setAmountDisplay(finalText); + setAmountDisplay(finalText); - // Update form value - if (finalText === "" || finalText === ".") { - onChange(0); - } else { - onChange(parseFloat(finalText) || 0); - } - }} - /> - + // Update form value + if (finalText === "" || finalText === ".") { + onChange(0); + } else { + onChange(parseFloat(finalText) || 0); + } + }} + /> )} /> {errors.currentAmount && ( @@ -221,36 +197,24 @@ const Home = () => { )} - - {t("reference")} - ( - - - - - - + )} /> {errors.reference && ( @@ -259,36 +223,24 @@ const Home = () => { )} - - {t("usage")} - ( - - - - - - + )} /> {errors.usage && ( diff --git a/src/assets/Icons/bank.png b/src/assets/Icons/bank.png deleted file mode 100644 index 7dbebf17186a1c317df9a06da761a8fb11404bfe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 705 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=EX7WqAsj$Z!;#Vf4nJ zFx~)R#$PeZihzQWC9V-A!TD(=<%vb94CUqJdYO6I#mR{Use1WE>9gP2NHH)l*?GD+ zhE&XXJ9}@INFYP&{+ql>2CEB&J%l?HTW|biP*OAt3(Ne(=cIFlB`I;HQmb8C(sOQ& zh$S7K;@bi|6pnJu_VqF7(#h$v?k=1&SANFc^tyNK%Wv#F{d5lV>AlwFY$hfRr4J5V z-&Mc%ix0zU2k95=R~X|Mw}(7)^9=cr=<1`@ljb|?fun5j10}TvQ=g742b+H}<^)Yp z+QcJwMbc7lQ6uL))!z>eckRekjpQ+Fe!hw$wIEZllFM#Q$1{!Q`A-xw=A7HC^C+#c zDLu+Xcr&MF(uWO7_ZW;loBXG0RIE~BJLq4Wa3xD{Ui<-`0_8O`g+2@aSZDSy;4$Op zKYuktdiSxvVf=p8&*javhVm?Nr+Za1R6j_o?iCk5sJS74y^e8zxsyW4S@n#M%eWHx77zdH#Ld7D1lfXaok3AnOSOdeB; + const Button: React.FC = ({ href, ...props }) => { const router = useRouter(); @@ -69,11 +95,7 @@ const Button: React.FC = ({ href, ...props }) => { {props.children} diff --git a/src/components/ui/container.tsx b/src/components/ui/container.tsx index fbb7ec0..49b02a5 100644 --- a/src/components/ui/container.tsx +++ b/src/components/ui/container.tsx @@ -13,43 +13,11 @@ */ import { ImageSourcePropType, View } from "react-native"; -import { cn } from "~/utils/lib"; +import { cn, isLucideIcon } from "~/utils/lib"; import { CircleArrowRight, LucideIcon } from "lucide-react-native"; import { cssInterop } from "nativewind"; import AppImage from "~/components/ui/AppImage"; -/** - * Type guard to determine whether a value is a Lucide icon component. - * - * Accepts: - * - Function components (common case for Lucide icons) - * - React.forwardRef(...) wrapped components - * - React.memo(...) wrapped components - * - * Notes: - * - We peek at the internal React $$$typeof symbol to differentiate memo/forwardRef. - * - This avoids false positives when an image source (object) is provided instead. - */ -const isLucideIcon = (val: unknown): val is LucideIcon => { - // Fast-fail nullish values - if (!val) return false; - - // Plain function component (most Lucide exports) - if (typeof val === "function") return true; - - // Object-wrapped components (forwardRef or memo) - if (typeof val === "object") { - const t = (val as any).$$typeof; - // Detect React.forwardRef / React.memo component types - return ( - t === Symbol.for("react.forward_ref") || t === Symbol.for("react.memo") - ); - } - - // Anything else (e.g., numbers, strings) is not a component - return false; -}; - /** * Container props * diff --git a/src/components/ui/text-input.tsx b/src/components/ui/text-input.tsx index 2477a3f..767cb13 100644 --- a/src/components/ui/text-input.tsx +++ b/src/components/ui/text-input.tsx @@ -6,54 +6,89 @@ * - Accepts all React Native `TextInputProps` and a required `name` for the label. */ -import AppText from "~/components/ui/AppText"; // Project typography component; use instead of RN +import AppText from "~/components/ui/AppText"; import React from "react"; -import { TextInputProps, TextInput as RNTextInput } from "react-native"; // Alias to avoid name collision -import { cn } from "~/utils/lib"; // Utility to merge/compose class names conditionally +import { + TextInputProps, + TextInput as RNTextInput, + View, + ImageSourcePropType, +} from "react-native"; +import { cn, isLucideIcon } from "~/utils/lib"; +import { LucideIcon } from "lucide-react-native"; +import AppImage from "~/components/ui/AppImage"; +import { cssInterop } from "nativewind"; /** * Props: * - `name`: Visible field label and base for accessibility labels. + * - `required`: If true, shows an asterisk (*) in the accent colour next to the label. + * - `icon`: Optional icon (LucideIcon or ImageSourcePropType) to display inside the input. * - `...TextInputProps`: Forwarded to RN TextInput. Explicit props override defaults. */ export const TextInput = ({ name, + required, + icon, + className, ...props -}: { name: string } & TextInputProps) => { +}: { + name: string; + required?: boolean; + icon?: LucideIcon | ImageSourcePropType; +} & TextInputProps) => { + // Resolve the "icon" prop into a concrete React node + let IconNode: React.ReactNode | null = null; + if (icon) { + if (isLucideIcon(icon)) { + const StyledIcon = cssInterop(icon, { className: { target: "style" } }); + IconNode = ( + + ); + } else { + IconNode = ( + + ); + } + } + return ( <> - {/* Field label — use AppText per project lint rule */} - - {name} - + + {required && *} + + {name} + + - {/* Underlying input. Note: `className` is supported via NativeWind. */} - + {IconNode && ( + + {IconNode} + )} - // Value is fully controlled by the parent when provided - value={props.value} - // Placeholder text shown when `value` is empty - placeholder={props.placeholder} - // Neutral placeholder color; override by passing `placeholderTextColor` - placeholderTextColor="gray" - // Mask input for secrets like passwords/PINs - secureTextEntry={props.secureTextEntry} - // Controlled change handler; prefer stable callbacks from parent components - onChangeText={props.onChangeText} - // Accessibility: default to a descriptive label if not provided - accessibilityLabel={props.accessibilityLabel || `${name} input`} - // Accessibility hint to guide users on expected input - accessibilityHint={props.accessibilityHint || `Enter your ${name}`} - // Default: avoid auto-cap for credentials/usernames. Pass explicitly to change. - autoCapitalize={props.autoCapitalize || "none"} - // Default: use OS default keyboard unless a specific type is requested - keyboardType={props.keyboardType || "default"} - /> + + ); }; diff --git a/src/utils/lib.ts b/src/utils/lib.ts index a5ef193..0102a13 100644 --- a/src/utils/lib.ts +++ b/src/utils/lib.ts @@ -1,6 +1,39 @@ import { clsx, type ClassValue } from "clsx"; import { twMerge } from "tailwind-merge"; +import { LucideIcon } from "lucide-react-native"; export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)); } + +/** + * Type guard to determine whether a value is a Lucide icon component. + * + * Accepts: + * - Function components (common case for Lucide icons) + * - React.forwardRef(...) wrapped components + * - React.memo(...) wrapped components + * + * Notes: + * - We peek at the internal React $$$typeof symbol to differentiate memo/forwardRef. + * - This avoids false positives when an image source (object) is provided instead. + */ +export const isLucideIcon = (val: unknown): val is LucideIcon => { + // Fast-fail nullish values + if (!val) return false; + + // Plain function component (most Lucide exports) + if (typeof val === "function") return true; + + // Object-wrapped components (forwardRef or memo) + if (typeof val === "object") { + const t = (val as any).$$typeof; + // Detect React.forwardRef / React.memo component types + return ( + t === Symbol.for("react.forward_ref") || t === Symbol.for("react.memo") + ); + } + + // Anything else (e.g., numbers, strings) is not a component + return false; +}; From 136209492e94ed1780e8f903728f592dc724fc2f Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Wed, 22 Oct 2025 22:16:22 +0200 Subject: [PATCH 12/13] WIP profile page --- localization/en/settings.json | 3 +- localization/it/settings.json | 3 +- .../settings/(profile)/change-email.tsx | 224 ++++++++++++++++++ .../settings/(profile)/change-password.tsx | 147 ++++++++++++ src/app/(tabs)/settings/(profile)/profile.tsx | 109 +++++++++ src/app/(tabs)/settings/profile.tsx | 27 --- src/components/ui/text-input.tsx | 5 +- 7 files changed, 488 insertions(+), 30 deletions(-) create mode 100644 src/app/(tabs)/settings/(profile)/change-email.tsx create mode 100644 src/app/(tabs)/settings/(profile)/change-password.tsx create mode 100644 src/app/(tabs)/settings/(profile)/profile.tsx delete mode 100644 src/app/(tabs)/settings/profile.tsx diff --git a/localization/en/settings.json b/localization/en/settings.json index a1e3750..f7e2c81 100644 --- a/localization/en/settings.json +++ b/localization/en/settings.json @@ -8,5 +8,6 @@ "income": "Income", "bankAccounts": "Bank Accounts", "saveChanges": "Save Changes", - "changePassword": "Change Password" + "changePassword": "Change Password", + "changeEmail": "Change Email", } diff --git a/localization/it/settings.json b/localization/it/settings.json index 5421aa5..2b6df2c 100644 --- a/localization/it/settings.json +++ b/localization/it/settings.json @@ -8,5 +8,6 @@ "income": "Reddito", "bankAccounts": "Conti Bancari", "saveChanges": "Salva Modifiche", - "changePassword": "Cambia Password" + "changePassword": "Cambia Password", + "changeEmail": "Cambia Email" } diff --git a/src/app/(tabs)/settings/(profile)/change-email.tsx b/src/app/(tabs)/settings/(profile)/change-email.tsx new file mode 100644 index 0000000..0e49281 --- /dev/null +++ b/src/app/(tabs)/settings/(profile)/change-email.tsx @@ -0,0 +1,224 @@ +import { SafeAreaView } from "react-native-safe-area-context"; +import { Header } from "~/components/Header"; +import { NavigationItems } from "~/types"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { TextInput } from "~/components/ui/text-input"; +import Button from "~/components/ui/button"; +import { View, Alert, ActivityIndicator } from "react-native"; +import { useUser } from "@clerk/clerk-expo"; +import AppText from "~/components/ui/AppText"; +import { useRouter } from "expo-router"; + +const ChangeEmailPage = () => { + const { t: tCommon } = useTranslation("common"); + const { t: tSettings } = useTranslation("settings"); + const { user, isLoaded } = useUser(); + const router = useRouter(); + + const [newEmail, setNewEmail] = useState(""); + const [verificationCode, setVerificationCode] = useState(""); + const [isUpdating, setIsUpdating] = useState(false); + const [showVerification, setShowVerification] = useState(false); + const [emailAddressId, setEmailAddressId] = useState(null); + + const handleSendVerification = async () => { + if (!user) { + Alert.alert("Error", "User not found. Please sign in again."); + return; + } + + if (!newEmail) { + Alert.alert("Error", "Please enter a new email address."); + return; + } + + // Basic email validation + const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + if (!emailRegex.test(newEmail)) { + Alert.alert("Error", "Please enter a valid email address."); + return; + } + + // Check if the new email is the same as the current one + if (newEmail === user.primaryEmailAddress?.emailAddress) { + Alert.alert("Error", "This is already your current email address."); + return; + } + + setIsUpdating(true); + try { + const emailAddress = await user.createEmailAddress({ email: newEmail }); + await emailAddress.prepareVerification({ strategy: "email_code" }); + + setEmailAddressId(emailAddress.id); + setShowVerification(true); + + Alert.alert( + "Verification Sent", + `A verification code has been sent to ${newEmail}. Please check your inbox.`, + ); + } catch (error: any) { + console.error("Error sending verification:", error); + Alert.alert( + "Error", + error?.errors?.[0]?.message || + "Failed to send verification email. Please try again.", + ); + } finally { + setIsUpdating(false); + } + }; + + const handleVerifyEmail = async () => { + if (!user || !emailAddressId) { + Alert.alert("Error", "Session expired. Please start over."); + setShowVerification(false); + return; + } + + if (!verificationCode) { + Alert.alert("Error", "Please enter the verification code."); + return; + } + + setIsUpdating(true); + try { + const emailAddress = user.emailAddresses.find( + (e) => e.id === emailAddressId, + ); + + if (!emailAddress) { + throw new Error("Email address not found."); + } + + await emailAddress.attemptVerification({ code: verificationCode }); + + // Set as primary email + await user.update({ + primaryEmailAddressId: emailAddressId, + }); + + Alert.alert( + "Success", + "Your email address has been changed successfully!", + [ + { + text: "OK", + onPress: () => router.back(), + }, + ], + ); + } catch (error: any) { + console.error("Error verifying email:", error); + Alert.alert( + "Error", + error?.errors?.[0]?.message || + "Invalid verification code. Please try again.", + ); + } finally { + setIsUpdating(false); + } + }; + + const handleCancel = () => { + router.back(); + }; + + if (!isLoaded) { + return ( + + + + ); + } + + if (!user) { + return ( + + + Please sign in to change your email. + + + ); + } + + return ( + +
+ + + {tSettings("changeEmail") || "Change Email"} + + + + Current email: {user.primaryEmailAddress?.emailAddress} + + + {!showVerification ? ( + <> + + + + + + + ) : ( + <> + + A verification code has been sent to {newEmail}. Please enter it + below to confirm your new email address. + + + + + + + + + + + )} + + + ); +}; + +export default ChangeEmailPage; diff --git a/src/app/(tabs)/settings/(profile)/change-password.tsx b/src/app/(tabs)/settings/(profile)/change-password.tsx new file mode 100644 index 0000000..29e2c3f --- /dev/null +++ b/src/app/(tabs)/settings/(profile)/change-password.tsx @@ -0,0 +1,147 @@ +import { SafeAreaView } from "react-native-safe-area-context"; +import { Header } from "~/components/Header"; +import { NavigationItems } from "~/types"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { TextInput } from "~/components/ui/text-input"; +import Button from "~/components/ui/button"; +import { View, Alert, ActivityIndicator } from "react-native"; +import { useUser } from "@clerk/clerk-expo"; +import AppText from "~/components/ui/AppText"; +import { useRouter } from "expo-router"; + +const ChangePasswordPage = () => { + const { t: tSettings } = useTranslation("settings"); + const { user, isLoaded } = useUser(); + const router = useRouter(); + + const [currentPassword, setCurrentPassword] = useState(""); + const [newPassword, setNewPassword] = useState(""); + const [confirmPassword, setConfirmPassword] = useState(""); + const [isUpdating, setIsUpdating] = useState(false); + + const handleChangePassword = async () => { + if (!user) { + Alert.alert("Error", "User not found. Please sign in again."); + return; + } + + // Validation + if (!currentPassword || !newPassword || !confirmPassword) { + Alert.alert("Error", "Please fill in all fields."); + return; + } + + if (newPassword !== confirmPassword) { + Alert.alert("Error", "New passwords do not match."); + return; + } + + if (newPassword.length < 8) { + Alert.alert("Error", "Password must be at least 8 characters long."); + return; + } + + setIsUpdating(true); + try { + await user.updatePassword({ + currentPassword: currentPassword, + newPassword: newPassword, + }); + + Alert.alert("Success", "Password updated successfully!", [ + { + text: "OK", + onPress: () => router.back(), + }, + ]); + + // Clear fields + setCurrentPassword(""); + setNewPassword(""); + setConfirmPassword(""); + } catch (error: any) { + console.error("Error updating password:", error); + const errorMessage = + error?.errors?.[0]?.message || + "Failed to update password. Please check your current password and try again."; + Alert.alert("Error", errorMessage); + } finally { + setIsUpdating(false); + } + }; + + if (!isLoaded) { + return ( + + + + ); + } + + if (!user) { + return ( + + + Please sign in to change your password. + + + ); + } + + return ( + +
+ + + {tSettings("changePassword")} + + + + + + + + + + Password must be at least 8 characters long + + + + + + + + ); +}; + +export default ChangePasswordPage; diff --git a/src/app/(tabs)/settings/(profile)/profile.tsx b/src/app/(tabs)/settings/(profile)/profile.tsx new file mode 100644 index 0000000..dafb965 --- /dev/null +++ b/src/app/(tabs)/settings/(profile)/profile.tsx @@ -0,0 +1,109 @@ +import { SafeAreaView } from "react-native-safe-area-context"; +import { Header } from "~/components/Header"; +import { NavigationItems } from "~/types"; +import React, { useState, useEffect } from "react"; +import { useTranslation } from "react-i18next"; +import { TextInput } from "~/components/ui/text-input"; +import Button from "~/components/ui/button"; +import { View, Alert, ActivityIndicator } from "react-native"; +import { useUser } from "@clerk/clerk-expo"; +import AppText from "~/components/ui/AppText"; + +const ProfilePage = () => { + const { t: tCommon } = useTranslation("common"); + const { t: tSettings } = useTranslation("settings"); + const { user, isLoaded } = useUser(); + + const [firstName, setFirstName] = useState(""); + const [lastName, setLastName] = useState(""); + const [email, setEmail] = useState(""); + const [isUpdating, setIsUpdating] = useState(false); + + // Load user data when component mounts + useEffect(() => { + if (user) { + setFirstName(user.firstName || ""); + setLastName(user.lastName || ""); + setEmail(user.primaryEmailAddress?.emailAddress || ""); + } + }, [user]); + + const handleSaveChanges = async () => { + if (!user) { + Alert.alert("Error", "User not found. Please sign in again."); + return; + } + + setIsUpdating(true); + try { + await user.update({ + firstName: firstName, + lastName: lastName, + }); + + Alert.alert("Success", "Profile updated successfully!"); + } catch (error) { + console.error("Error updating profile:", error); + Alert.alert("Error", "Failed to update profile. Please try again."); + } finally { + setIsUpdating(false); + } + }; + + if (!isLoaded) { + return ( + + + + ); + } + + if (!user) { + return ( + + + Please sign in to view your profile. + + + ); + } + + return ( + +
+ + + + + + + + + ); +}; + +export default ProfilePage; diff --git a/src/app/(tabs)/settings/profile.tsx b/src/app/(tabs)/settings/profile.tsx deleted file mode 100644 index e79e1ed..0000000 --- a/src/app/(tabs)/settings/profile.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { SafeAreaView } from "react-native-safe-area-context"; -import { Header } from "~/components/Header"; -import { NavigationItems } from "~/types"; -import React from "react"; -import { useTranslation } from "react-i18next"; -import { TextInput } from "~/components/ui/text-input"; -import Button from "~/components/ui/button"; - -const ProfilePage = () => { - const { t: tCommon } = useTranslation("common"); - const { t: tSettings } = useTranslation("settings"); - - return ( - -
- - - - - - - ); -}; - -export default ProfilePage; diff --git a/src/components/ui/text-input.tsx b/src/components/ui/text-input.tsx index 767cb13..d6b1ee4 100644 --- a/src/components/ui/text-input.tsx +++ b/src/components/ui/text-input.tsx @@ -24,7 +24,10 @@ import { cssInterop } from "nativewind"; * - `name`: Visible field label and base for accessibility labels. * - `required`: If true, shows an asterisk (*) in the accent colour next to the label. * - `icon`: Optional icon (LucideIcon or ImageSourcePropType) to display inside the input. - * - `...TextInputProps`: Forwarded to RN TextInput. Explicit props override defaults. + * - `className`: + * Additional Tailwind classes for the input container to merge with the default NOT applying to the input itself. + * - `...TextInputProps`: Forwarded to RN TextInput. + * Explicit props override defaults. */ export const TextInput = ({ name, From 61451978fcf0750ea1b553acab79fe213e66d739 Mon Sep 17 00:00:00 2001 From: FleetAdmiralJakob Date: Thu, 23 Oct 2025 14:26:51 +0200 Subject: [PATCH 13/13] format correctly --- localization/en/settings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/localization/en/settings.json b/localization/en/settings.json index f7e2c81..ab8c4fa 100644 --- a/localization/en/settings.json +++ b/localization/en/settings.json @@ -9,5 +9,5 @@ "bankAccounts": "Bank Accounts", "saveChanges": "Save Changes", "changePassword": "Change Password", - "changeEmail": "Change Email", + "changeEmail": "Change Email" }