feat(app): initial data flow

This commit is contained in:
uttam 2026-07-01 09:55:55 +05:30
parent 2500962320
commit 8f6e162e5e
133 changed files with 5011 additions and 220 deletions

View File

@ -32,7 +32,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# your application. You should enable this flag either if you want # your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that # to write custom TurboModules/Fabric components OR use libraries that
# are providing them. # are providing them.
newArchEnabled=true newArchEnabled=false
# Use this property to enable or disable the Hermes JS engine. # Use this property to enable or disable the Hermes JS engine.
# If set to false, you will be using JSC instead. # If set to false, you will be using JSC instead.
@ -42,3 +42,4 @@ hermesEnabled=true
# This allows your app to draw behind system bars for an immersive UI. # This allows your app to draw behind system bars for an immersive UI.
# Note: Only works with ReactActivity and should not be used with custom Activity. # Note: Only works with ReactActivity and should not be used with custom Activity.
edgeToEdgeEnabled=false edgeToEdgeEnabled=false
android.experimental.cxx.cmake.arguments=-DCMAKE_OBJECT_PATH_MAX=128

View File

@ -1,38 +1,24 @@
import React from 'react';
import { StatusBar, StyleSheet, useColorScheme, View } from 'react-native'; import { StatusBar, useColorScheme } from 'react-native';
import { import { SafeAreaProvider } from 'react-native-safe-area-context';
SafeAreaProvider, import { NavigationContainer } from '@react-navigation/native';
useSafeAreaInsets, import { Provider } from 'react-redux';
} from 'react-native-safe-area-context'; import { store } from './store';
import { LoginScreen } from '@features/screens'; import { RootNavigator } from './navigation/rootNavigator';
import { useAppTheme } from '@theme';
function App() { function App() {
const isDarkMode = useColorScheme() === 'dark'; const isDarkMode = useColorScheme() === 'dark';
return ( return (
<Provider store={store}>
<SafeAreaProvider> <SafeAreaProvider>
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} /> <StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
<AppContent /> <NavigationContainer>
<RootNavigator />
</NavigationContainer>
</SafeAreaProvider> </SafeAreaProvider>
</Provider>
); );
} }
function AppContent() {
const safeAreaInsets = useSafeAreaInsets();
const { colors } = useAppTheme();
return (
<View style={[styles.container, { paddingTop: safeAreaInsets.top, paddingBottom: safeAreaInsets.bottom, backgroundColor: colors.background }]}>
<LoginScreen />
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
});
export default App; export default App;

View File

@ -1,7 +1,7 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity } from 'react-native'; import { View, Text, TextInput, TouchableOpacity } from 'react-native';
import { CustomInputProps } from './CustomInput.props'; import { CustomInputProps } from './customInput.props';
import { getStyles } from './CustomInput.styles'; import { getStyles } from './customInput.styles';
import { useAppTheme } from '@theme'; import { useAppTheme } from '@theme';
export const CustomInput: React.FC<CustomInputProps> = ({ export const CustomInput: React.FC<CustomInputProps> = ({

View File

@ -1,2 +1,2 @@
export * from './CustomInput'; export * from './customInput';
export * from './CustomInput.props'; export * from './customInput.props';

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { TouchableOpacity, Text, ActivityIndicator } from 'react-native'; import { TouchableOpacity, Text, ActivityIndicator } from 'react-native';
import { PrimaryButtonProps } from './PrimaryButton.props'; import { PrimaryButtonProps } from './primaryButton.props';
import { styles } from './PrimaryButton.styles'; import { styles } from './primaryButton.styles';
import { colors } from '@theme'; import { colors } from '@theme';
export const PrimaryButton: React.FC<PrimaryButtonProps> = ({ export const PrimaryButton: React.FC<PrimaryButtonProps> = ({

View File

@ -1,2 +1,2 @@
export * from './PrimaryButton'; export * from './primaryButton';
export * from './PrimaryButton.props'; export * from './primaryButton.props';

View File

@ -1,7 +1,7 @@
import React from 'react'; import React from 'react';
import { TouchableOpacity, Text } from 'react-native'; import { TouchableOpacity, Text } from 'react-native';
import { SocialButtonProps } from './SocialButton.props'; import { SocialButtonProps } from './socialButton.props';
import { getStyles } from './SocialButton.styles'; import { getStyles } from './socialButton.styles';
import { useAppTheme } from '@theme'; import { useAppTheme } from '@theme';
import Svg, { Path } from 'react-native-svg'; import Svg, { Path } from 'react-native-svg';

View File

@ -1,2 +1,2 @@
export * from './SocialButton'; export * from './socialButton';
export * from './SocialButton.props'; export * from './socialButton.props';

View File

@ -0,0 +1,4 @@
export interface BadgeProps {
text: string;
variant?: 'primary' | 'success' | 'warning' | 'danger';
}

View File

@ -0,0 +1,30 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
const variantColors = {
primary: { bg: '#E8F5E9', text: '#05824C' },
success: { bg: '#E8F5E9', text: '#2E7D32' },
warning: { bg: '#FFF8E1', text: '#F57F17' },
danger: { bg: '#FFE7E7', text: '#D32F2F' },
};
export const getStyles = () => StyleSheet.create({
badge: {
paddingHorizontal: 8,
paddingVertical: 3,
borderRadius: 12,
alignSelf: 'flex-start',
},
text: {
fontSize: typography.fontSize.xs,
fontWeight: typography.fontWeight.semibold,
},
});
export const getVariantStyle = (_colors: any, variant: string) => {
const v = variantColors[variant as keyof typeof variantColors] || variantColors.primary;
return {
backgroundColor: v.bg,
color: v.text,
};
};

View File

@ -0,0 +1,17 @@
import React from 'react';
import { View, Text } from 'react-native';
import { BadgeProps } from './badge.props';
import { getStyles, getVariantStyle } from './badge.styles';
import { useAppTheme } from '@theme';
export const Badge: React.FC<BadgeProps> = ({ text, variant = 'primary' }) => {
const { colors } = useAppTheme();
const styles = getStyles();
const variantStyle = getVariantStyle(colors, variant);
return (
<View style={[styles.badge, { backgroundColor: variantStyle.backgroundColor }]}>
<Text style={[styles.text, { color: variantStyle.color }]}>{text}</Text>
</View>
);
};

View File

@ -0,0 +1,2 @@
export * from './badge';
export * from './badge.props';

View File

@ -0,0 +1,9 @@
export interface CatalogItemRowProps {
imageUrl: string;
title: string;
description: string;
price: number;
quantity: number;
onAdd: () => void;
onRemove: () => void;
}

View File

@ -0,0 +1,75 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flexDirection: 'row',
paddingVertical: 12,
paddingHorizontal: 16,
borderBottomWidth: 1,
borderBottomColor: colors.border,
backgroundColor: colors.background,
},
image: {
width: 70,
height: 70,
borderRadius: 8,
backgroundColor: colors.border,
justifyContent: 'center',
alignItems: 'center',
},
imagePlaceholder: {
fontSize: 28,
},
info: {
flex: 1,
marginLeft: 12,
justifyContent: 'center',
},
title: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
marginBottom: 2,
},
description: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
marginBottom: 4,
},
price: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.bold,
color: colors.text,
},
actions: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
marginLeft: 8,
},
stepperButton: {
width: 32,
height: 32,
borderRadius: 16,
backgroundColor: colors.primary,
justifyContent: 'center',
alignItems: 'center',
},
stepperButtonDisabled: {
backgroundColor: colors.border,
},
stepperButtonText: {
color: colors.white,
fontSize: 18,
fontWeight: typography.fontWeight.bold,
},
quantity: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
marginHorizontal: 12,
minWidth: 20,
textAlign: 'center',
},
});

View File

@ -0,0 +1,60 @@
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { CatalogItemRowProps } from './catalogItemRow.props';
import { getStyles } from './catalogItemRow.styles';
import { useAppTheme } from '@theme';
export const CatalogItemRow: React.FC<CatalogItemRowProps> = ({
imageUrl,
title,
description,
price,
quantity,
onAdd,
onRemove,
}) => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<View style={styles.container}>
<View style={styles.image}>
<Text style={styles.imagePlaceholder}>{imageUrl || '🍕'}</Text>
</View>
<View style={styles.info}>
<Text style={styles.title} numberOfLines={1}>{title}</Text>
<Text style={styles.description} numberOfLines={2}>{description}</Text>
<Text style={styles.price}>{price}</Text>
</View>
<View style={styles.actions}>
{quantity > 0 ? (
<>
<TouchableOpacity
style={[styles.stepperButton, quantity === 0 && styles.stepperButtonDisabled]}
onPress={onRemove}
activeOpacity={0.7}
>
<Text style={styles.stepperButtonText}>-</Text>
</TouchableOpacity>
<Text style={styles.quantity}>{quantity}</Text>
<TouchableOpacity
style={styles.stepperButton}
onPress={onAdd}
activeOpacity={0.7}
>
<Text style={styles.stepperButtonText}>+</Text>
</TouchableOpacity>
</>
) : (
<TouchableOpacity
style={styles.stepperButton}
onPress={onAdd}
activeOpacity={0.7}
>
<Text style={styles.stepperButtonText}>+</Text>
</TouchableOpacity>
)}
</View>
</View>
);
};

View File

@ -0,0 +1,2 @@
export * from './catalogItemRow';
export * from './catalogItemRow.props';

View File

@ -0,0 +1,6 @@
export interface CategoryChipProps {
icon: string;
label: string;
isSelected: boolean;
onPress: () => void;
}

