UI Updates and Bug Fixes

This commit is contained in:
2026-08-14 17:42:18 +05:30
parent 29e97a7316
commit 6dac115811
57 changed files with 16680 additions and 543 deletions
+105
View File
@@ -0,0 +1,105 @@
import React from 'react';
import { TouchableOpacity, TouchableOpacityProps, ActivityIndicator, GestureResponderEvent, StyleProp, ViewStyle, StyleSheet } from 'react-native';
import { Typography } from './Typography';
interface ButtonProps extends TouchableOpacityProps {
variant?: 'primary' | 'secondary' | 'outline' | 'ghost';
title: string;
loading?: boolean;
onPress?: (event: GestureResponderEvent) => void;
disabled?: boolean;
style?: StyleProp<ViewStyle>;
}
export const Button: React.FC<ButtonProps> = ({
variant = 'primary',
title,
loading = false,
disabled,
style,
...props
}) => {
const getBackgroundStyle = () => {
if (disabled) return styles.bgDisabled;
switch (variant) {
case 'primary':
return styles.bgPrimary;
case 'secondary':
return styles.bgSecondary;
case 'outline':
return styles.bgOutline;
case 'ghost':
return styles.bgGhost;
default:
return styles.bgPrimary;
}
};
const getTextStyle = () => {
if (disabled) return styles.textDisabled;
switch (variant) {
case 'primary':
case 'secondary':
return styles.textWhite;
case 'outline':
case 'ghost':
return styles.textPrimary;
default:
return styles.textWhite;
}
};
return (
<TouchableOpacity
style={[styles.container, getBackgroundStyle(), style]}
disabled={disabled || loading}
activeOpacity={0.8}
{...props}
>
{loading ? (
<ActivityIndicator color={variant === 'outline' || variant === 'ghost' ? '#4F46E5' : '#FFFFFF'} />
) : (
<Typography weight="medium" style={getTextStyle()}>
{title}
</Typography>
)}
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
container: {
paddingVertical: 12,
paddingHorizontal: 24,
borderRadius: 12,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
},
bgDisabled: {
backgroundColor: '#D1D5DB',
},
bgPrimary: {
backgroundColor: '#4F46E5',
},
bgSecondary: {
backgroundColor: '#10B981',
},
bgOutline: {
backgroundColor: 'transparent',
borderColor: '#4F46E5',
borderWidth: 2,
},
bgGhost: {
backgroundColor: 'transparent',
},
textDisabled: {
color: '#6B7280',
},
textWhite: {
color: '#FFFFFF',
},
textPrimary: {
color: '#4F46E5',
},
});
+45
View File
@@ -0,0 +1,45 @@
import React from 'react';
import { View, ViewProps, StyleProp, ViewStyle, StyleSheet } from 'react-native';
interface CardProps extends ViewProps {
children: React.ReactNode;
noPadding?: boolean;
style?: StyleProp<ViewStyle>;
}
export const Card: React.FC<CardProps> = ({
children,
noPadding = false,
style,
...props
}) => {
return (
<View
style={[
styles.card,
noPadding ? null : styles.padding,
style
]}
{...props}
>
{children}
</View>
);
};
const styles = StyleSheet.create({
card: {
backgroundColor: '#FFFFFF',
borderRadius: 16,
borderWidth: 1,
borderColor: '#F3F4F6', // gray-100
shadowColor: '#000',
shadowOffset: { width: 0, height: 2 },
shadowOpacity: 0.05,
shadowRadius: 15,
elevation: 2,
},
padding: {
padding: 16, // p-4
}
});
+72
View File
@@ -0,0 +1,72 @@
import React from 'react';
import { View, TouchableOpacity, StyleSheet } from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { ChevronLeft } from 'lucide-react-native';
import { Typography } from './Typography';
import { useResponsiveLayout } from '../../hooks/useResponsiveLayout';
import { THEME } from '../../constants/theme';
interface HeaderProps {
title: string;
showBack?: boolean;
rightComponent?: React.ReactNode;
}
export const Header: React.FC<HeaderProps> = ({ title, showBack = false, rightComponent }) => {
const navigation = useNavigation();
const { screenPadding } = useResponsiveLayout();
return (
<View style={[styles.header, { paddingHorizontal: screenPadding }]}>
<View style={styles.leftContainer}>
{showBack && (
<TouchableOpacity
onPress={() => navigation.goBack()}
style={styles.backButton}
hitSlop={{ top: 10, bottom: 10, left: 10, right: 10 }}
>
<ChevronLeft color="#FFF" size={26} strokeWidth={2.5} />
</TouchableOpacity>
)}
<Typography variant="h3" weight="bold" style={styles.title}>
{title}
</Typography>
</View>
{rightComponent && (
<View style={styles.rightContainer}>
{rightComponent}
</View>
)}
</View>
);
};
const styles = StyleSheet.create({
header: {
paddingHorizontal: 16,
paddingVertical: 16,
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
backgroundColor: THEME.colors.primary, // Primary brand color
},
leftContainer: {
flexDirection: 'row',
alignItems: 'center',
},
rightContainer: {
justifyContent: 'center',
},
backButton: {
marginRight: 12,
width: 40,
height: 40,
borderRadius: 20,
backgroundColor: 'rgba(255,255,255,0.2)',
alignItems: 'center',
justifyContent: 'center',
},
title: {
color: '#FFFFFF',
}
});
+70
View File
@@ -0,0 +1,70 @@
import React from 'react';
import { TextInput, TextInputProps, View, StyleSheet } from 'react-native';
import { Typography } from './Typography';
export interface InputProps extends TextInputProps {
label?: string;
error?: string;
placeholder?: string;
keyboardType?: 'default' | 'email-address' | 'numeric' | 'phone-pad';
autoCapitalize?: 'none' | 'sentences' | 'words' | 'characters';
secureTextEntry?: boolean;
value?: string;
onChangeText?: (text: string) => void;
style?: any;
labelStyle?: any;
}
export function Input({ label, error, style, labelStyle, ...props }: InputProps) {
return (
<View style={styles.container}>
{label && (
<Typography variant="caption" weight="medium" style={[styles.label, labelStyle]}>
{label}
</Typography>
)}
<TextInput
style={[
styles.input,
error ? styles.inputError : null,
style,
]}
placeholderTextColor="#9CA3AF"
{...props}
/>
{error && (
<Typography variant="caption" style={styles.errorText}>
{error}
</Typography>
)}
</View>
);
};
const styles = StyleSheet.create({
container: {
marginBottom: 16,
width: '100%',
},
label: {
marginBottom: 6,
color: '#374151', // gray-700
},
input: {
backgroundColor: '#FFFFFF',
borderWidth: 1,
borderColor: '#D1D5DB', // gray-300
borderRadius: 12,
paddingHorizontal: 16,
paddingVertical: 14,
fontSize: 16,
color: '#111827', // gray-900
},
inputError: {
borderColor: '#EF4444', // red-500
},
errorText: {
color: '#EF4444',
marginTop: 4,
},
});
+92
View File
@@ -0,0 +1,92 @@
import React from 'react';
import { Text, TextProps, StyleProp, TextStyle, StyleSheet } from 'react-native';
import { useResponsiveLayout } from '../../hooks/useResponsiveLayout';
interface TypographyProps extends TextProps {
variant?: 'h1' | 'h2' | 'h3' | 'body' | 'caption';
weight?: 'normal' | 'medium' | 'bold';
children: React.ReactNode;
style?: StyleProp<TextStyle>;
}
export const Typography: React.FC<TypographyProps> = ({
variant = 'body',
weight = 'normal',
children,
style,
...props
}) => {
const { scale } = useResponsiveLayout();
const getVariantStyles = () => {
switch (variant) {
case 'h1': return styles.h1;
case 'h2': return styles.h2;
case 'h3': return styles.h3;
case 'caption': return styles.caption;
case 'body':
default: return styles.body;
}
};
const getWeightStyles = () => {
switch (weight) {
case 'bold': return styles.bold;
case 'medium': return styles.medium;
case 'normal':
default: return styles.normal;
}
};
const variantStyle = StyleSheet.flatten(getVariantStyles());
const scaledStyle = {
fontSize: scale(variantStyle.fontSize as number),
lineHeight: scale(variantStyle.lineHeight as number),
};
return (
<Text
style={[variantStyle, scaledStyle, getWeightStyles(), style]}
{...props}
>
{children}
</Text>
);
};
const styles = StyleSheet.create({
h1: {
fontSize: 30,
lineHeight: 36,
color: '#111827', // text-gray-900
},
h2: {
fontSize: 24,
lineHeight: 32,
color: '#111827', // text-gray-900
},
h3: {
fontSize: 20,
lineHeight: 28,
color: '#111827', // text-gray-900
},
body: {
fontSize: 16,
lineHeight: 24,
color: '#111827', // text-gray-900
},
caption: {
fontSize: 14,
lineHeight: 20,
color: '#6B7280', // text-gray-500
},
bold: {
fontWeight: 'bold',
},
medium: {
fontWeight: '500',
},
normal: {
fontWeight: 'normal',
},
});