feat: update TableBackground and LobbyScreen for improved UI and functionality; add promo code support in LobbyScreen

This commit is contained in:
2025-12-21 00:46:00 +01:00
parent 0bac2dd4f6
commit 46835c6071
3 changed files with 119 additions and 75 deletions

View File

@@ -6,7 +6,7 @@ export default function TableBackground({ children, style }) {
return ( return (
<View style={[styles.wrapper, style]}> <View style={[styles.wrapper, style]}>
<LinearGradient <LinearGradient
colors={[colors.tableEdge, '#3e2a10']} colors={[colors.tableFeltDark, '#0a3a2a']}
start={{ x: 0, y: 0 }} start={{ x: 0, y: 0 }}
end={{ x: 1, y: 1 }} end={{ x: 1, y: 1 }}
style={styles.edge} style={styles.edge}
@@ -37,7 +37,7 @@ const styles = StyleSheet.create({
borderTopRightRadius: 12, borderTopRightRadius: 12,
borderBottomLeftRadius: 280, borderBottomLeftRadius: 280,
borderBottomRightRadius: 280, borderBottomRightRadius: 280,
padding: 6 padding: 4
}, },
felt: { felt: {
flex: 1, flex: 1,

View File

@@ -1,5 +1,5 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { ActivityIndicator, StyleSheet, Text, TextInput, View } from 'react-native'; import { ActivityIndicator, ScrollView, StyleSheet, Text, TextInput, View } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient'; import { LinearGradient } from 'expo-linear-gradient';
import { useStripe } from '@stripe/stripe-react-native'; import { useStripe } from '@stripe/stripe-react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context'; import { useSafeAreaInsets } from 'react-native-safe-area-context';
@@ -13,6 +13,7 @@ export default function LobbyScreen({ user, token, onLogout, onSelectTable, onRe
const [depositLoading, setDepositLoading] = useState(false); const [depositLoading, setDepositLoading] = useState(false);
const [depositError, setDepositError] = useState(''); const [depositError, setDepositError] = useState('');
const [customAmount, setCustomAmount] = useState(''); const [customAmount, setCustomAmount] = useState('');
const [promoCode, setPromoCode] = useState('');
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const { initPaymentSheet, presentPaymentSheet } = useStripe(); const { initPaymentSheet, presentPaymentSheet } = useStripe();
@@ -35,9 +36,10 @@ export default function LobbyScreen({ user, token, onLogout, onSelectTable, onRe
try { try {
setDepositLoading(true); setDepositLoading(true);
setDepositError(''); setDepositError('');
const code = promoCode.trim().toUpperCase();
const data = await apiFetch('/api/wallet/deposit-intent', token, { const data = await apiFetch('/api/wallet/deposit-intent', token, {
method: 'POST', method: 'POST',
body: JSON.stringify({ amount }) body: JSON.stringify(code ? { amount, promoCode: code } : { amount })
}); });
const init = await initPaymentSheet({ const init = await initPaymentSheet({
@@ -71,6 +73,7 @@ export default function LobbyScreen({ user, token, onLogout, onSelectTable, onRe
} }
await onRefreshUser(); await onRefreshUser();
setPromoCode('');
} catch (err) { } catch (err) {
setDepositError(err.message || 'Nem sikerult a feltoltes.'); setDepositError(err.message || 'Nem sikerult a feltoltes.');
} finally { } finally {
@@ -97,76 +100,95 @@ export default function LobbyScreen({ user, token, onLogout, onSelectTable, onRe
} }
]} ]}
> >
<View style={styles.header}> <ScrollView
<View> contentContainerStyle={styles.scrollContent}
<Text style={styles.title}>Lobbi</Text> showsVerticalScrollIndicator={false}
<Text style={styles.balance}>Egyenleg: {user.balance} Ft</Text> keyboardShouldPersistTaps="handled"
>
<View style={styles.header}>
<View>
<Text style={styles.title}>Lobbi</Text>
<Text style={styles.balance}>Egyenleg: {user.balance} Ft</Text>
</View>
<CasinoButton label="Kilépés" onPress={onLogout} variant="red" />
</View> </View>
<CasinoButton label="Kilépés" onPress={onLogout} variant="red" />
</View>
<View style={styles.depositRow}> <View style={styles.depositRow}>
<Text style={styles.sectionTitle}>Gyors feltöltés</Text> <Text style={styles.sectionTitle}>Gyors feltöltés</Text>
<View style={styles.chips}> <View style={styles.chips}>
<CasinoButton label="200 Ft" onPress={() => quickDeposit(200)} variant="gold" disabled={depositLoading} /> <CasinoButton label="200 Ft" onPress={() => quickDeposit(200)} variant="gold" disabled={depositLoading} />
<CasinoButton label="500 Ft" onPress={() => quickDeposit(500)} variant="gold" disabled={depositLoading} /> <CasinoButton label="500 Ft" onPress={() => quickDeposit(500)} variant="gold" disabled={depositLoading} />
<CasinoButton label="1000 Ft" onPress={() => quickDeposit(1000)} variant="gold" disabled={depositLoading} /> <CasinoButton label="1000 Ft" onPress={() => quickDeposit(1000)} variant="gold" disabled={depositLoading} />
</View> </View>
<View style={styles.customRow}> <View style={styles.customRow}>
<View style={styles.customInputWrap}> <View style={styles.customInputWrap}>
<Text style={styles.customLabel}>Egyedi összeg</Text> <Text style={styles.customLabel}>Egyedi összeg</Text>
<View style={styles.customInputRow}> <View style={styles.customInputRow}>
<TextInput
value={customAmount}
onChangeText={(value) => setCustomAmount(value.replace(/[^0-9]/g, ''))}
placeholder="pl. 250"
placeholderTextColor="rgba(255,255,255,0.35)"
keyboardType="number-pad"
style={styles.customInput}
/>
<Text style={styles.customSuffix}>Ft</Text>
</View>
<Text style={styles.customHint}>Minimum 175 Ft, maximum 1000 Ft.</Text>
</View>
<CasinoButton
label="Feltöltés"
onPress={() => handleDeposit(customValue)}
variant="gold"
disabled={depositLoading || !customValid}
/>
</View>
<View style={styles.promoRow}>
<Text style={styles.customLabel}>Promó kód</Text>
<View style={styles.promoInputRow}>
<TextInput <TextInput
value={customAmount} value={promoCode}
onChangeText={(value) => setCustomAmount(value.replace(/[^0-9]/g, ''))} onChangeText={(value) => setPromoCode(value.replace(/\\s/g, '').toUpperCase())}
placeholder="pl. 250" placeholder="PL. BLACKJACK2025"
placeholderTextColor="rgba(255,255,255,0.35)" placeholderTextColor="rgba(255,255,255,0.35)"
keyboardType="number-pad" autoCapitalize="characters"
style={styles.customInput} style={styles.customInput}
/> />
<Text style={styles.customSuffix}>Ft</Text>
</View> </View>
<Text style={styles.customHint}>Minimum 175 Ft, maximum 1000 Ft.</Text>
</View> </View>
<CasinoButton {depositError ? <Text style={styles.error}>{depositError}</Text> : null}
label="Feltöltés"
onPress={() => handleDeposit(customValue)}
variant="gold"
disabled={depositLoading || !customValid}
/>
</View> </View>
{depositError ? <Text style={styles.error}>{depositError}</Text> : null}
</View>
<Text style={styles.sectionTitle}>Asztalok</Text> <Text style={styles.sectionTitle}>Asztalok</Text>
{loading ? ( {loading ? (
<ActivityIndicator color={colors.goldBright} /> <ActivityIndicator color={colors.goldBright} />
) : ( ) : (
<View style={styles.tableList}> <View style={styles.tableList}>
{tables.map((table) => { {tables.map((table) => {
const free = table.seatCount - table.occupied; const free = table.seatCount - table.occupied;
return ( return (
<View key={table.id} style={styles.tableCard}> <View key={table.id} style={styles.tableCard}>
<View> <View>
<Text style={styles.tableName}>Asztal {table.id}</Text> <Text style={styles.tableName}>Asztal {table.id}</Text>
<Text style={styles.tableMeta}>Szabad hely: {free} / {table.seatCount}</Text> <Text style={styles.tableMeta}>Szabad hely: {free} / {table.seatCount}</Text>
<View style={styles.seatRow}> <View style={styles.seatRow}>
{Array.from({ length: table.seatCount }).map((_, idx) => ( {Array.from({ length: table.seatCount }).map((_, idx) => (
<View key={`${table.id}-${idx}`} style={idx < table.occupied ? styles.seatFilled : styles.seatEmpty} /> <View key={`${table.id}-${idx}`} style={idx < table.occupied ? styles.seatFilled : styles.seatEmpty} />
))} ))}
</View>
</View> </View>
<CasinoButton
label={free > 0 ? 'Beülök' : 'Tele'}
onPress={() => free > 0 && onSelectTable(table.id)}
variant="green"
disabled={free === 0}
/>
</View> </View>
<CasinoButton );
label={free > 0 ? 'Beülök' : 'Tele'} })}
onPress={() => free > 0 && onSelectTable(table.id)} </View>
variant="green" )}
disabled={free === 0} </ScrollView>
/>
</View>
);
})}
</View>
)}
</LinearGradient> </LinearGradient>
); );
} }
@@ -176,6 +198,9 @@ const styles = StyleSheet.create({
flex: 1, flex: 1,
padding: 20 padding: 20
}, },
scrollContent: {
paddingBottom: 20
},
header: { header: {
flexDirection: 'row', flexDirection: 'row',
justifyContent: 'space-between', justifyContent: 'space-between',
@@ -249,6 +274,19 @@ const styles = StyleSheet.create({
fontSize: 11, fontSize: 11,
marginTop: 6 marginTop: 6
}, },
promoRow: {
marginTop: 12
},
promoInputRow: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: 'rgba(255,255,255,0.08)',
borderRadius: 12,
paddingHorizontal: 12,
paddingVertical: 10,
borderWidth: 1,
borderColor: 'rgba(255,255,255,0.12)'
},
tableList: { tableList: {
gap: 12 gap: 12
}, },

View File

@@ -145,6 +145,9 @@ export default function TableScreen({ token, tableId, user, onLeave }) {
<View style={styles.rotateWrap}> <View style={styles.rotateWrap}>
<Text style={styles.rotateTitle}>Fordítsd el a telefont</Text> <Text style={styles.rotateTitle}>Fordítsd el a telefont</Text>
<Text style={styles.rotateSubtitle}>A blackjack asztal fekvő nézetben működik jól.</Text> <Text style={styles.rotateSubtitle}>A blackjack asztal fekvő nézetben működik jól.</Text>
<View style={styles.rotateActions}>
<CasinoButton label="Vissza" onPress={onLeave} variant="red" />
</View>
</View> </View>
</LinearGradient> </LinearGradient>
); );
@@ -270,7 +273,7 @@ const styles = StyleSheet.create({
tableContent: { tableContent: {
flex: 1, flex: 1,
justifyContent: 'space-between', justifyContent: 'space-between',
paddingBottom: 8 paddingBottom: 120
}, },
seatLayer: { seatLayer: {
flex: 1, flex: 1,
@@ -278,14 +281,14 @@ const styles = StyleSheet.create({
}, },
seatSpot: { seatSpot: {
position: 'absolute', position: 'absolute',
width: 96, width: 92,
transform: [{ translateX: -48 }] transform: [{ translateX: -46 }]
}, },
controlsOverlay: { controlsOverlay: {
position: 'absolute', position: 'absolute',
left: 12, left: 18,
right: 12, right: 18,
bottom: 14, bottom: 12,
gap: 10 gap: 10
}, },
blurPanel: { blurPanel: {
@@ -365,15 +368,18 @@ const styles = StyleSheet.create({
fontSize: 14, fontSize: 14,
marginTop: 12, marginTop: 12,
textAlign: 'center' textAlign: 'center'
},
rotateActions: {
marginTop: 20
} }
}); });
const seatPositions = { const seatPositions = {
0: { left: '8%', top: '62%' }, 0: { left: '8%', top: '52%' },
1: { left: '22%', top: '55%' }, 1: { left: '22%', top: '45%' },
2: { left: '36%', top: '50%' }, 2: { left: '36%', top: '40%' },
3: { left: '50%', top: '48%' }, 3: { left: '50%', top: '38%' },
4: { left: '64%', top: '50%' }, 4: { left: '64%', top: '40%' },
5: { left: '78%', top: '55%' }, 5: { left: '78%', top: '45%' },
6: { left: '92%', top: '62%' } 6: { left: '92%', top: '52%' }
}; };