View File

@ -0,0 +1,33 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 16,
paddingVertical: 10,
borderRadius: 24,
borderWidth: 1.5,
borderColor: colors.border,
backgroundColor: colors.background,
marginRight: 10,
},
selected: {
borderColor: colors.primary,
backgroundColor: '#E8F5E9',
},
icon: {
fontSize: 18,
marginRight: 6,
},
label: {
fontSize: typography.fontSize.sm,
fontWeight: typography.fontWeight.medium,
color: colors.textSecondary,
},
labelSelected: {
color: colors.primary,
fontWeight: typography.fontWeight.semibold,
},
});

View File

@ -0,0 +1,28 @@
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
import { CategoryChipProps } from './categoryChip.props';
import { getStyles } from './categoryChip.styles';
import { useAppTheme } from '@theme';
export const CategoryChip: React.FC<CategoryChipProps> = ({
icon,
label,
isSelected,
onPress,
}) => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<TouchableOpacity
style={[styles.container, isSelected && styles.selected]}
onPress={onPress}
activeOpacity={0.7}
>
<Text style={styles.icon}>{icon}</Text>
<Text style={[styles.label, isSelected && styles.labelSelected]}>
{label}
</Text>
</TouchableOpacity>
);
};

View File

@ -0,0 +1,2 @@
export * from './categoryChip';
export * from './categoryChip.props';

View File

@ -0,0 +1,7 @@
import { ReactNode } from 'react';
export interface HeaderProps {
title: string;
onBack?: () => void;
rightComponent?: ReactNode;
}

View File

@ -0,0 +1,36 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingHorizontal: 16,
paddingVertical: 12,
backgroundColor: colors.background,
borderBottomWidth: 1,
borderBottomColor: colors.border,
},
backButton: {
padding: 4,
marginRight: 8,
},
backText: {
fontSize: 24,
color: colors.text,
},
titleContainer: {
flex: 1,
alignItems: 'center',
},
title: {
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
},
rightContainer: {
minWidth: 40,
alignItems: 'flex-end',
},
});

View File

@ -0,0 +1,28 @@
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { HeaderProps } from './header.props';
import { getStyles } from './header.styles';
import { useAppTheme } from '@theme';
export const Header: React.FC<HeaderProps> = ({ title, onBack, rightComponent }) => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<View style={styles.container}>
<View style={styles.backButton}>
{onBack && (
<TouchableOpacity onPress={onBack} activeOpacity={0.7}>
<Text style={styles.backText}>{'<'}</Text>
</TouchableOpacity>
)}
</View>
<View style={styles.titleContainer}>
<Text style={styles.title} numberOfLines={1}>{title}</Text>
</View>
<View style={styles.rightContainer}>
{rightComponent}
</View>
</View>
);
};

View File

@ -0,0 +1,2 @@
export * from './header';
export * from './header.props';

View File

@ -1,3 +1,14 @@
export * from './CustomInput'; export * from './customInput';
export * from './PrimaryButton'; export * from './primaryButton';
export * from './SocialButton'; export * from './socialButton';
export * from './header';
export * from './searchBar';
export * from './providerCard';
export * from './catalogItemRow';
export * from './stepProgress';
export * from './badge';
export * from './quantitySelector';
export * from './categoryChip';
export * from './ratingStars';
export * from './orderHistoryCard';
export * from './paymentOption';

View File

@ -0,0 +1,2 @@
export * from './orderHistoryCard';
export * from './orderHistoryCard.props';

View File

@ -0,0 +1,9 @@
export interface OrderHistoryCardProps {
providerName: string;
providerImage: string;
orderDate: string;
status: string;
total: number;
onReorder?: () => void;
onDetails?: () => void;
}

View File

@ -0,0 +1,86 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
backgroundColor: colors.cardBg,
borderRadius: 12,
padding: 16,
marginHorizontal: 16,
marginVertical: 6,
borderWidth: 1,
borderColor: colors.border,
},
header: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 10,
},
image: {
width: 44,
height: 44,
borderRadius: 22,
backgroundColor: colors.border,
justifyContent: 'center',
alignItems: 'center',
},
imagePlaceholder: {
fontSize: 22,
},
headerInfo: {
flex: 1,
marginLeft: 12,
},
providerName: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
},
orderDate: {
fontSize: typography.fontSize.xs,
color: colors.textSecondary,
marginTop: 2,
},
statusBadge: {
alignSelf: 'flex-start',
paddingHorizontal: 10,
paddingVertical: 3,
borderRadius: 12,
backgroundColor: '#E8F5E9',
},
statusText: {
fontSize: typography.fontSize.xs,
fontWeight: typography.fontWeight.medium,
color: colors.primary,
},
footer: {
flexDirection: 'row',
justifyContent: 'space-between',
alignItems: 'center',
marginTop: 10,
paddingTop: 10,
borderTopWidth: 1,
borderTopColor: colors.border,
},
total: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.bold,
color: colors.text,
},
actions: {
flexDirection: 'row',
gap: 8,
},
actionButton: {
paddingHorizontal: 14,
paddingVertical: 6,
borderRadius: 8,
borderWidth: 1,
borderColor: colors.primary,
},
actionButtonText: {
fontSize: typography.fontSize.sm,
color: colors.primary,
fontWeight: typography.fontWeight.medium,
},
});

View File

@ -0,0 +1,58 @@
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { OrderHistoryCardProps } from './orderHistoryCard.props';
import { getStyles } from './orderHistoryCard.styles';
import { useAppTheme } from '@theme';
export const OrderHistoryCard: React.FC<OrderHistoryCardProps> = ({
providerName,
providerImage,
orderDate,
status,
total,
onReorder,
onDetails,
}) => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<View style={styles.container}>
<View style={styles.header}>
<View style={styles.image}>
<Text style={styles.imagePlaceholder}>{providerImage || '🏪'}</Text>
</View>
<View style={styles.headerInfo}>
<Text style={styles.providerName}>{providerName}</Text>
<Text style={styles.orderDate}>{orderDate}</Text>
</View>
<View style={styles.statusBadge}>
<Text style={styles.statusText}>{status}</Text>
</View>
</View>
<View style={styles.footer}>
<Text style={styles.total}>{total}</Text>
<View style={styles.actions}>
{onReorder && (
<TouchableOpacity
style={styles.actionButton}
onPress={onReorder}
activeOpacity={0.7}
>
<Text style={styles.actionButtonText}>Reorder</Text>
</TouchableOpacity>
)}
{onDetails && (
<TouchableOpacity
style={styles.actionButton}
onPress={onDetails}
activeOpacity={0.7}
>
<Text style={styles.actionButtonText}>Details</Text>
</TouchableOpacity>
)}
</View>
</View>
</View>
);
};

View File

@ -0,0 +1,2 @@
export * from './paymentOption';
export * from './paymentOption.props';

View File

@ -0,0 +1,6 @@
export interface PaymentOptionProps {
type: string;
label: string;
isSelected: boolean;
onSelect: () => void;
}

View File

@ -0,0 +1,48 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 14,
paddingHorizontal: 16,
borderWidth: 1.5,
borderColor: colors.border,
borderRadius: 12,
marginBottom: 10,
backgroundColor: colors.background,
},
selected: {
borderColor: colors.primary,
backgroundColor: '#E8F5E9',
},
icon: {
fontSize: 24,
marginRight: 12,
},
label: {
flex: 1,
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.medium,
color: colors.text,
},
radio: {
width: 22,
height: 22,
borderRadius: 11,
borderWidth: 2,
borderColor: colors.border,
justifyContent: 'center',
alignItems: 'center',
},
radioSelected: {
borderColor: colors.primary,
},
radioInner: {
width: 12,
height: 12,
borderRadius: 6,
backgroundColor: colors.primary,
},
});

View File

@ -0,0 +1,29 @@
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { PaymentOptionProps } from './paymentOption.props';
import { getStyles } from './paymentOption.styles';
import { useAppTheme } from '@theme';
export const PaymentOption: React.FC<PaymentOptionProps> = ({
type,
label,
isSelected,
onSelect,
}) => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<TouchableOpacity
style={[styles.container, isSelected && styles.selected]}
onPress={onSelect}
activeOpacity={0.7}
>
<Text style={styles.icon}>{type === 'UPI' ? '📱' : type === 'Card' ? '💳' : type === 'Wallet' ? '👛' : '💵'}</Text>
<Text style={styles.label}>{label}</Text>
<View style={[styles.radio, isSelected && styles.radioSelected]}>
{isSelected && <View style={styles.radioInner} />}
</View>
</TouchableOpacity>
);
};

View File

@ -0,0 +1,2 @@
export * from './providerCard';
export * from './providerCard.props';

View File

@ -0,0 +1,9 @@
export interface ProviderCardProps {
imageUrl: string;
name: string;
rating: number;
deliveryTime: string;
tag: string;
discountText?: string;
onPress?: () => void;
}

View File

@ -0,0 +1,69 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flexDirection: 'row',
backgroundColor: colors.cardBg,
borderRadius: 12,
padding: 12,
marginHorizontal: 16,
marginVertical: 6,
borderWidth: 1,
borderColor: colors.border,
},
image: {
width: 80,
height: 80,
borderRadius: 8,
backgroundColor: colors.border,
justifyContent: 'center',
alignItems: 'center',
},
imagePlaceholder: {
fontSize: 32,
},
info: {
flex: 1,
marginLeft: 12,
justifyContent: 'center',
},
name: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
marginBottom: 4,
},
row: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 2,
},
rating: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
marginRight: 8,
},
deliveryTime: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
},
tag: {
fontSize: typography.fontSize.xs,
color: colors.primary,
fontWeight: typography.fontWeight.medium,
},
discountBadge: {
alignSelf: 'flex-start',
backgroundColor: '#FFE7E7',
paddingHorizontal: 8,
paddingVertical: 2,
borderRadius: 4,
marginTop: 4,
},
discountText: {
fontSize: typography.fontSize.xs,
color: '#D32F2F',
fontWeight: typography.fontWeight.semibold,
},
});

