/** * EarningsScreen — saldo + utbetalningshistorik */ import { useCallback, useEffect, useState } from 'react' import { View, Text, ScrollView, TouchableOpacity, StyleSheet, ActivityIndicator, RefreshControl, Alert, } from 'react-native' import { wallet, payouts, type WalletBalance, type Payout } from '../lib/api' type TabKey = 'oversikt' | 'utbetalningar' const PAYOUT_STATUS: Record = { pending: { label: 'Väntar', color: '#f59e0b' }, approved: { label: 'Godkänd', color: '#10b981' }, paid: { label: 'Utbetald', color: '#6366f1' }, rejected: { label: 'Nekad', color: '#ef4444' }, } export function EarningsScreen() { const [tab, setTab] = useState('oversikt') const [balance, setBalance] = useState(null) const [payoutList, setPayoutList] = useState([]) const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) const [requesting, setRequesting] = useState(false) const load = useCallback(async () => { try { const [b, p] = await Promise.all([ wallet.balance(), payouts.history(), ]) setBalance(b) setPayoutList(p) } catch { // silent } finally { setLoading(false) setRefreshing(false) } }, []) useEffect(() => { load() }, []) const onRefresh = useCallback(() => { setRefreshing(true); load() }, [load]) const handleRequestPayout = () => { if (!balance) return const available = balance.balance - balance.pending_payouts if (available < 10000) { // 100 SEK minimum Alert.alert('Minsta utbetalning 100 kr', `Du har ${(available / 100).toFixed(0)} kr tillgängligt.`) return } Alert.alert( 'Begär utbetalning', `Vill du ta ut ${(available / 100).toFixed(0)} SEK till ditt bankkonto?`, [ { text: 'Avbryt', style: 'cancel' }, { text: 'Ta ut', onPress: async () => { setRequesting(true) try { await payouts.request(available) Alert.alert('✅ Utbetalning begärd!', 'Pengarna är hos dig inom 1–3 bankdagar.') load() } catch (e: any) { Alert.alert('Fel', e?.message ?? 'Kunde inte begära utbetalning.') } finally { setRequesting(false) } }, }, ], ) } if (loading) { return ( ) } return ( } > {/* Big balance card */} Tillgängligt {balance ? (balance.balance / 100).toFixed(0) : '0'} SEK {balance && balance.pending_payouts > 0 && ( + {(balance.pending_payouts / 100).toFixed(0)} SEK under behandling )} {requesting ? : 💳 Begär utbetalning } {/* Tab bar */} {([['oversikt', 'Översikt'], ['utbetalningar', 'Utbetalningar']] as [TabKey, string][]).map(([key, label]) => ( setTab(key)} > {label} ))} {/* Översikt */} {tab === 'oversikt' && ( Din ekonomi Valuta {balance?.currency ?? 'SEK'} Tillgängligt {balance ? (balance.balance / 100).toFixed(0) : '0'} kr Väntande utbetalningar {balance ? (balance.pending_payouts / 100).toFixed(0) : '0'} kr Totalt utbetalt {payoutList .filter(p => p.status === 'paid') .reduce((sum, p) => sum + p.amount, 0) / 100} kr )} {/* Utbetalningar */} {tab === 'utbetalningar' && ( {payoutList.length === 0 ? ( 💳 Inga utbetalningar ännu Begär din första utbetalning ovan. ) : payoutList.map(p => { const s = PAYOUT_STATUS[p.status] ?? { label: p.status, color: '#fff' } return ( {new Date(p.created_at).toLocaleDateString('sv-SE', { month: 'short', day: 'numeric', year: 'numeric' })} {p.paid_at && ( Betald {new Date(p.paid_at).toLocaleDateString('sv-SE')} )} {(p.amount / 100).toFixed(0)} {p.currency} {s.label} ) }) } )} ) } const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: '#0A0A1B' }, center: { flex: 1, backgroundColor: '#0A0A1B', alignItems: 'center', justifyContent: 'center' }, balanceCard: { margin: 16, marginTop: 56, backgroundColor: '#6366f1', borderRadius: 24, padding: 28, alignItems: 'center', }, balanceLabel: { color: 'rgba(255,255,255,0.7)', fontSize: 14, marginBottom: 6 }, balanceAmount: { color: '#fff', fontSize: 48, fontWeight: '900', letterSpacing: -2 }, pendingNote: { color: 'rgba(255,255,255,0.6)', fontSize: 13, marginTop: 4 }, payoutBtn: { marginTop: 20, backgroundColor: 'rgba(255,255,255,0.15)', borderRadius: 14, paddingHorizontal: 28, paddingVertical: 12, borderWidth: 1, borderColor: 'rgba(255,255,255,0.25)', }, payoutBtnDisabled: { opacity: 0.6 }, payoutBtnText: { color: '#fff', fontWeight: '700', fontSize: 15 }, tabBar: { flexDirection: 'row', marginHorizontal: 16, marginTop: 16, marginBottom: 4, backgroundColor: 'rgba(255,255,255,0.04)', borderRadius: 14, padding: 4, }, tab: { flex: 1, paddingVertical: 8, alignItems: 'center', borderRadius: 10 }, tabActive: { backgroundColor: '#6366f1' }, tabText: { color: 'rgba(255,255,255,0.4)', fontSize: 12, fontWeight: '600' }, tabTextActive: { color: '#fff' }, section: { paddingHorizontal: 16, paddingTop: 16 }, sectionTitle: { color: 'rgba(255,255,255,0.35)', fontSize: 12, fontWeight: '700', letterSpacing: 1, textTransform: 'uppercase', marginBottom: 16 }, infoCard: { backgroundColor: 'rgba(255,255,255,0.04)', borderRadius: 16, padding: 20, borderWidth: 1, borderColor: 'rgba(255,255,255,0.07)', }, infoRow: { flexDirection: 'row', justifyContent: 'space-between', paddingVertical: 12, borderBottomWidth: 1, borderBottomColor: 'rgba(255,255,255,0.05)', }, infoLabel: { color: 'rgba(255,255,255,0.5)', fontSize: 14 }, infoValue: { color: '#fff', fontSize: 14, fontWeight: '700' }, emptyWrap: { alignItems: 'center', paddingTop: 40 }, emptyEmoji: { fontSize: 40, marginBottom: 12 }, emptyTitle: { color: '#fff', fontSize: 16, fontWeight: '700', marginBottom: 6 }, empty: { color: 'rgba(255,255,255,0.3)', textAlign: 'center', fontSize: 14 }, payoutRow: { flexDirection: 'row', alignItems: 'center', justifyContent: 'space-between', paddingVertical: 14, borderBottomWidth: 1, borderBottomColor: 'rgba(255,255,255,0.05)', }, payoutDate: { color: '#fff', fontSize: 13, fontWeight: '500' }, payoutPaidAt: { color: 'rgba(255,255,255,0.3)', fontSize: 11 }, payoutAmount: { color: '#fff', fontWeight: '700', fontSize: 14 }, payoutStatusBadge: { borderRadius: 20, paddingHorizontal: 10, paddingVertical: 4 }, payoutStatusText: { fontSize: 12, fontWeight: '600' }, })