Skip to content
Open
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"java.compile.nullAnalysis.mode": "automatic"
"java.compile.nullAnalysis.mode": "automatic",
"cSpell.words": ["Verticborder"]
}
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"@neondatabase/serverless": "^1.0.0",
"@react-native-community/datetimepicker": "8.4.1",
"@react-navigation/bottom-tabs": "^7.2.0",
"@react-navigation/native": "^7.1.17",
"@t3-oss/env-core": "^0.13.6",
"@tanstack/react-query": "^5.75.7",
"@trpc/client": "^11.1.2",
Expand Down Expand Up @@ -94,5 +95,5 @@
"typescript": "^5.3.3"
},
"private": true,
"packageManager": "pnpm@10.15.1+sha512.34e538c329b5553014ca8e8f4535997f96180a1d0f614339357449935350d924e22f8614682191264ec33d1462ac21561aff97f6bb18065351c162c7e8f6de67"
"packageManager": "pnpm@10.17.1+sha512.17c560fca4867ae9473a3899ad84a88334914f379be46d455cbf92e5cf4b39d34985d452d2583baf19967fa76cb5c17bc9e245529d0b98745721aa7200ecaf7a"
}
3,102 changes: 1,563 additions & 1,539 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions pnpm-workspace.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
packages:
- "src/*"
- "."

onlyBuiltDependencies:
Expand Down
9 changes: 6 additions & 3 deletions src/app/dashboard/banking/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { mockDashboardData } from "~/data/mockData";
import { SectionHeader } from "~/components/SectionHeader";
import { AccountItem } from "~/components/AccountItem";
import { useTranslation } from "react-i18next";
import { Header } from "~/components/Header"; // ✅ keep header