View File

@ -0,0 +1,43 @@
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { ProviderCardProps } from './providerCard.props';
import { getStyles } from './providerCard.styles';
import { useAppTheme } from '@theme';
export const ProviderCard: React.FC<ProviderCardProps> = ({
imageUrl,
name,
rating,
deliveryTime,
tag,
discountText,
onPress,
}) => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<TouchableOpacity
style={styles.container}
onPress={onPress}
activeOpacity={0.8}
>
<View style={styles.image}>
<Text style={styles.imagePlaceholder}>{imageUrl || '🍽️'}</Text>
</View>
<View style={styles.info}>
<Text style={styles.name} numberOfLines={1}>{name}</Text>
<View style={styles.row}>
<Text style={styles.rating}> {rating.toFixed(1)}</Text>
<Text style={styles.deliveryTime}>🕐 {deliveryTime}</Text>
</View>
<Text style={styles.tag}>{tag}</Text>
{discountText && (
<View style={styles.discountBadge}>
<Text style={styles.discountText}>{discountText}</Text>
</View>
)}
</View>
</TouchableOpacity>
);
};

View File

@ -0,0 +1,2 @@
export * from './quantitySelector';
export * from './quantitySelector.props';

View File

@ -0,0 +1,7 @@
export interface QuantitySelectorProps {
value: number;
onIncrement: () => void;
onDecrement: () => void;
min?: number;
max?: number;
}

View File

@ -0,0 +1,33 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
},
button: {
width: 36,
height: 36,
borderRadius: 18,
backgroundColor: colors.primary,
justifyContent: 'center',
alignItems: 'center',
},
buttonDisabled: {
backgroundColor: colors.border,
},
buttonText: {
color: colors.white,
fontSize: 20,
fontWeight: typography.fontWeight.bold,
},
value: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
marginHorizontal: 16,
minWidth: 24,
textAlign: 'center',
},
});

View File

@ -0,0 +1,38 @@
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { QuantitySelectorProps } from './quantitySelector.props';
import { getStyles } from './quantitySelector.styles';
import { useAppTheme } from '@theme';
export const QuantitySelector: React.FC<QuantitySelectorProps> = ({
value,
onIncrement,
onDecrement,
min = 0,
max = 99,
}) => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<View style={styles.container}>
<TouchableOpacity
style={[styles.button, value <= min && styles.buttonDisabled]}
onPress={onDecrement}
disabled={value <= min}
activeOpacity={0.7}
>
<Text style={styles.buttonText}>-</Text>
</TouchableOpacity>
<Text style={styles.value}>{value}</Text>
<TouchableOpacity
style={[styles.button, value >= max && styles.buttonDisabled]}
onPress={onIncrement}
disabled={value >= max}
activeOpacity={0.7}
>
<Text style={styles.buttonText}>+</Text>
</TouchableOpacity>
</View>
);
};

View File

@ -0,0 +1,2 @@
export * from './ratingStars';
export * from './ratingStars.props';

View File

@ -0,0 +1,5 @@
export interface RatingStarsProps {
rating: number;
onRate?: (rating: number) => void;
size?: number;
}

View File

@ -0,0 +1,12 @@
import { StyleSheet } from 'react-native';
export const getStyles = () => StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
},
star: {
marginHorizontal: 4,
},
});

View File

@ -0,0 +1,29 @@
import React from 'react';
import { View, Text, TouchableOpacity } from 'react-native';
import { RatingStarsProps } from './ratingStars.props';
import { getStyles } from './ratingStars.styles';
export const RatingStars: React.FC<RatingStarsProps> = ({
rating,
onRate,
size = 32,
}) => {
const styles = getStyles();
return (
<View style={styles.container}>
{[1, 2, 3, 4, 5].map((star) => (
<TouchableOpacity
key={star}
onPress={() => onRate?.(star)}
disabled={!onRate}
activeOpacity={0.7}
>
<Text style={[styles.star, { fontSize: size }]}>
{star <= rating ? '⭐' : '☆'}
</Text>
</TouchableOpacity>
))}
</View>
);
};

View File

@ -0,0 +1,2 @@
export * from './searchBar';
export * from './searchBar.props';

View File

@ -0,0 +1,7 @@
export interface SearchBarProps {
value: string;
onChangeText: (text: string) => void;
placeholder?: string;
onFocus?: () => void;
onSubmit?: () => void;
}

View File

@ -0,0 +1,28 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.inputBg,
borderRadius: 12,
borderWidth: 1,
borderColor: colors.border,
paddingHorizontal: 12,
height: 48,
marginHorizontal: 16,
marginVertical: 8,
},
icon: {
fontSize: 18,
marginRight: 8,
},
input: {
flex: 1,
height: '100%',
color: colors.text,
fontSize: typography.fontSize.md,
paddingVertical: 0,
},
});

View File

@ -0,0 +1,34 @@
import React from 'react';
import { View, TextInput, Text } from 'react-native';
import { SearchBarProps } from './searchBar.props';
import { getStyles } from './searchBar.styles';
import { useAppTheme } from '@theme';
export const SearchBar: React.FC<SearchBarProps> = ({
value,
onChangeText,
placeholder = 'Search...',
onFocus,
onSubmit,
}) => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<View style={styles.container}>
<Text style={styles.icon}>🔍</Text>
<TextInput
style={styles.input}
value={value}
onChangeText={onChangeText}
placeholder={placeholder}
placeholderTextColor={colors.placeholder}
onFocus={onFocus}
onSubmitEditing={onSubmit}
returnKeyType="search"
autoCapitalize="none"
autoCorrect={false}
/>
</View>
);
};

View File

@ -0,0 +1,2 @@
export * from './stepProgress';
export * from './stepProgress.props';

View File

@ -0,0 +1,4 @@
export interface StepProgressProps {
steps: { key: string; label: string }[];
activeStep: number;
}

View File

@ -0,0 +1,55 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'center',
paddingVertical: 16,
paddingHorizontal: 24,
},
stepContainer: {
alignItems: 'center',
flex: 1,
},
circle: {
width: 28,
height: 28,
borderRadius: 14,
backgroundColor: colors.border,
justifyContent: 'center',
alignItems: 'center',
},
circleActive: {
backgroundColor: colors.primary,
},
circleCompleted: {
backgroundColor: colors.primary,
},
circleText: {
fontSize: typography.fontSize.xs,
color: colors.white,
fontWeight: typography.fontWeight.bold,
},
label: {
fontSize: typography.fontSize.xs,
color: colors.textSecondary,
marginTop: 4,
textAlign: 'center',
},
labelActive: {
color: colors.primary,
fontWeight: typography.fontWeight.medium,
},
connector: {
flex: 1,
height: 2,
backgroundColor: colors.border,
marginHorizontal: -4,
marginBottom: 20,
},
connectorActive: {
backgroundColor: colors.primary,
},
});

View File

@ -0,0 +1,45 @@
import React from 'react';
import { View, Text } from 'react-native';
import { StepProgressProps } from './stepProgress.props';
import { getStyles } from './stepProgress.styles';
import { useAppTheme } from '@theme';
export const StepProgress: React.FC<StepProgressProps> = ({ steps, activeStep }) => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<View style={styles.container}>
{steps.map((step, index) => (
<React.Fragment key={step.key}>
<View style={styles.stepContainer}>
<View
style={[
styles.circle,
index <= activeStep && styles.circleActive,
]}
>
<Text style={styles.circleText}>{index + 1}</Text>
</View>
<Text
style={[
styles.label,
index <= activeStep && styles.labelActive,
]}
>
{step.label}
</Text>
</View>
{index < steps.length - 1 && (
<View
style={[
styles.connector,
index < activeStep && styles.connectorActive,
]}
/>
)}
</React.Fragment>
))}
</View>
);
};

View File

@ -29,51 +29,4 @@ export const getStyles = (colors: any) => StyleSheet.create({
formContainer: { formContainer: {
width: '100%', width: '100%',
}, },
row: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
marginBottom: 24,
marginTop: 4,
},
rememberMeContainer: {
flexDirection: 'row',
alignItems: 'center',
},
checkboxText: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
fontWeight: typography.fontWeight.medium,
},
dividerContainer: {
flexDirection: 'row',
alignItems: 'center',
marginVertical: 24,
},
line: {
flex: 1,
height: 1,
backgroundColor: colors.border,
},
dividerText: {
marginHorizontal: 16,
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
},
socialContainer: {
flexDirection: 'row',
justifyContent: 'space-between',
gap: 16,
marginBottom: 40,
},
footerText: {
textAlign: 'center',
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
},
signUpLink: {
color: colors.primary,
fontWeight: typography.fontWeight.bold,
},
}); });

View File

