fixing frist
This commit is contained in:
parent
1ec97b320e
commit
dbb4abc326
@ -1,35 +1,49 @@
|
|||||||
import { Tabs } from 'expo-router';
|
import { Tabs } from "expo-router";
|
||||||
import React from 'react';
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
|
||||||
import { HapticTab } from '@/components/haptic-tab';
|
|
||||||
import { IconSymbol } from '@/components/ui/icon-symbol';
|
|
||||||
import { Colors } from '@/constants/theme';
|
|
||||||
import { useColorScheme } from '@/hooks/use-color-scheme';
|
|
||||||
|
|
||||||
export default function TabLayout() {
|
export default function TabLayout() {
|
||||||
const colorScheme = useColorScheme();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tabs
|
<Tabs
|
||||||
screenOptions={{
|
screenOptions={{
|
||||||
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
|
|
||||||
headerShown: false,
|
headerShown: false,
|
||||||
tabBarButton: HapticTab,
|
tabBarActiveTintColor: "#2563EB",
|
||||||
}}>
|
tabBarInactiveTintColor: "#9CA3AF",
|
||||||
|
tabBarStyle: {
|
||||||
|
height: 65,
|
||||||
|
paddingBottom: 8,
|
||||||
|
paddingTop: 8,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="index"
|
name="index"
|
||||||
options={{
|
options={{
|
||||||
title: 'Home',
|
title: "Home",
|
||||||
tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
|
tabBarIcon: ({ color, size }) => (
|
||||||
|
<Ionicons name="home" size={size} color={color} />
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Tabs.Screen
|
<Tabs.Screen
|
||||||
name="explore"
|
name="history"
|
||||||
options={{
|
options={{
|
||||||
title: 'Explore',
|
title: "History",
|
||||||
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
|
tabBarIcon: ({ color, size }) => (
|
||||||
|
<Ionicons name="time" size={size} color={color} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Tabs.Screen
|
||||||
|
name="profile"
|
||||||
|
options={{
|
||||||
|
title: "Profile",
|
||||||
|
tabBarIcon: ({ color, size }) => (
|
||||||
|
<Ionicons name="person" size={size} color={color} />
|
||||||
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Tabs>
|
</Tabs>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -1,112 +0,0 @@
|
|||||||
import { Image } from 'expo-image';
|
|
||||||
import { Platform, StyleSheet } from 'react-native';
|
|
||||||
|
|
||||||
import { Collapsible } from '@/components/ui/collapsible';
|
|
||||||
import { ExternalLink } from '@/components/external-link';
|
|
||||||
import ParallaxScrollView from '@/components/parallax-scroll-view';
|
|
||||||
import { ThemedText } from '@/components/themed-text';
|
|
||||||
import { ThemedView } from '@/components/themed-view';
|
|
||||||
import { IconSymbol } from '@/components/ui/icon-symbol';
|
|
||||||
import { Fonts } from '@/constants/theme';
|
|
||||||
|
|
||||||
export default function TabTwoScreen() {
|
|
||||||
return (
|
|
||||||
<ParallaxScrollView
|
|
||||||
headerBackgroundColor={{ light: '#D0D0D0', dark: '#353636' }}
|
|
||||||
headerImage={
|
|
||||||
<IconSymbol
|
|
||||||
size={310}
|
|
||||||
color="#808080"
|
|
||||||
name="chevron.left.forwardslash.chevron.right"
|
|
||||||
style={styles.headerImage}
|
|
||||||
/>
|
|
||||||
}>
|
|
||||||
<ThemedView style={styles.titleContainer}>
|
|
||||||
<ThemedText
|
|
||||||
type="title"
|
|
||||||
style={{
|
|
||||||
fontFamily: Fonts.rounded,
|
|
||||||
}}>
|
|
||||||
Explore
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedText>This app includes example code to help you get started.</ThemedText>
|
|
||||||
<Collapsible title="File-based routing">
|
|
||||||
<ThemedText>
|
|
||||||
This app has two screens:{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> and{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">app/(tabs)/explore.tsx</ThemedText>
|
|
||||||
</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
The layout file in <ThemedText type="defaultSemiBold">app/(tabs)/_layout.tsx</ThemedText>{' '}
|
|
||||||
sets up the tab navigator.
|
|
||||||
</ThemedText>
|
|
||||||
<ExternalLink href="https://docs.expo.dev/router/introduction">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Android, iOS, and web support">
|
|
||||||
<ThemedText>
|
|
||||||
You can open this project on Android, iOS, and the web. To open the web version, press{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">w</ThemedText> in the terminal running this project.
|
|
||||||
</ThemedText>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Images">
|
|
||||||
<ThemedText>
|
|
||||||
For static images, you can use the <ThemedText type="defaultSemiBold">@2x</ThemedText> and{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to provide files for
|
|
||||||
different screen densities
|
|
||||||
</ThemedText>
|
|
||||||
<Image
|
|
||||||
source={require('@/assets/images/react-logo.png')}
|
|
||||||
style={{ width: 100, height: 100, alignSelf: 'center' }}
|
|
||||||
/>
|
|
||||||
<ExternalLink href="https://reactnative.dev/docs/images">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Light and dark mode components">
|
|
||||||
<ThemedText>
|
|
||||||
This template has light and dark mode support. The{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">useColorScheme()</ThemedText> hook lets you inspect
|
|
||||||
what the user's current color scheme is, and so you can adjust UI colors accordingly.
|
|
||||||
</ThemedText>
|
|
||||||
<ExternalLink href="https://docs.expo.dev/develop/user-interface/color-themes/">
|
|
||||||
<ThemedText type="link">Learn more</ThemedText>
|
|
||||||
</ExternalLink>
|
|
||||||
</Collapsible>
|
|
||||||
<Collapsible title="Animations">
|
|
||||||
<ThemedText>
|
|
||||||
This template includes an example of an animated component. The{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">components/HelloWave.tsx</ThemedText> component uses
|
|
||||||
the powerful{' '}
|
|
||||||
<ThemedText type="defaultSemiBold" style={{ fontFamily: Fonts.mono }}>
|
|
||||||
react-native-reanimated
|
|
||||||
</ThemedText>{' '}
|
|
||||||
library to create a waving hand animation.
|
|
||||||
</ThemedText>
|
|
||||||
{Platform.select({
|
|
||||||
ios: (
|
|
||||||
<ThemedText>
|
|
||||||
The <ThemedText type="defaultSemiBold">components/ParallaxScrollView.tsx</ThemedText>{' '}
|
|
||||||
component provides a parallax effect for the header image.
|
|
||||||
</ThemedText>
|
|
||||||
),
|
|
||||||
})}
|
|
||||||
</Collapsible>
|
|
||||||
</ParallaxScrollView>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
|
||||||
headerImage: {
|
|
||||||
color: '#808080',
|
|
||||||
bottom: -90,
|
|
||||||
left: -35,
|
|
||||||
position: 'absolute',
|
|
||||||
},
|
|
||||||
titleContainer: {
|
|
||||||
flexDirection: 'row',
|
|
||||||
gap: 8,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
166
app/(tabs)/history.tsx
Normal file
166
app/(tabs)/history.tsx
Normal file
@ -0,0 +1,166 @@
|
|||||||
|
import { View, Text, StyleSheet, ScrollView } from "react-native";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
|
||||||
|
export default function HistoryScreen() {
|
||||||
|
const transactions = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
title: "Gaji Bulanan",
|
||||||
|
amount: "Rp 5.000.000",
|
||||||
|
type: "income",
|
||||||
|
date: "12 April 2026",
|
||||||
|
icon: "wallet",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
title: "Makan Siang",
|
||||||
|
amount: "Rp 50.000",
|
||||||
|
type: "expense",
|
||||||
|
date: "13 April 2026",
|
||||||
|
icon: "restaurant",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 3,
|
||||||
|
title: "Transport",
|
||||||
|
amount: "Rp 20.000",
|
||||||
|
type: "expense",
|
||||||
|
date: "14 April 2026",
|
||||||
|
icon: "car",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 4,
|
||||||
|
title: "Freelance Project",
|
||||||
|
amount: "Rp 1.500.000",
|
||||||
|
type: "income",
|
||||||
|
date: "15 April 2026",
|
||||||
|
icon: "briefcase",
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ScrollView style={styles.container}>
|
||||||
|
<Text style={styles.title}>Riwayat Transaksi</Text>
|
||||||
|
<Text style={styles.subtitle}>
|
||||||
|
Semua pemasukan dan pengeluaran kamu
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
{transactions.map((item) => (
|
||||||
|
<View key={item.id} style={styles.card}>
|
||||||
|
<View style={styles.leftSection}>
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.iconContainer,
|
||||||
|
item.type === "income"
|
||||||
|
? styles.incomeBg
|
||||||
|
: styles.expenseBg,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Ionicons
|
||||||
|
name={item.icon as any}
|
||||||
|
size={22}
|
||||||
|
color={item.type === "income" ? "#16A34A" : "#DC2626"}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View>
|
||||||
|
<Text style={styles.transactionTitle}>{item.title}</Text>
|
||||||
|
<Text style={styles.transactionDate}>{item.date}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text
|
||||||
|
style={[
|
||||||
|
styles.amount,
|
||||||
|
item.type === "income"
|
||||||
|
? styles.incomeText
|
||||||
|
: styles.expenseText,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
{item.type === "income" ? "+" : "-"} {item.amount}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</ScrollView>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "#F5F7FA",
|
||||||
|
padding: 20,
|
||||||
|
paddingTop: 55,
|
||||||
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
fontSize: 30,
|
||||||
|
fontWeight: "bold",
|
||||||
|
marginBottom: 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
subtitle: {
|
||||||
|
fontSize: 15,
|
||||||
|
color: "#666",
|
||||||
|
marginBottom: 28,
|
||||||
|
},
|
||||||
|
|
||||||
|
card: {
|
||||||
|
backgroundColor: "#FFFFFF",
|
||||||
|
borderRadius: 18,
|
||||||
|
padding: 18,
|
||||||
|
marginBottom: 16,
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
alignItems: "center",
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOpacity: 0.05,
|
||||||
|
shadowRadius: 6,
|
||||||
|
elevation: 3,
|
||||||
|
},
|
||||||
|
|
||||||
|
leftSection: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 14,
|
||||||
|
},
|
||||||
|
|
||||||
|
iconContainer: {
|
||||||
|
width: 48,
|
||||||
|
height: 48,
|
||||||
|
borderRadius: 14,
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
|
||||||
|
incomeBg: {
|
||||||
|
backgroundColor: "#DCFCE7",
|
||||||
|
},
|
||||||
|
|
||||||
|
expenseBg: {
|
||||||
|
backgroundColor: "#FEE2E2",
|
||||||
|
},
|
||||||
|
|
||||||
|
transactionTitle: {
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "600",
|
||||||
|
marginBottom: 4,
|
||||||
|
},
|
||||||
|
|
||||||
|
transactionDate: {
|
||||||
|
fontSize: 13,
|
||||||
|
color: "#777",
|
||||||
|
},
|
||||||
|
|
||||||
|
amount: {
|
||||||
|
fontSize: 15,
|
||||||
|
fontWeight: "700",
|
||||||
|
},
|
||||||
|
|
||||||
|
incomeText: {
|
||||||
|
color: "#16A34A",
|
||||||
|
},
|
||||||
|
|
||||||
|
expenseText: {
|
||||||
|
color: "#DC2626",
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,98 +1,136 @@
|
|||||||
import { Image } from 'expo-image';
|
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
|
||||||
import { Platform, StyleSheet } from 'react-native';
|
import { router } from "expo-router";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
import { HelloWave } from '@/components/hello-wave';
|
|
||||||
import ParallaxScrollView from '@/components/parallax-scroll-view';
|
|
||||||
import { ThemedText } from '@/components/themed-text';
|
|
||||||
import { ThemedView } from '@/components/themed-view';
|
|
||||||
import { Link } from 'expo-router';
|
|
||||||
|
|
||||||
export default function HomeScreen() {
|
export default function HomeScreen() {
|
||||||
return (
|
return (
|
||||||
<ParallaxScrollView
|
<View style={styles.container}>
|
||||||
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
|
<Text style={styles.title}>Tabungan H.Hadi</Text>
|
||||||
headerImage={
|
<Text style={styles.subtitle}>Kelola pemasukan & pengeluaranmu untuk nikah </Text>
|
||||||
<Image
|
|
||||||
source={require('@/assets/images/partial-react-logo.png')}
|
|
||||||
style={styles.reactLogo}
|
|
||||||
/>
|
|
||||||
}>
|
|
||||||
<ThemedView style={styles.titleContainer}>
|
|
||||||
<ThemedText type="title">Welcome!</ThemedText>
|
|
||||||
<HelloWave />
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedView style={styles.stepContainer}>
|
|
||||||
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
|
|
||||||
<ThemedText>
|
|
||||||
Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes.
|
|
||||||
Press{' '}
|
|
||||||
<ThemedText type="defaultSemiBold">
|
|
||||||
{Platform.select({
|
|
||||||
ios: 'cmd + d',
|
|
||||||
android: 'cmd + m',
|
|
||||||
web: 'F12',
|
|
||||||
})}
|
|
||||||
</ThemedText>{' '}
|
|
||||||
to open developer tools.
|
|
||||||
</ThemedText>
|
|
||||||
</ThemedView>
|
|
||||||
<ThemedView style={styles.stepContainer}>
|
|
||||||
<Link href="/modal">
|
|
||||||
<Link.Trigger>
|
|
||||||
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
|
|
||||||
</Link.Trigger>
|
|
||||||
<Link.Preview />
|
|
||||||
<Link.Menu>
|
|
||||||
<Link.MenuAction title="Action" icon="cube" onPress={() => alert('Action pressed')} />
|
|
||||||
<Link.MenuAction
|
|
||||||
title="Share"
|
|
||||||
icon="square.and.arrow.up"
|
|
||||||
onPress={() => alert('Share pressed')}
|
|
||||||
/>
|
|
||||||
<Link.Menu title="More" icon="ellipsis">
|
|
||||||
<Link.MenuAction
|
|
||||||
title="Delete"
|
|
||||||
icon="trash"
|
|
||||||
destructive
|
|
||||||
onPress={() => alert('Delete pressed')}
|
|
||||||
/>
|
|
||||||
</Link.Menu>
|
|
||||||
</Link.Menu>
|
|
||||||
</Link>
|
|
||||||
|
|
||||||
<ThemedText>
|
<View style={styles.balanceCard}>
|
||||||
{`Tap the Explore tab to learn more about what's included in this starter app.`}
|
<Text style={styles.balanceLabel}>Total Saldo</Text>
|
||||||
</ThemedText>
|
<Text style={styles.balanceAmount}>Rp 500.000.000.000</Text>
|
||||||
</ThemedView>
|
</View>
|
||||||
<ThemedView style={styles.stepContainer}>
|
|
||||||
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
|
<View style={styles.summaryContainer}>
|
||||||
<ThemedText>
|
<View style={styles.summaryCard}>
|
||||||
{`When you're ready, run `}
|
<Ionicons name="arrow-down-circle" size={30} color="#16A34A" />
|
||||||
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
|
<Text style={styles.summaryTitle}>Income</Text>
|
||||||
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
|
<Text style={styles.summaryAmount}>Rp 7.000.000</Text>
|
||||||
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
|
</View>
|
||||||
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
|
|
||||||
</ThemedText>
|
<View style={styles.summaryCard}>
|
||||||
</ThemedView>
|
<Ionicons name="arrow-up-circle" size={30} color="#DC2626" />
|
||||||
</ParallaxScrollView>
|
<Text style={styles.summaryTitle}>Expense</Text>
|
||||||
|
<Text style={styles.summaryAmount}>Rp 2.000.000</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.buttonIncome}
|
||||||
|
onPress={() => router.push("/add-income")}
|
||||||
|
>
|
||||||
|
<Text style={styles.buttonText}>+ Tambah Pemasukan</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.buttonExpense}
|
||||||
|
onPress={() => router.push("/add-expense")}
|
||||||
|
>
|
||||||
|
<Text style={styles.buttonText}>- Tambah Pengeluaran</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const styles = StyleSheet.create({
|
const styles = StyleSheet.create({
|
||||||
titleContainer: {
|
container: {
|
||||||
flexDirection: 'row',
|
flex: 1,
|
||||||
alignItems: 'center',
|
backgroundColor: "#F5F7FA",
|
||||||
gap: 8,
|
padding: 24,
|
||||||
|
paddingTop: 60,
|
||||||
},
|
},
|
||||||
stepContainer: {
|
|
||||||
gap: 8,
|
title: {
|
||||||
|
fontSize: 30,
|
||||||
|
fontWeight: "bold",
|
||||||
marginBottom: 8,
|
marginBottom: 8,
|
||||||
},
|
},
|
||||||
reactLogo: {
|
|
||||||
height: 178,
|
subtitle: {
|
||||||
width: 290,
|
fontSize: 16,
|
||||||
bottom: 0,
|
color: "#666",
|
||||||
left: 0,
|
marginBottom: 28,
|
||||||
position: 'absolute',
|
|
||||||
},
|
},
|
||||||
});
|
|
||||||
|
balanceCard: {
|
||||||
|
backgroundColor: "#2563EB",
|
||||||
|
borderRadius: 20,
|
||||||
|
padding: 24,
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
|
||||||
|
balanceLabel: {
|
||||||
|
color: "#E0E7FF",
|
||||||
|
fontSize: 16,
|
||||||
|
marginBottom: 8,
|
||||||
|
},
|
||||||
|
|
||||||
|
balanceAmount: {
|
||||||
|
color: "#FFFFFF",
|
||||||
|
fontSize: 32,
|
||||||
|
fontWeight: "bold",
|
||||||
|
},
|
||||||
|
|
||||||
|
summaryContainer: {
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "space-between",
|
||||||
|
marginBottom: 32,
|
||||||
|
},
|
||||||
|
|
||||||
|
summaryCard: {
|
||||||
|
backgroundColor: "#FFFFFF",
|
||||||
|
width: "48%",
|
||||||
|
padding: 20,
|
||||||
|
borderRadius: 16,
|
||||||
|
alignItems: "center",
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOpacity: 0.05,
|
||||||
|
shadowRadius: 6,
|
||||||
|
elevation: 3,
|
||||||
|
},
|
||||||
|
|
||||||
|
summaryTitle: {
|
||||||
|
marginTop: 10,
|
||||||
|
fontSize: 15,
|
||||||
|
color: "#666",
|
||||||
|
},
|
||||||
|
|
||||||
|
summaryAmount: {
|
||||||
|
marginTop: 6,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "600",
|
||||||
|
},
|
||||||
|
|
||||||
|
buttonIncome: {
|
||||||
|
backgroundColor: "#16A34A",
|
||||||
|
paddingVertical: 16,
|
||||||
|
borderRadius: 14,
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
|
||||||
|
buttonExpense: {
|
||||||
|
backgroundColor: "#DC2626",
|
||||||
|
paddingVertical: 16,
|
||||||
|
borderRadius: 14,
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
|
||||||
|
buttonText: {
|
||||||
|
color: "#FFFFFF",
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "600",
|
||||||
|
},
|
||||||
|
});
|
||||||
137
app/(tabs)/profile.tsx
Normal file
137
app/(tabs)/profile.tsx
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
import { View, Text, StyleSheet, TouchableOpacity } from "react-native";
|
||||||
|
import { Ionicons } from "@expo/vector-icons";
|
||||||
|
|
||||||
|
export default function ProfileScreen() {
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{/* Header */}
|
||||||
|
<Text style={styles.title}>Profile</Text>
|
||||||
|
<Text style={styles.subtitle}>Informasi pengguna aplikasi</Text>
|
||||||
|
|
||||||
|
{/* Profile Card */}
|
||||||
|
<View style={styles.profileCard}>
|
||||||
|
<View style={styles.avatar}>
|
||||||
|
<Ionicons name="person" size={40} color="#FFFFFF" />
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<Text style={styles.name}>Fahrul</Text>
|
||||||
|
<Text style={styles.email}>fahrul@email.com</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Info Card */}
|
||||||
|
<View style={styles.infoCard}>
|
||||||
|
<View style={styles.infoRow}>
|
||||||
|
<Ionicons name="wallet-outline" size={22} color="#2563EB" />
|
||||||
|
<Text style={styles.infoText}>Aplikasi: Hitung Duit</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.infoRow}>
|
||||||
|
<Ionicons name="phone-portrait-outline" size={22} color="#2563EB" />
|
||||||
|
<Text style={styles.infoText}>Versi: 1.0.0</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View style={styles.infoRow}>
|
||||||
|
<Ionicons name="school-outline" size={22} color="#2563EB" />
|
||||||
|
<Text style={styles.infoText}>Project Tugas Mobile Programming</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/* Logout Button */}
|
||||||
|
<TouchableOpacity style={styles.logoutButton}>
|
||||||
|
<Ionicons name="log-out-outline" size={20} color="#FFFFFF" />
|
||||||
|
<Text style={styles.logoutText}>Logout</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "#F5F7FA",
|
||||||
|
padding: 24,
|
||||||
|
paddingTop: 60,
|
||||||
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
fontSize: 30,
|
||||||
|
fontWeight: "bold",
|
||||||
|
marginBottom: 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
subtitle: {
|
||||||
|
fontSize: 15,
|
||||||
|
color: "#666",
|
||||||
|
marginBottom: 28,
|
||||||
|
},
|
||||||
|
|
||||||
|
profileCard: {
|
||||||
|
backgroundColor: "#2563EB",
|
||||||
|
borderRadius: 20,
|
||||||
|
padding: 28,
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 24,
|
||||||
|
},
|
||||||
|
|
||||||
|
avatar: {
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
borderRadius: 40,
|
||||||
|
backgroundColor: "rgba(255,255,255,0.25)",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
|
||||||
|
name: {
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: "bold",
|
||||||
|
color: "#FFFFFF",
|
||||||
|
marginBottom: 6,
|
||||||
|
},
|
||||||
|
|
||||||
|
email: {
|
||||||
|
fontSize: 14,
|
||||||
|
color: "#DBEAFE",
|
||||||
|
},
|
||||||
|
|
||||||
|
infoCard: {
|
||||||
|
backgroundColor: "#FFFFFF",
|
||||||
|
borderRadius: 18,
|
||||||
|
padding: 20,
|
||||||
|
marginBottom: 28,
|
||||||
|
shadowColor: "#000",
|
||||||
|
shadowOpacity: 0.05,
|
||||||
|
shadowRadius: 6,
|
||||||
|
elevation: 3,
|
||||||
|
},
|
||||||
|
|
||||||
|
infoRow: {
|
||||||
|
flexDirection: "row",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 12,
|
||||||
|
marginBottom: 18,
|
||||||
|
},
|
||||||
|
|
||||||
|
infoText: {
|
||||||
|
fontSize: 15,
|
||||||
|
color: "#333",
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
logoutButton: {
|
||||||
|
backgroundColor: "#DC2626",
|
||||||
|
paddingVertical: 16,
|
||||||
|
borderRadius: 14,
|
||||||
|
flexDirection: "row",
|
||||||
|
justifyContent: "center",
|
||||||
|
alignItems: "center",
|
||||||
|
gap: 10,
|
||||||
|
},
|
||||||
|
|
||||||
|
logoutText: {
|
||||||
|
color: "#FFFFFF",
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: "600",
|
||||||
|
},
|
||||||
|
});
|
||||||
@ -1,24 +1,16 @@
|
|||||||
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
|
import { Stack } from "expo-router";
|
||||||
import { Stack } from 'expo-router';
|
import { StatusBar } from "expo-status-bar";
|
||||||
import { StatusBar } from 'expo-status-bar';
|
|
||||||
import 'react-native-reanimated';
|
|
||||||
|
|
||||||
import { useColorScheme } from '@/hooks/use-color-scheme';
|
|
||||||
|
|
||||||
export const unstable_settings = {
|
|
||||||
anchor: '(tabs)',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function RootLayout() {
|
export default function RootLayout() {
|
||||||
const colorScheme = useColorScheme();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
|
<>
|
||||||
<Stack>
|
<Stack screenOptions={{ headerShown: false }}>
|
||||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
<Stack.Screen name="(tabs)" />
|
||||||
<Stack.Screen name="modal" options={{ presentation: 'modal', title: 'Modal' }} />
|
<Stack.Screen name="add-income" />
|
||||||
|
<Stack.Screen name="add-expense" />
|
||||||
</Stack>
|
</Stack>
|
||||||
|
|
||||||
<StatusBar style="auto" />
|
<StatusBar style="auto" />
|
||||||
</ThemeProvider>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
97
app/add-expense.tsx
Normal file
97
app/add-expense.tsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableOpacity,
|
||||||
|
} from "react-native";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
|
||||||
|
export default function AddExpenseScreen() {
|
||||||
|
const [amount, setAmount] = useState("");
|
||||||
|
|
||||||
|
const formatRupiah = (value: string) => {
|
||||||
|
const numberString = value.replace(/[^,\d]/g, "");
|
||||||
|
const split = numberString.split(",");
|
||||||
|
const sisa = split[0].length % 3;
|
||||||
|
|
||||||
|
let rupiah = split[0].substring(0, sisa);
|
||||||
|
const ribuan = split[0].substring(sisa).match(/\d{3}/gi);
|
||||||
|
|
||||||
|
if (ribuan) {
|
||||||
|
const separator = sisa ? "." : "";
|
||||||
|
rupiah += separator + ribuan.join(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
return rupiah ? `Rp ${rupiah}` : "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAmountChange = (value: string) => {
|
||||||
|
setAmount(formatRupiah(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>Tambah Pengeluaran</Text>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
placeholder="Nama pengeluaran"
|
||||||
|
style={styles.input}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
placeholder="Jumlah uang"
|
||||||
|
keyboardType="numeric"
|
||||||
|
value={amount}
|
||||||
|
onChangeText={handleAmountChange}
|
||||||
|
style={styles.input}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.button}
|
||||||
|
onPress={() => router.back()}
|
||||||
|
>
|
||||||
|
<Text style={styles.buttonText}>Simpan</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 24,
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: "bold",
|
||||||
|
marginBottom: 24,
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
|
||||||
|
input: {
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "#ddd",
|
||||||
|
padding: 16,
|
||||||
|
borderRadius: 12,
|
||||||
|
marginBottom: 16,
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
|
||||||
|
button: {
|
||||||
|
backgroundColor: "#DC2626",
|
||||||
|
padding: 16,
|
||||||
|
borderRadius: 12,
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
|
||||||
|
buttonText: {
|
||||||
|
color: "#fff",
|
||||||
|
fontWeight: "600",
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
});
|
||||||
97
app/add-income.tsx
Normal file
97
app/add-income.tsx
Normal file
@ -0,0 +1,97 @@
|
|||||||
|
import { useState } from "react";
|
||||||
|
import {
|
||||||
|
View,
|
||||||
|
Text,
|
||||||
|
TextInput,
|
||||||
|
StyleSheet,
|
||||||
|
TouchableOpacity,
|
||||||
|
} from "react-native";
|
||||||
|
import { router } from "expo-router";
|
||||||
|
|
||||||
|
export default function AddIncomeScreen() {
|
||||||
|
const [amount, setAmount] = useState("");
|
||||||
|
|
||||||
|
const formatRupiah = (value: string) => {
|
||||||
|
const numberString = value.replace(/[^,\d]/g, "");
|
||||||
|
const split = numberString.split(",");
|
||||||
|
const sisa = split[0].length % 3;
|
||||||
|
|
||||||
|
let rupiah = split[0].substring(0, sisa);
|
||||||
|
const ribuan = split[0].substring(sisa).match(/\d{3}/gi);
|
||||||
|
|
||||||
|
if (ribuan) {
|
||||||
|
const separator = sisa ? "." : "";
|
||||||
|
rupiah += separator + ribuan.join(".");
|
||||||
|
}
|
||||||
|
|
||||||
|
return rupiah ? `Rp ${rupiah}` : "";
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleAmountChange = (value: string) => {
|
||||||
|
setAmount(formatRupiah(value));
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>Tambah Pemasukan</Text>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
placeholder="Nama pemasukan"
|
||||||
|
style={styles.input}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextInput
|
||||||
|
placeholder="Jumlah uang"
|
||||||
|
keyboardType="numeric"
|
||||||
|
value={amount}
|
||||||
|
onChangeText={handleAmountChange}
|
||||||
|
style={styles.input}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TouchableOpacity
|
||||||
|
style={styles.button}
|
||||||
|
onPress={() => router.back()}
|
||||||
|
>
|
||||||
|
<Text style={styles.buttonText}>Simpan</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
padding: 24,
|
||||||
|
backgroundColor: "#fff",
|
||||||
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
|
||||||
|
title: {
|
||||||
|
fontSize: 28,
|
||||||
|
fontWeight: "bold",
|
||||||
|
marginBottom: 24,
|
||||||
|
textAlign: "center",
|
||||||
|
},
|
||||||
|
|
||||||
|
input: {
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: "#ddd",
|
||||||
|
padding: 16,
|
||||||
|
borderRadius: 12,
|
||||||
|
marginBottom: 16,
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
|
||||||
|
button: {
|
||||||
|
backgroundColor: "#16A34A",
|
||||||
|
padding: 16,
|
||||||
|
borderRadius: 12,
|
||||||
|
alignItems: "center",
|
||||||
|
},
|
||||||
|
|
||||||
|
buttonText: {
|
||||||
|
color: "#fff",
|
||||||
|
fontWeight: "600",
|
||||||
|
fontSize: 16,
|
||||||
|
},
|
||||||
|
});
|
||||||
1
package-lock.json
generated
1
package-lock.json
generated
@ -5811,6 +5811,7 @@
|
|||||||
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
"integrity": "sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"peer": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@rtsao/scc": "^1.1.0",
|
"@rtsao/scc": "^1.1.0",
|
||||||
"array-includes": "^3.1.9",
|
"array-includes": "^3.1.9",
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user