diff --git a/android/gradle.properties b/android/gradle.properties
index 9afe615..126768a 100644
--- a/android/gradle.properties
+++ b/android/gradle.properties
@@ -32,7 +32,7 @@ reactNativeArchitectures=armeabi-v7a,arm64-v8a,x86,x86_64
# your application. You should enable this flag either if you want
# to write custom TurboModules/Fabric components OR use libraries that
# are providing them.
-newArchEnabled=true
+newArchEnabled=false
# Use this property to enable or disable the Hermes JS engine.
# 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.
# Note: Only works with ReactActivity and should not be used with custom Activity.
edgeToEdgeEnabled=false
+android.experimental.cxx.cmake.arguments=-DCMAKE_OBJECT_PATH_MAX=128
diff --git a/app/App.tsx b/app/App.tsx
index 0aa7559..311e27e 100644
--- a/app/App.tsx
+++ b/app/App.tsx
@@ -1,38 +1,24 @@
-
-import { StatusBar, StyleSheet, useColorScheme, View } from 'react-native';
-import {
- SafeAreaProvider,
- useSafeAreaInsets,
-} from 'react-native-safe-area-context';
-import { LoginScreen } from '@features/screens';
-import { useAppTheme } from '@theme';
+import React from 'react';
+import { StatusBar, useColorScheme } from 'react-native';
+import { SafeAreaProvider } from 'react-native-safe-area-context';
+import { NavigationContainer } from '@react-navigation/native';
+import { Provider } from 'react-redux';
+import { store } from './store';
+import { RootNavigator } from './navigation/rootNavigator';
function App() {
const isDarkMode = useColorScheme() === 'dark';
return (
-
-
-
-
+
+
+
+
+
+
+
+
);
}
-function AppContent() {
- const safeAreaInsets = useSafeAreaInsets();
- const { colors } = useAppTheme();
-
- return (
-
-
-
- );
-}
-
-const styles = StyleSheet.create({
- container: {
- flex: 1,
- },
-});
-
export default App;
diff --git a/app/components/CustomInput/CustomInput.tsx b/app/components/CustomInput/CustomInput.tsx
index 118e8fc..7216143 100644
--- a/app/components/CustomInput/CustomInput.tsx
+++ b/app/components/CustomInput/CustomInput.tsx
@@ -1,7 +1,7 @@
import React, { useState } from 'react';
import { View, Text, TextInput, TouchableOpacity } from 'react-native';
-import { CustomInputProps } from './CustomInput.props';
-import { getStyles } from './CustomInput.styles';
+import { CustomInputProps } from './customInput.props';
+import { getStyles } from './customInput.styles';
import { useAppTheme } from '@theme';
export const CustomInput: React.FC = ({
diff --git a/app/components/CustomInput/index.ts b/app/components/CustomInput/index.ts
index d483952..60e1ef1 100644
--- a/app/components/CustomInput/index.ts
+++ b/app/components/CustomInput/index.ts
@@ -1,2 +1,2 @@
-export * from './CustomInput';
-export * from './CustomInput.props';
+export * from './customInput';
+export * from './customInput.props';
diff --git a/app/components/PrimaryButton/PrimaryButton.tsx b/app/components/PrimaryButton/PrimaryButton.tsx
index 6aee4b2..91bb7b2 100644
--- a/app/components/PrimaryButton/PrimaryButton.tsx
+++ b/app/components/PrimaryButton/PrimaryButton.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { TouchableOpacity, Text, ActivityIndicator } from 'react-native';
-import { PrimaryButtonProps } from './PrimaryButton.props';
-import { styles } from './PrimaryButton.styles';
+import { PrimaryButtonProps } from './primaryButton.props';
+import { styles } from './primaryButton.styles';
import { colors } from '@theme';
export const PrimaryButton: React.FC = ({
diff --git a/app/components/PrimaryButton/index.ts b/app/components/PrimaryButton/index.ts
index 78d30a4..a841b05 100644
--- a/app/components/PrimaryButton/index.ts
+++ b/app/components/PrimaryButton/index.ts
@@ -1,2 +1,2 @@
-export * from './PrimaryButton';
-export * from './PrimaryButton.props';
+export * from './primaryButton';
+export * from './primaryButton.props';
diff --git a/app/components/SocialButton/SocialButton.tsx b/app/components/SocialButton/SocialButton.tsx
index ae9a614..1d1ffe4 100644
--- a/app/components/SocialButton/SocialButton.tsx
+++ b/app/components/SocialButton/SocialButton.tsx
@@ -1,7 +1,7 @@
import React from 'react';
import { TouchableOpacity, Text } from 'react-native';
-import { SocialButtonProps } from './SocialButton.props';
-import { getStyles } from './SocialButton.styles';
+import { SocialButtonProps } from './socialButton.props';
+import { getStyles } from './socialButton.styles';
import { useAppTheme } from '@theme';
import Svg, { Path } from 'react-native-svg';
diff --git a/app/components/SocialButton/index.ts b/app/components/SocialButton/index.ts
index 15529da..c65c305 100644
--- a/app/components/SocialButton/index.ts
+++ b/app/components/SocialButton/index.ts
@@ -1,2 +1,2 @@
-export * from './SocialButton';
-export * from './SocialButton.props';
+export * from './socialButton';
+export * from './socialButton.props';
diff --git a/app/components/badge/badge.props.ts b/app/components/badge/badge.props.ts
new file mode 100644
index 0000000..3541459
--- /dev/null
+++ b/app/components/badge/badge.props.ts
@@ -0,0 +1,4 @@
+export interface BadgeProps {
+ text: string;
+ variant?: 'primary' | 'success' | 'warning' | 'danger';
+}
diff --git a/app/components/badge/badge.styles.ts b/app/components/badge/badge.styles.ts
new file mode 100644
index 0000000..60b7524
--- /dev/null
+++ b/app/components/badge/badge.styles.ts
@@ -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,
+ };
+};
diff --git a/app/components/badge/badge.tsx b/app/components/badge/badge.tsx
new file mode 100644
index 0000000..cc2c749
--- /dev/null
+++ b/app/components/badge/badge.tsx
@@ -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 = ({ text, variant = 'primary' }) => {
+ const { colors } = useAppTheme();
+ const styles = getStyles();
+ const variantStyle = getVariantStyle(colors, variant);
+
+ return (
+
+ {text}
+
+ );
+};
diff --git a/app/components/badge/index.ts b/app/components/badge/index.ts
new file mode 100644
index 0000000..96b8971
--- /dev/null
+++ b/app/components/badge/index.ts
@@ -0,0 +1,2 @@
+export * from './badge';
+export * from './badge.props';
diff --git a/app/components/catalogItemRow/catalogItemRow.props.ts b/app/components/catalogItemRow/catalogItemRow.props.ts
new file mode 100644
index 0000000..5f93f58
--- /dev/null
+++ b/app/components/catalogItemRow/catalogItemRow.props.ts
@@ -0,0 +1,9 @@
+export interface CatalogItemRowProps {
+ imageUrl: string;
+ title: string;
+ description: string;
+ price: number;
+ quantity: number;
+ onAdd: () => void;
+ onRemove: () => void;
+}
diff --git a/app/components/catalogItemRow/catalogItemRow.styles.ts b/app/components/catalogItemRow/catalogItemRow.styles.ts
new file mode 100644
index 0000000..312f5a5
--- /dev/null
+++ b/app/components/catalogItemRow/catalogItemRow.styles.ts
@@ -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',
+ },
+});
diff --git a/app/components/catalogItemRow/catalogItemRow.tsx b/app/components/catalogItemRow/catalogItemRow.tsx
new file mode 100644
index 0000000..475bb24
--- /dev/null
+++ b/app/components/catalogItemRow/catalogItemRow.tsx
@@ -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 = ({
+ imageUrl,
+ title,
+ description,
+ price,
+ quantity,
+ onAdd,
+ onRemove,
+}) => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+
+ return (
+
+
+ {imageUrl || 'π'}
+
+
+ {title}
+ {description}
+ βΉ{price}
+
+
+ {quantity > 0 ? (
+ <>
+
+ -
+
+ {quantity}
+
+ +
+
+ >
+ ) : (
+
+ +
+
+ )}
+
+
+ );
+};
diff --git a/app/components/catalogItemRow/index.ts b/app/components/catalogItemRow/index.ts
new file mode 100644
index 0000000..4f4dca7
--- /dev/null
+++ b/app/components/catalogItemRow/index.ts
@@ -0,0 +1,2 @@
+export * from './catalogItemRow';
+export * from './catalogItemRow.props';
diff --git a/app/components/categoryChip/categoryChip.props.ts b/app/components/categoryChip/categoryChip.props.ts
new file mode 100644
index 0000000..c53b693
--- /dev/null
+++ b/app/components/categoryChip/categoryChip.props.ts
@@ -0,0 +1,6 @@
+export interface CategoryChipProps {
+ icon: string;
+ label: string;
+ isSelected: boolean;
+ onPress: () => void;
+}
diff --git a/app/components/categoryChip/categoryChip.styles.ts b/app/components/categoryChip/categoryChip.styles.ts
new file mode 100644
index 0000000..cd9f167
--- /dev/null
+++ b/app/components/categoryChip/categoryChip.styles.ts
@@ -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,
+ },
+});
diff --git a/app/components/categoryChip/categoryChip.tsx b/app/components/categoryChip/categoryChip.tsx
new file mode 100644
index 0000000..7f00036
--- /dev/null
+++ b/app/components/categoryChip/categoryChip.tsx
@@ -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 = ({
+ icon,
+ label,
+ isSelected,
+ onPress,
+}) => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+
+ return (
+
+ {icon}
+
+ {label}
+
+
+ );
+};
diff --git a/app/components/categoryChip/index.ts b/app/components/categoryChip/index.ts
new file mode 100644
index 0000000..6500022
--- /dev/null
+++ b/app/components/categoryChip/index.ts
@@ -0,0 +1,2 @@
+export * from './categoryChip';
+export * from './categoryChip.props';
diff --git a/app/components/header/header.props.ts b/app/components/header/header.props.ts
new file mode 100644
index 0000000..5271130
--- /dev/null
+++ b/app/components/header/header.props.ts
@@ -0,0 +1,7 @@
+import { ReactNode } from 'react';
+
+export interface HeaderProps {
+ title: string;
+ onBack?: () => void;
+ rightComponent?: ReactNode;
+}
diff --git a/app/components/header/header.styles.ts b/app/components/header/header.styles.ts
new file mode 100644
index 0000000..975ae6e
--- /dev/null
+++ b/app/components/header/header.styles.ts
@@ -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',
+ },
+});
diff --git a/app/components/header/header.tsx b/app/components/header/header.tsx
new file mode 100644
index 0000000..e05987d
--- /dev/null
+++ b/app/components/header/header.tsx
@@ -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 = ({ title, onBack, rightComponent }) => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+
+ return (
+
+
+ {onBack && (
+
+ {'<'}
+
+ )}
+
+
+ {title}
+
+
+ {rightComponent}
+
+
+ );
+};
diff --git a/app/components/header/index.ts b/app/components/header/index.ts
new file mode 100644
index 0000000..1d85bf9
--- /dev/null
+++ b/app/components/header/index.ts
@@ -0,0 +1,2 @@
+export * from './header';
+export * from './header.props';
diff --git a/app/components/index.ts b/app/components/index.ts
index a4b2da5..494c65f 100644
--- a/app/components/index.ts
+++ b/app/components/index.ts
@@ -1,3 +1,14 @@
-export * from './CustomInput';
-export * from './PrimaryButton';
-export * from './SocialButton';
\ No newline at end of file
+export * from './customInput';
+export * from './primaryButton';
+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';
diff --git a/app/components/orderHistoryCard/index.ts b/app/components/orderHistoryCard/index.ts
new file mode 100644
index 0000000..4d91d70
--- /dev/null
+++ b/app/components/orderHistoryCard/index.ts
@@ -0,0 +1,2 @@
+export * from './orderHistoryCard';
+export * from './orderHistoryCard.props';
diff --git a/app/components/orderHistoryCard/orderHistoryCard.props.ts b/app/components/orderHistoryCard/orderHistoryCard.props.ts
new file mode 100644
index 0000000..196c1ca
--- /dev/null
+++ b/app/components/orderHistoryCard/orderHistoryCard.props.ts
@@ -0,0 +1,9 @@
+export interface OrderHistoryCardProps {
+ providerName: string;
+ providerImage: string;
+ orderDate: string;
+ status: string;
+ total: number;
+ onReorder?: () => void;
+ onDetails?: () => void;
+}
diff --git a/app/components/orderHistoryCard/orderHistoryCard.styles.ts b/app/components/orderHistoryCard/orderHistoryCard.styles.ts
new file mode 100644
index 0000000..a8bb88a
--- /dev/null
+++ b/app/components/orderHistoryCard/orderHistoryCard.styles.ts
@@ -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,
+ },
+});
diff --git a/app/components/orderHistoryCard/orderHistoryCard.tsx b/app/components/orderHistoryCard/orderHistoryCard.tsx
new file mode 100644
index 0000000..098a362
--- /dev/null
+++ b/app/components/orderHistoryCard/orderHistoryCard.tsx
@@ -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 = ({
+ providerName,
+ providerImage,
+ orderDate,
+ status,
+ total,
+ onReorder,
+ onDetails,
+}) => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+
+ return (
+
+
+
+ {providerImage || 'πͺ'}
+
+
+ {providerName}
+ {orderDate}
+
+
+ {status}
+
+
+
+ βΉ{total}
+
+ {onReorder && (
+
+ Reorder
+
+ )}
+ {onDetails && (
+
+ Details
+
+ )}
+
+
+
+ );
+};
diff --git a/app/components/paymentOption/index.ts b/app/components/paymentOption/index.ts
new file mode 100644
index 0000000..5c2703a
--- /dev/null
+++ b/app/components/paymentOption/index.ts
@@ -0,0 +1,2 @@
+export * from './paymentOption';
+export * from './paymentOption.props';
diff --git a/app/components/paymentOption/paymentOption.props.ts b/app/components/paymentOption/paymentOption.props.ts
new file mode 100644
index 0000000..1b993ff
--- /dev/null
+++ b/app/components/paymentOption/paymentOption.props.ts
@@ -0,0 +1,6 @@
+export interface PaymentOptionProps {
+ type: string;
+ label: string;
+ isSelected: boolean;
+ onSelect: () => void;
+}
diff --git a/app/components/paymentOption/paymentOption.styles.ts b/app/components/paymentOption/paymentOption.styles.ts
new file mode 100644
index 0000000..f44a9fd
--- /dev/null
+++ b/app/components/paymentOption/paymentOption.styles.ts
@@ -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,
+ },
+});
diff --git a/app/components/paymentOption/paymentOption.tsx b/app/components/paymentOption/paymentOption.tsx
new file mode 100644
index 0000000..5fea9a2
--- /dev/null
+++ b/app/components/paymentOption/paymentOption.tsx
@@ -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 = ({
+ type,
+ label,
+ isSelected,
+ onSelect,
+}) => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+
+ return (
+
+ {type === 'UPI' ? 'π±' : type === 'Card' ? 'π³' : type === 'Wallet' ? 'π' : 'π΅'}
+ {label}
+
+ {isSelected && }
+
+
+ );
+};
diff --git a/app/components/providerCard/index.ts b/app/components/providerCard/index.ts
new file mode 100644
index 0000000..6456569
--- /dev/null
+++ b/app/components/providerCard/index.ts
@@ -0,0 +1,2 @@
+export * from './providerCard';
+export * from './providerCard.props';
diff --git a/app/components/providerCard/providerCard.props.ts b/app/components/providerCard/providerCard.props.ts
new file mode 100644
index 0000000..e88311f
--- /dev/null
+++ b/app/components/providerCard/providerCard.props.ts
@@ -0,0 +1,9 @@
+export interface ProviderCardProps {
+ imageUrl: string;
+ name: string;
+ rating: number;
+ deliveryTime: string;
+ tag: string;
+ discountText?: string;
+ onPress?: () => void;
+}
diff --git a/app/components/providerCard/providerCard.styles.ts b/app/components/providerCard/providerCard.styles.ts
new file mode 100644
index 0000000..d60589a
--- /dev/null
+++ b/app/components/providerCard/providerCard.styles.ts
@@ -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,
+ },
+});
diff --git a/app/components/providerCard/providerCard.tsx b/app/components/providerCard/providerCard.tsx
new file mode 100644
index 0000000..82af2e8
--- /dev/null
+++ b/app/components/providerCard/providerCard.tsx
@@ -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 = ({
+ imageUrl,
+ name,
+ rating,
+ deliveryTime,
+ tag,
+ discountText,
+ onPress,
+}) => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+
+ return (
+
+
+ {imageUrl || 'π½οΈ'}
+
+
+ {name}
+
+ β {rating.toFixed(1)}
+ π {deliveryTime}
+
+ {tag}
+ {discountText && (
+
+ {discountText}
+
+ )}
+
+
+ );
+};
diff --git a/app/components/quantitySelector/index.ts b/app/components/quantitySelector/index.ts
new file mode 100644
index 0000000..c3c7677
--- /dev/null
+++ b/app/components/quantitySelector/index.ts
@@ -0,0 +1,2 @@
+export * from './quantitySelector';
+export * from './quantitySelector.props';
diff --git a/app/components/quantitySelector/quantitySelector.props.ts b/app/components/quantitySelector/quantitySelector.props.ts
new file mode 100644
index 0000000..bd8a338
--- /dev/null
+++ b/app/components/quantitySelector/quantitySelector.props.ts
@@ -0,0 +1,7 @@
+export interface QuantitySelectorProps {
+ value: number;
+ onIncrement: () => void;
+ onDecrement: () => void;
+ min?: number;
+ max?: number;
+}
diff --git a/app/components/quantitySelector/quantitySelector.styles.ts b/app/components/quantitySelector/quantitySelector.styles.ts
new file mode 100644
index 0000000..a8b41a9
--- /dev/null
+++ b/app/components/quantitySelector/quantitySelector.styles.ts
@@ -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',
+ },
+});
diff --git a/app/components/quantitySelector/quantitySelector.tsx b/app/components/quantitySelector/quantitySelector.tsx
new file mode 100644
index 0000000..5eb5665
--- /dev/null
+++ b/app/components/quantitySelector/quantitySelector.tsx
@@ -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 = ({
+ value,
+ onIncrement,
+ onDecrement,
+ min = 0,
+ max = 99,
+}) => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+
+ return (
+
+
+ -
+
+ {value}
+ = max && styles.buttonDisabled]}
+ onPress={onIncrement}
+ disabled={value >= max}
+ activeOpacity={0.7}
+ >
+ +
+
+
+ );
+};
diff --git a/app/components/ratingStars/index.ts b/app/components/ratingStars/index.ts
new file mode 100644
index 0000000..7e97b82
--- /dev/null
+++ b/app/components/ratingStars/index.ts
@@ -0,0 +1,2 @@
+export * from './ratingStars';
+export * from './ratingStars.props';
diff --git a/app/components/ratingStars/ratingStars.props.ts b/app/components/ratingStars/ratingStars.props.ts
new file mode 100644
index 0000000..f2fea91
--- /dev/null
+++ b/app/components/ratingStars/ratingStars.props.ts
@@ -0,0 +1,5 @@
+export interface RatingStarsProps {
+ rating: number;
+ onRate?: (rating: number) => void;
+ size?: number;
+}
diff --git a/app/components/ratingStars/ratingStars.styles.ts b/app/components/ratingStars/ratingStars.styles.ts
new file mode 100644
index 0000000..a8dbf33
--- /dev/null
+++ b/app/components/ratingStars/ratingStars.styles.ts
@@ -0,0 +1,12 @@
+import { StyleSheet } from 'react-native';
+
+export const getStyles = () => StyleSheet.create({
+ container: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'center',
+ },
+ star: {
+ marginHorizontal: 4,
+ },
+});
diff --git a/app/components/ratingStars/ratingStars.tsx b/app/components/ratingStars/ratingStars.tsx
new file mode 100644
index 0000000..0fb8f18
--- /dev/null
+++ b/app/components/ratingStars/ratingStars.tsx
@@ -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 = ({
+ rating,
+ onRate,
+ size = 32,
+}) => {
+ const styles = getStyles();
+
+ return (
+
+ {[1, 2, 3, 4, 5].map((star) => (
+ onRate?.(star)}
+ disabled={!onRate}
+ activeOpacity={0.7}
+ >
+
+ {star <= rating ? 'β' : 'β'}
+
+
+ ))}
+
+ );
+};
diff --git a/app/components/searchBar/index.ts b/app/components/searchBar/index.ts
new file mode 100644
index 0000000..e5e40ec
--- /dev/null
+++ b/app/components/searchBar/index.ts
@@ -0,0 +1,2 @@
+export * from './searchBar';
+export * from './searchBar.props';
diff --git a/app/components/searchBar/searchBar.props.ts b/app/components/searchBar/searchBar.props.ts
new file mode 100644
index 0000000..f7ba489
--- /dev/null
+++ b/app/components/searchBar/searchBar.props.ts
@@ -0,0 +1,7 @@
+export interface SearchBarProps {
+ value: string;
+ onChangeText: (text: string) => void;
+ placeholder?: string;
+ onFocus?: () => void;
+ onSubmit?: () => void;
+}
diff --git a/app/components/searchBar/searchBar.styles.ts b/app/components/searchBar/searchBar.styles.ts
new file mode 100644
index 0000000..445ebe1
--- /dev/null
+++ b/app/components/searchBar/searchBar.styles.ts
@@ -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,
+ },
+});
diff --git a/app/components/searchBar/searchBar.tsx b/app/components/searchBar/searchBar.tsx
new file mode 100644
index 0000000..8b6a603
--- /dev/null
+++ b/app/components/searchBar/searchBar.tsx
@@ -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 = ({
+ value,
+ onChangeText,
+ placeholder = 'Search...',
+ onFocus,
+ onSubmit,
+}) => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+
+ return (
+
+ π
+
+
+ );
+};
diff --git a/app/components/stepProgress/index.ts b/app/components/stepProgress/index.ts
new file mode 100644
index 0000000..e39b473
--- /dev/null
+++ b/app/components/stepProgress/index.ts
@@ -0,0 +1,2 @@
+export * from './stepProgress';
+export * from './stepProgress.props';
diff --git a/app/components/stepProgress/stepProgress.props.ts b/app/components/stepProgress/stepProgress.props.ts
new file mode 100644
index 0000000..338e648
--- /dev/null
+++ b/app/components/stepProgress/stepProgress.props.ts
@@ -0,0 +1,4 @@
+export interface StepProgressProps {
+ steps: { key: string; label: string }[];
+ activeStep: number;
+}
diff --git a/app/components/stepProgress/stepProgress.styles.ts b/app/components/stepProgress/stepProgress.styles.ts
new file mode 100644
index 0000000..8446deb
--- /dev/null
+++ b/app/components/stepProgress/stepProgress.styles.ts
@@ -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,
+ },
+});
diff --git a/app/components/stepProgress/stepProgress.tsx b/app/components/stepProgress/stepProgress.tsx
new file mode 100644
index 0000000..7e1547c
--- /dev/null
+++ b/app/components/stepProgress/stepProgress.tsx
@@ -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 = ({ steps, activeStep }) => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+
+ return (
+
+ {steps.map((step, index) => (
+
+
+
+ {index + 1}
+
+
+ {step.label}
+
+
+ {index < steps.length - 1 && (
+
+ )}
+
+ ))}
+
+ );
+};
diff --git a/app/features/screens/LoginScreen/LoginScreen.styles.ts b/app/features/screens/LoginScreen/LoginScreen.styles.ts
index bb5ab70..51a8b22 100644
--- a/app/features/screens/LoginScreen/LoginScreen.styles.ts
+++ b/app/features/screens/LoginScreen/LoginScreen.styles.ts
@@ -29,51 +29,4 @@ export const getStyles = (colors: any) => StyleSheet.create({
formContainer: {
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,
- },
});
diff --git a/app/features/screens/LoginScreen/LoginScreen.tsx b/app/features/screens/LoginScreen/LoginScreen.tsx
index 00424fe..c2dfb75 100644
--- a/app/features/screens/LoginScreen/LoginScreen.tsx
+++ b/app/features/screens/LoginScreen/LoginScreen.tsx
@@ -5,63 +5,39 @@ import {
KeyboardAvoidingView,
Platform,
ScrollView,
- TouchableOpacity,
- Alert,
} from 'react-native';
-import { getStyles } from './LoginScreen.styles';
-import { CustomInput, PrimaryButton, SocialButton } from '@components';
-import CheckBox from '@react-native-community/checkbox';
+import { useNavigation } from '@react-navigation/native';
+import { StackNavigationProp } from '@react-navigation/stack';
+import { getStyles } from './loginScreen.styles';
+import { CustomInput, PrimaryButton } from '@components';
import { useAppTheme } from '@theme';
+import { useAppDispatch } from '../../../hooks/useAppDispatch';
+import { loginWithPhone } from '../../../store/authSlice';
+import { AuthStackParamList } from '../../../navigation/authStack';
+
+type LoginNavProp = StackNavigationProp;
export const LoginScreen: React.FC = () => {
const { colors } = useAppTheme();
const styles = getStyles(colors);
+ const navigation = useNavigation();
+ const dispatch = useAppDispatch();
const [mobileNumber, setMobileNumber] = useState('');
- const [password, setPassword] = useState('');
- 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 [error, setError] = useState(undefined);
const handleLogin = () => {
- if (!validate()) return;
-
- setIsLoading(true);
- // Simulate API request
- setTimeout(() => {
- setIsLoading(false);
- Alert.alert('Success', 'Logged in successfully!');
- }, 1500);
- };
-
- const handleSocialLogin = (provider: 'google' | 'apple') => {
- 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...');
+ if (!mobileNumber) {
+ setError('Mobile number is required');
+ return;
+ }
+ if (mobileNumber.length < 10) {
+ setError('Please enter a valid mobile number');
+ return;
+ }
+ setError(undefined);
+ dispatch(loginWithPhone(mobileNumber));
+ navigation.navigate('OtpScreen', { mobileNumber });
};
return (
@@ -74,7 +50,7 @@ export const LoginScreen: React.FC = () => {
showsVerticalScrollIndicator={false}
>
- Welcome back π
+ Welcome back
Login to continue
@@ -86,68 +62,16 @@ export const LoginScreen: React.FC = () => {
value={mobileNumber}
onChangeText={(text) => {
setMobileNumber(text);
- if (errors.mobile) setErrors({ ...errors, mobile: undefined });
+ if (error) setError(undefined);
}}
- error={errors.mobile}
+ error={error}
/>
- {
- setPassword(text);
- if (errors.password) setErrors({ ...errors, password: undefined });
- }}
- error={errors.password}
- />
-
-
- setRememberMe(!rememberMe)}
- activeOpacity={0.8}
- >
-
- Remember me
-
-
-
-
-
-
- or continue with
-
-
-
-
- handleSocialLogin('google')} />
- handleSocialLogin('apple')} />
-
-
-
-
- Donβt have an account? Sign up
-
-
diff --git a/app/features/screens/LoginScreen/index.ts b/app/features/screens/LoginScreen/index.ts
index 95c95bb..5c766db 100644
--- a/app/features/screens/LoginScreen/index.ts
+++ b/app/features/screens/LoginScreen/index.ts
@@ -1,2 +1,2 @@
-export * from './LoginScreen';
-export * from './LoginScreen.styles';
+export * from './loginScreen';
+export * from './loginScreen.styles';
diff --git a/app/features/screens/accountScreen/accountScreen.styles.ts b/app/features/screens/accountScreen/accountScreen.styles.ts
new file mode 100644
index 0000000..17045d4
--- /dev/null
+++ b/app/features/screens/accountScreen/accountScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/accountScreen/accountScreen.tsx b/app/features/screens/accountScreen/accountScreen.tsx
new file mode 100644
index 0000000..5846909
--- /dev/null
+++ b/app/features/screens/accountScreen/accountScreen.tsx
@@ -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 (
+
+
+
+
+
+
+ {user?.name?.charAt(0)?.toUpperCase() || 'U'}
+
+
+ {user?.name || 'User'}
+ {user?.mobileNumber || '+91 98765 43210'}
+
+
+
+ {MENU_ITEMS.map((item, index) => (
+
+ {item.icon}
+ {item.label}
+ {'>'}
+
+ ))}
+
+
+ dispatch(logout())}
+ style={{ marginTop: 24 }}
+ />
+
+
+ );
+};
diff --git a/app/features/screens/accountScreen/index.ts b/app/features/screens/accountScreen/index.ts
new file mode 100644
index 0000000..efe9de3
--- /dev/null
+++ b/app/features/screens/accountScreen/index.ts
@@ -0,0 +1 @@
+export * from './accountScreen';
diff --git a/app/features/screens/cartScreen/cartScreen.styles.ts b/app/features/screens/cartScreen/cartScreen.styles.ts
new file mode 100644
index 0000000..91c2dbc
--- /dev/null
+++ b/app/features/screens/cartScreen/cartScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/cartScreen/cartScreen.tsx b/app/features/screens/cartScreen/cartScreen.tsx
new file mode 100644
index 0000000..72bc185
--- /dev/null
+++ b/app/features/screens/cartScreen/cartScreen.tsx
@@ -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 (
+
+ {}} />
+ item.id}
+ renderItem={({ item }) => (
+
+
+ {item.item.title}
+ βΉ{item.item.price}
+
+ {}}
+ onDecrement={() => dispatch(removeItem(item.item.id))}
+ />
+
+ )}
+ ListFooterComponent={
+
+
+
+ {
+ if (couponCode) {
+ dispatch(removeCoupon());
+ setCouponInput('');
+ } else if (couponInput) {
+ dispatch(applyCoupon(couponInput));
+ }
+ }}
+ activeOpacity={0.7}
+ >
+
+ {couponCode ? 'Remove' : 'Apply'}
+
+
+
+
+
+ Subtotal
+ βΉ{totals.subtotal}
+
+
+ Delivery Fee
+ βΉ{totals.deliveryFee}
+
+
+ Platform Fee
+ βΉ{totals.platformFee}
+
+ {totals.discount > 0 && (
+
+
+ Discount
+
+
+ -βΉ{totals.discount}
+
+
+ )}
+
+ Total
+ βΉ{totals.total}
+
+
+ }
+ contentContainerStyle={styles.list}
+ />
+
+ Total: βΉ{totals.total}
+ {}}
+ style={{ flex: 1, marginLeft: 12 }}
+ />
+
+
+ );
+};
diff --git a/app/features/screens/cartScreen/index.ts b/app/features/screens/cartScreen/index.ts
new file mode 100644
index 0000000..2740d95
--- /dev/null
+++ b/app/features/screens/cartScreen/index.ts
@@ -0,0 +1 @@
+export * from './cartScreen';
diff --git a/app/features/screens/checkoutAddressScreen/checkoutAddressScreen.styles.ts b/app/features/screens/checkoutAddressScreen/checkoutAddressScreen.styles.ts
new file mode 100644
index 0000000..29caf10
--- /dev/null
+++ b/app/features/screens/checkoutAddressScreen/checkoutAddressScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/checkoutAddressScreen/checkoutAddressScreen.tsx b/app/features/screens/checkoutAddressScreen/checkoutAddressScreen.tsx
new file mode 100644
index 0000000..70f2ee2
--- /dev/null
+++ b/app/features/screens/checkoutAddressScreen/checkoutAddressScreen.tsx
@@ -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 (
+
+ {}} />
+
+
+
+ Deliver to
+
+ Koramangala 4th Block, Bengaluru, 560034
+
+ Home
+
+
+ Delivery Option
+ setDeliveryOption('standard')}
+ activeOpacity={0.7}
+ >
+
+ Standard Delivery
+ Free β’ 25-30 min
+
+
+ {deliveryOption === 'standard' && }
+
+
+
+ setDeliveryOption('express')}
+ activeOpacity={0.7}
+ >
+
+ Express Delivery
+ βΉ40 β’ 10-15 min
+
+
+ {deliveryOption === 'express' && }
+
+
+
+
+ {}} />
+
+
+ );
+};
diff --git a/app/features/screens/checkoutAddressScreen/index.ts b/app/features/screens/checkoutAddressScreen/index.ts
new file mode 100644
index 0000000..46caf4c
--- /dev/null
+++ b/app/features/screens/checkoutAddressScreen/index.ts
@@ -0,0 +1 @@
+export * from './checkoutAddressScreen';
diff --git a/app/features/screens/checkoutPaymentScreen/checkoutPaymentScreen.styles.ts b/app/features/screens/checkoutPaymentScreen/checkoutPaymentScreen.styles.ts
new file mode 100644
index 0000000..578b1af
--- /dev/null
+++ b/app/features/screens/checkoutPaymentScreen/checkoutPaymentScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/checkoutPaymentScreen/checkoutPaymentScreen.tsx b/app/features/screens/checkoutPaymentScreen/checkoutPaymentScreen.tsx
new file mode 100644
index 0000000..dd937bd
--- /dev/null
+++ b/app/features/screens/checkoutPaymentScreen/checkoutPaymentScreen.tsx
@@ -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 (
+
+ {}} />
+
+
+ Select Payment Method
+ {PAYMENT_METHODS.map((method) => (
+ setSelectedMethod(method.id)}
+ />
+ ))}
+
+
+ {}} />
+
+
+ );
+};
diff --git a/app/features/screens/checkoutPaymentScreen/index.ts b/app/features/screens/checkoutPaymentScreen/index.ts
new file mode 100644
index 0000000..6d7df67
--- /dev/null
+++ b/app/features/screens/checkoutPaymentScreen/index.ts
@@ -0,0 +1 @@
+export * from './checkoutPaymentScreen';
diff --git a/app/features/screens/completeProfileScreen/completeProfileScreen.styles.ts b/app/features/screens/completeProfileScreen/completeProfileScreen.styles.ts
new file mode 100644
index 0000000..fcd2624
--- /dev/null
+++ b/app/features/screens/completeProfileScreen/completeProfileScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/completeProfileScreen/completeProfileScreen.tsx b/app/features/screens/completeProfileScreen/completeProfileScreen.tsx
new file mode 100644
index 0000000..8ae1d2e
--- /dev/null
+++ b/app/features/screens/completeProfileScreen/completeProfileScreen.tsx
@@ -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;
+
+const LOCATION_LABELS = ['Home', 'Work', 'Other'];
+
+export const CompleteProfileScreen: React.FC = () => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+ const navigation = useNavigation();
+ 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 (
+
+
+ Complete Profile
+ Tell us about yourself
+
+
+
+
+
+ Location Label
+
+ {LOCATION_LABELS.map((label) => (
+ setSelectedLabel(label)}
+ activeOpacity={0.7}
+ >
+
+ {label}
+
+
+ ))}
+
+
+
+
+
+
+ );
+};
diff --git a/app/features/screens/completeProfileScreen/index.ts b/app/features/screens/completeProfileScreen/index.ts
new file mode 100644
index 0000000..1aaebda
--- /dev/null
+++ b/app/features/screens/completeProfileScreen/index.ts
@@ -0,0 +1 @@
+export * from './completeProfileScreen';
diff --git a/app/features/screens/helpSupportScreen/helpSupportScreen.styles.ts b/app/features/screens/helpSupportScreen/helpSupportScreen.styles.ts
new file mode 100644
index 0000000..9983ee6
--- /dev/null
+++ b/app/features/screens/helpSupportScreen/helpSupportScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/helpSupportScreen/helpSupportScreen.tsx b/app/features/screens/helpSupportScreen/helpSupportScreen.tsx
new file mode 100644
index 0000000..ec1d8d2
--- /dev/null
+++ b/app/features/screens/helpSupportScreen/helpSupportScreen.tsx
@@ -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 (
+
+
+
+ Frequently Asked Questions
+ {FAQS.map((faq, index) => (
+
+ {faq.q}
+ {faq.a}
+
+ ))}
+
+ Contact Support
+
+ π
+
+ Call us
+ +91 1800 123 4567
+
+
+
+ βοΈ
+
+ Email us
+ support@sgdelivery.com
+
+
+
+
+ );
+};
diff --git a/app/features/screens/helpSupportScreen/index.ts b/app/features/screens/helpSupportScreen/index.ts
new file mode 100644
index 0000000..cad255b
--- /dev/null
+++ b/app/features/screens/helpSupportScreen/index.ts
@@ -0,0 +1 @@
+export * from './helpSupportScreen';
diff --git a/app/features/screens/homeScreen/homeScreen.styles.ts b/app/features/screens/homeScreen/homeScreen.styles.ts
new file mode 100644
index 0000000..8fa542b
--- /dev/null
+++ b/app/features/screens/homeScreen/homeScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/homeScreen/homeScreen.tsx b/app/features/screens/homeScreen/homeScreen.tsx
new file mode 100644
index 0000000..aaec42e
--- /dev/null
+++ b/app/features/screens/homeScreen/homeScreen.tsx
@@ -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;
+
+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();
+
+ const [providers, setProviders] = useState([]);
+ 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 (
+
+
+ Deliver to
+
+ Koramangala 4th Block, B...
+ βΌ
+
+
+
+ {}}
+ placeholder="Search providers or items..."
+ onFocus={handleSearchFocus}
+ />
+
+
+ 50% OFF
+ on your first order
+
+
+ item.key}
+ showsHorizontalScrollIndicator={false}
+ contentContainerStyle={styles.chipRow}
+ renderItem={({ item }) => (
+ setSelectedCategory(item.key)}
+ />
+ )}
+ />
+
+
+ {selectedCategory === 'all' ? 'Popular Providers' : selectedCategory}
+
+
+ {filteredProviders.map((provider) => (
+ navigation.navigate('SearchScreen')}
+ />
+ ))}
+
+
+
+ );
+};
diff --git a/app/features/screens/homeScreen/index.ts b/app/features/screens/homeScreen/index.ts
new file mode 100644
index 0000000..eacd088
--- /dev/null
+++ b/app/features/screens/homeScreen/index.ts
@@ -0,0 +1 @@
+export * from './homeScreen';
diff --git a/app/features/screens/index.ts b/app/features/screens/index.ts
index 2c7fb45..fcbffc6 100644
--- a/app/features/screens/index.ts
+++ b/app/features/screens/index.ts
@@ -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';
diff --git a/app/features/screens/liveTrackingScreen/index.ts b/app/features/screens/liveTrackingScreen/index.ts
new file mode 100644
index 0000000..293397b
--- /dev/null
+++ b/app/features/screens/liveTrackingScreen/index.ts
@@ -0,0 +1 @@
+export * from './liveTrackingScreen';
diff --git a/app/features/screens/liveTrackingScreen/liveTrackingScreen.styles.ts b/app/features/screens/liveTrackingScreen/liveTrackingScreen.styles.ts
new file mode 100644
index 0000000..5c80750
--- /dev/null
+++ b/app/features/screens/liveTrackingScreen/liveTrackingScreen.styles.ts
@@ -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',
+ },
+});
diff --git a/app/features/screens/liveTrackingScreen/liveTrackingScreen.tsx b/app/features/screens/liveTrackingScreen/liveTrackingScreen.tsx
new file mode 100644
index 0000000..a31fab9
--- /dev/null
+++ b/app/features/screens/liveTrackingScreen/liveTrackingScreen.tsx
@@ -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 (
+
+ {}} />
+
+ πΊοΈ
+ Live Map
+
+ Delivery partner location tracking
+
+
+ β Live
+
+
+
+ Rahul Sharma
+ β 4.8
+ KA-01-AB-1234 β’ Honda Activa
+
+ {}}
+ style={{ flex: 1, marginRight: 8 }}
+ />
+ {}}
+ style={{ flex: 1, marginLeft: 8 }}
+ />
+
+
+
+ );
+};
diff --git a/app/features/screens/myOrdersScreen/index.ts b/app/features/screens/myOrdersScreen/index.ts
new file mode 100644
index 0000000..b31e5a4
--- /dev/null
+++ b/app/features/screens/myOrdersScreen/index.ts
@@ -0,0 +1 @@
+export * from './myOrdersScreen';
diff --git a/app/features/screens/myOrdersScreen/myOrdersScreen.styles.ts b/app/features/screens/myOrdersScreen/myOrdersScreen.styles.ts
new file mode 100644
index 0000000..8fddd74
--- /dev/null
+++ b/app/features/screens/myOrdersScreen/myOrdersScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/myOrdersScreen/myOrdersScreen.tsx b/app/features/screens/myOrdersScreen/myOrdersScreen.tsx
new file mode 100644
index 0000000..8bb9be8
--- /dev/null
+++ b/app/features/screens/myOrdersScreen/myOrdersScreen.tsx
@@ -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 (
+
+
+
+ {TABS.map((tab) => (
+ setActiveTab(tab)}
+ activeOpacity={0.7}
+ >
+
+ {tab}
+
+
+ ))}
+
+ item.id}
+ renderItem={({ item }) => (
+ {}}
+ onDetails={() => {}}
+ />
+ )}
+ contentContainerStyle={styles.list}
+ ListEmptyComponent={
+
+ No orders found
+
+ }
+ />
+
+ );
+};
diff --git a/app/features/screens/offersScreen/index.ts b/app/features/screens/offersScreen/index.ts
new file mode 100644
index 0000000..4df59cb
--- /dev/null
+++ b/app/features/screens/offersScreen/index.ts
@@ -0,0 +1 @@
+export * from './offersScreen';
diff --git a/app/features/screens/offersScreen/offersScreen.styles.ts b/app/features/screens/offersScreen/offersScreen.styles.ts
new file mode 100644
index 0000000..819124f
--- /dev/null
+++ b/app/features/screens/offersScreen/offersScreen.styles.ts
@@ -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',
+ },
+});
diff --git a/app/features/screens/offersScreen/offersScreen.tsx b/app/features/screens/offersScreen/offersScreen.tsx
new file mode 100644
index 0000000..c72a682
--- /dev/null
+++ b/app/features/screens/offersScreen/offersScreen.tsx
@@ -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 (
+
+
+ item.id}
+ renderItem={({ item }) => (
+
+
+ {item.code}
+ {item.description}
+ Expires: {item.expiry}
+
+ {}}
+ activeOpacity={0.7}
+ >
+ Copy
+
+
+ )}
+ contentContainerStyle={styles.list}
+ />
+
+ );
+};
diff --git a/app/features/screens/onboardingCompleteScreen/index.ts b/app/features/screens/onboardingCompleteScreen/index.ts
new file mode 100644
index 0000000..f1d2bfd
--- /dev/null
+++ b/app/features/screens/onboardingCompleteScreen/index.ts
@@ -0,0 +1 @@
+export * from './onboardingCompleteScreen';
diff --git a/app/features/screens/onboardingCompleteScreen/onboardingCompleteScreen.styles.ts b/app/features/screens/onboardingCompleteScreen/onboardingCompleteScreen.styles.ts
new file mode 100644
index 0000000..f7b2304
--- /dev/null
+++ b/app/features/screens/onboardingCompleteScreen/onboardingCompleteScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/onboardingCompleteScreen/onboardingCompleteScreen.tsx b/app/features/screens/onboardingCompleteScreen/onboardingCompleteScreen.tsx
new file mode 100644
index 0000000..8046efd
--- /dev/null
+++ b/app/features/screens/onboardingCompleteScreen/onboardingCompleteScreen.tsx
@@ -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 (
+
+
+ π
+ All Set!
+
+ You're ready to explore and order from the best providers near you.
+
+
+
+
+
+
+ );
+};
diff --git a/app/features/screens/orderConfirmedScreen/index.ts b/app/features/screens/orderConfirmedScreen/index.ts
new file mode 100644
index 0000000..09707e7
--- /dev/null
+++ b/app/features/screens/orderConfirmedScreen/index.ts
@@ -0,0 +1 @@
+export * from './orderConfirmedScreen';
diff --git a/app/features/screens/orderConfirmedScreen/orderConfirmedScreen.styles.ts b/app/features/screens/orderConfirmedScreen/orderConfirmedScreen.styles.ts
new file mode 100644
index 0000000..521e5a1
--- /dev/null
+++ b/app/features/screens/orderConfirmedScreen/orderConfirmedScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/orderConfirmedScreen/orderConfirmedScreen.tsx b/app/features/screens/orderConfirmedScreen/orderConfirmedScreen.tsx
new file mode 100644
index 0000000..8a1d5e1
--- /dev/null
+++ b/app/features/screens/orderConfirmedScreen/orderConfirmedScreen.tsx
@@ -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 (
+
+ {}} />
+
+ β
+ Order Confirmed!
+
+ Your order has been placed successfully
+
+
+ Order ID: ORD-123456
+ Estimated delivery: 25-30 min
+
+
+
+ {}} />
+
+
+ );
+};
diff --git a/app/features/screens/orderDeliveredScreen/index.ts b/app/features/screens/orderDeliveredScreen/index.ts
new file mode 100644
index 0000000..11924b3
--- /dev/null
+++ b/app/features/screens/orderDeliveredScreen/index.ts
@@ -0,0 +1 @@
+export * from './orderDeliveredScreen';
diff --git a/app/features/screens/orderDeliveredScreen/orderDeliveredScreen.styles.ts b/app/features/screens/orderDeliveredScreen/orderDeliveredScreen.styles.ts
new file mode 100644
index 0000000..5f05676
--- /dev/null
+++ b/app/features/screens/orderDeliveredScreen/orderDeliveredScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/orderDeliveredScreen/orderDeliveredScreen.tsx b/app/features/screens/orderDeliveredScreen/orderDeliveredScreen.tsx
new file mode 100644
index 0000000..655de4e
--- /dev/null
+++ b/app/features/screens/orderDeliveredScreen/orderDeliveredScreen.tsx
@@ -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 (
+
+ {}} />
+
+ π
+ Your order has been delivered!
+ How was your experience?
+
+
+
+ {}}
+ disabled={rating === 0}
+ style={{ marginTop: 32 }}
+ />
+
+
+ );
+};
diff --git a/app/features/screens/orderTrackingScreen/index.ts b/app/features/screens/orderTrackingScreen/index.ts
new file mode 100644
index 0000000..f51dbf1
--- /dev/null
+++ b/app/features/screens/orderTrackingScreen/index.ts
@@ -0,0 +1 @@
+export * from './orderTrackingScreen';
diff --git a/app/features/screens/orderTrackingScreen/orderTrackingScreen.styles.ts b/app/features/screens/orderTrackingScreen/orderTrackingScreen.styles.ts
new file mode 100644
index 0000000..2ef7ccb
--- /dev/null
+++ b/app/features/screens/orderTrackingScreen/orderTrackingScreen.styles.ts
@@ -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,
+ },
+});
diff --git a/app/features/screens/orderTrackingScreen/orderTrackingScreen.tsx b/app/features/screens/orderTrackingScreen/orderTrackingScreen.tsx
new file mode 100644
index 0000000..5cabb87
--- /dev/null
+++ b/app/features/screens/orderTrackingScreen/orderTrackingScreen.tsx
@@ -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 (
+
+ {}} />
+
+
+ {MILESTONES.map((milestone, index) => (
+
+
+
+ {index < MILESTONES.length - 1 && (
+
+ )}
+
+
+
+ {milestone.label}
+
+ {milestone.time}
+
+
+ ))}
+
+
+ Need help?
+ Contact support for assistance
+ {}} />
+
+
+
+ );
+};
diff --git a/app/features/screens/otpScreen/index.ts b/app/features/screens/otpScreen/index.ts
new file mode 100644
index 0000000..ebdf118
--- /dev/null
+++ b/app/features/screens/otpScreen/index.ts
@@ -0,0 +1 @@
+export * from './otpScreen';
diff --git a/app/features/screens/otpScreen/otpScreen.styles.ts b/app/features/screens/otpScreen/otpScreen.styles.ts
new file mode 100644
index 0000000..9db8d41
--- /dev/null
+++ b/app/features/screens/otpScreen/otpScreen.styles.ts
@@ -0,0 +1,85 @@
+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,
+ paddingHorizontal: 24,
+ justifyContent: 'center',
+ alignItems: 'center',
+ },
+ title: {
+ fontSize: typography.fontSize.xxl,
+ fontWeight: typography.fontWeight.bold,
+ color: colors.text,
+ marginBottom: 8,
+ },
+ subtitle: {
+ fontSize: typography.fontSize.md,
+ color: colors.textSecondary,
+ marginBottom: 40,
+ },
+ otpContainer: {
+ flexDirection: 'row',
+ gap: 10,
+ marginBottom: 32,
+ },
+ otpCell: {
+ width: 48,
+ height: 56,
+ borderWidth: 1.5,
+ borderColor: colors.border,
+ borderRadius: 12,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: colors.inputBg,
+ },
+ otpCellActive: {
+ borderColor: colors.primary,
+ },
+ otpCellFilled: {
+ borderColor: colors.primary,
+ backgroundColor: '#E8F5E9',
+ },
+ otpCellText: {
+ fontSize: typography.fontSize.xl,
+ fontWeight: typography.fontWeight.bold,
+ color: colors.text,
+ },
+ keypad: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ justifyContent: 'center',
+ gap: 12,
+ maxWidth: 300,
+ marginBottom: 24,
+ },
+ key: {
+ width: 80,
+ height: 56,
+ justifyContent: 'center',
+ alignItems: 'center',
+ borderRadius: 12,
+ backgroundColor: colors.inputBg,
+ borderWidth: 1,
+ borderColor: colors.border,
+ },
+ keyText: {
+ fontSize: typography.fontSize.xl,
+ fontWeight: typography.fontWeight.medium,
+ color: colors.text,
+ },
+ timerText: {
+ fontSize: typography.fontSize.sm,
+ color: colors.textSecondary,
+ },
+ resendText: {
+ fontSize: typography.fontSize.sm,
+ color: colors.primary,
+ fontWeight: typography.fontWeight.semibold,
+ },
+});
diff --git a/app/features/screens/otpScreen/otpScreen.tsx b/app/features/screens/otpScreen/otpScreen.tsx
new file mode 100644
index 0000000..9e83cdf
--- /dev/null
+++ b/app/features/screens/otpScreen/otpScreen.tsx
@@ -0,0 +1,154 @@
+import React, { useState, useEffect } from 'react';
+import {
+ View,
+ Text,
+ TouchableOpacity,
+ KeyboardAvoidingView,
+ Platform,
+} from 'react-native';
+import { useNavigation, useRoute, RouteProp } from '@react-navigation/native';
+import { StackNavigationProp } from '@react-navigation/stack';
+import { getStyles } from './otpScreen.styles';
+import { PrimaryButton } from '@components';
+import { useAppTheme } from '@theme';
+import { useAppDispatch } from '../../../hooks/useAppDispatch';
+import { verifyOtp } from '../../../store/authSlice';
+import { AuthStackParamList } from '../../../navigation/authStack';
+
+type OtpNavProp = StackNavigationProp;
+type OtpRouteProp = RouteProp;
+
+const OTP_LENGTH = 6;
+const RESEND_TIMER = 30;
+
+export const OtpScreen: React.FC = () => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+ const navigation = useNavigation();
+ const route = useRoute();
+ const dispatch = useAppDispatch();
+ const { mobileNumber } = route.params;
+
+ const [otp, setOtp] = useState(Array(OTP_LENGTH).fill(''));
+ const [activeIndex, setActiveIndex] = useState(0);
+ const [timer, setTimer] = useState(RESEND_TIMER);
+ const [isLoading, setIsLoading] = useState(false);
+
+ useEffect(() => {
+ if (timer > 0) {
+ const interval = setInterval(() => setTimer((t) => t - 1), 1000);
+ return () => clearInterval(interval);
+ }
+ }, [timer]);
+
+ const handleKeyPress = (key: string) => {
+ if (key === 'backspace') {
+ if (otp[activeIndex] || activeIndex > 0) {
+ const newOtp = [...otp];
+ if (otp[activeIndex]) {
+ newOtp[activeIndex] = '';
+ } else if (activeIndex > 0) {
+ newOtp[activeIndex - 1] = '';
+ setActiveIndex(activeIndex - 1);
+ }
+ setOtp(newOtp);
+ }
+ } else if (key >= '0' && key <= '9' && activeIndex < OTP_LENGTH) {
+ const newOtp = [...otp];
+ newOtp[activeIndex] = key;
+ setOtp(newOtp);
+ if (activeIndex < OTP_LENGTH - 1) {
+ setActiveIndex(activeIndex + 1);
+ }
+ }
+ };
+
+ const handleVerify = async () => {
+ const otpString = otp.join('');
+ if (otpString.length !== OTP_LENGTH) return;
+ setIsLoading(true);
+ await dispatch(verifyOtp({ mobileNumber, otp: otpString }));
+ setIsLoading(false);
+ navigation.navigate('SetLocationScreen');
+ };
+
+ return (
+
+
+ Verify OTP
+
+ Code sent to {mobileNumber}
+
+
+
+ {otp.map((digit, index) => (
+
+ {digit}
+
+ ))}
+
+
+
+ {[1, 2, 3, 4, 5, 6, 7, 8, 9].map((num) => (
+ handleKeyPress(String(num))}
+ activeOpacity={0.6}
+ >
+ {num}
+
+ ))}
+ handleKeyPress('backspace')}
+ activeOpacity={0.6}
+ >
+ β«
+
+ handleKeyPress('0')}
+ activeOpacity={0.6}
+ >
+ 0
+
+
+
+
+ {timer > 0 ? (
+ Resend code in {timer}s
+ ) : (
+ {
+ setTimer(RESEND_TIMER);
+ setOtp(Array(OTP_LENGTH).fill(''));
+ setActiveIndex(0);
+ }}
+ activeOpacity={0.7}
+ >
+ Resend OTP
+
+ )}
+
+
+
+
+ );
+};
diff --git a/app/features/screens/preferencesScreen/index.ts b/app/features/screens/preferencesScreen/index.ts
new file mode 100644
index 0000000..7dc5967
--- /dev/null
+++ b/app/features/screens/preferencesScreen/index.ts
@@ -0,0 +1 @@
+export * from './preferencesScreen';
diff --git a/app/features/screens/preferencesScreen/preferencesScreen.styles.ts b/app/features/screens/preferencesScreen/preferencesScreen.styles.ts
new file mode 100644
index 0000000..6607804
--- /dev/null
+++ b/app/features/screens/preferencesScreen/preferencesScreen.styles.ts
@@ -0,0 +1,76 @@
+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: {
+ 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,
+ },
+ grid: {
+ flexDirection: 'row',
+ flexWrap: 'wrap',
+ gap: 12,
+ marginBottom: 32,
+ },
+ gridItem: {
+ width: '47%',
+ aspectRatio: 1.2,
+ borderRadius: 16,
+ borderWidth: 1.5,
+ borderColor: colors.border,
+ justifyContent: 'center',
+ alignItems: 'center',
+ backgroundColor: colors.background,
+ },
+ gridItemSelected: {
+ borderColor: colors.primary,
+ backgroundColor: '#E8F5E9',
+ },
+ gridIcon: {
+ fontSize: 40,
+ marginBottom: 8,
+ },
+ gridLabel: {
+ fontSize: typography.fontSize.md,
+ fontWeight: typography.fontWeight.medium,
+ color: colors.text,
+ },
+ gridLabelSelected: {
+ color: colors.primary,
+ fontWeight: typography.fontWeight.semibold,
+ },
+ toggleRow: {
+ flexDirection: 'row',
+ justifyContent: 'space-between',
+ alignItems: 'center',
+ paddingVertical: 16,
+ borderTopWidth: 1,
+ borderTopColor: colors.border,
+ },
+ toggleLabel: {
+ fontSize: typography.fontSize.md,
+ fontWeight: typography.fontWeight.medium,
+ color: colors.text,
+ },
+ toggleSubtext: {
+ fontSize: typography.fontSize.xs,
+ color: colors.textSecondary,
+ marginTop: 2,
+ },
+});
diff --git a/app/features/screens/preferencesScreen/preferencesScreen.tsx b/app/features/screens/preferencesScreen/preferencesScreen.tsx
new file mode 100644
index 0000000..eb515a9
--- /dev/null
+++ b/app/features/screens/preferencesScreen/preferencesScreen.tsx
@@ -0,0 +1,90 @@
+import React, { useState } from 'react';
+import {
+ View,
+ Text,
+ TouchableOpacity,
+ Switch,
+ ScrollView,
+} from 'react-native';
+import { useNavigation } from '@react-navigation/native';
+import { StackNavigationProp } from '@react-navigation/stack';
+import { getStyles } from './preferencesScreen.styles';
+import { PrimaryButton } from '@components';
+import { useAppTheme } from '@theme';
+import { AuthStackParamList } from '../../../navigation/authStack';
+
+type NavProp = StackNavigationProp;
+
+const CATEGORIES = [
+ { key: 'food', icon: 'π', label: 'Food' },
+ { key: 'groceries', icon: 'π', label: 'Groceries' },
+ { key: 'pharmacy', icon: 'π', label: 'Pharmacy' },
+ { key: 'others', icon: 'π¦', label: 'Others' },
+];
+
+export const PreferencesScreen: React.FC = () => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+ const navigation = useNavigation();
+
+ const [selectedCategories, setSelectedCategories] = useState(['food']);
+ const [notificationsEnabled, setNotificationsEnabled] = useState(true);
+
+ const toggleCategory = (key: string) => {
+ setSelectedCategories((prev) =>
+ prev.includes(key)
+ ? prev.filter((c) => c !== key)
+ : [...prev, key],
+ );
+ };
+
+ return (
+
+ Preferences
+ Select your interests
+
+
+ {CATEGORIES.map((cat) => (
+ toggleCategory(cat.key)}
+ activeOpacity={0.7}
+ >
+ {cat.icon}
+
+ {cat.label}
+
+
+ ))}
+
+
+
+
+ Enable Notifications
+ Get order updates and offers
+
+
+
+
+ navigation.navigate('OnboardingCompleteScreen')}
+ style={{ marginTop: 32 }}
+ />
+
+ );
+};
diff --git a/app/features/screens/providerDetailsScreen/index.ts b/app/features/screens/providerDetailsScreen/index.ts
new file mode 100644
index 0000000..a976177
--- /dev/null
+++ b/app/features/screens/providerDetailsScreen/index.ts
@@ -0,0 +1 @@
+export * from './providerDetailsScreen';
diff --git a/app/features/screens/providerDetailsScreen/providerDetailsScreen.styles.ts b/app/features/screens/providerDetailsScreen/providerDetailsScreen.styles.ts
new file mode 100644
index 0000000..9e80219
--- /dev/null
+++ b/app/features/screens/providerDetailsScreen/providerDetailsScreen.styles.ts
@@ -0,0 +1,95 @@
+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,
+ },
+ hero: {
+ alignItems: 'center',
+ paddingVertical: 24,
+ borderBottomWidth: 1,
+ borderBottomColor: colors.border,
+ },
+ heroImage: {
+ width: 100,
+ height: 100,
+ borderRadius: 50,
+ backgroundColor: colors.inputBg,
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginBottom: 12,
+ },
+ heroEmoji: {
+ fontSize: 44,
+ },
+ heroName: {
+ fontSize: typography.fontSize.xl,
+ fontWeight: typography.fontWeight.bold,
+ color: colors.text,
+ marginBottom: 8,
+ },
+ heroStats: {
+ flexDirection: 'row',
+ gap: 16,
+ },
+ heroStat: {
+ fontSize: typography.fontSize.sm,
+ color: colors.textSecondary,
+ },
+ categoryRow: {
+ paddingVertical: 12,
+ paddingHorizontal: 16,
+ flexGrow: 0,
+ },
+ categoryTab: {
+ paddingHorizontal: 20,
+ paddingVertical: 8,
+ borderRadius: 20,
+ borderWidth: 1,
+ borderColor: colors.border,
+ marginRight: 8,
+ },
+ categoryTabActive: {
+ borderColor: colors.primary,
+ backgroundColor: '#E8F5E9',
+ },
+ categoryTabText: {
+ fontSize: typography.fontSize.sm,
+ color: colors.textSecondary,
+ },
+ categoryTabTextActive: {
+ color: colors.primary,
+ fontWeight: typography.fontWeight.semibold,
+ },
+ cartStrip: {
+ flexDirection: 'row',
+ alignItems: 'center',
+ justifyContent: 'space-between',
+ backgroundColor: colors.primary,
+ paddingHorizontal: 20,
+ paddingVertical: 14,
+ borderTopLeftRadius: 16,
+ borderTopRightRadius: 16,
+ },
+ cartStripText: {
+ fontSize: typography.fontSize.md,
+ fontWeight: typography.fontWeight.semibold,
+ color: '#FFFFFF',
+ },
+ viewCartButton: {
+ backgroundColor: '#FFFFFF',
+ paddingHorizontal: 16,
+ paddingVertical: 8,
+ borderRadius: 8,
+ },
+ viewCartText: {
+ fontSize: typography.fontSize.sm,
+ fontWeight: typography.fontWeight.semibold,
+ color: colors.primary,
+ },
+});
diff --git a/app/features/screens/providerDetailsScreen/providerDetailsScreen.tsx b/app/features/screens/providerDetailsScreen/providerDetailsScreen.tsx
new file mode 100644
index 0000000..24c55a0
--- /dev/null
+++ b/app/features/screens/providerDetailsScreen/providerDetailsScreen.tsx
@@ -0,0 +1,121 @@
+import React, { useState, useEffect } from 'react';
+import {
+ View,
+ Text,
+ TouchableOpacity,
+ ScrollView,
+} from 'react-native';
+import { getStyles } from './providerDetailsScreen.styles';
+import { Header, CatalogItemRow } from '@components';
+import { useAppTheme } from '@theme';
+import { useAppDispatch, useAppSelector } from '../../../hooks/useAppDispatch';
+import { addItem } from '../../../store/cartSlice';
+import { deliveryService } from '../../../services/deliveryService';
+import { CatalogItem } from '../../../interfaces';
+
+const CATEGORIES = ['Pizza', 'Sides', 'Beverages'];
+
+export const ProviderDetailsScreen: React.FC = () => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+ const dispatch = useAppDispatch();
+ const cartItems = useAppSelector((state) => state.cart.items);
+
+ const [catalog, setCatalog] = useState([]);
+ const [activeCategory, setActiveCategory] = useState(CATEGORIES[0]);
+
+ useEffect(() => {
+ deliveryService.getProviderCatalog('p1').then(setCatalog);
+ }, []);
+
+ const filteredItems = catalog.filter((item) => item.category === activeCategory);
+
+ const getItemQuantity = (itemId: string) => {
+ const item = cartItems.find((i) => i.item.id === itemId);
+ return item ? item.quantity : 0;
+ };
+
+ const totalItems = cartItems.reduce((sum, i) => sum + i.quantity, 0);
+ const totalPrice = cartItems.reduce(
+ (sum, i) => sum + i.item.price * i.quantity,
+ 0,
+ );
+
+ return (
+
+ {}} />
+
+
+
+ π
+
+ Pizza Planet
+
+ β 4.5
+ π 25 min
+ π Italian
+
+
+
+
+ {CATEGORIES.map((cat) => (
+ setActiveCategory(cat)}
+ activeOpacity={0.7}
+ >
+
+ {cat}
+
+
+ ))}
+
+
+ {filteredItems.map((item) => (
+
+ dispatch(
+ addItem({
+ providerId: 'p1',
+ providerName: 'Pizza Planet',
+ item,
+ }),
+ )
+ }
+ onRemove={() => {}}
+ />
+ ))}
+
+
+ {totalItems > 0 && (
+
+
+ {totalItems} Items | βΉ{totalPrice}
+
+
+ View Cart
+
+
+ )}
+
+ );
+};
diff --git a/app/features/screens/providerListScreen/index.ts b/app/features/screens/providerListScreen/index.ts
new file mode 100644
index 0000000..d5b538b
--- /dev/null
+++ b/app/features/screens/providerListScreen/index.ts
@@ -0,0 +1 @@
+export * from './providerListScreen';
diff --git a/app/features/screens/providerListScreen/providerListScreen.styles.ts b/app/features/screens/providerListScreen/providerListScreen.styles.ts
new file mode 100644
index 0000000..694a8ca
--- /dev/null
+++ b/app/features/screens/providerListScreen/providerListScreen.styles.ts
@@ -0,0 +1,38 @@
+import { StyleSheet } from 'react-native';
+import { typography } from '@theme';
+
+export const getStyles = (colors: any) => StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.background,
+ },
+ filterRow: {
+ paddingVertical: 8,
+ paddingHorizontal: 16,
+ flexGrow: 0,
+ },
+ filterChip: {
+ paddingHorizontal: 16,
+ paddingVertical: 8,
+ borderRadius: 20,
+ borderWidth: 1,
+ borderColor: colors.border,
+ marginRight: 8,
+ backgroundColor: colors.background,
+ },
+ filterChipActive: {
+ borderColor: colors.primary,
+ backgroundColor: '#E8F5E9',
+ },
+ filterChipText: {
+ fontSize: typography.fontSize.sm,
+ color: colors.textSecondary,
+ },
+ filterChipTextActive: {
+ color: colors.primary,
+ fontWeight: typography.fontWeight.semibold,
+ },
+ list: {
+ paddingBottom: 24,
+ },
+});
diff --git a/app/features/screens/providerListScreen/providerListScreen.tsx b/app/features/screens/providerListScreen/providerListScreen.tsx
new file mode 100644
index 0000000..b0b314a
--- /dev/null
+++ b/app/features/screens/providerListScreen/providerListScreen.tsx
@@ -0,0 +1,69 @@
+import React, { useState, useEffect } from 'react';
+import {
+ View,
+ Text,
+ FlatList,
+ TouchableOpacity,
+ ScrollView,
+} from 'react-native';
+import { getStyles } from './providerListScreen.styles';
+import { ProviderCard, Header } from '@components';
+import { useAppTheme } from '@theme';
+import { deliveryService } from '../../../services/deliveryService';
+import { Provider } from '../../../interfaces';
+
+const FILTERS = ['Filters', 'Rating 4.0+', 'Fast Delivery'];
+
+export const ProviderListScreen: React.FC = () => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+ const [providers, setProviders] = useState([]);
+ const [activeFilter, setActiveFilter] = useState(null);
+
+ useEffect(() => {
+ deliveryService.getProviders().then(setProviders);
+ }, []);
+
+ return (
+
+ {}} />
+
+ {FILTERS.map((filter) => (
+ setActiveFilter(activeFilter === filter ? null : filter)}
+ activeOpacity={0.7}
+ >
+
+ {filter}
+
+
+ ))}
+
+ item.id}
+ renderItem={({ item }) => (
+
+ )}
+ contentContainerStyle={styles.list}
+ />
+
+ );
+};
diff --git a/app/features/screens/searchScreen/index.ts b/app/features/screens/searchScreen/index.ts
new file mode 100644
index 0000000..9b0c7b3
--- /dev/null
+++ b/app/features/screens/searchScreen/index.ts
@@ -0,0 +1 @@
+export * from './searchScreen';
diff --git a/app/features/screens/searchScreen/searchScreen.styles.ts b/app/features/screens/searchScreen/searchScreen.styles.ts
new file mode 100644
index 0000000..849ec49
--- /dev/null
+++ b/app/features/screens/searchScreen/searchScreen.styles.ts
@@ -0,0 +1,27 @@
+import { StyleSheet } from 'react-native';
+import { typography } from '@theme';
+
+export const getStyles = (colors: any) => StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.background,
+ paddingTop: 8,
+ },
+ list: {
+ paddingBottom: 24,
+ },
+ empty: {
+ alignItems: 'center',
+ paddingTop: 60,
+ },
+ emptyIcon: {
+ fontSize: 48,
+ marginBottom: 12,
+ },
+ emptyText: {
+ fontSize: typography.fontSize.md,
+ color: colors.textSecondary,
+ textAlign: 'center',
+ paddingHorizontal: 40,
+ },
+});
diff --git a/app/features/screens/searchScreen/searchScreen.tsx b/app/features/screens/searchScreen/searchScreen.tsx
new file mode 100644
index 0000000..3686f21
--- /dev/null
+++ b/app/features/screens/searchScreen/searchScreen.tsx
@@ -0,0 +1,64 @@
+import React, { useState, useEffect } from 'react';
+import {
+ View,
+ Text,
+ FlatList,
+} from 'react-native';
+import { getStyles } from './searchScreen.styles';
+import { SearchBar, ProviderCard } from '@components';
+import { useAppTheme } from '@theme';
+import { deliveryService } from '../../../services/deliveryService';
+import { Provider } from '../../../interfaces';
+
+export const SearchScreen: React.FC = () => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+
+ const [query, setQuery] = useState('');
+ const [results, setResults] = useState([]);
+
+ useEffect(() => {
+ if (query.trim().length > 0) {
+ deliveryService.searchProviders(query).then(setResults);
+ } else {
+ setResults([]);
+ }
+ }, [query]);
+
+ return (
+
+
+ item.id}
+ renderItem={({ item }) => (
+
+ )}
+ ListEmptyComponent={
+ query.trim().length > 0 ? (
+
+ No results found
+
+ ) : (
+
+ π
+ Search for food, groceries, medicines...
+
+ )
+ }
+ contentContainerStyle={styles.list}
+ />
+
+ );
+};
diff --git a/app/features/screens/setLocationScreen/index.ts b/app/features/screens/setLocationScreen/index.ts
new file mode 100644
index 0000000..1bd476b
--- /dev/null
+++ b/app/features/screens/setLocationScreen/index.ts
@@ -0,0 +1 @@
+export * from './setLocationScreen';
diff --git a/app/features/screens/setLocationScreen/setLocationScreen.styles.ts b/app/features/screens/setLocationScreen/setLocationScreen.styles.ts
new file mode 100644
index 0000000..f768f02
--- /dev/null
+++ b/app/features/screens/setLocationScreen/setLocationScreen.styles.ts
@@ -0,0 +1,75 @@
+import { StyleSheet } from 'react-native';
+import { typography } from '@theme';
+
+export const getStyles = (colors: any) => StyleSheet.create({
+ container: {
+ flex: 1,
+ backgroundColor: colors.background,
+ },
+ scrollContainer: {
+ flexGrow: 1,
+ paddingTop: 60,
+ },
+ mapPlaceholder: {
+ height: 240,
+ backgroundColor: colors.inputBg,
+ marginHorizontal: 24,
+ borderRadius: 16,
+ justifyContent: 'center',
+ alignItems: 'center',
+ marginBottom: 24,
+ 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,
+ },
+ card: {
+ backgroundColor: colors.cardBg,
+ borderRadius: 12,
+ padding: 16,
+ marginHorizontal: 24,
+ marginBottom: 24,
+ borderWidth: 1,
+ borderColor: colors.border,
+ },
+ cardTitle: {
+ fontSize: typography.fontSize.sm,
+ color: colors.textSecondary,
+ marginBottom: 4,
+ },
+ cardAddress: {
+ fontSize: typography.fontSize.md,
+ fontWeight: typography.fontWeight.semibold,
+ color: colors.text,
+ marginBottom: 8,
+ },
+ changeLink: {
+ alignSelf: 'flex-end',
+ },
+ changeText: {
+ fontSize: typography.fontSize.sm,
+ color: colors.primary,
+ fontWeight: typography.fontWeight.semibold,
+ },
+ manualButton: {
+ alignItems: 'center',
+ paddingVertical: 16,
+ },
+ manualButtonText: {
+ fontSize: typography.fontSize.sm,
+ color: colors.primary,
+ fontWeight: typography.fontWeight.semibold,
+ },
+});
diff --git a/app/features/screens/setLocationScreen/setLocationScreen.tsx b/app/features/screens/setLocationScreen/setLocationScreen.tsx
new file mode 100644
index 0000000..4922c02
--- /dev/null
+++ b/app/features/screens/setLocationScreen/setLocationScreen.tsx
@@ -0,0 +1,64 @@
+import React from 'react';
+import {
+ View,
+ Text,
+ TouchableOpacity,
+ KeyboardAvoidingView,
+ Platform,
+ ScrollView,
+} from 'react-native';
+import { useNavigation } from '@react-navigation/native';
+import { StackNavigationProp } from '@react-navigation/stack';
+import { getStyles } from './setLocationScreen.styles';
+import { PrimaryButton } from '@components';
+import { useAppTheme } from '@theme';
+import { AuthStackParamList } from '../../../navigation/authStack';
+
+type NavProp = StackNavigationProp;
+
+export const SetLocationScreen: React.FC = () => {
+ const { colors } = useAppTheme();
+ const styles = getStyles(colors);
+ const navigation = useNavigation();
+
+ return (
+
+
+
+ πΊοΈ
+ Map View
+
+ (react-native-maps integration)
+
+
+
+
+ Delivery Location
+
+ Koramangala 4th Block, Bengaluru, 560034
+
+
+ Change
+
+
+
+ navigation.navigate('CompleteProfileScreen')}
+ style={{ marginHorizontal: 24 }}
+ />
+
+ navigation.navigate('CompleteProfileScreen')}
+ activeOpacity={0.7}
+ >
+ Enter address manually
+
+
+
+ );
+};
diff --git a/app/hooks/useAppDispatch.ts b/app/hooks/useAppDispatch.ts
new file mode 100644
index 0000000..39f730f
--- /dev/null
+++ b/app/hooks/useAppDispatch.ts
@@ -0,0 +1,5 @@
+import { useDispatch, useSelector, TypedUseSelectorHook } from 'react-redux';
+import type { RootState, AppDispatch } from '../store';
+
+export const useAppDispatch = () => useDispatch();
+export const useAppSelector: TypedUseSelectorHook = useSelector;
diff --git a/app/interfaces/index.ts b/app/interfaces/index.ts
new file mode 100644
index 0000000..2066c82
--- /dev/null
+++ b/app/interfaces/index.ts
@@ -0,0 +1,85 @@
+export interface User {
+ id: string;
+ mobileNumber: string;
+ name?: string;
+ email?: string;
+ locationLabels?: string[];
+}
+
+export interface Location {
+ latitude: number;
+ longitude: number;
+ address: string;
+ city: string;
+ pincode: string;
+ label?: string;
+}
+
+export interface Provider {
+ id: string;
+ imageUrl: string;
+ name: string;
+ rating: number;
+ deliveryTime: string;
+ tag: string;
+ discountText?: string;
+ serviceType: string;
+}
+
+export interface CatalogItem {
+ id: string;
+ imageUrl: string;
+ title: string;
+ description: string;
+ price: number;
+ category: string;
+}
+
+export interface CartItem {
+ id: string;
+ providerId: string;
+ providerName: string;
+ item: CatalogItem;
+ quantity: number;
+}
+
+export type OrderStatus = 'placed' | 'confirmed' | 'dispatched' | 'delivered' | 'cancelled';
+
+export interface Order {
+ id: string;
+ providerId: string;
+ providerName: string;
+ providerImage: string;
+ items: CartItem[];
+ status: OrderStatus;
+ total: number;
+ deliveryFee: number;
+ platformFee: number;
+ discount: number;
+ couponCode?: string;
+ createdAt: string;
+ estimatedDelivery: string;
+ deliveryAddress: Location;
+}
+
+export interface TrackingStep {
+ key: OrderStatus;
+ label: string;
+ timestamp?: string;
+ isCompleted: boolean;
+ isActive: boolean;
+}
+
+export interface DeliveryAgent {
+ name: string;
+ rating: number;
+ vehicleDetails: string;
+ phoneNumber: string;
+}
+
+export interface PaymentMethod {
+ id: string;
+ type: 'UPI' | 'Card' | 'Wallet' | 'COD';
+ label: string;
+ icon?: string;
+}
diff --git a/app/navigation/appStack.tsx b/app/navigation/appStack.tsx
new file mode 100644
index 0000000..9fc5e43
--- /dev/null
+++ b/app/navigation/appStack.tsx
@@ -0,0 +1,82 @@
+import React from 'react';
+import { createBottomTabNavigator } from '@react-navigation/bottom-tabs';
+import { Text } from 'react-native';
+import {
+ HomeScreen,
+ SearchScreen,
+ MyOrdersScreen,
+ OffersScreen,
+ AccountScreen,
+} from '@features/screens';
+
+export type AppStackParamList = {
+ HomeScreen: undefined;
+ SearchScreen: undefined;
+ MyOrdersScreen: undefined;
+ OffersScreen: undefined;
+ AccountScreen: undefined;
+};
+
+const Tab = createBottomTabNavigator();
+
+const tabIcons: Record = {
+ HomeScreen: 'π ',
+ SearchScreen: 'π',
+ MyOrdersScreen: 'π¦',
+ OffersScreen: 'π·οΈ',
+ AccountScreen: 'π€',
+};
+
+const renderTabIcon = (routeName: string, focused: boolean) => (
+
+ {tabIcons[routeName]}
+
+);
+
+export const AppStack: React.FC = () => {
+ return (
+ ({
+ headerShown: false,
+ tabBarIcon: ({ focused }) => renderTabIcon(route.name, focused),
+ tabBarActiveTintColor: '#05824C',
+ tabBarInactiveTintColor: '#666666',
+ tabBarStyle: {
+ paddingBottom: 8,
+ paddingTop: 8,
+ height: 60,
+ },
+ tabBarLabelStyle: {
+ fontSize: 11,
+ fontWeight: '500',
+ },
+ })}
+ >
+
+
+
+
+
+
+ );
+};
diff --git a/app/navigation/authStack.tsx b/app/navigation/authStack.tsx
new file mode 100644
index 0000000..27c69d7
--- /dev/null
+++ b/app/navigation/authStack.tsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { createStackNavigator } from '@react-navigation/stack';
+import {
+ LoginScreen,
+ OtpScreen,
+ SetLocationScreen,
+ CompleteProfileScreen,
+ PreferencesScreen,
+ OnboardingCompleteScreen,
+} from '@features/screens';
+
+export type AuthStackParamList = {
+ LoginScreen: undefined;
+ OtpScreen: { mobileNumber: string };
+ SetLocationScreen: undefined;
+ CompleteProfileScreen: undefined;
+ PreferencesScreen: undefined;
+ OnboardingCompleteScreen: undefined;
+};
+
+const Stack = createStackNavigator();
+
+export const AuthStack: React.FC = () => {
+ return (
+
+
+
+
+
+
+
+
+ );
+};
diff --git a/app/navigation/rootNavigator.tsx b/app/navigation/rootNavigator.tsx
new file mode 100644
index 0000000..43127d0
--- /dev/null
+++ b/app/navigation/rootNavigator.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import { useSelector } from 'react-redux';
+import { RootState } from '../store';
+import { AuthStack } from './authStack';
+import { AppStack } from './appStack';
+
+export const RootNavigator: React.FC = () => {
+ const { isAuthenticated, onboardingComplete } = useSelector(
+ (state: RootState) => state.auth,
+ );
+
+ if (!isAuthenticated || !onboardingComplete) {
+ return ;
+ }
+
+ return ;
+};
diff --git a/app/services/apiClient.ts b/app/services/apiClient.ts
new file mode 100644
index 0000000..5bbe5f4
--- /dev/null
+++ b/app/services/apiClient.ts
@@ -0,0 +1,18 @@
+const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
+
+export const apiClient = {
+ async get(url: string, mockData: T): Promise {
+ await delay(800);
+ return mockData;
+ },
+
+ async post(url: string, body: unknown, mockResponse: T): Promise {
+ await delay(1000);
+ return mockResponse;
+ },
+
+ async put(url: string, body: unknown, mockResponse: T): Promise {
+ await delay(800);
+ return mockResponse;
+ },
+};
diff --git a/app/services/authService.ts b/app/services/authService.ts
new file mode 100644
index 0000000..8945b3c
--- /dev/null
+++ b/app/services/authService.ts
@@ -0,0 +1,49 @@
+import { apiClient } from './apiClient';
+import { User } from '../interfaces';
+
+const mockUser: User = {
+ id: 'user-001',
+ mobileNumber: '',
+ name: '',
+ email: '',
+ locationLabels: [],
+};
+
+export const authService = {
+ async login(mobileNumber: string) {
+ return apiClient.post(
+ '/auth/login',
+ { mobileNumber },
+ { success: true, message: 'OTP sent successfully' },
+ );
+ },
+
+ async verifyOtp(mobileNumber: string, otp: string) {
+ return apiClient.post(
+ '/auth/verify-otp',
+ { mobileNumber, otp },
+ {
+ success: true,
+ user: {
+ ...mockUser,
+ mobileNumber,
+ id: 'user-001',
+ },
+ },
+ );
+ },
+
+ async updateProfile(data: { name: string; email: string; locationLabels: string[] }) {
+ return apiClient.put('/auth/profile', data, {
+ success: true,
+ user: {
+ ...mockUser,
+ ...data,
+ },
+ });
+ },
+
+ async logout() {
+ return apiClient.post('/auth/logout', {}, { success: true });
+ },
+};
diff --git a/app/services/deliveryService.ts b/app/services/deliveryService.ts
new file mode 100644
index 0000000..9c9d8b0
--- /dev/null
+++ b/app/services/deliveryService.ts
@@ -0,0 +1,71 @@
+import { apiClient } from './apiClient';
+import { Provider, CatalogItem, Order, DeliveryAgent, Location } from '../interfaces';
+
+const mockProviders: Provider[] = [
+ { id: 'p1', imageUrl: '', name: 'Pizza Planet', rating: 4.5, deliveryTime: '25 min', tag: 'Food', discountText: '50% OFF', serviceType: 'Italian' },
+ { id: 'p2', imageUrl: '', name: 'Fresh Mart', rating: 4.2, deliveryTime: '30 min', tag: 'Groceries', discountText: '20% OFF', serviceType: 'Grocery' },
+ { id: 'p3', imageUrl: '', name: 'MediQuick', rating: 4.7, deliveryTime: '15 min', tag: 'Pharmacy', discountText: undefined, serviceType: 'Pharmacy' },
+ { id: 'p4', imageUrl: '', name: 'Burger Barn', rating: 4.3, deliveryTime: '20 min', tag: 'Food', discountText: 'Buy 1 Get 1', serviceType: 'Fast Food' },
+ { id: 'p5', imageUrl: '', name: 'Green Basket', rating: 4.0, deliveryTime: '35 min', tag: 'Groceries', discountText: undefined, serviceType: 'Organic' },
+];
+
+const mockCatalogItems: CatalogItem[] = [
+ { id: 'i1', imageUrl: '', title: 'Margherita Pizza', description: 'Classic cheese and tomato', price: 299, category: 'Pizza' },
+ { id: 'i2', imageUrl: '', title: 'Pepperoni Pizza', description: 'Loaded with pepperoni slices', price: 399, category: 'Pizza' },
+ { id: 'i3', imageUrl: '', title: 'French Fries', description: 'Crispy golden fries', price: 99, category: 'Sides' },
+ { id: 'i4', imageUrl: '', title: 'Garlic Bread', description: 'Toasted bread with garlic butter', price: 149, category: 'Sides' },
+ { id: 'i5', imageUrl: '', title: 'Chocolate Shake', description: 'Rich chocolate milkshake', price: 199, category: 'Beverages' },
+ { id: 'i6', imageUrl: '', title: 'Cola', description: 'Refreshing cold drink', price: 49, category: 'Beverages' },
+];
+
+export const deliveryService = {
+ async getProviders() {
+ return apiClient.get('/providers', mockProviders);
+ },
+
+ async getProviderCatalog(providerId: string) {
+ return apiClient.get(`/providers/${providerId}/catalog`, mockCatalogItems);
+ },
+
+ async searchProviders(query: string) {
+ const results = mockProviders.filter(
+ (p) =>
+ p.name.toLowerCase().includes(query.toLowerCase()) ||
+ p.tag.toLowerCase().includes(query.toLowerCase()),
+ );
+ return apiClient.get(`/search?q=${query}`, results);
+ },
+
+ async placeOrder(orderData: Partial) {
+ return apiClient.post('/orders', orderData, {
+ success: true,
+ order: {
+ id: `ORD-${Date.now()}`,
+ ...orderData,
+ status: 'placed' as const,
+ createdAt: new Date().toISOString(),
+ },
+ });
+ },
+
+ async getDeliveryAgent() {
+ const agent: DeliveryAgent = {
+ name: 'Rahul Sharma',
+ rating: 4.8,
+ vehicleDetails: 'KA-01-AB-1234 - Honda Activa',
+ phoneNumber: '+91 98765 43210',
+ };
+ return apiClient.get('/delivery/agent', agent);
+ },
+
+ async getLiveLocation() {
+ const location: Location = {
+ latitude: 12.9352,
+ longitude: 77.6245,
+ address: 'Koramangala 4th Block',
+ city: 'Bengaluru',
+ pincode: '560034',
+ };
+ return apiClient.get('/delivery/live-location', location);
+ },
+};
diff --git a/app/store/authSlice.ts b/app/store/authSlice.ts
new file mode 100644
index 0000000..915498f
--- /dev/null
+++ b/app/store/authSlice.ts
@@ -0,0 +1,102 @@
+import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
+import { User, Location } from '../interfaces';
+import { authService } from '../services/authService';
+
+interface AuthState {
+ user: User | null;
+ isAuthenticated: boolean;
+ isLoading: boolean;
+ onboardingComplete: boolean;
+ error: string | null;
+}
+
+const initialState: AuthState = {
+ user: null,
+ isAuthenticated: false,
+ isLoading: false,
+ onboardingComplete: false,
+ error: null,
+};
+
+export const loginWithPhone = createAsyncThunk(
+ 'auth/loginWithPhone',
+ async (mobileNumber: string) => {
+ const response = await authService.login(mobileNumber);
+ return response;
+ },
+);
+
+export const verifyOtp = createAsyncThunk(
+ 'auth/verifyOtp',
+ async ({ mobileNumber, otp }: { mobileNumber: string; otp: string }) => {
+ const response = await authService.verifyOtp(mobileNumber, otp);
+ return response;
+ },
+);
+
+const authSlice = createSlice({
+ name: 'auth',
+ initialState,
+ reducers: {
+ setUser(state, action: PayloadAction) {
+ state.user = action.payload;
+ },
+ setLocation(state, _action: PayloadAction) {
+ if (state.user) {
+ state.user = { ...state.user };
+ }
+ },
+ completeProfile(state, action: PayloadAction<{ name: string; email: string; locationLabels: string[] }>) {
+ if (state.user) {
+ state.user = {
+ ...state.user,
+ name: action.payload.name,
+ email: action.payload.email,
+ locationLabels: action.payload.locationLabels,
+ };
+ }
+ },
+ completeOnboarding(state) {
+ state.onboardingComplete = true;
+ },
+ logout(state) {
+ state.user = null;
+ state.isAuthenticated = false;
+ state.onboardingComplete = false;
+ state.error = null;
+ },
+ clearError(state) {
+ state.error = null;
+ },
+ },
+ extraReducers: (builder) => {
+ builder
+ .addCase(loginWithPhone.pending, (state) => {
+ state.isLoading = true;
+ state.error = null;
+ })
+ .addCase(loginWithPhone.fulfilled, (state) => {
+ state.isLoading = false;
+ })
+ .addCase(loginWithPhone.rejected, (state, action) => {
+ state.isLoading = false;
+ state.error = action.error.message || 'Login failed';
+ })
+ .addCase(verifyOtp.pending, (state) => {
+ state.isLoading = true;
+ state.error = null;
+ })
+ .addCase(verifyOtp.fulfilled, (state, action) => {
+ state.isLoading = false;
+ state.isAuthenticated = true;
+ state.user = action.payload.user;
+ })
+ .addCase(verifyOtp.rejected, (state, action) => {
+ state.isLoading = false;
+ state.error = action.error.message || 'OTP verification failed';
+ });
+ },
+});
+
+export const { setUser, setLocation, completeProfile, completeOnboarding, logout, clearError } = authSlice.actions;
+export default authSlice.reducer;
diff --git a/app/store/cartSlice.ts b/app/store/cartSlice.ts
new file mode 100644
index 0000000..c8986d8
--- /dev/null
+++ b/app/store/cartSlice.ts
@@ -0,0 +1,96 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+import { CartItem, CatalogItem } from '../interfaces';
+
+interface CartState {
+ items: CartItem[];
+ couponCode: string | null;
+ discount: number;
+ providerId: string | null;
+ providerName: string | null;
+}
+
+const initialState: CartState = {
+ items: [],
+ couponCode: null,
+ discount: 0,
+ providerId: null,
+ providerName: null,
+};
+
+const cartSlice = createSlice({
+ name: 'cart',
+ initialState,
+ reducers: {
+ addItem(state, action: PayloadAction<{ providerId: string; providerName: string; item: CatalogItem }>) {
+ const { providerId, providerName, item } = action.payload;
+ if (state.providerId && state.providerId !== providerId) {
+ state.items = [];
+ }
+ state.providerId = providerId;
+ state.providerName = providerName;
+ const existing = state.items.find((i) => i.item.id === item.id);
+ if (existing) {
+ existing.quantity += 1;
+ } else {
+ state.items.push({
+ id: `${providerId}-${item.id}`,
+ providerId,
+ providerName,
+ item,
+ quantity: 1,
+ });
+ }
+ },
+ removeItem(state, action: PayloadAction) {
+ const existing = state.items.find((i) => i.item.id === action.payload);
+ if (existing) {
+ if (existing.quantity > 1) {
+ existing.quantity -= 1;
+ } else {
+ state.items = state.items.filter((i) => i.item.id !== action.payload);
+ }
+ }
+ if (state.items.length === 0) {
+ state.providerId = null;
+ state.providerName = null;
+ }
+ },
+ clearCart(state) {
+ state.items = [];
+ state.couponCode = null;
+ state.discount = 0;
+ state.providerId = null;
+ state.providerName = null;
+ },
+ applyCoupon(state, action: PayloadAction) {
+ state.couponCode = action.payload;
+ state.discount = 50;
+ },
+ removeCoupon(state) {
+ state.couponCode = null;
+ state.discount = 0;
+ },
+ },
+});
+
+export const { addItem, removeItem, clearCart, applyCoupon, removeCoupon } = cartSlice.actions;
+
+export const selectCartTotal = (state: { cart: CartState }) => {
+ const subtotal = state.cart.items.reduce(
+ (sum, item) => sum + item.item.price * item.quantity,
+ 0,
+ );
+ const deliveryFee = 40;
+ const platformFee = 10;
+ const discount = state.cart.discount;
+ return {
+ subtotal,
+ deliveryFee,
+ platformFee,
+ discount,
+ total: Math.max(0, subtotal + deliveryFee + platformFee - discount),
+ itemCount: state.cart.items.reduce((sum, item) => sum + item.quantity, 0),
+ };
+};
+
+export default cartSlice.reducer;
diff --git a/app/store/index.ts b/app/store/index.ts
new file mode 100644
index 0000000..4c0bcbd
--- /dev/null
+++ b/app/store/index.ts
@@ -0,0 +1,15 @@
+import { configureStore } from '@reduxjs/toolkit';
+import authReducer from './authSlice';
+import cartReducer from './cartSlice';
+import orderReducer from './orderSlice';
+
+export const store = configureStore({
+ reducer: {
+ auth: authReducer,
+ cart: cartReducer,
+ order: orderReducer,
+ },
+});
+
+export type RootState = ReturnType;
+export type AppDispatch = typeof store.dispatch;
diff --git a/app/store/orderSlice.ts b/app/store/orderSlice.ts
new file mode 100644
index 0000000..17ae137
--- /dev/null
+++ b/app/store/orderSlice.ts
@@ -0,0 +1,67 @@
+import { createSlice, PayloadAction } from '@reduxjs/toolkit';
+import { Order, OrderStatus, TrackingStep, DeliveryAgent, Location } from '../interfaces';
+
+interface OrderState {
+ currentOrder: Order | null;
+ orders: Order[];
+ trackingSteps: TrackingStep[];
+ deliveryAgent: DeliveryAgent | null;
+ liveLocation: Location | null;
+}
+
+const buildTrackingSteps = (status: OrderStatus): TrackingStep[] => {
+ const steps: { key: OrderStatus; label: string }[] = [
+ { key: 'placed', label: 'Order Placed' },
+ { key: 'confirmed', label: 'Confirmed' },
+ { key: 'dispatched', label: 'Dispatched' },
+ { key: 'delivered', label: 'Delivered' },
+ ];
+ const currentIndex = steps.findIndex((s) => s.key === status);
+ return steps.map((step, index) => ({
+ ...step,
+ isCompleted: index < currentIndex,
+ isActive: index === currentIndex,
+ }));
+};
+
+const initialState: OrderState = {
+ currentOrder: null,
+ orders: [],
+ trackingSteps: [],
+ deliveryAgent: null,
+ liveLocation: null,
+};
+
+const orderSlice = createSlice({
+ name: 'order',
+ initialState,
+ reducers: {
+ placeOrder(state, action: PayloadAction) {
+ state.currentOrder = action.payload;
+ state.orders.unshift(action.payload);
+ state.trackingSteps = buildTrackingSteps('placed');
+ },
+ updateOrderStatus(state, action: PayloadAction) {
+ if (state.currentOrder) {
+ state.currentOrder.status = action.payload;
+ state.trackingSteps = buildTrackingSteps(action.payload);
+ const idx = state.orders.findIndex((o) => o.id === state.currentOrder!.id);
+ if (idx !== -1) {
+ state.orders[idx] = { ...state.orders[idx], status: action.payload };
+ }
+ }
+ },
+ setDeliveryAgent(state, action: PayloadAction) {
+ state.deliveryAgent = action.payload;
+ },
+ updateLiveLocation(state, action: PayloadAction) {
+ state.liveLocation = action.payload;
+ },
+ addOrder(state, action: PayloadAction) {
+ state.orders.unshift(action.payload);
+ },
+ },
+});
+
+export const { placeOrder, updateOrderStatus, setDeliveryAgent, updateLiveLocation, addOrder } = orderSlice.actions;
+export default orderSlice.reducer;
diff --git a/babel.config.js b/babel.config.js
index 73c2cce..4bfa3f7 100644
--- a/babel.config.js
+++ b/babel.config.js
@@ -12,5 +12,6 @@ module.exports = {
},
},
],
+ 'react-native-worklets/plugin',
],
};
diff --git a/implementation.md b/implementation.md
new file mode 100644
index 0000000..6456fa4
--- /dev/null
+++ b/implementation.md
@@ -0,0 +1,253 @@
+Multi-Delivery Mobile App β Reusable Screen Flow Implementation Plan
+Overview
+This plan details the implementation of a generic Multi-Delivery Customer Mobile App screen flow (supporting Food, Groceries, Pharmacy, and other delivery services).
+
+To ensure the codebase is easily portable and reusable in other projects:
+
+Clean Architecture & Separation of Concerns: All components are presentation-only, fully driven by props, and decoupled from state.
+Pluggable API Service Layer: The API and store layers are modular, allowing simple swapping of mock data with real APIs later.
+Generic Terminology: Uses general naming (e.g., provider instead of restaurant, catalogItem instead of food/menu item) to support any delivery vertical.
+camelCase Convention: All directory, file, and variable names use camelCase.
+Decisions (Updated)
+Design Element Updated Requirement
+Login Flow π« No password, Forgot Password?, or Sign Up links required. Simply request Mobile Number and navigate to OTP.
+API Integration π Structure with pluggable service clients. Currently returns mock data, easily swappable with real endpoints later.
+Naming & Portability π¦ Modular, highly generic component names (e.g., providerCard, catalogItemRow). Built to be highly reusable in other projects.
+User Review Required
+WARNING
+
+Native libraries (react-native-maps, React Navigation, etc.) require building native binaries (yarn android / yarn ios) after installation.
+
+Naming & Folder Structure Conventions
+camelCase for all file and folder names (e.g. otpScreen/, providerCard/).
+Avoid domain-specific terminology:
+Use provider instead of restaurant / shop.
+Use catalogItem or product instead of menu / foodItem.
+Use serviceType instead of cuisine.
+Proposed Changes
+Phase 1 β Foundation, Navigation & Dependencies
+Install core dependencies:
+
+@react-navigation/native, @react-navigation/stack, @react-navigation/bottom-tabs
+react-native-screens, react-native-safe-area-context
+react-native-gesture-handler, react-native-reanimated
+react-native-maps
+@reduxjs/toolkit, react-redux
+react-native-push-notification, @react-native-community/push-notification-ios
+[NEW] app/navigation/rootNavigator.tsx
+Root navigator that directs to either authStack or appStack based on Redux authentication state.
+
+[NEW] app/navigation/authStack.tsx
+Stack navigator for the onboarding flow (screens 1.1β1.6):
+
+
+loginScreen β otpScreen β setLocationScreen β completeProfileScreen β preferencesScreen β onboardingCompleteScreen
+[NEW] app/navigation/appStack.tsx
+Bottom Tab Navigator with 5 tabs:
+
+Tab Icon Screen / Navigator
+Home π homeScreen
+Search π searchScreen
+Orders π¦ myOrdersScreen
+Offers π·οΈ offersScreen
+Account π€ accountScreen
+Phase 2 β Reusable & Generic Components (app/components/)
+All components are strictly presentation-only, driven by typed props, and decoupled from state.
+
+[NEW] app/components/header/
+Generic top action bar with back navigation, center title, and optional right component (e.g., Change link, edit icon).
+
+[NEW] app/components/searchBar/
+Clean search text input with icon prefix.
+
+[NEW] app/components/providerCard/
+(was RestaurantCard) β Represents any merchant/store/delivery provider.
+
+Props: imageUrl, name, rating, deliveryTime, tag (e.g. "Food", "Groceries"), discountText.
+[NEW] app/components/catalogItemRow/
+(was MenuItem) β Individual product/item detail row.
+
+Props: imageUrl, title, description, price, quantity, onAdd(), onRemove().
+[NEW] app/components/stepProgress/
+Progress tracker indicator for checkout steps: Address β Payment β Confirm.
+
+[NEW] app/components/badge/
+Status/offer pills (e.g. 50% OFF, Express, Popular).
+
+[NEW] app/components/quantitySelector/
+A modular stepper button (β / +) to increment/decrement item counts.
+
+[NEW] app/components/categoryChip/
+Tappable icon/text pill selector for vertical categories (e.g., Food, Groceries, Pharmacy).
+
+[NEW] app/components/ratingStars/
+1-to-5 star rating component for post-delivery reviews.
+
+[NEW] app/components/orderHistoryCard/
+General item list card for order history: provider name, order date, order status, total price, reorder and detail actions.
+
+[NEW] app/components/paymentOption/
+Selectable list item row representing payment methods.
+
+[MODIFY] app/components/index.ts
+Export all new components.
+
+Phase 3 β Auth & Onboarding (1.1 β 1.6)
+[MODIFY] app/features/screens/loginScreen/
+1.1 Login
+
+Modify existing login layout to remove Password input, "Forgot Password?", and "Sign Up" links.
+Only show Mobile Number input field and a "Login" CTA.
+Clicking "Login" navigates directly to otpScreen.
+[NEW] app/features/screens/otpScreen/
+1.2 OTP Verification
+
+Displays code delivery status text.
+6-cell OTP input box grid (custom numeric keypad: 1β9, 0, β«).
+Resend OTP counter.
+Navigates to setLocationScreen upon verification.
+[NEW] app/features/screens/setLocationScreen/
+1.3 Set Delivery Location
+
+Interactive Map View pinning current location.
+Card displaying details: Koramangala 4th Block, Bengaluru, 560034.
+"Use this location" primary button & "Enter address manually" secondary button.
+[NEW] app/features/screens/completeProfileScreen/
+1.4 Complete Profile
+
+Form requesting Name and Email.
+Location label tags selector (Home, Work, Other).
+"Save & Continue" action.
+[NEW] app/features/screens/preferencesScreen/
+1.5 Preferences
+
+Selection grid for category interests: Food, Groceries, Pharmacy, Others.
+Notifications toggle control.
+"Continue" button.
+[NEW] app/features/screens/onboardingCompleteScreen/
+1.6 Onboarding Complete
+
+Welcome success illustration.
+"Explore Now" primary action button to dismiss stack and launch appStack tabs.
+Phase 4 β Discover, Cart & Checkout (2.1 β 2.6)
+[NEW] app/features/screens/homeScreen/
+2.1 Home
+
+Address bar header with quick selector dropdown.
+Integrated search bar (tapping redirects focus to Search tab).
+General promotional banner card (e.g., 50% OFF banner).
+Category chip row: Food, Grocery, Pharmacy, More.
+"Popular Providers" vertical list feed using generic providerCard components.
+[NEW] app/features/screens/searchScreen/
+Search Tab
+
+Search field.
+List of matching search results (providers or items).
+[NEW] app/features/screens/providerListScreen/
+2.2 Provider List (was RestaurantListScreen)
+
+Shows categorized list of stores/merchants.
+Filter chips: Filters, Rating 4.0+, Fast Delivery.
+Dynamic list of providerCard components.
+[NEW] app/features/screens/providerDetailsScreen/
+2.3 Provider Details & Catalog (was RestaurantDetailScreen)
+
+Large merchant image, name, rating, tag, and delivery statistics.
+Category catalog tab selection.
+List of products/items via catalogItemRow.
+Bottom active cart action strip ("X Items | βΉY View Cart").
+[NEW] app/features/screens/cartScreen/
+2.4 Cart
+
+List of current cart selections with quantity adjustments.
+Fee line breakdown: Delivery Fee, Platform Fee, discounts.
+Coupon input box.
+Bottom action panel ("Total: βΉY View Bill").
+[NEW] app/features/screens/checkoutAddressScreen/
+2.5 Checkout β Address
+
+Checkout progress steps indicator (Step 1).
+Deliver-to details block.
+Delivery options switcher: Standard (Free) vs. Express (Charge).
+Action button.
+[NEW] app/features/screens/checkoutPaymentScreen/
+2.6 Checkout β Payment
+
+Checkout progress steps indicator (Step 2).
+Payment method list component: UPI, Card, Wallet, COD.
+Primary Checkout action.
+Phase 5 β Order & Tracking (3.1 β 3.4)
+[NEW] app/features/screens/orderConfirmedScreen/
+3.1 Order Confirmed
+
+Success confirmation banner.
+Order details header (Order ID, time estimate).
+"View Tracking" action routing to orderTrackingScreen.
+[NEW] app/features/screens/orderTrackingScreen/
+3.2 Order Tracking
+
+General milestones tracking layout: Order Placed β Confirmed β Dispatched β Delivered.
+Active status highlighting.
+Customer support entry point.
+[NEW] app/features/screens/liveTrackingScreen/
+3.3 Live Tracking
+
+Real-time map displaying mock delivery partner marker coordinates (react-native-maps).
+Blinking active tracking indicator badge.
+Delivery agent detail sheet (Name, rating, vehicle details, contact shortcuts).
+[NEW] app/features/screens/orderDeliveredScreen/
+3.4 Order Delivered
+
+Post-delivery review modal with ratingStars.
+"Rate & Tip" action button.
+Phase 6 β Orders, Support & Account (4.1 β 4.3)
+[NEW] app/features/screens/myOrdersScreen/
+4.1 My Orders
+
+Order tabs: All, Ongoing, Completed.
+List of order cards using orderHistoryCard.
+[NEW] app/features/screens/offersScreen/
+Offers Tab
+
+Available coupons list with copy/apply action triggers.
+[NEW] app/features/screens/helpSupportScreen/
+4.2 Help & Support
+
+General FAQ directory categories.
+Support hotlines call action block.
+[NEW] app/features/screens/accountScreen/
+4.3 Account
+
+Profile details card.
+User management panel options (Addresses, Payments, Notifications, Logout).
+Phase 7 β Store, Clean Services & API Client Layer
+To support real backend replacement easily, we decouple state and actions from components:
+
+[NEW] app/services/apiClient.ts
+Base API config. Currently calls local mock delay functions, easily swapped for fetch or axios instances later.
+
+[NEW] app/services/authService.ts
+Handles logic for authentication (e.g. login, verifyOtp, logout).
+
+[NEW] app/services/deliveryService.ts
+API integration service for fetching providers, items, cart management, and active deliveries.
+
+[NEW] app/store/authSlice.ts
+Redux slice managing session states.
+
+[NEW] app/store/cartSlice.ts
+Redux slice managing cart items state, totals, and active coupon.
+
+[NEW] app/store/orderSlice.ts
+Redux slice managing active order statuses, tracking steps, and live coordinates.
+
+[NEW] app/store/index.ts
+Main Redux store entry point.
+
+Verification Plan
+Automated Tests
+Run npx tsc --noEmit validation compile.
+Manual Verification
+Login & Onboarding: Input phone β OTP screen keypad input β Map location β User profile details.
+Provider & Cart Flow: Home category select β providerListScreen -> providerDetailsScreen -> Catalog item quantity add -> Cart page.
+Order Tracking Map: Confirm simulated marker movement maps correctly on Android & iOS.
\ No newline at end of file
diff --git a/package.json b/package.json
index 7d5e404..21c9129 100644
--- a/package.json
+++ b/package.json
@@ -12,10 +12,20 @@
"dependencies": {
"@react-native-community/checkbox": "^0.5.20",
"@react-native/new-app-screen": "0.86.0",
+ "@react-navigation/bottom-tabs": "^7.18.3",
+ "@react-navigation/native": "^7.3.4",
+ "@react-navigation/stack": "^7.10.6",
+ "@reduxjs/toolkit": "^2.12.0",
"react": "19.2.3",
"react-native": "0.86.0",
- "react-native-safe-area-context": "^5.5.2",
- "react-native-svg": "^15.15.5"
+ "react-native-gesture-handler": "^3.0.2",
+ "react-native-maps": "^1.29.0",
+ "react-native-reanimated": "^4.5.0",
+ "react-native-safe-area-context": "^5.8.0",
+ "react-native-screens": "^4.25.2",
+ "react-native-svg": "^15.15.5",
+ "react-native-worklets": "^0.10.0",
+ "react-redux": "^9.3.0"
},
"devDependencies": {
"@babel/core": "^7.25.2",
diff --git a/yarn.lock b/yarn.lock
index 7d7095a..99c2d71 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -429,7 +429,7 @@
"@babel/helper-create-regexp-features-plugin" "^7.18.6"
"@babel/helper-plugin-utils" "^7.18.6"
-"@babel/plugin-transform-arrow-functions@^7.29.7":
+"@babel/plugin-transform-arrow-functions@^7.27.1", "@babel/plugin-transform-arrow-functions@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-arrow-functions/-/plugin-transform-arrow-functions-7.29.7.tgz#d651343f562c03f47951bd1802195d0e10605f27"
integrity sha512-N7zArUXWzAMzm+/N0uPBeVB3Fam5lMxtUwMmDK5f/IBBS7a7p1qeUoxd/6CckXoxUdgsntq1Dh8xNW06maZbDQ==
@@ -468,7 +468,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.29.7"
-"@babel/plugin-transform-class-properties@^7.25.4", "@babel/plugin-transform-class-properties@^7.29.7":
+"@babel/plugin-transform-class-properties@^7.25.4", "@babel/plugin-transform-class-properties@^7.28.6", "@babel/plugin-transform-class-properties@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.29.7.tgz#034897b8a21beec163332fac2de235b14409abdf"
integrity sha512-GtcpjFvanPfzNQi3eTitsCqtRRmmqzpy/A+yhTR1HaZo1Ly3EA8ZXxlPyHdR8/IuRMYc3E4wdGBewB2QKQjAaA==
@@ -484,7 +484,7 @@
"@babel/helper-create-class-features-plugin" "^7.29.7"
"@babel/helper-plugin-utils" "^7.29.7"
-"@babel/plugin-transform-classes@^7.25.4", "@babel/plugin-transform-classes@^7.29.7":
+"@babel/plugin-transform-classes@^7.25.4", "@babel/plugin-transform-classes@^7.28.6", "@babel/plugin-transform-classes@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-classes/-/plugin-transform-classes-7.29.7.tgz#61d3e5aaae0c838acc3204d9db7c8dc05c25815b"
integrity sha512-qV0OGGBVacduzQHE649JyCneOFI/maT+YKsO+K4Yi3xv2wTPNjM/W2o2gdzMwEAZz7fXNTHAe0NcSg30bIN69g==
@@ -666,7 +666,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.29.7"
-"@babel/plugin-transform-nullish-coalescing-operator@^7.24.7", "@babel/plugin-transform-nullish-coalescing-operator@^7.29.7":
+"@babel/plugin-transform-nullish-coalescing-operator@^7.24.7", "@babel/plugin-transform-nullish-coalescing-operator@^7.28.6", "@babel/plugin-transform-nullish-coalescing-operator@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-nullish-coalescing-operator/-/plugin-transform-nullish-coalescing-operator-7.29.7.tgz#8a54cdf88c3f50433a6173117a286195b67714cc"
integrity sha512-idmp1dFaekP9GbcMvG24Kvw2BfhFZjHnNJCkV4WuIY4PskJzwI3f1N5OdgYke38T7rftO6ERulFRn2cFeZwRkg==
@@ -706,7 +706,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.29.7"
-"@babel/plugin-transform-optional-chaining@^7.24.8", "@babel/plugin-transform-optional-chaining@^7.29.7":
+"@babel/plugin-transform-optional-chaining@^7.24.8", "@babel/plugin-transform-optional-chaining@^7.28.6", "@babel/plugin-transform-optional-chaining@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-optional-chaining/-/plugin-transform-optional-chaining-7.29.7.tgz#b84a1b574b3c73001023092567e16c492b720e51"
integrity sha512-6GM1dhvK3gNODkXcEcMCOLEDCLSoZ/sBbro2Ax8HURyasQ4NshagQixkRFdh5niI6E4gmA/jYI/4aT7rRos3ZQ==
@@ -811,7 +811,7 @@
babel-plugin-polyfill-regenerator "^0.6.5"
semver "^6.3.1"
-"@babel/plugin-transform-shorthand-properties@^7.29.7":
+"@babel/plugin-transform-shorthand-properties@^7.27.1", "@babel/plugin-transform-shorthand-properties@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-shorthand-properties/-/plugin-transform-shorthand-properties-7.29.7.tgz#25c0436b98f4bd9ca4b98e1fbd662743bbaab9bf"
integrity sha512-I+WYbGBAiCn7nA6xBrlgPH+MB7HWb4u8pv5S0Pv7OtwNvIFvCCb24YlttKEeUFVurfBCEaOTnuhlqsb7f0Z5Dg==
@@ -833,7 +833,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.29.7"
-"@babel/plugin-transform-template-literals@^7.29.7":
+"@babel/plugin-transform-template-literals@^7.27.1", "@babel/plugin-transform-template-literals@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-template-literals/-/plugin-transform-template-literals-7.29.7.tgz#ada97d8e0832bca8edb315888aa654b1570f3835"
integrity sha512-NCSEJ4sLFU2gqAub45HYh4fus2yQ36rr6ei6vpU7NdoJqCpxvEG8E6eJpscGyXP3VHD2Ny+fSXr04k1hoUrFqA==
@@ -847,7 +847,7 @@
dependencies:
"@babel/helper-plugin-utils" "^7.29.7"
-"@babel/plugin-transform-typescript@^7.25.2":
+"@babel/plugin-transform-typescript@^7.25.2", "@babel/plugin-transform-typescript@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.29.7.tgz#f0449c3df7037bbe232043476851c38f5e4a7615"
integrity sha512-jK52h8LaLc7JarhQV2ofeFMts4H7vnOXnqZNA6fYglBTZewRBE51KWt3BUltW1P+KoPsYkHoJeXePuz4zo2LMw==
@@ -873,7 +873,7 @@
"@babel/helper-create-regexp-features-plugin" "^7.29.7"
"@babel/helper-plugin-utils" "^7.29.7"
-"@babel/plugin-transform-unicode-regex@^7.24.7", "@babel/plugin-transform-unicode-regex@^7.29.7":
+"@babel/plugin-transform-unicode-regex@^7.24.7", "@babel/plugin-transform-unicode-regex@^7.27.1", "@babel/plugin-transform-unicode-regex@^7.29.7":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-unicode-regex/-/plugin-transform-unicode-regex-7.29.7.tgz#c3064b293ff7f1794b71f7650eec8db9896d3e59"
integrity sha512-7D/x/23/d/3VqZ0QA+LGbZMlGwZjztBygSWWWsfTPoQ1oQ6Q1P6Mr3d0kk42XabyUVw+fha3LqdRsFqeKqvCyA==
@@ -975,6 +975,17 @@
"@babel/types" "^7.4.4"
esutils "^2.0.2"
+"@babel/preset-typescript@^7.28.5":
+ version "7.29.7"
+ resolved "https://registry.yarnpkg.com/@babel/preset-typescript/-/preset-typescript-7.29.7.tgz#de9be1f47b785c979ec7b3a71f4cd8bae5267b62"
+ integrity sha512-/Foi8vKY2EVbed/1eZx0gJEEwHAIxogrySI7rULcRIvhZzbvoE/b5qG5Ghc0WKAFKOHA9SD1x7RsFlOYdutIiQ==
+ dependencies:
+ "@babel/helper-plugin-utils" "^7.29.7"
+ "@babel/helper-validator-option" "^7.29.7"
+ "@babel/plugin-syntax-jsx" "^7.29.7"
+ "@babel/plugin-transform-modules-commonjs" "^7.29.7"
+ "@babel/plugin-transform-typescript" "^7.29.7"
+
"@babel/runtime@^7.25.0":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.29.7.tgz#12022450c45a4da6d8d8287b18a4ff2ddb23f768"
@@ -1002,7 +1013,7 @@
"@babel/types" "^7.29.7"
debug "^4.3.1"
-"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.28.2", "@babel/types@^7.29.0", "@babel/types@^7.29.7", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
+"@babel/types@^7.0.0", "@babel/types@^7.20.7", "@babel/types@^7.27.1", "@babel/types@^7.28.2", "@babel/types@^7.29.0", "@babel/types@^7.29.7", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
version "7.29.7"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.29.7.tgz#8005e31d82712ee7adaef6e23c63b71a62770a92"
integrity sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==
@@ -1704,6 +1715,78 @@
invariant "^2.2.4"
nullthrows "^1.1.1"
+"@react-navigation/bottom-tabs@^7.18.3":
+ version "7.18.3"
+ resolved "https://registry.yarnpkg.com/@react-navigation/bottom-tabs/-/bottom-tabs-7.18.3.tgz#371c39aee03aa90979d64e8d97da78559fbbcc92"
+ integrity sha512-715guj97M1yklVkN9ikUl6KJL9wO6hNot7URyUm+A1r5ltEHVgahKTgR8w7e4dNwiMjUjAnOdSErR0LaX1h+kQ==
+ dependencies:
+ "@react-navigation/elements" "^2.9.26"
+ color "^4.2.3"
+ sf-symbols-typescript "^2.1.0"
+
+"@react-navigation/core@^7.21.2":
+ version "7.21.2"
+ resolved "https://registry.yarnpkg.com/@react-navigation/core/-/core-7.21.2.tgz#448591d27d6c1472491a8ea9b901954ea727a310"
+ integrity sha512-EsJhQnsL5NuIhXsWF7URijS5hd2AaJUREdq1fOIowlyNNQBA3jCnkGU0DkW7+eI40cQ+GXH8DQw+ci7TefLIxQ==
+ dependencies:
+ "@react-navigation/routers" "^7.6.0"
+ escape-string-regexp "^4.0.0"
+ fast-deep-equal "^3.1.3"
+ nanoid "^3.3.11"
+ query-string "^7.1.3"
+ react-is "^19.1.0"
+ use-latest-callback "^0.2.4"
+ use-sync-external-store "^1.5.0"
+
+"@react-navigation/elements@^2.9.26":
+ version "2.9.26"
+ resolved "https://registry.yarnpkg.com/@react-navigation/elements/-/elements-2.9.26.tgz#89cd09db890534c115031b1a6d99d916fdcda96c"
+ integrity sha512-EaHSJf42MjnH5VFL+KZfQIyqJvdQWK9Z27J7WUSuIVX5NXlR925sPmhWf540DX9gD1ny8BfUGPZAuw/PO4tK2w==
+ dependencies:
+ color "^4.2.3"
+ use-latest-callback "^0.2.4"
+ use-sync-external-store "^1.5.0"
+
+"@react-navigation/native@^7.3.4":
+ version "7.3.4"
+ resolved "https://registry.yarnpkg.com/@react-navigation/native/-/native-7.3.4.tgz#a9ba7b0fde595b019a16a87ab609ad5b618bfbef"
+ integrity sha512-zyAqFLeZuaakerMMnhYqBeGAzJ+WFQUTgezP3FxSlXNwFq5XxnjP28vi2XHGqm4zg4pGWls9Ay4JKshIHUOpwA==
+ dependencies:
+ "@react-navigation/core" "^7.21.2"
+ escape-string-regexp "^4.0.0"
+ fast-deep-equal "^3.1.3"
+ nanoid "^3.3.11"
+ standard-navigation "^0.0.7"
+ use-latest-callback "^0.2.4"
+
+"@react-navigation/routers@^7.6.0":
+ version "7.6.0"
+ resolved "https://registry.yarnpkg.com/@react-navigation/routers/-/routers-7.6.0.tgz#04ee630c4ecbdfa3c8ef65000909aa2dbf0ff78f"
+ integrity sha512-lblhDXfS75jLc7G2K7BZGM+7cjqQXk13X/MA4fq/12r62zM+fBhhreLzYflSitrDDXFRJpSvJXy0ziiGU04Xow==
+ dependencies:
+ nanoid "^3.3.11"
+
+"@react-navigation/stack@^7.10.6":
+ version "7.10.6"
+ resolved "https://registry.yarnpkg.com/@react-navigation/stack/-/stack-7.10.6.tgz#5adf039d106a097bcb5514f07c1afc9e3ec75483"
+ integrity sha512-LWtBSCPNi3Htnb3051wan5hVTsoORxUocBeuAjZ3VS1u6wTtfObeqOOhanz/L+uiAzvTWcAHhevQhtlGeFn/mg==
+ dependencies:
+ "@react-navigation/elements" "^2.9.26"
+ color "^4.2.3"
+ use-latest-callback "^0.2.4"
+
+"@reduxjs/toolkit@^2.12.0":
+ version "2.12.0"
+ resolved "https://registry.yarnpkg.com/@reduxjs/toolkit/-/toolkit-2.12.0.tgz#e62787503a38561e04bb8f39e29ca8db689590f9"
+ integrity sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==
+ dependencies:
+ "@standard-schema/spec" "^1.0.0"
+ "@standard-schema/utils" "^0.3.0"
+ immer "^11.0.0"
+ redux "^5.0.1"
+ redux-thunk "^3.1.0"
+ reselect "^5.1.0"
+
"@sideway/address@^4.1.5":
version "4.1.5"
resolved "https://registry.yarnpkg.com/@sideway/address/-/address-4.1.5.tgz#4bc149a0076623ced99ca8208ba780d65a99b9d5"
@@ -1740,6 +1823,16 @@
dependencies:
"@sinonjs/commons" "^3.0.0"
+"@standard-schema/spec@^1.0.0":
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/@standard-schema/spec/-/spec-1.1.0.tgz#a79b55dbaf8604812f52d140b2c9ab41bc150bb8"
+ integrity sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==
+
+"@standard-schema/utils@^0.3.0":
+ version "0.3.0"
+ resolved "https://registry.yarnpkg.com/@standard-schema/utils/-/utils-0.3.0.tgz#3d5e608f16c2390c10528e98e59aef6bf73cae7b"
+ integrity sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==
+
"@types/babel__core@^7.1.14":
version "7.20.5"
resolved "https://registry.yarnpkg.com/@types/babel__core/-/babel__core-7.20.5.tgz#3df15f27ba85319caa07ba08d0721889bb39c017"
@@ -1773,6 +1866,11 @@
dependencies:
"@babel/types" "^7.28.2"
+"@types/geojson@^7946.0.13":
+ version "7946.0.16"
+ resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.16.tgz#8ebe53d69efada7044454e3305c19017d97ced2a"
+ integrity sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==
+
"@types/graceful-fs@^4.1.3":
version "4.1.9"
resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.9.tgz#2a06bc0f68a20ab37b3e36aa238be6abdf49e8b4"
@@ -1833,6 +1931,11 @@
resolved "https://registry.yarnpkg.com/@types/stack-utils/-/stack-utils-2.0.3.tgz#6209321eb2c1712a7e7466422b8cb1fc0d9dd5d8"
integrity sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==
+"@types/use-sync-external-store@^0.0.6":
+ version "0.0.6"
+ resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc"
+ integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==
+
"@types/yargs-parser@*":
version "21.0.3"
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.3.tgz#815e30b786d2e8f0dcd85fd5bcf5e1a04d008f15"
@@ -2458,9 +2561,9 @@ camelcase@^6.2.0:
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
caniuse-lite@^1.0.30001799:
- version "1.0.30001799"
- resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz#5c909138c27f1a61219d3e092071c1cc7d32dc55"
- integrity sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==
+ version "1.0.30001800"
+ resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001800.tgz#b896c773e1c39400809415162bb5320371291b36"
+ integrity sha512-MMHtuAz9Ys840zAY5F4k6fV5GaivZ9sPk+nz0mY+GYVzRBnYkN0mpqkSR92oWRQ19yQWo4HvBV/FnC16AJX8MA==
chalk@^4.0.0, chalk@^4.1.0:
version "4.1.2"
@@ -2575,11 +2678,27 @@ color-name@1.1.3:
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
-color-name@~1.1.4:
+color-name@^1.0.0, color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
+color-string@^1.9.0:
+ version "1.9.1"
+ resolved "https://registry.yarnpkg.com/color-string/-/color-string-1.9.1.tgz#4467f9146f036f855b764dfb5bf8582bf342c7a4"
+ integrity sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==
+ dependencies:
+ color-name "^1.0.0"
+ simple-swizzle "^0.2.2"
+
+color@^4.2.3:
+ version "4.2.3"
+ resolved "https://registry.yarnpkg.com/color/-/color-4.2.3.tgz#d781ecb5e57224ee43ea9627560107c0e0c6463a"
+ integrity sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==
+ dependencies:
+ color-convert "^2.0.1"
+ color-string "^1.9.0"
+
colorette@^1.0.7:
version "1.4.0"
resolved "https://registry.yarnpkg.com/colorette/-/colorette-1.4.0.tgz#5190fbb87276259a86ad700bff2c6d6faa3fca40"
@@ -2769,6 +2888,11 @@ decamelize@^1.2.0:
resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==
+decode-uri-component@^0.2.2:
+ version "0.2.2"
+ resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.2.tgz#e69dbe25d37941171dd540e024c444cd5188e1e9"
+ integrity sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==
+
dedent@^1.0.0:
version "1.7.2"
resolved "https://registry.yarnpkg.com/dedent/-/dedent-1.7.2.tgz#34e2264ab538301e27cf7b07bf2369c19baa8dd9"
@@ -2888,9 +3012,9 @@ ee-first@1.1.1:
integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
electron-to-chromium@^1.5.376:
- version "1.5.381"
- resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.381.tgz#037549001adc80e04834ede96dc52a1fbf6c9fde"
- integrity sha512-n9Wa6yB+vDsGuA8AKbl/0z7HbvWqt5jxIdvr1IUicd0ryPrk7/xzwqLv8D9AbbvZ6avVNtXYLTfmgFHkwkyelg==
+ version "1.5.382"
+ resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.5.382.tgz#e76f05d3ec337524b9c61761ebbc16fe91ecf5b4"
+ integrity sha512-8ETaWbV6SZOrno+G93Ffd9ENsMtetqdnqj4nlfxFW90Sm5GgnuV28Kf62hqQVD6VUgzm7qFQKsTsAPmeUiU3Ug==
emittery@^0.13.1:
version "0.13.1"
@@ -3422,6 +3546,11 @@ fill-range@^7.1.1:
dependencies:
to-regex-range "^5.0.1"
+filter-obj@^1.1.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/filter-obj/-/filter-obj-1.1.0.tgz#9b311112bc6c6127a16e016c6c5d7f19e0805c5b"
+ integrity sha512-8rXg1ZnX7xzy2NGDVkBVaAy+lSlPNwad13BtgSlLuxfIslyt5Vg64U7tFcCt4WS1R0hvtnQybT/IyCkGZ3DpXQ==
+
finalhandler@1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d"
@@ -3806,6 +3935,11 @@ image-size@^1.0.2:
dependencies:
queue "6.0.2"
+immer@^11.0.0:
+ version "11.1.8"
+ resolved "https://registry.yarnpkg.com/immer/-/immer-11.1.8.tgz#08a6426f7019dbce8d6dff8c4a43bb25c550a575"
+ integrity sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA==
+
import-fresh@^3.2.1, import-fresh@^3.3.0:
version "3.3.1"
resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.1.tgz#9cecb56503c0ada1f2741dbbd6546e4b13b57ccf"
@@ -3870,6 +4004,11 @@ is-arrayish@^0.2.1:
resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d"
integrity sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==
+is-arrayish@^0.3.1:
+ version "0.3.4"
+ resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.3.4.tgz#1ee5553818511915685d33bb13d31bf854e5059d"
+ integrity sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==
+
is-async-function@^2.0.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/is-async-function/-/is-async-function-2.1.1.tgz#3e69018c8e04e73b738793d020bfe884b9fd3523"
@@ -5077,6 +5216,11 @@ ms@2.1.3, ms@^2.1.3:
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
+nanoid@^3.3.11:
+ version "3.3.15"
+ resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.15.tgz#36c490fad8c6e86c824c940dfdde999b69ed4316"
+ integrity sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==
+
natural-compare@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
@@ -5494,6 +5638,16 @@ qs@~6.15.1:
es-define-property "^1.0.1"
side-channel "^1.1.1"
+query-string@^7.1.3:
+ version "7.1.3"
+ resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328"
+ integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg==
+ dependencies:
+ decode-uri-component "^0.2.2"
+ filter-obj "^1.1.0"
+ split-on-first "^1.0.0"
+ strict-uri-encode "^2.0.0"
+
queue-microtask@^1.2.2:
version "1.2.3"
resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
@@ -5529,6 +5683,11 @@ react-devtools-core@^6.1.5:
shell-quote "^1.6.1"
ws "^7"
+react-freeze@^1.0.0:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/react-freeze/-/react-freeze-1.0.4.tgz#cbbea2762b0368b05cbe407ddc9d518c57c6f3ad"
+ integrity sha512-r4F0Sec0BLxWicc7HEyo2x3/2icUTrRmDjaaRyzzn+7aDyFZliszMDOgLVwSnQnYENOlL1o569Ze2HZefk8clA==
+
react-is@^16.13.1:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
@@ -5539,16 +5698,52 @@ react-is@^18.0.0:
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.3.1.tgz#e83557dc12eae63a99e003a46388b1dcbb44db7e"
integrity sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==
-react-is@^19.2.3:
+react-is@^19.1.0, react-is@^19.2.3:
version "19.2.7"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-19.2.7.tgz#57668ee86a78574a542b0a539455212b2c086df2"
integrity sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==
-react-native-safe-area-context@^5.5.2:
+react-native-gesture-handler@^3.0.2:
+ version "3.0.2"
+ resolved "https://registry.yarnpkg.com/react-native-gesture-handler/-/react-native-gesture-handler-3.0.2.tgz#ea80fd2424d08f8b624977221f0544a5201f6d83"
+ integrity sha512-XBTgmvr2jh/xrMwlHTV2Z1x6IFUl3zfLxyaCAnvj3GSXK5YlY3ZmiIs57hniPmh3I7RtjPFxtGdRr0i+PzyBrg==
+ dependencies:
+ "@types/react-test-renderer" "^19.1.0"
+ invariant "^2.2.4"
+
+react-native-is-edge-to-edge@^1.3.1:
+ version "1.3.1"
+ resolved "https://registry.yarnpkg.com/react-native-is-edge-to-edge/-/react-native-is-edge-to-edge-1.3.1.tgz#feb9a6a8faf0874298947edd556e5af22044e139"
+ integrity sha512-NIXU/iT5+ORyCc7p0z2nnlkouYKX425vuU1OEm6bMMtWWR9yvb+Xg5AZmImTKoF9abxCPqrKC3rOZsKzUYgYZA==
+
+react-native-maps@^1.29.0:
+ version "1.29.0"
+ resolved "https://registry.yarnpkg.com/react-native-maps/-/react-native-maps-1.29.0.tgz#7ba5b9f70c9dcedeb53d31847809c56661341742"
+ integrity sha512-tXyYyyeZgiThHQvr/d22V7MMOxUfxdkXujFkGEuGP6FwBvVew/QmTS8VNiPbPce95uBIqE/SaOjai3Zwndpadg==
+ dependencies:
+ "@types/geojson" "^7946.0.13"
+
+react-native-reanimated@^4.5.0:
+ version "4.5.0"
+ resolved "https://registry.yarnpkg.com/react-native-reanimated/-/react-native-reanimated-4.5.0.tgz#7029849859625a6247c7cf11161d5bef2cb99b12"
+ integrity sha512-+iPfvK34PKKYP/p/4TaBliFkbfvjGDIvXuiiaxvISP5ip7sWegvlacwU/uAV6zNDSSmX0tDyER7PurPMKGDipA==
+ dependencies:
+ react-native-is-edge-to-edge "^1.3.1"
+ semver "^7.7.3"
+
+react-native-safe-area-context@^5.8.0:
version "5.8.0"
resolved "https://registry.yarnpkg.com/react-native-safe-area-context/-/react-native-safe-area-context-5.8.0.tgz#d2fb9fc8e0f0583311e261dc484b95ceb80f8902"
integrity sha512-t+ZsAVzY/wWzzx34vqGbo3/as9EEESJdbyZNL7Yg5EYX+toYMtMqFoDDCvqZUi35eeGVsXc6pAaEk4edMwbuCQ==
+react-native-screens@^4.25.2:
+ version "4.25.2"
+ resolved "https://registry.yarnpkg.com/react-native-screens/-/react-native-screens-4.25.2.tgz#f12c3d8d2cb9d69e1267af1bbba1b37d1d05452d"
+ integrity sha512-1Nj1fusFd+rIMKU/qC9yGKVG+3ofh11d3OdBQKL1iVvQfKvcB8vhvTGQf2TkfxW3bamxN+hCZIXmNuU0mRkyDg==
+ dependencies:
+ react-freeze "^1.0.0"
+ warn-once "^0.1.0"
+
react-native-svg@^15.15.5:
version "15.15.5"
resolved "https://registry.yarnpkg.com/react-native-svg/-/react-native-svg-15.15.5.tgz#822805c14481b8ec16d5fbac1e3ce2b27a5509d7"
@@ -5557,6 +5752,24 @@ react-native-svg@^15.15.5:
css-select "^5.1.0"
css-tree "^1.1.3"
+react-native-worklets@^0.10.0:
+ version "0.10.0"
+ resolved "https://registry.yarnpkg.com/react-native-worklets/-/react-native-worklets-0.10.0.tgz#e2e8f8e1ba0eccc4e69f3a4e75894ecab4ba19b0"
+ integrity sha512-JhE6IxDf6iabC0qu3+TAKA4v9RlluXmoIngPQX7/QUByf75lfrsHZ6/dQhyjEWnp1EEQiwzz8Cpew140ZcewDw==
+ dependencies:
+ "@babel/plugin-transform-arrow-functions" "^7.27.1"
+ "@babel/plugin-transform-class-properties" "^7.28.6"
+ "@babel/plugin-transform-classes" "^7.28.6"
+ "@babel/plugin-transform-nullish-coalescing-operator" "^7.28.6"
+ "@babel/plugin-transform-optional-chaining" "^7.28.6"
+ "@babel/plugin-transform-shorthand-properties" "^7.27.1"
+ "@babel/plugin-transform-template-literals" "^7.27.1"
+ "@babel/plugin-transform-unicode-regex" "^7.27.1"
+ "@babel/preset-typescript" "^7.28.5"
+ "@babel/types" "^7.27.1"
+ convert-source-map "^2.0.0"
+ semver "^7.7.4"
+
react-native@0.86.0:
version "0.86.0"
resolved "https://registry.yarnpkg.com/react-native/-/react-native-0.86.0.tgz#a5d62adbf350010ad7a61617b9bc300b2ff12870"
@@ -5595,6 +5808,14 @@ react-native@0.86.0:
ws "^7.5.10"
yargs "^17.6.2"
+react-redux@^9.3.0:
+ version "9.3.0"
+ resolved "https://registry.yarnpkg.com/react-redux/-/react-redux-9.3.0.tgz#a30113bb6d95c0a715d54dda4308d450fca6ce09"
+ integrity sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==
+ dependencies:
+ "@types/use-sync-external-store" "^0.0.6"
+ use-sync-external-store "^1.4.0"
+
react-refresh@^0.14.0:
version "0.14.2"
resolved "https://registry.yarnpkg.com/react-refresh/-/react-refresh-0.14.2.tgz#3833da01ce32da470f1f936b9d477da5c7028bf9"
@@ -5622,6 +5843,16 @@ readable-stream@^3.4.0:
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
+redux-thunk@^3.1.0:
+ version "3.1.0"
+ resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-3.1.0.tgz#94aa6e04977c30e14e892eae84978c1af6058ff3"
+ integrity sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==
+
+redux@^5.0.1:
+ version "5.0.1"
+ resolved "https://registry.yarnpkg.com/redux/-/redux-5.0.1.tgz#97fa26881ce5746500125585d5642c77b6e9447b"
+ integrity sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==
+
reflect.getprototypeof@^1.0.10, reflect.getprototypeof@^1.0.9:
version "1.0.10"
resolved "https://registry.yarnpkg.com/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz#c629219e78a3316d8b604c765ef68996964e7bf9"
@@ -5704,6 +5935,11 @@ reselect@^4.1.7:
resolved "https://registry.yarnpkg.com/reselect/-/reselect-4.1.8.tgz#3f5dc671ea168dccdeb3e141236f69f02eaec524"
integrity sha512-ab9EmR80F/zQTMNeneUr4cv+jSwPJgIlvEmVwLerwrWVbpLlBuls9XHzIeTFy4cegU2NHBp3va0LKOzU5qFEYQ==
+reselect@^5.1.0:
+ version "5.2.0"
+ resolved "https://registry.yarnpkg.com/reselect/-/reselect-5.2.0.tgz#f380ef7664332d26ea06c1cba04bdbbdcaa955f1"
+ integrity sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==
+
resolve-cwd@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d"
@@ -5823,7 +6059,7 @@ semver@^6.3.0, semver@^6.3.1:
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@^7.1.3, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.7.3:
+semver@^7.1.3, semver@^7.5.2, semver@^7.5.3, semver@^7.5.4, semver@^7.7.3, semver@^7.7.4:
version "7.8.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.5.tgz#39b646037dd50c14fb451e7e4cac58ed8b863f69"
integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==
@@ -5903,6 +6139,11 @@ setprototypeof@~1.2.0:
resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
+sf-symbols-typescript@^2.1.0:
+ version "2.2.0"
+ resolved "https://registry.yarnpkg.com/sf-symbols-typescript/-/sf-symbols-typescript-2.2.0.tgz#926d6e0715e3d8784cadf7658431e36581254208"
+ integrity sha512-TPbeg0b7ylrswdGCji8FRGFAKuqbpQlLbL8SOle3j1iHSs5Ob5mhvMAxWN2UItOjgALAB5Zp3fmMfj8mbWvXKw==
+
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
@@ -5965,6 +6206,13 @@ signal-exit@^3.0.2, signal-exit@^3.0.3, signal-exit@^3.0.7:
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
+simple-swizzle@^0.2.2:
+ version "0.2.4"
+ resolved "https://registry.yarnpkg.com/simple-swizzle/-/simple-swizzle-0.2.4.tgz#a8d11a45a11600d6a1ecdff6363329e3648c3667"
+ integrity sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==
+ dependencies:
+ is-arrayish "^0.3.1"
+
sisteransi@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"
@@ -6010,6 +6258,11 @@ source-map@^0.6.0, source-map@^0.6.1:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
+split-on-first@^1.0.0:
+ version "1.1.0"
+ resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
+ integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
+
sprintf-js@~1.0.2:
version "1.0.3"
resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c"
@@ -6034,6 +6287,11 @@ stacktrace-parser@^0.1.10:
dependencies:
type-fest "^0.7.1"
+standard-navigation@^0.0.7:
+ version "0.0.7"
+ resolved "https://registry.yarnpkg.com/standard-navigation/-/standard-navigation-0.0.7.tgz#8c5265c4244446e0fb2b4bc9861a9979dd34f22f"
+ integrity sha512-NCGLCNyuXrFOkGHxdNZFnpsehGtiq1oXbPhKl7ZuxFO5J//H2evqqOchmD4YwEUJnkjO4kH9Xp4hQX6hdAYCKQ==
+
statuses@~1.5.0:
version "1.5.0"
resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c"
@@ -6052,6 +6310,11 @@ stop-iteration-iterator@^1.1.0:
es-errors "^1.3.0"
internal-slot "^1.1.0"
+strict-uri-encode@^2.0.0:
+ version "2.0.0"
+ resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
+ integrity sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ==
+
string-length@^4.0.1:
version "4.0.2"
resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a"
@@ -6401,6 +6664,16 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
+use-latest-callback@^0.2.4:
+ version "0.2.6"
+ resolved "https://registry.yarnpkg.com/use-latest-callback/-/use-latest-callback-0.2.6.tgz#e5ea752808c86219acc179ace0ae3c1203255e77"
+ integrity sha512-FvRG9i1HSo0wagmX63Vrm8SnlUU3LMM3WyZkQ76RnslpBrX694AdG4A0zQBx2B3ZifFA0yv/BaEHGBnEax5rZg==
+
+use-sync-external-store@^1.4.0, use-sync-external-store@^1.5.0:
+ version "1.6.0"
+ resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d"
+ integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==
+
util-deprecate@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
@@ -6437,6 +6710,11 @@ walker@^1.0.7, walker@^1.0.8:
dependencies:
makeerror "1.0.12"
+warn-once@^0.1.0:
+ version "0.1.1"
+ resolved "https://registry.yarnpkg.com/warn-once/-/warn-once-0.1.1.tgz#952088f4fb56896e73fd4e6a3767272a3fccce43"
+ integrity sha512-VkQZJbO8zVImzYFteBXvBOZEl1qL175WH8VmZcxF2fZAoudNhNDvHi+doCaAEdU2l2vtcIwa2zn0QK5+I1HQ3Q==
+
wcwidth@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8"