@ -5,63 +5,39 @@ import {
KeyboardAvoidingView, KeyboardAvoidingView,
Platform, Platform,
ScrollView, ScrollView,
TouchableOpacity,
Alert,
} from 'react-native'; } from 'react-native';
import { getStyles } from './LoginScreen.styles'; import { useNavigation } from '@react-navigation/native';
import { CustomInput, PrimaryButton, SocialButton } from '@components'; import { StackNavigationProp } from '@react-navigation/stack';
import CheckBox from '@react-native-community/checkbox'; import { getStyles } from './loginScreen.styles';
import { CustomInput, PrimaryButton } from '@components';
import { useAppTheme } from '@theme'; import { useAppTheme } from '@theme';
import { useAppDispatch } from '../../../hooks/useAppDispatch';
import { loginWithPhone } from '../../../store/authSlice';
import { AuthStackParamList } from '../../../navigation/authStack';
type LoginNavProp = StackNavigationProp<AuthStackParamList, 'LoginScreen'>;
export const LoginScreen: React.FC = () => { export const LoginScreen: React.FC = () => {
const { colors } = useAppTheme(); const { colors } = useAppTheme();
const styles = getStyles(colors); const styles = getStyles(colors);
const navigation = useNavigation<LoginNavProp>();
const dispatch = useAppDispatch();
const [mobileNumber, setMobileNumber] = useState(''); const [mobileNumber, setMobileNumber] = useState('');
const [password, setPassword] = useState(''); const [error, setError] = useState<string | undefined>(undefined);
const [rememberMe, setRememberMe] = useState(false);
const [isLoading, setIsLoading] = useState(false);
const [errors, setErrors] = useState<{ mobile?: string; password?: string }>({});
const validate = () => {
const newErrors: { mobile?: string; password?: string } = {};
if (!mobileNumber) {
newErrors.mobile = 'Mobile number is required';
} else if (mobileNumber.length < 9) {
newErrors.mobile = 'Please enter a valid mobile number';
}
if (!password) {
newErrors.password = 'Password is required';
} else if (password.length < 6) {
newErrors.password = 'Password must be at least 6 characters';
}
setErrors(newErrors);
return Object.keys(newErrors).length === 0;
};
const handleLogin = () => { const handleLogin = () => {
if (!validate()) return; if (!mobileNumber) {
setError('Mobile number is required');
setIsLoading(true); return;
// Simulate API request }
setTimeout(() => { if (mobileNumber.length < 10) {
setIsLoading(false); setError('Please enter a valid mobile number');
Alert.alert('Success', 'Logged in successfully!'); return;
}, 1500); }
}; setError(undefined);
dispatch(loginWithPhone(mobileNumber));
const handleSocialLogin = (provider: 'google' | 'apple') => { navigation.navigate('OtpScreen', { mobileNumber });
Alert.alert('Social Login', `Initiating ${provider} login...`);
};
const handleForgotPassword = () => {
Alert.alert('Forgot Password', 'Redirecting to reset password...');
};
const handleSignUp = () => {
Alert.alert('Sign Up', 'Redirecting to registration...');
}; };
return ( return (
@ -74,7 +50,7 @@ export const LoginScreen: React.FC = () => {
showsVerticalScrollIndicator={false} showsVerticalScrollIndicator={false}
> >
<View style={styles.headerContainer}> <View style={styles.headerContainer}>
<Text style={styles.title}>Welcome back 👋</Text> <Text style={styles.title}>Welcome back</Text>
<Text style={styles.subtitle}>Login to continue</Text> <Text style={styles.subtitle}>Login to continue</Text>
</View> </View>
@ -86,68 +62,16 @@ export const LoginScreen: React.FC = () => {
value={mobileNumber} value={mobileNumber}
onChangeText={(text) => { onChangeText={(text) => {
setMobileNumber(text); setMobileNumber(text);
if (errors.mobile) setErrors({ ...errors, mobile: undefined }); if (error) setError(undefined);
}} }}
error={errors.mobile} error={error}
/> />
<CustomInput
label="Password"
placeholder="••••••••"
isPassword
rightActionText="Forgot?"
onRightActionPress={handleForgotPassword}
value={password}
onChangeText={(text) => {
setPassword(text);
if (errors.password) setErrors({ ...errors, password: undefined });
}}
error={errors.password}
/>
<View style={styles.row}>
<TouchableOpacity
style={styles.rememberMeContainer}
onPress={() => setRememberMe(!rememberMe)}
activeOpacity={0.8}
>
<CheckBox
value={rememberMe}
onValueChange={setRememberMe}
tintColors={{ true: colors.primary, false: colors.textSecondary }}
tintColor={colors.textSecondary}
onTintColor={colors.primary}
onCheckColor={colors.white}
onFillColor={colors.primary}
style={{ marginRight: 8 }}
/>
<Text style={styles.checkboxText}>Remember me</Text>
</TouchableOpacity>
</View>
<PrimaryButton <PrimaryButton
title="Login" title="Login"
onPress={handleLogin} onPress={handleLogin}
isLoading={isLoading} style={{ marginTop: 12 }}
style={{ marginBottom: 12 }}
/> />
<View style={styles.dividerContainer}>
<View style={styles.line} />
<Text style={styles.dividerText}>or continue with</Text>
<View style={styles.line} />
</View>
<View style={styles.socialContainer}>
<SocialButton provider="google" onPress={() => handleSocialLogin('google')} />
<SocialButton provider="apple" onPress={() => handleSocialLogin('apple')} />
</View>
<TouchableOpacity onPress={handleSignUp} activeOpacity={0.8}>
<Text style={styles.footerText}>
Dont have an account? <Text style={styles.signUpLink}>Sign up</Text>
</Text>
</TouchableOpacity>
</View> </View>
</ScrollView> </ScrollView>
</KeyboardAvoidingView> </KeyboardAvoidingView>

View File

@ -1,2 +1,2 @@
export * from './LoginScreen'; export * from './loginScreen';
export * from './LoginScreen.styles'; export * from './loginScreen.styles';

View File

@ -0,0 +1,68 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
content: {
paddingBottom: 40,
},
profileCard: {
alignItems: 'center',
paddingVertical: 32,
borderBottomWidth: 1,
borderBottomColor: colors.border,
marginHorizontal: 16,
},
avatar: {
width: 72,
height: 72,
borderRadius: 36,
backgroundColor: colors.primary,
justifyContent: 'center',
alignItems: 'center',
marginBottom: 12,
},
avatarText: {
fontSize: 28,
fontWeight: typography.fontWeight.bold,
color: '#FFFFFF',
},
profileName: {
fontSize: typography.fontSize.xl,
fontWeight: typography.fontWeight.bold,
color: colors.text,
marginBottom: 4,
},
profilePhone: {
fontSize: typography.fontSize.md,
color: colors.textSecondary,
},
menuSection: {
marginTop: 16,
paddingHorizontal: 16,
},
menuItem: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 16,
borderBottomWidth: 1,
borderBottomColor: colors.border,
},
menuIcon: {
fontSize: 20,
marginRight: 14,
},
menuLabel: {
flex: 1,
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.medium,
color: colors.text,
},
menuArrow: {
fontSize: 18,
color: colors.textSecondary,
},
});

View File

@ -0,0 +1,60 @@
import React from 'react';
import { View, Text, TouchableOpacity, ScrollView } from 'react-native';
import { getStyles } from './accountScreen.styles';
import { Header, PrimaryButton } from '@components';
import { useAppTheme } from '@theme';
import { useAppDispatch, useAppSelector } from '../../../hooks/useAppDispatch';
import { logout } from '../../../store/authSlice';
const MENU_ITEMS = [
{ icon: '📍', label: 'My Addresses' },
{ icon: '💳', label: 'Payment Methods' },
{ icon: '🔔', label: 'Notifications' },
{ icon: '❓', label: 'Help & Support' },
{ icon: '📋', label: 'Terms & Conditions' },
{ icon: '🔒', label: 'Privacy Policy' },
];
export const AccountScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const dispatch = useAppDispatch();
const user = useAppSelector((state) => state.auth.user);
return (
<View style={styles.container}>
<Header title="Account" />
<ScrollView contentContainerStyle={styles.content}>
<View style={styles.profileCard}>
<View style={styles.avatar}>
<Text style={styles.avatarText}>
{user?.name?.charAt(0)?.toUpperCase() || 'U'}
</Text>
</View>
<Text style={styles.profileName}>{user?.name || 'User'}</Text>
<Text style={styles.profilePhone}>{user?.mobileNumber || '+91 98765 43210'}</Text>
</View>
<View style={styles.menuSection}>
{MENU_ITEMS.map((item, index) => (
<TouchableOpacity
key={index}
style={styles.menuItem}
activeOpacity={0.7}
>
<Text style={styles.menuIcon}>{item.icon}</Text>
<Text style={styles.menuLabel}>{item.label}</Text>
<Text style={styles.menuArrow}>{'>'}</Text>
</TouchableOpacity>
))}
</View>
<PrimaryButton
title="Logout"
onPress={() => dispatch(logout())}
style={{ marginTop: 24 }}
/>
</ScrollView>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './accountScreen';

View File

@ -0,0 +1,109 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
list: {
paddingBottom: 100,
},
cartItem: {
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-between',
paddingVertical: 14,
paddingHorizontal: 16,
borderBottomWidth: 1,
borderBottomColor: colors.border,
},
itemInfo: {
flex: 1,
},
itemTitle: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.medium,
color: colors.text,
marginBottom: 4,
},
itemPrice: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.bold,
color: colors.text,
},
footer: {
padding: 16,
},
couponRow: {
flexDirection: 'row',
alignItems: 'center',
marginBottom: 20,
},
couponInput: {
flex: 1,
height: 48,
borderWidth: 1,
borderColor: colors.border,
borderRadius: 12,
paddingHorizontal: 16,
fontSize: typography.fontSize.md,
color: colors.text,
backgroundColor: colors.inputBg,
marginRight: 8,
},
applyButton: {
paddingHorizontal: 20,
paddingVertical: 14,
borderRadius: 12,
backgroundColor: colors.primary,
},
applyButtonText: {
color: '#FFFFFF',
fontWeight: typography.fontWeight.semibold,
fontSize: typography.fontSize.sm,
},
feeRow: {
flexDirection: 'row',
justifyContent: 'space-between',
marginBottom: 8,
},
feeLabel: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
},
feeValue: {
fontSize: typography.fontSize.sm,
fontWeight: typography.fontWeight.medium,
color: colors.text,
},
totalRow: {
borderTopWidth: 1,
borderTopColor: colors.border,
paddingTop: 12,
marginTop: 4,
},
totalLabel: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.bold,
color: colors.text,
},
totalValue: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.bold,
color: colors.text,
},
bottomPanel: {
flexDirection: 'row',
alignItems: 'center',
padding: 16,
borderTopWidth: 1,
borderTopColor: colors.border,
backgroundColor: colors.background,
},
bottomTotal: {
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.bold,
color: colors.text,
},
});