const Dashboard = () => {
const { colors, isDark } = useTheme();
Expand All @@ -28,6 +29,9 @@ const Dashboard = () => {
/>

<ScrollView className="flex-1 p-8" showsVerticalScrollIndicator={false}>
{/* Header */}
<Header title={`${mockDashboardData.user.name} - Dashboard`} />

{/* Cards Section */}
{/* <SectionHeader title="Cards" delay={100} />
<SectionHeader title="Debitcard" size="medium" delay={150} />
Expand Down Expand Up @@ -79,7 +83,6 @@ const Dashboard = () => {
/>
))}

{/* Safe Accounts Section */}
<SectionHeader title={t("dashboardSafeAccounts")} delay={1000} />
{mockDashboardData.accounts.safe.map((account, index) => (
<AccountItem
Expand All @@ -92,15 +95,15 @@ const Dashboard = () => {
/>
))}

<SectionHeader title={t("sharedFunds")} delay={1000} />
<SectionHeader title={t("sharedFunds")} delay={1200} />
{mockDashboardData.sharedFunds && (
<AccountItem
key={mockDashboardData.sharedFunds.id}
icon={mockDashboardData.sharedFunds.icon}
name={mockDashboardData.sharedFunds.title}
arrow={mockDashboardData.sharedFunds.arrow}
iconWrapped
delay={1100}
delay={1300}
onPress={() =>
handleAccountPress(
mockDashboardData.sharedFunds.title,
Expand Down
47 changes: 47 additions & 0 deletions src/app/profile/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from "react";
import { ThemeProvider } from "~/contexts/ThemeContext";
import { ProfileScreen } from "~/components/profile/ProfileScreen";
import { LimitsScreen } from "~/components/profile/Limits";
import { IncomeScreen } from "~/components/profile/Income";
import { NotificationScreen } from "~/components/profile/Notification";
import { UserProfileScreen } from "~/components/profile/UserProfile";
import { BankAccountsScreen } from "~/components/profile/BankAccounts";

const ProfileAppContent: React.FC = () => {
const [currentScreen, setCurrentScreen] = React.useState("Profile");

const handleNavigate = (screen: string) => {
setCurrentScreen(screen);
};

const renderScreen = () => {
switch (currentScreen) {
case "Profile":
return <ProfileScreen onNavigate={handleNavigate} />;
case "Limits":
return <LimitsScreen onNavigate={handleNavigate} />;
case "Income":
return <IncomeScreen onNavigate={handleNavigate} />;
case "Notification":
return <NotificationScreen onNavigate={handleNavigate} />;
case "UserProfile":
return <UserProfileScreen onNavigate={handleNavigate} />;
case "BankAccounts":
return <BankAccountsScreen onNavigate={handleNavigate} />;
default:
return <ProfileScreen onNavigate={handleNavigate} />;
}
};

return renderScreen();
};

const ProfileApp: React.FC = () => {
return (
<ThemeProvider>
<ProfileAppContent />
</ThemeProvider>
);
};

export default ProfileApp;
Binary file added src/assets/Icons/Y.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/arrow.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/bank_account.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/banking.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/entertainment.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/income.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/insights.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/key-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/key.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/limits.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/netflix.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/notification.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/plus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/profile.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/rent.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/assets/Icons/settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 22 additions & 0 deletions src/components/AccountItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,27 @@ export const AccountItem: React.FC<AccountItemProps> = ({

const Component = onPress ? TouchableOpacity : View;

const IconWrapper = ({
children,
H_size = 40,
W_size = 60,
}: {
children: React.ReactNode;
H_size?: number;
W_size?: number;
}) => (
<View
className="items-center justify-center rounded-2xl"
style={{
width: W_size,
height: H_size,
backgroundColor: colors.border,
}}
>
{children}
</View>
);

return (
<Animated.View
entering={FadeInDown.delay(delay).springify()}
Expand Down Expand Up @@ -64,6 +85,7 @@ export const AccountItem: React.FC<AccountItemProps> = ({
{amount}
</AppText>
)}

{arrow && (
<View className="ml-2">
{typeof arrow === "string" ? (
Expand Down
73 changes: 73 additions & 0 deletions src/components/BottomNavigation.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import type React from "react";
import { View, TouchableOpacity } from "react-native";
import { useTheme } from "~/contexts/ThemeContext";
import AppText from "./ui/AppText";
import AppImage from "~/components/ui/AppImage";

interface BottomNavigationProps {
activeTab: "Banking" | "Insights" | "Settings";
onTabPress: (tab: "Banking" | "Insights" | "Settings") => void;
}

export const BottomNavigation: React.FC<BottomNavigationProps> = ({
activeTab,
onTabPress,
}) => {
const { colors } = useTheme();

const TabItem = ({
tab,
label,
iconSource,
}: {
tab: "Banking" | "Insights" | "Settings";
label: string;
iconSource: any;
}) => {
const isActive = activeTab === tab;
return (
<TouchableOpacity
className="flex-1 items-center py-3"
onPress={() => onTabPress(tab)}
>
<AppImage
source={iconSource}
className="h-5 w-5"
resizeMode="contain"
/>
<AppText
className="mt-1 text-xs"
style={{ color: isActive ? colors.text : colors.text }}
>
{label}
</AppText>
</TouchableOpacity>
);
};

return (
<View
className="flex-row border-t pb-3"
style={{
borderTopColor: colors.border,
backgroundColor: colors.cardBackground,
}}
>
<TabItem
tab="Banking"
label="Banking"
iconSource={require("~/assets/Icons/banking.png")}
/>
<TabItem
tab="Insights"
label="Insights"
iconSource={require("~/assets/Icons/insights.png")}
/>
<TabItem
tab="Settings"
label="Settings"
iconSource={require("~/assets/Icons/settings.png")}
/>
</View>
);
};
39 changes: 39 additions & 0 deletions src/components/Button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React from "react";
import { TouchableOpacity } from "react-native";
import AppText from "./ui/AppText";

type ButtonProps = {
title: string;
onPress: () => void;
className?: string;
textClassName?: string;
disabled?: boolean;
};

export default function Button({
title,
onPress,
className = "",
textClassName = "",
disabled = false,
}: ButtonProps) {
return (
<TouchableOpacity
onPress={onPress}
disabled={disabled}
className={`items-center justify-center rounded-xl px-4 py-3 ${
disabled ? "bg-gray-400" : "bg-blue-600"
} ${className}`}
>
<AppText
semibold
className={`text-base ${
disabled ? "text-gray-200" : "text-white"
} ${textClassName}`}
style={{ color: undefined }}
>
{title}
</AppText>
</TouchableOpacity>
);
}
53 changes: 53 additions & 0 deletions src/components/FloatingActionButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import type React from "react";
import { TouchableOpacity } from "react-native";
import Animated, {
useSharedValue,
useAnimatedStyle,
withSpring,
} from "react-native-reanimated";
import { useTheme } from "~/contexts/ThemeContext";
import AppImage from "~/components/ui/AppImage";

interface FloatingActionButtonProps {
onPress: () => void;
}

export const FloatingActionButton: React.FC<FloatingActionButtonProps> = ({
onPress,
}) => {
const { colors } = useTheme();
const scale = useSharedValue(1);

const animatedStyle = useAnimatedStyle(() => ({
transform: [{ scale: scale.value }],
}));

const handlePressIn = () => {
scale.value = withSpring(0.95);
};

const handlePressOut = () => {
scale.value = withSpring(1);
};

return (
<Animated.View
className="absolute bottom-24 right-5 h-12 w-12 items-center justify-center rounded-full shadow-lg"
style={[{ backgroundColor: colors.primary }, animatedStyle]}
>
<TouchableOpacity
className="h-full w-full items-center justify-center"
onPress={onPress}
onPressIn={handlePressIn}
onPressOut={handlePressOut}
activeOpacity={1}
>
<AppImage
source={require("~/assets/Icons/plus.png")}
className="h-3 w-3"
resizeMode="contain"
/>
</TouchableOpacity>
</Animated.View>
);
};
13 changes: 7 additions & 6 deletions src/components/Header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,12 @@ import { ProfileSectionType } from "~/types";
const AnimatedView = Animated.createAnimatedComponent(Animated.View);

interface HeaderProps {
name: string;
type: ProfileSectionType; // enum key, not a translated string
title?: string; // keep backward compatibility
name?: string;
type?: ProfileSectionType; // enum key for translation
}

export const Header: React.FC<HeaderProps> = ({ name, type }) => {
export const Header: React.FC<HeaderProps> = ({ title, name, type }) => {
const { colors } = useTheme();
const headerOpacity = useSharedValue(0);
const { t } = useTranslation();
Expand All @@ -32,8 +33,8 @@ export const Header: React.FC<HeaderProps> = ({ name, type }) => {

return (
<AnimatedView
className="bg-black border-gray-800 w-full flex-row items-center justify-center gap-3 space-x-2 rounded-lg border-b px-4 py-3"
style={headerAnimatedStyle}
className="border-gray-800 absolute left-0 right-0 top-0 flex-row items-center justify-center gap-3 border-b px-4 py-4"
style={[headerAnimatedStyle, { backgroundColor: colors.cardBackground }]}
>
<AppImage
source={require("../assets/images/avatar.png")}
Expand All @@ -44,7 +45,7 @@ export const Header: React.FC<HeaderProps> = ({ name, type }) => {
className="text-center text-sm"
style={{ color: colors.text }}
>
{name} - {t(type)}
{title ?? `${name ?? ""}${type ? ` - ${t(type)}` : ""}`}
</AppText>
</AnimatedView>
);
Expand Down
Loading