File size: 3,166 Bytes
5c876be | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 | import React from 'react';
import { StyleSheet, View } from 'react-native';
import { useTheme, Avatar, Card, Text } from 'react-native-paper';
export interface UserCardProps {
/** User data to display */
user: {
displayName: string;
email?: string;
avatarUrl?: string;
ratingAvg?: number;
totalRides?: number;
};
/** Callback when the card is pressed */
onPress?: () => void;
/** Visual size variant */
size?: 'small' | 'medium' | 'large';
}
/**
* User profile summary card showing avatar, name, email,
* rating, and ride count. Supports three size variants.
*/
export function UserCard({ user, onPress, size = 'medium' }: UserCardProps) {
const theme = useTheme();
const avatarSize = size === 'small' ? 36 : size === 'medium' ? 52 : 72;
const initials = user.displayName
.split(' ')
.map((w) => w.charAt(0))
.join('')
.toUpperCase()
.slice(0, 2);
return (
<Card
style={[styles.card, { backgroundColor: theme.colors.surface }]}
onPress={onPress}
mode={size === 'large' ? 'elevated' : 'outlined'}
elevation={size === 'large' ? 2 : 0}
>
<Card.Content
style={[
styles.content,
size === 'large' && styles.contentLarge,
]}
>
<Avatar.Text
size={avatarSize}
label={initials}
source={user.avatarUrl ? { uri: user.avatarUrl } : undefined}
style={{ backgroundColor: theme.colors.primaryContainer }}
color={theme.colors.onPrimaryContainer}
/>
<View style={styles.info}>
<Text
variant={size === 'large' ? 'headlineSmall' : 'titleMedium'}
style={{ color: theme.colors.onSurface }}
>
{user.displayName}
</Text>
{user.email ? (
<Text
variant="bodySmall"
style={{ color: theme.colors.onSurfaceVariant }}
>
{user.email}
</Text>
) : null}
<View style={styles.metaRow}>
{user.ratingAvg != null && user.ratingAvg > 0 && (
<Text
variant="labelSmall"
style={{ color: theme.colors.onSurfaceVariant }}
>
⭐ {user.ratingAvg.toFixed(1)}
</Text>
)}
{user.totalRides != null && user.totalRides > 0 && (
<Text
variant="labelSmall"
style={{ color: theme.colors.onSurfaceVariant }}
>
{user.totalRides} ride{user.totalRides !== 1 ? 's' : ''}
</Text>
)}
</View>
</View>
</Card.Content>
</Card>
);
}
const styles = StyleSheet.create({
card: {
borderRadius: 12,
},
content: {
flexDirection: 'row',
alignItems: 'center',
gap: 14,
paddingVertical: 10,
paddingHorizontal: 14,
},
contentLarge: {
flexDirection: 'column',
alignItems: 'center',
gap: 10,
paddingVertical: 20,
},
info: {
flex: 1,
gap: 2,
},
metaRow: {
flexDirection: 'row',
gap: 12,
marginTop: 2,
},
});
|