View File

@ -0,0 +1,110 @@
import React, { useState } from 'react';
import {
View,
Text,
FlatList,
TextInput,
TouchableOpacity,
} from 'react-native';
import { getStyles } from './cartScreen.styles';
import { Header, QuantitySelector, PrimaryButton } from '@components';
import { useAppTheme } from '@theme';
import { useAppDispatch, useAppSelector } from '../../../hooks/useAppDispatch';
import { removeItem, applyCoupon, removeCoupon, selectCartTotal } from '../../../store/cartSlice';
export const CartScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const dispatch = useAppDispatch();
const { items, couponCode } = useAppSelector((state) => state.cart);
const totals = useAppSelector(selectCartTotal);
const [couponInput, setCouponInput] = useState('');
return (
<View style={styles.container}>
<Header title="Cart" onBack={() => {}} />
<FlatList
data={items}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={styles.cartItem}>
<View style={styles.itemInfo}>
<Text style={styles.itemTitle}>{item.item.title}</Text>
<Text style={styles.itemPrice}>{item.item.price}</Text>
</View>
<QuantitySelector
value={item.quantity}
onIncrement={() => {}}
onDecrement={() => dispatch(removeItem(item.item.id))}
/>
</View>
)}
ListFooterComponent={
<View style={styles.footer}>
<View style={styles.couponRow}>
<TextInput
style={styles.couponInput}
placeholder="Enter coupon code"
placeholderTextColor={colors.placeholder}
value={couponInput}
onChangeText={setCouponInput}
/>
<TouchableOpacity
style={styles.applyButton}
onPress={() => {
if (couponCode) {
dispatch(removeCoupon());
setCouponInput('');
} else if (couponInput) {
dispatch(applyCoupon(couponInput));
}
}}
activeOpacity={0.7}
>
<Text style={styles.applyButtonText}>
{couponCode ? 'Remove' : 'Apply'}
</Text>
</TouchableOpacity>
</View>
<View style={styles.feeRow}>
<Text style={styles.feeLabel}>Subtotal</Text>
<Text style={styles.feeValue}>{totals.subtotal}</Text>
</View>
<View style={styles.feeRow}>
<Text style={styles.feeLabel}>Delivery Fee</Text>
<Text style={styles.feeValue}>{totals.deliveryFee}</Text>
</View>
<View style={styles.feeRow}>
<Text style={styles.feeLabel}>Platform Fee</Text>
<Text style={styles.feeValue}>{totals.platformFee}</Text>
</View>
{totals.discount > 0 && (
<View style={styles.feeRow}>
<Text style={[styles.feeLabel, { color: colors.primary }]}>
Discount
</Text>
<Text style={[styles.feeValue, { color: colors.primary }]}>
-{totals.discount}
</Text>
</View>
)}
<View style={[styles.feeRow, styles.totalRow]}>
<Text style={styles.totalLabel}>Total</Text>
<Text style={styles.totalValue}>{totals.total}</Text>
</View>
</View>
}
contentContainerStyle={styles.list}
/>
<View style={styles.bottomPanel}>
<Text style={styles.bottomTotal}>Total: {totals.total}</Text>
<PrimaryButton
title="View Bill"
onPress={() => {}}
style={{ flex: 1, marginLeft: 12 }}
/>
</View>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './cartScreen';

View File

@ -0,0 +1,92 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
content: {
padding: 16,
},
addressCard: {
backgroundColor: colors.cardBg,
borderRadius: 12,
padding: 16,
borderWidth: 1,
borderColor: colors.border,
marginBottom: 24,
},
addressLabel: {
fontSize: typography.fontSize.xs,
color: colors.textSecondary,
marginBottom: 4,
},
addressText: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
marginBottom: 4,
},
addressSubtext: {
fontSize: typography.fontSize.sm,
color: colors.primary,
fontWeight: typography.fontWeight.medium,
},
sectionTitle: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
marginBottom: 12,
},
optionCard: {
flexDirection: 'row',
alignItems: 'center',
padding: 16,
borderRadius: 12,
borderWidth: 1.5,
borderColor: colors.border,
marginBottom: 10,
backgroundColor: colors.background,
},
optionCardSelected: {
borderColor: colors.primary,
backgroundColor: '#E8F5E9',
},
optionInfo: {
flex: 1,
},
optionTitle: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.medium,
color: colors.text,
marginBottom: 2,
},
optionSubtext: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
},
radio: {
width: 22,
height: 22,
borderRadius: 11,
borderWidth: 2,
borderColor: colors.border,
justifyContent: 'center',
alignItems: 'center',
},
radioSelected: {
borderColor: colors.primary,
},
radioInner: {
width: 12,
height: 12,
borderRadius: 6,
backgroundColor: colors.primary,
},
footer: {
padding: 16,
borderTopWidth: 1,
borderTopColor: colors.border,
},
});

View File

@ -0,0 +1,76 @@
import React, { useState } from 'react';
import {
View,
Text,
TouchableOpacity,
ScrollView,
} from 'react-native';
import { getStyles } from './checkoutAddressScreen.styles';
import { Header, StepProgress, PrimaryButton } from '@components';
import { useAppTheme } from '@theme';
const CHECKOUT_STEPS = [
{ key: 'address', label: 'Address' },
{ key: 'payment', label: 'Payment' },
{ key: 'confirm', label: 'Confirm' },
];
export const CheckoutAddressScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const [deliveryOption, setDeliveryOption] = useState<'standard' | 'express'>('standard');
return (
<View style={styles.container}>
<Header title="Checkout" onBack={() => {}} />
<StepProgress steps={CHECKOUT_STEPS} activeStep={0} />
<ScrollView contentContainerStyle={styles.content}>
<View style={styles.addressCard}>
<Text style={styles.addressLabel}>Deliver to</Text>
<Text style={styles.addressText}>
Koramangala 4th Block, Bengaluru, 560034
</Text>
<Text style={styles.addressSubtext}>Home</Text>
</View>
<Text style={styles.sectionTitle}>Delivery Option</Text>
<TouchableOpacity
style={[
styles.optionCard,
deliveryOption === 'standard' && styles.optionCardSelected,
]}
onPress={() => setDeliveryOption('standard')}
activeOpacity={0.7}
>
<View style={styles.optionInfo}>
<Text style={styles.optionTitle}>Standard Delivery</Text>
<Text style={styles.optionSubtext}>Free 25-30 min</Text>
</View>
<View style={[styles.radio, deliveryOption === 'standard' && styles.radioSelected]}>
{deliveryOption === 'standard' && <View style={styles.radioInner} />}
</View>
</TouchableOpacity>
<TouchableOpacity
style={[
styles.optionCard,
deliveryOption === 'express' && styles.optionCardSelected,
]}
onPress={() => setDeliveryOption('express')}
activeOpacity={0.7}
>
<View style={styles.optionInfo}>
<Text style={styles.optionTitle}>Express Delivery</Text>
<Text style={styles.optionSubtext}>40 10-15 min</Text>
</View>
<View style={[styles.radio, deliveryOption === 'express' && styles.radioSelected]}>
{deliveryOption === 'express' && <View style={styles.radioInner} />}
</View>
</TouchableOpacity>
</ScrollView>
<View style={styles.footer}>
<PrimaryButton title="Continue to Payment" onPress={() => {}} />
</View>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './checkoutAddressScreen';

View File

@ -0,0 +1,23 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
content: {
padding: 16,
},
sectionTitle: {
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.bold,
color: colors.text,
marginBottom: 16,
},
footer: {
padding: 16,
borderTopWidth: 1,
borderTopColor: colors.border,
},
});

View File

