- Surse complete web (React/Vite) + mobil (React Native/Expo) + extensie (MV3) - Documentație de livrare: ghid utilizare, matrice trasabilitate cerințe, raport testare furnizor - Artefacte binare: imagine Docker didi-frontend:lot3-1.0, APK, extensie v3.2.6 + SHA256SUMS - Configurare adresă platformă externalizată (build args / .env / config.js) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
105 lines
2.6 KiB
TypeScript
105 lines
2.6 KiB
TypeScript
import React from 'react';
|
||
import { View, TouchableOpacity, Text, StyleSheet, ViewStyle } from 'react-native';
|
||
import { LinearGradient } from 'expo-linear-gradient';
|
||
import { COLORS, SPACING, RADIUS, FONT_SIZES, FONT_WEIGHTS, LAYOUT } from '../theme/colors';
|
||
|
||
interface GlassCardProps {
|
||
title: string;
|
||
subtitle: string;
|
||
iconLabel?: string;
|
||
iconColors?: readonly [string, string];
|
||
onPress?: () => void;
|
||
style?: ViewStyle;
|
||
borderColor?: string;
|
||
}
|
||
|
||
export default function GlassCard({
|
||
title,
|
||
subtitle,
|
||
iconLabel,
|
||
iconColors,
|
||
onPress,
|
||
style,
|
||
borderColor,
|
||
}: GlassCardProps) {
|
||
const Wrapper = onPress ? TouchableOpacity : View;
|
||
|
||
return (
|
||
<Wrapper
|
||
activeOpacity={0.7}
|
||
onPress={onPress}
|
||
accessibilityRole={onPress ? 'button' : undefined}
|
||
accessibilityLabel={onPress ? `${title}. ${subtitle}` : undefined}
|
||
style={[
|
||
styles.card,
|
||
borderColor ? { borderColor } : undefined,
|
||
style,
|
||
]}
|
||
>
|
||
<View style={styles.content}>
|
||
{iconLabel && (
|
||
<LinearGradient
|
||
colors={iconColors ? [...iconColors] as [string, string] : [COLORS.brand.primary, COLORS.brand.accentMagenta]}
|
||
start={{ x: 0, y: 0 }}
|
||
end={{ x: 1, y: 1 }}
|
||
style={styles.iconCircle}
|
||
>
|
||
<Text style={styles.iconText}>{iconLabel}</Text>
|
||
</LinearGradient>
|
||
)}
|
||
<View style={styles.textContainer}>
|
||
<Text style={styles.title}>{title}</Text>
|
||
<Text style={styles.subtitle}>{subtitle}</Text>
|
||
</View>
|
||
{onPress && <Text style={styles.arrow}>›</Text>}
|
||
</View>
|
||
</Wrapper>
|
||
);
|
||
}
|
||
|
||
const styles = StyleSheet.create({
|
||
card: {
|
||
backgroundColor: COLORS.bg.card,
|
||
borderWidth: 1,
|
||
borderColor: COLORS.border.accent,
|
||
borderRadius: RADIUS.xxl,
|
||
padding: SPACING.lg,
|
||
marginBottom: SPACING.sm + 4,
|
||
},
|
||
content: {
|
||
flexDirection: 'row',
|
||
alignItems: 'center',
|
||
justifyContent: 'center',
|
||
},
|
||
iconCircle: {
|
||
width: LAYOUT.ICON_CIRCLE_SIZE,
|
||
height: LAYOUT.ICON_CIRCLE_SIZE,
|
||
borderRadius: RADIUS.full,
|
||
justifyContent: 'center',
|
||
alignItems: 'center',
|
||
marginRight: SPACING.md,
|
||
},
|
||
iconText: {
|
||
color: COLORS.text.primary,
|
||
fontSize: FONT_SIZES.lg,
|
||
fontWeight: FONT_WEIGHTS.bold,
|
||
},
|
||
textContainer: {
|
||
flex: 1,
|
||
},
|
||
title: {
|
||
color: COLORS.text.primary,
|
||
fontSize: FONT_SIZES.base,
|
||
fontWeight: FONT_WEIGHTS.bold,
|
||
},
|
||
subtitle: {
|
||
color: COLORS.text.secondary,
|
||
fontSize: FONT_SIZES.sm,
|
||
marginTop: 2,
|
||
},
|
||
arrow: {
|
||
color: COLORS.text.hint,
|
||
fontSize: FONT_SIZES.xxl,
|
||
fontWeight: FONT_WEIGHTS.bold,
|
||
},
|
||
});
|