Kantongku: onboarding bersih - sambutan + poin fitur (tanpa form kategori)

This commit is contained in:
Bro
2026-08-16 16:01:26 +07:00
parent 6c9276c7fa
commit c5a853ae94
+54 -203
View File
@@ -2,55 +2,38 @@ import { Ionicons } from '@expo/vector-icons';
import { LinearGradient } from 'expo-linear-gradient'; import { LinearGradient } from 'expo-linear-gradient';
import { useNavigation } from '@react-navigation/native'; import { useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack'; import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import { useState } from 'react'; import { Image, Pressable, ScrollView, StyleSheet, Text, View } from 'react-native';
import {
Alert,
Image,
Pressable,
ScrollView,
StyleSheet,
Text,
TextInput,
View,
} from 'react-native';
import { SafeAreaView } from 'react-native-safe-area-context'; import { SafeAreaView } from 'react-native-safe-area-context';
import { colors } from '../theme/colors'; import { colors } from '../theme/colors';
import { import { setSetting } from '../db/repositories';
addCategory,
getCategories,
setSetting,
} from '../db/repositories';
import type { Category, TransactionType } from '../types';
import type { RootStackParamList } from '../navigation'; import type { RootStackParamList } from '../navigation';
const QUICK_EMOJIS = ['🍜', '🚌', '🛒', '🧾', '💊', '🎬', '💼', '🎁', '🐱', '📱', '🏠', '🎮']; const FEATURES = [
{
icon: 'create-outline' as const,
title: 'Catat dengan cepat',
desc: 'Pemasukan & pengeluaran, langsung terformat rupiah.',
},
{
icon: 'lock-closed-outline' as const,
title: 'Auto-sisih otomatis',
desc: '10% dari setiap pemasukan langsung masuk tabungan.',
},
{
icon: 'phone-portrait-outline' as const,
title: 'Offline & privat',
desc: 'Semua data tersimpan di HP-mu. Tanpa server, tanpa internet.',
},
{
icon: 'trophy-outline' as const,
title: 'Tetap semangat',
desc: 'Target, badge, dan streak yang bikin kamu terus menabung.',
},
];
export function OnboardingScreen() { export function OnboardingScreen() {
const navigation = const navigation =
useNavigation<NativeStackNavigationProp<RootStackParamList>>(); useNavigation<NativeStackNavigationProp<RootStackParamList>>();
const [type, setType] = useState<TransactionType>('expense');
const [name, setName] = useState('');
const [icon, setIcon] = useState('🍜');
const [categories, setCategories] = useState<Category[]>([]);
const [loading, setLoading] = useState(true);
// Muat kategori yang sudah ada (default)
useState(() => {
getCategories().then((c) => {
setCategories(c);
setLoading(false);
});
});
const handleAdd = async () => {
if (!name.trim()) {
Alert.alert('Nama kosong', 'Kasih nama kategorinya dulu ya.');
return;
}
await addCategory({ name: name.trim(), type, icon });
setName('');
setCategories(await getCategories());
};
const handleStart = async () => { const handleStart = async () => {
await setSetting('onboarded', '1'); await setSetting('onboarded', '1');
@@ -73,92 +56,25 @@ export function OnboardingScreen() {
style={styles.logo} style={styles.logo}
resizeMode="contain" resizeMode="contain"
/> />
<Text style={styles.heroTitle}>Selamat datang di Kantongku! 👛</Text> <Text style={styles.heroTitle}>Kantongku</Text>
<Text style={styles.heroSub}> <Text style={styles.heroSub}>
Catat pemasukan, sisihkan otomatis, dan jaga semangat nabungmu — Asisten keuangan pribadi yang bikin kamu semangat menabung.
semua offline, semua di perangkatmu.
</Text> </Text>
</LinearGradient> </LinearGradient>
<Text style={styles.sectionTitle}> <View style={styles.featureList}>
Tambah kategori favoritmu (opsional) {FEATURES.map((f) => (
</Text> <View key={f.title} style={styles.featureRow}>
<Text style={styles.sectionHint}> <View style={styles.featureIcon}>
Misal: Kucing 🐱, Gadget 📱 — biar pencatatanmu makin personal. <Ionicons name={f.icon} size={20} color={colors.primaryDark} />
</Text>
{/* Toggle tipe */}
<View style={styles.toggleRow}>
<Pressable
style={[styles.toggleBtn, type === 'income' && styles.toggleIncome]}
onPress={() => setType('income')}
>
<Text
style={[
styles.toggleText,
type === 'income' && { color: colors.income, fontWeight: '700' },
]}
>
+ Pemasukan
</Text>
</Pressable>
<Pressable
style={[styles.toggleBtn, type === 'expense' && styles.toggleExpense]}
onPress={() => setType('expense')}
>
<Text
style={[
styles.toggleText,
type === 'expense' && { color: colors.expense, fontWeight: '700' },
]}
>
- Pengeluaran
</Text>
</Pressable>
</View> </View>
<View style={styles.featureInfo}>
<TextInput <Text style={styles.featureTitle}>{f.title}</Text>
style={[styles.card, styles.input]} <Text style={styles.featureDesc}>{f.desc}</Text>
value={name}
onChangeText={setName}
placeholder="Nama kategori (misal: Kucing)"
placeholderTextColor={colors.textMuted}
/>
<View style={styles.emojiGrid}>
{QUICK_EMOJIS.map((e) => (
<Pressable
key={e}
style={[styles.emojiChip, icon === e && styles.emojiChipActive]}
onPress={() => setIcon(e)}
>
<Text style={styles.emojiText}>{e}</Text>
</Pressable>
))}
</View> </View>
<Pressable
style={({ pressed }) => [styles.addBtn, pressed && styles.pressed]}
onPress={handleAdd}
>
<Ionicons name="add" size={18} color={colors.white} />
<Text style={styles.addBtnText}>Tambah Kategori</Text>
</Pressable>
{!loading && categories.length > 0 && (
<View style={styles.existingWrap}>
<Text style={styles.existingLabel}>Kategori yang tersedia:</Text>
<View style={styles.chipWrap}>
{categories.map((c) => (
<View key={c.id} style={styles.chip}>
<Text style={styles.chipText}>
{c.icon} {c.name}
</Text>
</View> </View>
))} ))}
</View> </View>
</View>
)}
<Pressable <Pressable
style={({ pressed }) => [styles.startBtn, pressed && styles.pressed]} style={({ pressed }) => [styles.startBtn, pressed && styles.pressed]}
@@ -170,11 +86,11 @@ export function OnboardingScreen() {
end={{ x: 1, y: 1 }} end={{ x: 1, y: 1 }}
style={styles.startGradient} style={styles.startGradient}
> >
<Text style={styles.startText}>🚀 Mulai Pakai Kantongku</Text> <Text style={styles.startText}>Mulai Pakai Kantongku</Text>
</LinearGradient> </LinearGradient>
</Pressable> </Pressable>
<Text style={styles.footerNote}> <Text style={styles.footerNote}>
Kategori bisa diubah kapan saja di menu Pengaturan. Kategori bisa diatur kapan saja di menu Pengaturan.
</Text> </Text>
</ScrollView> </ScrollView>
</SafeAreaView> </SafeAreaView>
@@ -188,7 +104,7 @@ const styles = StyleSheet.create({
marginHorizontal: 16, marginHorizontal: 16,
marginTop: 16, marginTop: 16,
borderRadius: 24, borderRadius: 24,
padding: 22, padding: 24,
overflow: 'hidden', overflow: 'hidden',
alignItems: 'center', alignItems: 'center',
shadowColor: colors.primaryDark, shadowColor: colors.primaryDark,
@@ -215,110 +131,45 @@ const styles = StyleSheet.create({
borderRadius: 55, borderRadius: 55,
backgroundColor: 'rgba(255,255,255,0.06)', backgroundColor: 'rgba(255,255,255,0.06)',
}, },
logo: { width: 96, height: 96, marginBottom: 12 }, logo: { width: 84, height: 84, marginBottom: 10 },
heroTitle: { heroTitle: {
fontSize: 22, fontSize: 26,
fontWeight: '800', fontWeight: '800',
color: colors.white, color: colors.white,
textAlign: 'center',
}, },
heroSub: { heroSub: {
fontSize: 13, fontSize: 13,
color: 'rgba(255,255,255,0.9)', color: 'rgba(255,255,255,0.9)',
textAlign: 'center', textAlign: 'center',
lineHeight: 19, lineHeight: 19,
marginTop: 8, marginTop: 6,
}, },
sectionTitle: { featureList: { marginHorizontal: 16, marginTop: 20, gap: 4 },
fontSize: 16, featureRow: {
fontWeight: '700',
color: colors.text,
marginTop: 20,
marginHorizontal: 16,
},
sectionHint: {
fontSize: 12,
color: colors.textMuted,
marginHorizontal: 16,
marginTop: 4,
marginBottom: 10,
},
toggleRow: {
flexDirection: 'row', flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.surface, backgroundColor: colors.surface,
borderRadius: 14,
borderWidth: 1, borderWidth: 1,
borderColor: colors.border, borderColor: colors.border,
padding: 4,
marginHorizontal: 16,
marginBottom: 10,
},
toggleBtn: { flex: 1, alignItems: 'center', paddingVertical: 10, borderRadius: 10 },
toggleIncome: { backgroundColor: colors.incomeSoft },
toggleExpense: { backgroundColor: colors.expenseSoft },
toggleText: { fontSize: 14, color: colors.textMuted },
card: {
backgroundColor: colors.surface,
borderRadius: 16, borderRadius: 16,
padding: 16, padding: 14,
borderWidth: 1, marginBottom: 8,
borderColor: colors.border,
}, },
input: { featureIcon: {
marginHorizontal: 16, width: 40,
paddingVertical: 12, height: 40,
fontSize: 15, borderRadius: 20,
color: colors.text,
marginBottom: 10,
},
emojiGrid: {
flexDirection: 'row',
flexWrap: 'wrap',
gap: 8,
marginHorizontal: 16,
},
emojiChip: {
width: 48,
height: 48,
borderRadius: 13,
backgroundColor: colors.surface,
borderWidth: 1,
borderColor: colors.border,
alignItems: 'center',
justifyContent: 'center',
},
emojiChipActive: {
borderColor: colors.primary,
backgroundColor: colors.primarySoft, backgroundColor: colors.primarySoft,
},
emojiText: { fontSize: 22 },
addBtn: {
flexDirection: 'row',
alignItems: 'center', alignItems: 'center',
justifyContent: 'center', justifyContent: 'center',
gap: 6, marginRight: 12,
backgroundColor: colors.primary,
borderRadius: 12,
paddingVertical: 12,
marginHorizontal: 16,
marginTop: 12,
}, },
addBtnText: { color: colors.white, fontWeight: '700', fontSize: 14 }, featureInfo: { flex: 1 },
existingWrap: { marginHorizontal: 16, marginTop: 16 }, featureTitle: { fontSize: 14, fontWeight: '700', color: colors.text },
existingLabel: { fontSize: 12, color: colors.textMuted, marginBottom: 8 }, featureDesc: { fontSize: 12, color: colors.textMuted, marginTop: 2, lineHeight: 17 },
chipWrap: { flexDirection: 'row', flexWrap: 'wrap', gap: 6 },
chip: {
backgroundColor: colors.surface,
borderWidth: 1,
borderColor: colors.border,
borderRadius: 999,
paddingHorizontal: 10,
paddingVertical: 6,
},
chipText: { fontSize: 12, color: colors.text },
startBtn: { startBtn: {
marginHorizontal: 16, marginHorizontal: 16,
marginTop: 24, marginTop: 20,
borderRadius: 14, borderRadius: 14,
shadowColor: colors.primaryDark, shadowColor: colors.primaryDark,
shadowOpacity: 0.25, shadowOpacity: 0.25,