@ -0,0 +1,46 @@
import React, { useState } from 'react';
import { View, Text, ScrollView } from 'react-native';
import { getStyles } from './checkoutPaymentScreen.styles';
import { Header, StepProgress, PaymentOption, PrimaryButton } from '@components';
import { useAppTheme } from '@theme';
const CHECKOUT_STEPS = [
{ key: 'address', label: 'Address' },
{ key: 'payment', label: 'Payment' },
{ key: 'confirm', label: 'Confirm' },
];
const PAYMENT_METHODS = [
{ id: 'upi', type: 'UPI' as const, label: 'UPI (Google Pay, PhonePe)' },
{ id: 'card', type: 'Card' as const, label: 'Credit / Debit Card' },
{ id: 'wallet', type: 'Wallet' as const, label: 'Wallet' },
{ id: 'cod', type: 'COD' as const, label: 'Cash on Delivery' },
];
export const CheckoutPaymentScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const [selectedMethod, setSelectedMethod] = useState('upi');
return (
<View style={styles.container}>
<Header title="Checkout" onBack={() => {}} />
<StepProgress steps={CHECKOUT_STEPS} activeStep={1} />
<ScrollView contentContainerStyle={styles.content}>
<Text style={styles.sectionTitle}>Select Payment Method</Text>
{PAYMENT_METHODS.map((method) => (
<PaymentOption
key={method.id}
type={method.type}
label={method.label}
isSelected={selectedMethod === method.id}
onSelect={() => setSelectedMethod(method.id)}
/>
))}
</ScrollView>
<View style={styles.footer}>
<PrimaryButton title="Place Order" onPress={() => {}} />
</View>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './checkoutPaymentScreen';

View File

@ -0,0 +1,62 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
paddingHorizontal: 24,
},
scrollContainer: {
flexGrow: 1,
paddingTop: 60,
paddingBottom: 24,
},
title: {
fontSize: typography.fontSize.xxl,
fontWeight: typography.fontWeight.bold,
color: colors.text,
marginBottom: 8,
},
subtitle: {
fontSize: typography.fontSize.md,
color: colors.textSecondary,
marginBottom: 32,
},
form: {
marginBottom: 24,
},
labelText: {
fontSize: typography.fontSize.sm,
fontWeight: typography.fontWeight.medium,
color: colors.textSecondary,
marginBottom: 10,
marginTop: 4,
},
labelRow: {
flexDirection: 'row',
gap: 12,
marginBottom: 16,
},
labelChip: {
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 20,
borderWidth: 1.5,
borderColor: colors.border,
backgroundColor: colors.background,
},
labelChipSelected: {
borderColor: colors.primary,
backgroundColor: '#E8F5E9',
},
labelChipText: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
fontWeight: typography.fontWeight.medium,
},
labelChipTextSelected: {
color: colors.primary,
fontWeight: typography.fontWeight.semibold,
},
});

View File

@ -0,0 +1,95 @@
import React, { useState } from 'react';
import {
View,
Text,
KeyboardAvoidingView,
Platform,
ScrollView,
TouchableOpacity,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { StackNavigationProp } from '@react-navigation/stack';
import { getStyles } from './completeProfileScreen.styles';
import { CustomInput, PrimaryButton } from '@components';
import { useAppTheme } from '@theme';
import { useAppDispatch } from '../../../hooks/useAppDispatch';
import { completeProfile } from '../../../store/authSlice';
import { AuthStackParamList } from '../../../navigation/authStack';
type NavProp = StackNavigationProp<AuthStackParamList, 'CompleteProfileScreen'>;
const LOCATION_LABELS = ['Home', 'Work', 'Other'];
export const CompleteProfileScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const navigation = useNavigation<NavProp>();
const dispatch = useAppDispatch();
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [selectedLabel, setSelectedLabel] = useState('Home');
const handleSave = () => {
dispatch(completeProfile({ name, email, locationLabels: [selectedLabel] }));
navigation.navigate('PreferencesScreen');
};
return (
<KeyboardAvoidingView
style={styles.container}
behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
>
<ScrollView contentContainerStyle={styles.scrollContainer}>
<Text style={styles.title}>Complete Profile</Text>
<Text style={styles.subtitle}>Tell us about yourself</Text>
<View style={styles.form}>
<CustomInput
label="Name"
placeholder="John Doe"
value={name}
onChangeText={setName}
/>
<CustomInput
label="Email"
placeholder="john@example.com"
keyboardType="email-address"
value={email}
onChangeText={setEmail}
/>
<Text style={styles.labelText}>Location Label</Text>
<View style={styles.labelRow}>
{LOCATION_LABELS.map((label) => (
<TouchableOpacity
key={label}
style={[
styles.labelChip,
selectedLabel === label && styles.labelChipSelected,
]}
onPress={() => setSelectedLabel(label)}
activeOpacity={0.7}
>
<Text
style={[
styles.labelChipText,
selectedLabel === label && styles.labelChipTextSelected,
]}
>
{label}
</Text>
</TouchableOpacity>
))}
</View>
</View>
<PrimaryButton
title="Save & Continue"
onPress={handleSave}
disabled={!name || !email}
/>
</ScrollView>
</KeyboardAvoidingView>
);
};

View File

@ -0,0 +1 @@
export * from './completeProfileScreen';

View File

@ -0,0 +1,62 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
content: {
padding: 16,
paddingBottom: 40,
},
sectionTitle: {
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.bold,
color: colors.text,
marginBottom: 16,
marginTop: 8,
},
faqItem: {
backgroundColor: colors.cardBg,
borderRadius: 12,
padding: 16,
marginBottom: 10,
borderWidth: 1,
borderColor: colors.border,
},
faqQuestion: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
marginBottom: 4,
},
faqAnswer: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
lineHeight: 20,
},
supportCard: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.cardBg,
borderRadius: 12,
padding: 16,
marginBottom: 10,
borderWidth: 1,
borderColor: colors.border,
},
supportIcon: {
fontSize: 24,
marginRight: 12,
},
supportLabel: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
},
supportValue: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
},
});

View File

@ -0,0 +1,48 @@
import React from 'react';
import { View, Text, ScrollView, TouchableOpacity } from 'react-native';
import { getStyles } from './helpSupportScreen.styles';
import { Header } from '@components';
import { useAppTheme } from '@theme';
const FAQS = [
{ q: 'How do I track my order?', a: 'Go to Orders tab and tap on your active order to see tracking.' },
{ q: 'What is the delivery time?', a: 'Standard delivery takes 25-30 min, Express takes 10-15 min.' },
{ q: 'How do I cancel an order?', a: 'Contact support to cancel an order before it is dispatched.' },
{ q: 'What payment methods are accepted?', a: 'UPI, Credit/Debit Card, Wallet, and Cash on Delivery.' },
];
export const HelpSupportScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<View style={styles.container}>
<Header title="Help & Support" />
<ScrollView contentContainerStyle={styles.content}>
<Text style={styles.sectionTitle}>Frequently Asked Questions</Text>
{FAQS.map((faq, index) => (
<View key={index} style={styles.faqItem}>
<Text style={styles.faqQuestion}>{faq.q}</Text>
<Text style={styles.faqAnswer}>{faq.a}</Text>
</View>
))}
<Text style={styles.sectionTitle}>Contact Support</Text>
<TouchableOpacity style={styles.supportCard} activeOpacity={0.7}>
<Text style={styles.supportIcon}>📞</Text>
<View>
<Text style={styles.supportLabel}>Call us</Text>
<Text style={styles.supportValue}>+91 1800 123 4567</Text>
</View>
</TouchableOpacity>
<TouchableOpacity style={styles.supportCard} activeOpacity={0.7}>
<Text style={styles.supportIcon}></Text>
<View>
<Text style={styles.supportLabel}>Email us</Text>
<Text style={styles.supportValue}>support@sgdelivery.com</Text>
</View>
</TouchableOpacity>
</ScrollView>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './helpSupportScreen';

View File

@ -0,0 +1,64 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
addressBar: {
paddingHorizontal: 16,
paddingTop: 12,
paddingBottom: 4,
},
addressLabel: {
fontSize: typography.fontSize.xs,
color: colors.textSecondary,
marginBottom: 2,
},
addressRow: {
flexDirection: 'row',
alignItems: 'center',
},
addressText: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
flex: 1,
},
dropdownArrow: {
fontSize: 12,
color: colors.textSecondary,
marginLeft: 4,
},
banner: {
backgroundColor: '#05824C',
marginHorizontal: 16,
borderRadius: 12,
padding: 20,
marginVertical: 8,
},
bannerText: {
fontSize: typography.fontSize.xxl,
fontWeight: typography.fontWeight.bold,
color: '#FFFFFF',
},
bannerSubtext: {
fontSize: typography.fontSize.sm,
color: '#FFFFFF',
opacity: 0.9,
marginTop: 4,
},
chipRow: {
paddingHorizontal: 16,
paddingVertical: 12,
},
sectionTitle: {
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.bold,
color: colors.text,
paddingHorizontal: 16,
marginBottom: 8,
marginTop: 4,
},
});

View File

@ -0,0 +1,106 @@
import React, { useState, useEffect, useCallback } from 'react';
import {
View,
Text,
ScrollView,
TouchableOpacity,
FlatList,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import { BottomTabNavigationProp } from '@react-navigation/bottom-tabs';
import { getStyles } from './homeScreen.styles';
import { SearchBar, ProviderCard, CategoryChip } from '@components';
import { useAppTheme } from '@theme';
import { deliveryService } from '../../../services/deliveryService';
import { Provider } from '../../../interfaces';
import { AppStackParamList } from '../../../navigation/appStack';
type NavProp = BottomTabNavigationProp<AppStackParamList, 'HomeScreen'>;
const CATEGORIES = [
{ key: 'all', icon: '🏠', label: 'All' },
{ key: 'Food', icon: '🍔', label: 'Food' },
{ key: 'Groceries', icon: '🛒', label: 'Groceries' },
{ key: 'Pharmacy', icon: '💊', label: 'Pharmacy' },
{ key: 'More', icon: '📦', label: 'More' },
];
export const HomeScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const navigation = useNavigation<NavProp>();
const [providers, setProviders] = useState<Provider[]>([]);
const [selectedCategory, setSelectedCategory] = useState('all');
useEffect(() => {
deliveryService.getProviders().then(setProviders);
}, []);
const filteredProviders = selectedCategory === 'all'
? providers
: providers.filter((p) => p.tag === selectedCategory);
const handleSearchFocus = useCallback(() => {
navigation.navigate('SearchScreen');
}, [navigation]);
return (
<ScrollView style={styles.container} showsVerticalScrollIndicator={false}>
<View style={styles.addressBar}>
<Text style={styles.addressLabel}>Deliver to</Text>
<TouchableOpacity style={styles.addressRow} activeOpacity={0.7}>
<Text style={styles.addressText}>Koramangala 4th Block, B... </Text>
<Text style={styles.dropdownArrow}></Text>
</TouchableOpacity>
</View>
<SearchBar
value=""
onChangeText={() => {}}
placeholder="Search providers or items..."
onFocus={handleSearchFocus}
/>
<View style={styles.banner}>
<Text style={styles.bannerText}>50% OFF</Text>
<Text style={styles.bannerSubtext}>on your first order</Text>
</View>
<FlatList
horizontal
data={CATEGORIES}
keyExtractor={(item) => item.key}
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.chipRow}
renderItem={({ item }) => (
<CategoryChip
icon={item.icon}
label={item.label}
isSelected={selectedCategory === item.key}
onPress={() => setSelectedCategory(item.key)}
/>
)}
/>
<Text style={styles.sectionTitle}>
{selectedCategory === 'all' ? 'Popular Providers' : selectedCategory}
</Text>
{filteredProviders.map((provider) => (
<ProviderCard
key={provider.id}
imageUrl={provider.imageUrl}
name={provider.name}
rating={provider.rating}
deliveryTime={provider.deliveryTime}
tag={provider.tag}
discountText={provider.discountText}
onPress={() => navigation.navigate('SearchScreen')}
/>
))}
<View style={{ height: 24 }} />
</ScrollView>
);
};

View File

@ -0,0 +1 @@
export * from './homeScreen';

View File

@ -1 +1,21 @@
export * from './LoginScreen'; export * from './loginScreen';
export * from './otpScreen';
export * from './setLocationScreen';
export * from './completeProfileScreen';
export * from './preferencesScreen';
export * from './onboardingCompleteScreen';
export * from './homeScreen';
export * from './searchScreen';
export * from './providerListScreen';
export * from './providerDetailsScreen';
export * from './cartScreen';
export * from './checkoutAddressScreen';
export * from './checkoutPaymentScreen';
export * from './orderConfirmedScreen';
export * from './orderTrackingScreen';
export * from './liveTrackingScreen';
export * from './orderDeliveredScreen';
export * from './myOrdersScreen';
export * from './offersScreen';
export * from './helpSupportScreen';
export * from './accountScreen';

View File

@ -0,0 +1 @@
export * from './liveTrackingScreen';

View File

@ -0,0 +1,72 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
mapPlaceholder: {
flex: 1,
backgroundColor: colors.inputBg,
justifyContent: 'center',
alignItems: 'center',
margin: 16,
borderRadius: 16,
borderWidth: 1,
borderColor: colors.border,
},
mapIcon: {
fontSize: 48,
marginBottom: 8,
},
mapText: {
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.semibold,
color: colors.textSecondary,
},
mapSubtext: {
fontSize: typography.fontSize.xs,
color: colors.placeholder,
marginTop: 4,
marginBottom: 16,
},
blinkingBadge: {
backgroundColor: '#4CAF50',
paddingHorizontal: 12,
paddingVertical: 4,
borderRadius: 12,
},
blinkingText: {
fontSize: typography.fontSize.sm,
color: '#FFFFFF',
fontWeight: typography.fontWeight.semibold,
},
agentSheet: {
padding: 20,
borderTopLeftRadius: 20,
borderTopRightRadius: 20,
backgroundColor: colors.cardBg,
borderTopWidth: 1,
borderTopColor: colors.border,
},
agentName: {
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.bold,
color: colors.text,
marginBottom: 4,
},
agentRating: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
marginBottom: 4,
},
agentVehicle: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
marginBottom: 16,
},
agentActions: {
flexDirection: 'row',
},
});

View File

@ -0,0 +1,43 @@
import React from 'react';
import { View, Text } from 'react-native';
import { getStyles } from './liveTrackingScreen.styles';
import { Header, PrimaryButton } from '@components';
import { useAppTheme } from '@theme';
export const LiveTrackingScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<View style={styles.container}>
<Header title="Live Tracking" onBack={() => {}} />
<View style={styles.mapPlaceholder}>
<Text style={styles.mapIcon}>🗺</Text>
<Text style={styles.mapText}>Live Map</Text>
<Text style={styles.mapSubtext}>
Delivery partner location tracking
</Text>
<View style={styles.blinkingBadge}>
<Text style={styles.blinkingText}> Live</Text>
</View>
</View>
<View style={styles.agentSheet}>
<Text style={styles.agentName}>Rahul Sharma</Text>
<Text style={styles.agentRating}> 4.8</Text>
<Text style={styles.agentVehicle}>KA-01-AB-1234 Honda Activa</Text>
<View style={styles.agentActions}>
<PrimaryButton
title="Call"
onPress={() => {}}
style={{ flex: 1, marginRight: 8 }}
/>
<PrimaryButton
title="Message"
onPress={() => {}}
style={{ flex: 1, marginLeft: 8 }}
/>
</View>
</View>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './myOrdersScreen';

View File

@ -0,0 +1,46 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
tabRow: {
flexDirection: 'row',
paddingHorizontal: 16,
paddingVertical: 8,
borderBottomWidth: 1,
borderBottomColor: colors.border,
},
tab: {
paddingHorizontal: 20,
paddingVertical: 8,
marginRight: 8,
borderRadius: 20,
backgroundColor: colors.inputBg,
},
tabActive: {
backgroundColor: colors.primary,
},
tabText: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
fontWeight: typography.fontWeight.medium,
},
tabTextActive: {
color: '#FFFFFF',
fontWeight: typography.fontWeight.semibold,
},
list: {
paddingVertical: 8,
},
empty: {
alignItems: 'center',
paddingTop: 60,
},
emptyText: {
fontSize: typography.fontSize.md,
color: colors.textSecondary,
},
});

View File

@ -0,0 +1,78 @@
import React, { useState } from 'react';
import {
View,
Text,
FlatList,
TouchableOpacity,
} from 'react-native';
import { getStyles } from './myOrdersScreen.styles';
import { Header, OrderHistoryCard } from '@components';
import { useAppTheme } from '@theme';
const TABS = ['All', 'Ongoing', 'Completed'];
const mockOrders = [
{ id: '1', providerName: 'Pizza Planet', providerImage: '', orderDate: '29 Jun 2026', status: 'Delivered', total: 599 },
{ id: '2', providerName: 'Fresh Mart', providerImage: '', orderDate: '28 Jun 2026', status: 'Ongoing', total: 349 },
{ id: '3', providerName: 'Burger Barn', providerImage: '', orderDate: '27 Jun 2026', status: 'Delivered', total: 449 },
];
export const MyOrdersScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const [activeTab, setActiveTab] = useState('All');
const filteredOrders = activeTab === 'All'
? mockOrders
: activeTab === 'Ongoing'
? mockOrders.filter((o) => o.status === 'Ongoing')
: mockOrders.filter((o) => o.status === 'Delivered');
return (
<View style={styles.container}>
<Header title="My Orders" />
<View style={styles.tabRow}>
{TABS.map((tab) => (
<TouchableOpacity
key={tab}
style={[
styles.tab,
activeTab === tab && styles.tabActive,
]}
onPress={() => setActiveTab(tab)}
activeOpacity={0.7}
>
<Text
style={[
styles.tabText,
activeTab === tab && styles.tabTextActive,
]}
>
{tab}
</Text>
</TouchableOpacity>
))}
</View>
<FlatList
data={filteredOrders}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<OrderHistoryCard
providerName={item.providerName}
providerImage={item.providerImage}
orderDate={item.orderDate}
status={item.status}
total={item.total}
onReorder={() => {}}
onDetails={() => {}}
/>
)}
contentContainerStyle={styles.list}
ListEmptyComponent={
<View style={styles.empty}>
<Text style={styles.emptyText}>No orders found</Text>
</View>
}
/>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './offersScreen';

View File

@ -0,0 +1,51 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
list: {
padding: 16,
},
offerCard: {
flexDirection: 'row',
alignItems: 'center',
backgroundColor: colors.cardBg,
borderRadius: 12,
padding: 16,
marginBottom: 12,
borderWidth: 1,
borderColor: colors.border,
},
offerInfo: {
flex: 1,
},
offerCode: {
fontSize: typography.fontSize.lg,
fontWeight: typography.fontWeight.bold,
color: colors.primary,
marginBottom: 4,
},
offerDescription: {
fontSize: typography.fontSize.sm,
color: colors.text,
marginBottom: 4,
},
offerExpiry: {
fontSize: typography.fontSize.xs,
color: colors.textSecondary,
},
copyButton: {
paddingHorizontal: 20,
paddingVertical: 10,
borderRadius: 8,
backgroundColor: colors.primary,
},
copyButtonText: {
fontSize: typography.fontSize.sm,
fontWeight: typography.fontWeight.semibold,
color: '#FFFFFF',
},
});

View File

@ -0,0 +1,43 @@
import React from 'react';
import { View, Text, FlatList, TouchableOpacity } from 'react-native';
import { getStyles } from './offersScreen.styles';
import { Header } from '@components';
import { useAppTheme } from '@theme';
const OFFERS = [
{ id: '1', code: 'WELCOME50', description: '50% off on your first order', expiry: '30 Jul 2026' },
{ id: '2', code: 'FLAT20', description: 'Flat ₹20 off on orders above ₹199', expiry: '15 Aug 2026' },
{ id: '3', code: 'FREEDEL', description: 'Free delivery on orders above ₹299', expiry: '31 Jul 2026' },
];
export const OffersScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<View style={styles.container}>
<Header title="Offers" />
<FlatList
data={OFFERS}
keyExtractor={(item) => item.id}
renderItem={({ item }) => (
<View style={styles.offerCard}>
<View style={styles.offerInfo}>
<Text style={styles.offerCode}>{item.code}</Text>
<Text style={styles.offerDescription}>{item.description}</Text>
<Text style={styles.offerExpiry}>Expires: {item.expiry}</Text>
</View>
<TouchableOpacity
style={styles.copyButton}
onPress={() => {}}
activeOpacity={0.7}
>
<Text style={styles.copyButtonText}>Copy</Text>
</TouchableOpacity>
</View>
)}
contentContainerStyle={styles.list}
/>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './onboardingCompleteScreen';

View File

@ -0,0 +1,35 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
paddingHorizontal: 24,
},
content: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
},
icon: {
fontSize: 80,
marginBottom: 24,
},
title: {
fontSize: typography.fontSize.xxl,
fontWeight: typography.fontWeight.bold,
color: colors.text,
marginBottom: 12,
},
subtitle: {
fontSize: typography.fontSize.md,
color: colors.textSecondary,
textAlign: 'center',
lineHeight: 24,
paddingHorizontal: 16,
},
footer: {
paddingBottom: 40,
},
});

View File

@ -0,0 +1,35 @@
import React from 'react';
import { View, Text } from 'react-native';
import { getStyles } from './onboardingCompleteScreen.styles';
import { PrimaryButton } from '@components';
import { useAppTheme } from '@theme';
import { useAppDispatch } from '../../../hooks/useAppDispatch';
import { completeOnboarding } from '../../../store/authSlice';
export const OnboardingCompleteScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const dispatch = useAppDispatch();
const handleExplore = () => {
dispatch(completeOnboarding());
};
return (
<View style={styles.container}>
<View style={styles.content}>
<Text style={styles.icon}>🎉</Text>
<Text style={styles.title}>All Set!</Text>
<Text style={styles.subtitle}>
You're ready to explore and order from the best providers near you.
</Text>
</View>
<View style={styles.footer}>
<PrimaryButton
title="Explore Now"
onPress={handleExplore}
/>
</View>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './orderConfirmedScreen';

View File

@ -0,0 +1,55 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
content: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 24,
},
icon: {
fontSize: 64,
marginBottom: 16,
},
title: {
fontSize: typography.fontSize.xxl,
fontWeight: typography.fontWeight.bold,
color: colors.text,
marginBottom: 8,
},
subtitle: {
fontSize: typography.fontSize.md,
color: colors.textSecondary,
textAlign: 'center',
marginBottom: 32,
},
orderCard: {
backgroundColor: colors.cardBg,
borderRadius: 12,
padding: 20,
width: '100%',
borderWidth: 1,
borderColor: colors.border,
alignItems: 'center',
},
orderId: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
marginBottom: 4,
},
orderEta: {
fontSize: typography.fontSize.sm,
color: colors.primary,
fontWeight: typography.fontWeight.medium,
},
footer: {
padding: 24,
paddingBottom: 40,
},
});

View File

@ -0,0 +1,30 @@
import React from 'react';
import { View, Text } from 'react-native';
import { getStyles } from './orderConfirmedScreen.styles';
import { Header, PrimaryButton } from '@components';
import { useAppTheme } from '@theme';
export const OrderConfirmedScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<View style={styles.container}>
<Header title="Order Confirmed" onBack={() => {}} />
<View style={styles.content}>
<Text style={styles.icon}></Text>
<Text style={styles.title}>Order Confirmed!</Text>
<Text style={styles.subtitle}>
Your order has been placed successfully
</Text>
<View style={styles.orderCard}>
<Text style={styles.orderId}>Order ID: ORD-123456</Text>
<Text style={styles.orderEta}>Estimated delivery: 25-30 min</Text>
</View>
</View>
<View style={styles.footer}>
<PrimaryButton title="View Tracking" onPress={() => {}} />
</View>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './orderDeliveredScreen';

View File

@ -0,0 +1,31 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
content: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
paddingHorizontal: 24,
},
icon: {
fontSize: 64,
marginBottom: 16,
},
title: {
fontSize: typography.fontSize.xl,
fontWeight: typography.fontWeight.bold,
color: colors.text,
textAlign: 'center',
marginBottom: 8,
},
subtitle: {
fontSize: typography.fontSize.md,
color: colors.textSecondary,
marginBottom: 32,
},
});

View File

@ -0,0 +1,31 @@
import React, { useState } from 'react';
import { View, Text } from 'react-native';
import { getStyles } from './orderDeliveredScreen.styles';
import { Header, RatingStars, PrimaryButton } from '@components';
import { useAppTheme } from '@theme';
export const OrderDeliveredScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
const [rating, setRating] = useState(0);
return (
<View style={styles.container}>
<Header title="Order Delivered" onBack={() => {}} />
<View style={styles.content}>
<Text style={styles.icon}>🎉</Text>
<Text style={styles.title}>Your order has been delivered!</Text>
<Text style={styles.subtitle}>How was your experience?</Text>
<RatingStars rating={rating} onRate={setRating} size={40} />
<PrimaryButton
title="Rate & Tip"
onPress={() => {}}
disabled={rating === 0}
style={{ marginTop: 32 }}
/>
</View>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './orderTrackingScreen';

View File

@ -0,0 +1,91 @@
import { StyleSheet } from 'react-native';
import { typography } from '@theme';
export const getStyles = (colors: any) => StyleSheet.create({
container: {
flex: 1,
backgroundColor: colors.background,
},
content: {
flex: 1,
padding: 24,
},
timeline: {
flex: 1,
justifyContent: 'center',
paddingLeft: 8,
},
milestoneRow: {
flexDirection: 'row',
alignItems: 'flex-start',
marginBottom: 8,
},
milestoneIndicator: {
alignItems: 'center',
width: 24,
marginRight: 16,
},
dot: {
width: 16,
height: 16,
borderRadius: 8,
backgroundColor: colors.border,
zIndex: 1,
},
dotActive: {
backgroundColor: colors.primary,
},
dotCurrent: {
width: 20,
height: 20,
borderRadius: 10,
backgroundColor: colors.primary,
borderWidth: 3,
borderColor: '#E8F5E9',
},
line: {
width: 2,
flex: 1,
backgroundColor: colors.border,
minHeight: 30,
marginVertical: -2,
},
lineActive: {
backgroundColor: colors.primary,
},
milestoneInfo: {
paddingBottom: 20,
},
milestoneLabel: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.medium,
color: colors.textSecondary,
marginBottom: 2,
},
milestoneLabelActive: {
color: colors.text,
fontWeight: typography.fontWeight.semibold,
},
milestoneTime: {
fontSize: typography.fontSize.sm,
color: colors.placeholder,
},
supportCard: {
backgroundColor: colors.cardBg,
borderRadius: 12,
padding: 20,
borderWidth: 1,
borderColor: colors.border,
},
supportTitle: {
fontSize: typography.fontSize.md,
fontWeight: typography.fontWeight.semibold,
color: colors.text,
marginBottom: 4,
},
supportSubtext: {
fontSize: typography.fontSize.sm,
color: colors.textSecondary,
marginBottom: 12,
},
});

View File

@ -0,0 +1,64 @@
import React from 'react';
import { View, Text } from 'react-native';
import { getStyles } from './orderTrackingScreen.styles';
import { Header, PrimaryButton } from '@components';
import { useAppTheme } from '@theme';
const MILESTONES = [
{ key: 'placed', label: 'Order Placed', time: '12:30 PM' },
{ key: 'confirmed', label: 'Confirmed', time: '12:32 PM' },
{ key: 'dispatched', label: 'Dispatched', time: '12:40 PM' },
{ key: 'delivered', label: 'Delivered', time: 'Pending' },
];
export const OrderTrackingScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
return (
<View style={styles.container}>
<Header title="Track Order" onBack={() => {}} />
<View style={styles.content}>
<View style={styles.timeline}>
{MILESTONES.map((milestone, index) => (
<View key={milestone.key} style={styles.milestoneRow}>
<View style={styles.milestoneIndicator}>
<View
style={[
styles.dot,
index <= 2 && styles.dotActive,
index === 2 && styles.dotCurrent,
]}
/>
{index < MILESTONES.length - 1 && (
<View
style={[
styles.line,
index < 2 && styles.lineActive,
]}
/>
)}
</View>
<View style={styles.milestoneInfo}>
<Text
style={[
styles.milestoneLabel,
index <= 2 && styles.milestoneLabelActive,
]}
>
{milestone.label}
</Text>
<Text style={styles.milestoneTime}>{milestone.time}</Text>
</View>
</View>
))}
</View>
<View style={styles.supportCard}>
<Text style={styles.supportTitle}>Need help?</Text>
<Text style={styles.supportSubtext}>Contact support for assistance</Text>
<PrimaryButton title="Contact Support" onPress={() => {}} />
</View>
</View>
</View>
);
};

View File

@ -0,0 +1 @@
export * from './otpScreen';

Some files were not shown because too many files have changed in this diff Show More