feat(app): initial commit
This commit is contained in:
parent
bdd81eef31
commit
a8a04a02f6
16
app/App.tsx
16
app/App.tsx
@ -3,17 +3,21 @@ import {
|
|||||||
SafeAreaProvider,
|
SafeAreaProvider,
|
||||||
useSafeAreaInsets,
|
useSafeAreaInsets,
|
||||||
} from 'react-native-safe-area-context';
|
} from 'react-native-safe-area-context';
|
||||||
import { LoginScreen } from '@features/screens';
|
import RootNavigator from '@navigation/rootNavigator';
|
||||||
|
import { Provider } from 'react-redux';
|
||||||
|
import { store } from '@store';
|
||||||
import { useAppTheme } from '@theme';
|
import { useAppTheme } from '@theme';
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const isDarkMode = useColorScheme() === 'dark';
|
const isDarkMode = useColorScheme() === 'dark';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SafeAreaProvider>
|
<Provider store={store}>
|
||||||
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
|
<SafeAreaProvider>
|
||||||
<AppContent />
|
<StatusBar barStyle={isDarkMode ? 'light-content' : 'dark-content'} />
|
||||||
</SafeAreaProvider>
|
<AppContent />
|
||||||
|
</SafeAreaProvider>
|
||||||
|
</Provider>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,7 +36,7 @@ function AppContent() {
|
|||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<LoginScreen />
|
<RootNavigator />
|
||||||
</View>
|
</View>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -46,4 +46,9 @@ export const getStyles = (colors: any) => StyleSheet.create({
|
|||||||
marginTop: 4,
|
marginTop: 4,
|
||||||
marginLeft: 4,
|
marginLeft: 4,
|
||||||
},
|
},
|
||||||
|
actionsRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
import { View, Text, TextInput, TouchableOpacity } from 'react-native';
|
import { View, Text, TextInput, TouchableOpacity } from 'react-native';
|
||||||
import { CustomInputProps } from './CustomInput.props';
|
import { CustomInputProps } from './customInput.props';
|
||||||
import { getStyles } from './CustomInput.styles';
|
import { getStyles } from './customInput.styles';
|
||||||
import { useAppTheme } from '@theme';
|
import { useAppTheme } from '@theme';
|
||||||
|
|
||||||
export const CustomInput: React.FC<CustomInputProps> = ({
|
export const CustomInput: React.FC<CustomInputProps> = ({
|
||||||
@ -34,7 +34,7 @@ export const CustomInput: React.FC<CustomInputProps> = ({
|
|||||||
autoCorrect={false}
|
autoCorrect={false}
|
||||||
{...rest}
|
{...rest}
|
||||||
/>
|
/>
|
||||||
<View style={{ flexDirection: 'row', alignItems: 'center', gap: 12 }}>
|
<View style={styles.actionsRow}>
|
||||||
{isPassword && (
|
{isPassword && (
|
||||||
<TouchableOpacity onPress={() => setIsSecure(!isSecure)} activeOpacity={0.7}>
|
<TouchableOpacity onPress={() => setIsSecure(!isSecure)} activeOpacity={0.7}>
|
||||||
<Text style={styles.rightAction}>{isSecure ? 'Show' : 'Hide'}</Text>
|
<Text style={styles.rightAction}>{isSecure ? 'Show' : 'Hide'}</Text>
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from './CustomInput';
|
export * from './customInput';
|
||||||
export * from './CustomInput.props';
|
export * from './customInput.props';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { TouchableOpacity, Text, ActivityIndicator } from 'react-native';
|
import { TouchableOpacity, Text, ActivityIndicator } from 'react-native';
|
||||||
import { PrimaryButtonProps } from './PrimaryButton.props';
|
import { PrimaryButtonProps } from './primaryButton.props';
|
||||||
import { styles } from './PrimaryButton.styles';
|
import { styles } from './primaryButton.styles';
|
||||||
import { colors } from '@theme';
|
import { colors } from '@theme';
|
||||||
|
|
||||||
export const PrimaryButton: React.FC<PrimaryButtonProps> = ({
|
export const PrimaryButton: React.FC<PrimaryButtonProps> = ({
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from './PrimaryButton';
|
export * from './primaryButton';
|
||||||
export * from './PrimaryButton.props';
|
export * from './primaryButton.props';
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { TouchableOpacity, Text } from 'react-native';
|
import { TouchableOpacity, Text } from 'react-native';
|
||||||
import { SocialButtonProps } from './SocialButton.props';
|
import { SocialButtonProps } from './socialButton.props';
|
||||||
import { getStyles } from './SocialButton.styles';
|
import { getStyles } from './socialButton.styles';
|
||||||
import { useAppTheme } from '@theme';
|
import { useAppTheme } from '@theme';
|
||||||
import Svg, { Path } from 'react-native-svg';
|
import Svg, { Path } from 'react-native-svg';
|
||||||
|
|
||||||
|
|||||||
@ -1,2 +1,2 @@
|
|||||||
export * from './SocialButton';
|
export * from './socialButton';
|
||||||
export * from './SocialButton.props';
|
export * from './socialButton.props';
|
||||||
|
|||||||
21
app/components/badge/badge.styles.ts
Normal file
21
app/components/badge/badge.styles.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, borderRadius } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
minWidth: 18,
|
||||||
|
height: 18,
|
||||||
|
borderRadius: borderRadius.full,
|
||||||
|
backgroundColor: colors.error,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingHorizontal: 4,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
color: colors.white,
|
||||||
|
fontSize: typography.fontSize.xs - 2,
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
30
app/components/badge/badge.tsx
Normal file
30
app/components/badge/badge.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, ViewStyle } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './badge.styles';
|
||||||
|
|
||||||
|
interface BadgeProps {
|
||||||
|
count: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Badge: React.FC<BadgeProps> = ({ count, color, style }) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
if (count <= 0) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.container,
|
||||||
|
color ? { backgroundColor: color } : null,
|
||||||
|
style,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Text style={styles.text}>{count > 99 ? '99+' : count}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Badge;
|
||||||
2
app/components/badge/index.ts
Normal file
2
app/components/badge/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './badge';
|
||||||
|
export { default } from './badge';
|
||||||
15
app/components/card/card.styles.ts
Normal file
15
app/components/card/card.styles.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { borderRadius, shadow } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
card: {
|
||||||
|
backgroundColor: colors.cardBg,
|
||||||
|
borderRadius: borderRadius.md,
|
||||||
|
padding: 16,
|
||||||
|
marginVertical: 8,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderColor: colors.border,
|
||||||
|
...shadow.sm,
|
||||||
|
},
|
||||||
|
});
|
||||||
30
app/components/card/card.tsx
Normal file
30
app/components/card/card.tsx
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, ViewStyle, TouchableOpacity } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './card.styles';
|
||||||
|
|
||||||
|
interface CardProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
style?: ViewStyle;
|
||||||
|
onPress?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Card: React.FC<CardProps> = ({ children, style, onPress }) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
if (onPress) {
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.card, style]}
|
||||||
|
activeOpacity={0.8}
|
||||||
|
onPress={onPress}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return <View style={[styles.card, style]}>{children}</View>;
|
||||||
|
};
|
||||||
|
export default Card;
|
||||||
2
app/components/card/index.ts
Normal file
2
app/components/card/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './card';
|
||||||
|
export { default } from './card';
|
||||||
34
app/components/chipSelector/chipSelector.styles.ts
Normal file
34
app/components/chipSelector/chipSelector.styles.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, borderRadius } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
marginVertical: 12,
|
||||||
|
gap: 12,
|
||||||
|
},
|
||||||
|
chip: {
|
||||||
|
flex: 1,
|
||||||
|
paddingVertical: 12,
|
||||||
|
borderWidth: 1.5,
|
||||||
|
borderColor: colors.border,
|
||||||
|
borderRadius: borderRadius.md,
|
||||||
|
backgroundColor: colors.chipInactive,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
chipActive: {
|
||||||
|
backgroundColor: colors.chipActive,
|
||||||
|
borderColor: colors.chipBorder,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
fontWeight: typography.fontWeight.medium,
|
||||||
|
color: colors.textSecondary,
|
||||||
|
},
|
||||||
|
labelActive: {
|
||||||
|
color: colors.primary,
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
},
|
||||||
|
});
|
||||||
46
app/components/chipSelector/chipSelector.tsx
Normal file
46
app/components/chipSelector/chipSelector.tsx
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, TouchableOpacity } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './chipSelector.styles';
|
||||||
|
|
||||||
|
interface Option {
|
||||||
|
label: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ChipSelectorProps {
|
||||||
|
options: Option[];
|
||||||
|
selected: string;
|
||||||
|
onChange: (value: any) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChipSelector: React.FC<ChipSelectorProps> = ({
|
||||||
|
options,
|
||||||
|
selected,
|
||||||
|
onChange,
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{options.map((option) => {
|
||||||
|
const isActive = option.value === selected;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={option.value}
|
||||||
|
style={[styles.chip, isActive && styles.chipActive]}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
onPress={() => onChange(option.value)}
|
||||||
|
>
|
||||||
|
<Text style={[styles.label, isActive && styles.labelActive]}>
|
||||||
|
{option.label}
|
||||||
|
</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ChipSelector;
|
||||||
2
app/components/chipSelector/index.ts
Normal file
2
app/components/chipSelector/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './chipSelector';
|
||||||
|
export { default } from './chipSelector';
|
||||||
25
app/components/countdownTimer/countdownTimer.styles.ts
Normal file
25
app/components/countdownTimer/countdownTimer.styles.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
paddingVertical: 10,
|
||||||
|
},
|
||||||
|
outerCircle: {
|
||||||
|
width: 70,
|
||||||
|
height: 70,
|
||||||
|
borderRadius: 35,
|
||||||
|
borderWidth: 3,
|
||||||
|
borderColor: colors.border,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
timerText: {
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.primary,
|
||||||
|
},
|
||||||
|
});
|
||||||
44
app/components/countdownTimer/countdownTimer.tsx
Normal file
44
app/components/countdownTimer/countdownTimer.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './countdownTimer.styles';
|
||||||
|
|
||||||
|
interface CountdownTimerProps {
|
||||||
|
durationSeconds: number;
|
||||||
|
onExpire: () => void;
|
||||||
|
color?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CountdownTimer: React.FC<CountdownTimerProps> = ({
|
||||||
|
durationSeconds,
|
||||||
|
onExpire,
|
||||||
|
color,
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
const [timeLeft, setTimeLeft] = useState(durationSeconds);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (timeLeft <= 0) {
|
||||||
|
onExpire();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const timer = setInterval(() => {
|
||||||
|
setTimeLeft((prev) => prev - 1);
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
return () => clearInterval(timer);
|
||||||
|
}, [timeLeft, onExpire]);
|
||||||
|
|
||||||
|
const progressColor = color || colors.primary;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={[styles.outerCircle, { borderColor: progressColor }]}>
|
||||||
|
<Text style={[styles.timerText, { color: progressColor }]}>{timeLeft}s</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default CountdownTimer;
|
||||||
2
app/components/countdownTimer/index.ts
Normal file
2
app/components/countdownTimer/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './countdownTimer';
|
||||||
|
export { default } from './countdownTimer';
|
||||||
10
app/components/divider/divider.styles.ts
Normal file
10
app/components/divider/divider.styles.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
divider: {
|
||||||
|
width: '100%',
|
||||||
|
backgroundColor: colors.divider,
|
||||||
|
marginVertical: 12,
|
||||||
|
},
|
||||||
|
});
|
||||||
31
app/components/divider/divider.tsx
Normal file
31
app/components/divider/divider.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, ViewStyle } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './divider.styles';
|
||||||
|
|
||||||
|
interface DividerProps {
|
||||||
|
color?: string;
|
||||||
|
thickness?: number;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const Divider: React.FC<DividerProps> = ({
|
||||||
|
color,
|
||||||
|
thickness = 1,
|
||||||
|
style,
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
style={[
|
||||||
|
styles.divider,
|
||||||
|
{ height: thickness },
|
||||||
|
color ? { backgroundColor: color } : null,
|
||||||
|
style,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default Divider;
|
||||||
2
app/components/divider/index.ts
Normal file
2
app/components/divider/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './divider';
|
||||||
|
export { default } from './divider';
|
||||||
41
app/components/emptyState/emptyState.styles.ts
Normal file
41
app/components/emptyState/emptyState.styles.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, spacing } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
padding: spacing.xxl,
|
||||||
|
backgroundColor: colors.background,
|
||||||
|
},
|
||||||
|
iconContainer: {
|
||||||
|
marginBottom: spacing.lg,
|
||||||
|
opacity: 0.8,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.text,
|
||||||
|
textAlign: 'center',
|
||||||
|
marginBottom: spacing.xs,
|
||||||
|
},
|
||||||
|
subtitle: {
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.textSecondary,
|
||||||
|
textAlign: 'center',
|
||||||
|
marginBottom: spacing.xl,
|
||||||
|
},
|
||||||
|
button: {
|
||||||
|
paddingVertical: spacing.md,
|
||||||
|
paddingHorizontal: spacing.xl,
|
||||||
|
backgroundColor: colors.primary,
|
||||||
|
borderRadius: 8,
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
color: colors.white,
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
},
|
||||||
|
});
|
||||||
37
app/components/emptyState/emptyState.tsx
Normal file
37
app/components/emptyState/emptyState.tsx
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, TouchableOpacity } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './emptyState.styles';
|
||||||
|
|
||||||
|
interface EmptyStateProps {
|
||||||
|
icon?: React.ReactNode;
|
||||||
|
title: string;
|
||||||
|
subtitle?: string;
|
||||||
|
actionLabel?: string;
|
||||||
|
onAction?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const EmptyState: React.FC<EmptyStateProps> = ({
|
||||||
|
icon,
|
||||||
|
title,
|
||||||
|
subtitle,
|
||||||
|
actionLabel,
|
||||||
|
onAction,
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{icon && <View style={styles.iconContainer}>{icon}</View>}
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
{subtitle ? <Text style={styles.subtitle}>{subtitle}</Text> : null}
|
||||||
|
{actionLabel && onAction ? (
|
||||||
|
<TouchableOpacity style={styles.button} activeOpacity={0.8} onPress={onAction}>
|
||||||
|
<Text style={styles.buttonText}>{actionLabel}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
) : null}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default EmptyState;
|
||||||
2
app/components/emptyState/index.ts
Normal file
2
app/components/emptyState/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './emptyState';
|
||||||
|
export { default } from './emptyState';
|
||||||
24
app/components/icons/backIcon.tsx
Normal file
24
app/components/icons/backIcon.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Path } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BackIcon: React.FC<IconProps> = ({ size = 24, color = '#666', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Path
|
||||||
|
d="M19 12H5M12 19l-7-7 7-7"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default BackIcon;
|
||||||
24
app/components/icons/bellIcon.tsx
Normal file
24
app/components/icons/bellIcon.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Path } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const BellIcon: React.FC<IconProps> = ({ size = 24, color = '#666', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Path
|
||||||
|
d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9M13.73 21a2 2 0 01-3.46 0"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default BellIcon;
|
||||||
24
app/components/icons/callIcon.tsx
Normal file
24
app/components/icons/callIcon.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Path } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CallIcon: React.FC<IconProps> = ({ size = 24, color = '#666', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Path
|
||||||
|
d="M22 16.92v3a2 2 0 01-2.18 2 19.79 19.79 0 01-8.63-3.07 19.5 19.5 0 01-6-6 19.79 19.79 0 01-3.07-8.67A2 2 0 014.11 2h3a2 2 0 012 1.72 12.84 12.84 0 00.7 2.81 2 2 0 01-.45 2.11L8.09 9.91a16 16 0 006 6l1.27-1.27a2 2 0 012.11-.45 12.84 12.84 0 002.81.7A2 2 0 0122 16.92z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default CallIcon;
|
||||||
31
app/components/icons/checkCircleIcon.tsx
Normal file
31
app/components/icons/checkCircleIcon.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Path } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CheckCircleIcon: React.FC<IconProps> = ({ size = 24, color = '#666', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Path
|
||||||
|
d="M22 11.08V12a10 10 0 11-5.93-9.14"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<Path
|
||||||
|
d="M22 4L12 14.01l-3-3"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default CheckCircleIcon;
|
||||||
24
app/components/icons/chevronRightIcon.tsx
Normal file
24
app/components/icons/chevronRightIcon.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Path } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ChevronRightIcon: React.FC<IconProps> = ({ size = 24, color = '#666', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Path
|
||||||
|
d="M9 18l6-6-6-6"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ChevronRightIcon;
|
||||||
35
app/components/icons/clipboardIcon.tsx
Normal file
35
app/components/icons/clipboardIcon.tsx
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Path, Rect } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ClipboardIcon: React.FC<IconProps> = ({ size = 24, color = '#666', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Path
|
||||||
|
d="M16 4h2a2 2 0 012 2v14a2 2 0 01-2 2H6a2 2 0 01-2-2V6a2 2 0 012-2h2"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<Rect
|
||||||
|
x="8"
|
||||||
|
y="2"
|
||||||
|
width="8"
|
||||||
|
height="4"
|
||||||
|
rx="1"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ClipboardIcon;
|
||||||
24
app/components/icons/closeIcon.tsx
Normal file
24
app/components/icons/closeIcon.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Path } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CloseIcon: React.FC<IconProps> = ({ size = 24, color = '#666', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Path
|
||||||
|
d="M18 6L6 18M6 6l12 12"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default CloseIcon;
|
||||||
31
app/components/icons/homeIcon.tsx
Normal file
31
app/components/icons/homeIcon.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Path } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const HomeIcon: React.FC<IconProps> = ({ size = 24, color = '#666', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Path
|
||||||
|
d="M3 9l9-7 9 7v11a2 2 0 01-2 2H5a2 2 0 01-2-2V9z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<Path
|
||||||
|
d="M9 22V12h6v10"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default HomeIcon;
|
||||||
12
app/components/icons/index.ts
Normal file
12
app/components/icons/index.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export * from './homeIcon';
|
||||||
|
export * from './walletIcon';
|
||||||
|
export * from './clipboardIcon';
|
||||||
|
export * from './bellIcon';
|
||||||
|
export * from './personIcon';
|
||||||
|
export * from './chevronRightIcon';
|
||||||
|
export * from './callIcon';
|
||||||
|
export * from './locationPinIcon';
|
||||||
|
export * from './checkCircleIcon';
|
||||||
|
export * from './starIcon';
|
||||||
|
export * from './closeIcon';
|
||||||
|
export * from './backIcon';
|
||||||
33
app/components/icons/locationPinIcon.tsx
Normal file
33
app/components/icons/locationPinIcon.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Path, Circle } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LocationPinIcon: React.FC<IconProps> = ({ size = 24, color = '#666', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Path
|
||||||
|
d="M21 10c0 7-9 13-9 13s-9-6-9-13a9 9 0 0118 0z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<Circle
|
||||||
|
cx="12"
|
||||||
|
cy="10"
|
||||||
|
r="3"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default LocationPinIcon;
|
||||||
33
app/components/icons/personIcon.tsx
Normal file
33
app/components/icons/personIcon.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Path, Circle } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const PersonIcon: React.FC<IconProps> = ({ size = 24, color = '#666', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Path
|
||||||
|
d="M20 21v-2a4 4 0 00-4-4H8a4 4 0 00-4 4v2"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<Circle
|
||||||
|
cx="12"
|
||||||
|
cy="7"
|
||||||
|
r="4"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default PersonIcon;
|
||||||
26
app/components/icons/starIcon.tsx
Normal file
26
app/components/icons/starIcon.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Polygon } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
fill?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StarIcon: React.FC<IconProps> = ({ size = 24, color = '#666', fill = 'none', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Polygon
|
||||||
|
points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
fill={fill}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default StarIcon;
|
||||||
31
app/components/icons/walletIcon.tsx
Normal file
31
app/components/icons/walletIcon.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import Svg, { Path } from 'react-native-svg';
|
||||||
|
import { ViewStyle } from 'react-native';
|
||||||
|
|
||||||
|
interface IconProps {
|
||||||
|
size?: number;
|
||||||
|
color?: string;
|
||||||
|
style?: ViewStyle;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WalletIcon: React.FC<IconProps> = ({ size = 24, color = '#666', style }) => {
|
||||||
|
return (
|
||||||
|
<Svg width={size} height={size} viewBox="0 0 24 24" fill="none" style={style}>
|
||||||
|
<Path
|
||||||
|
d="M20 12V8H6a2 2 0 01-2-2 2 2 0 012-2h12v1m2 11h-4a2 2 0 000 4h4v-4z"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
<Path
|
||||||
|
d="M4 6v12a2 2 0 002 2h14a2 2 0 002-2v-4"
|
||||||
|
stroke={color}
|
||||||
|
strokeWidth="2"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
/>
|
||||||
|
</Svg>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default WalletIcon;
|
||||||
@ -1,3 +1,3 @@
|
|||||||
export * from './CustomInput';
|
export * from './customInput';
|
||||||
export * from './PrimaryButton';
|
export * from './primaryButton';
|
||||||
export * from './SocialButton';
|
export * from './socialButton';
|
||||||
2
app/components/jobStatusBanner/index.ts
Normal file
2
app/components/jobStatusBanner/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './jobStatusBanner';
|
||||||
|
export { default } from './jobStatusBanner';
|
||||||
27
app/components/jobStatusBanner/jobStatusBanner.styles.ts
Normal file
27
app/components/jobStatusBanner/jobStatusBanner.styles.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, spacing } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
width: '100%',
|
||||||
|
paddingVertical: spacing.md,
|
||||||
|
paddingHorizontal: spacing.lg,
|
||||||
|
backgroundColor: colors.primaryLight,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: colors.border,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.primary,
|
||||||
|
},
|
||||||
|
detail: {
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.textSecondary,
|
||||||
|
fontWeight: typography.fontWeight.medium,
|
||||||
|
},
|
||||||
|
});
|
||||||
27
app/components/jobStatusBanner/jobStatusBanner.tsx
Normal file
27
app/components/jobStatusBanner/jobStatusBanner.tsx
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './jobStatusBanner.styles';
|
||||||
|
|
||||||
|
interface JobStatusBannerProps {
|
||||||
|
stepLabel: string;
|
||||||
|
stepDetail: string;
|
||||||
|
backgroundColor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const JobStatusBanner: React.FC<JobStatusBannerProps> = ({
|
||||||
|
stepLabel,
|
||||||
|
stepDetail,
|
||||||
|
backgroundColor,
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, backgroundColor ? { backgroundColor } : null]}>
|
||||||
|
<Text style={styles.label}>{stepLabel}</Text>
|
||||||
|
<Text style={styles.detail}>{stepDetail}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default JobStatusBanner;
|
||||||
2
app/components/loadingOverlay/index.ts
Normal file
2
app/components/loadingOverlay/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './loadingOverlay';
|
||||||
|
export { default } from './loadingOverlay';
|
||||||
31
app/components/loadingOverlay/loadingOverlay.styles.ts
Normal file
31
app/components/loadingOverlay/loadingOverlay.styles.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, borderRadius } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.4)',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
zIndex: 9999,
|
||||||
|
},
|
||||||
|
box: {
|
||||||
|
padding: 24,
|
||||||
|
backgroundColor: colors.cardBg,
|
||||||
|
borderRadius: borderRadius.md,
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: 0.15,
|
||||||
|
shadowRadius: 10,
|
||||||
|
elevation: 6,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
marginTop: 16,
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
fontWeight: typography.fontWeight.medium,
|
||||||
|
color: colors.text,
|
||||||
|
},
|
||||||
|
});
|
||||||
29
app/components/loadingOverlay/loadingOverlay.tsx
Normal file
29
app/components/loadingOverlay/loadingOverlay.tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, ActivityIndicator } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './loadingOverlay.styles';
|
||||||
|
|
||||||
|
interface LoadingOverlayProps {
|
||||||
|
visible: boolean;
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LoadingOverlay: React.FC<LoadingOverlayProps> = ({
|
||||||
|
visible,
|
||||||
|
message = 'Loading...',
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
if (!visible) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.box}>
|
||||||
|
<ActivityIndicator size="large" color={colors.primary} />
|
||||||
|
{message ? <Text style={styles.message}>{message}</Text> : null}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default LoadingOverlay;
|
||||||
2
app/components/locationRow/index.ts
Normal file
2
app/components/locationRow/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './locationRow';
|
||||||
|
export { default } from './locationRow';
|
||||||
38
app/components/locationRow/locationRow.styles.ts
Normal file
38
app/components/locationRow/locationRow.styles.ts
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, spacing } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'flex-start',
|
||||||
|
},
|
||||||
|
iconWrapper: {
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
borderRadius: 12,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginRight: spacing.md,
|
||||||
|
marginTop: 2,
|
||||||
|
},
|
||||||
|
pickupBg: {
|
||||||
|
backgroundColor: 'rgba(255, 107, 53, 0.1)',
|
||||||
|
},
|
||||||
|
dropBg: {
|
||||||
|
backgroundColor: 'rgba(46, 204, 113, 0.1)',
|
||||||
|
},
|
||||||
|
textContainer: {
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.text,
|
||||||
|
},
|
||||||
|
address: {
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.textSecondary,
|
||||||
|
marginTop: 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
33
app/components/locationRow/locationRow.tsx
Normal file
33
app/components/locationRow/locationRow.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { LocationPinIcon } from '@icons/locationPinIcon';
|
||||||
|
import { getStyles } from './locationRow.styles';
|
||||||
|
|
||||||
|
interface LocationRowProps {
|
||||||
|
type: 'pickup' | 'drop';
|
||||||
|
name: string;
|
||||||
|
address: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const LocationRow: React.FC<LocationRowProps> = ({ type, name, address }) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
const isPickup = type === 'pickup';
|
||||||
|
const iconColor = isPickup ? colors.statusOffline : colors.statusOnline;
|
||||||
|
const bgStyle = isPickup ? styles.pickupBg : styles.dropBg;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={[styles.iconWrapper, bgStyle]}>
|
||||||
|
<LocationPinIcon size={16} color={iconColor} />
|
||||||
|
</View>
|
||||||
|
<View style={styles.textContainer}>
|
||||||
|
<Text style={styles.name}>{name}</Text>
|
||||||
|
<Text style={styles.address}>{address}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default LocationRow;
|
||||||
2
app/components/mapBottomSheet/index.ts
Normal file
2
app/components/mapBottomSheet/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './mapBottomSheet';
|
||||||
|
export { default } from './mapBottomSheet';
|
||||||
30
app/components/mapBottomSheet/mapBottomSheet.styles.ts
Normal file
30
app/components/mapBottomSheet/mapBottomSheet.styles.ts
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { borderRadius, shadow } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
sheet: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: 0,
|
||||||
|
left: 0,
|
||||||
|
right: 0,
|
||||||
|
backgroundColor: colors.cardBg,
|
||||||
|
borderTopLeftRadius: borderRadius.xl,
|
||||||
|
borderTopRightRadius: borderRadius.xl,
|
||||||
|
paddingHorizontal: 20,
|
||||||
|
paddingBottom: 30,
|
||||||
|
paddingTop: 12,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderTopColor: colors.border,
|
||||||
|
...shadow.lg,
|
||||||
|
zIndex: 100,
|
||||||
|
},
|
||||||
|
handle: {
|
||||||
|
width: 40,
|
||||||
|
height: 5,
|
||||||
|
borderRadius: 3,
|
||||||
|
backgroundColor: colors.border,
|
||||||
|
alignSelf: 'center',
|
||||||
|
marginBottom: 16,
|
||||||
|
},
|
||||||
|
});
|
||||||
21
app/components/mapBottomSheet/mapBottomSheet.tsx
Normal file
21
app/components/mapBottomSheet/mapBottomSheet.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './mapBottomSheet.styles';
|
||||||
|
|
||||||
|
interface MapBottomSheetProps {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MapBottomSheet: React.FC<MapBottomSheetProps> = ({ children }) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.sheet}>
|
||||||
|
<View style={styles.handle} />
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default MapBottomSheet;
|
||||||
2
app/components/mapView/index.ts
Normal file
2
app/components/mapView/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './mapView';
|
||||||
|
export { default } from './mapView';
|
||||||
28
app/components/mapView/mapView.styles.ts
Normal file
28
app/components/mapView/mapView.styles.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flex: 1,
|
||||||
|
width: '100%',
|
||||||
|
height: '100%',
|
||||||
|
backgroundColor: colors.inputBg,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
map: {
|
||||||
|
...StyleSheet.absoluteFillObject,
|
||||||
|
},
|
||||||
|
mockOverlay: {
|
||||||
|
position: 'absolute',
|
||||||
|
top: 40,
|
||||||
|
padding: 10,
|
||||||
|
borderRadius: 20,
|
||||||
|
backgroundColor: 'rgba(255, 255, 255, 0.9)',
|
||||||
|
alignSelf: 'center',
|
||||||
|
},
|
||||||
|
mockText: {
|
||||||
|
color: colors.primary,
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
});
|
||||||
77
app/components/mapView/mapView.tsx
Normal file
77
app/components/mapView/mapView.tsx
Normal file
@ -0,0 +1,77 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
import MapView, { Marker, Polyline, PROVIDER_DEFAULT } from 'react-native-maps';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { Coordinates } from '@app-types/index';
|
||||||
|
import { getStyles } from './mapView.styles';
|
||||||
|
|
||||||
|
interface MapViewComponentProps {
|
||||||
|
origin?: Coordinates;
|
||||||
|
destination?: Coordinates;
|
||||||
|
currentLocation?: Coordinates;
|
||||||
|
showRoute?: boolean;
|
||||||
|
style?: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const MapViewComponent: React.FC<MapViewComponentProps> = ({
|
||||||
|
origin,
|
||||||
|
destination,
|
||||||
|
currentLocation,
|
||||||
|
showRoute = false,
|
||||||
|
style,
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
const defaultRegion = {
|
||||||
|
latitude: currentLocation?.latitude || origin?.latitude || 12.9716,
|
||||||
|
longitude: currentLocation?.longitude || origin?.longitude || 77.5946,
|
||||||
|
latitudeDelta: 0.05,
|
||||||
|
longitudeDelta: 0.05,
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, style]}>
|
||||||
|
<MapView
|
||||||
|
provider={PROVIDER_DEFAULT}
|
||||||
|
style={styles.map}
|
||||||
|
initialRegion={defaultRegion}
|
||||||
|
showsUserLocation={true}
|
||||||
|
loadingEnabled={true}
|
||||||
|
>
|
||||||
|
{currentLocation && (
|
||||||
|
<Marker
|
||||||
|
coordinate={currentLocation}
|
||||||
|
title="Your Location"
|
||||||
|
pinColor={colors.primary}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{origin && (
|
||||||
|
<Marker
|
||||||
|
coordinate={origin}
|
||||||
|
title="Pickup Location"
|
||||||
|
pinColor={colors.statusOffline}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{destination && (
|
||||||
|
<Marker
|
||||||
|
coordinate={destination}
|
||||||
|
title="Drop Location"
|
||||||
|
pinColor={colors.statusOnline}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{showRoute && origin && destination && (
|
||||||
|
<Polyline
|
||||||
|
coordinates={[origin, destination]}
|
||||||
|
strokeColor={colors.primary}
|
||||||
|
strokeWidth={4}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</MapView>
|
||||||
|
<View style={styles.mockOverlay}>
|
||||||
|
<Text style={styles.mockText}>📍 Live Navigation Map Active</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default MapViewComponent;
|
||||||
2
app/components/numericKeypad/index.ts
Normal file
2
app/components/numericKeypad/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './numericKeypad';
|
||||||
|
export { default } from './numericKeypad';
|
||||||
34
app/components/numericKeypad/numericKeypad.styles.ts
Normal file
34
app/components/numericKeypad/numericKeypad.styles.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
width: '100%',
|
||||||
|
paddingVertical: 10,
|
||||||
|
backgroundColor: colors.background,
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-around',
|
||||||
|
marginVertical: 8,
|
||||||
|
},
|
||||||
|
keyButton: {
|
||||||
|
width: 70,
|
||||||
|
height: 70,
|
||||||
|
borderRadius: 35,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
backgroundColor: colors.inputBg,
|
||||||
|
},
|
||||||
|
keyText: {
|
||||||
|
fontSize: typography.fontSize.xl,
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.text,
|
||||||
|
},
|
||||||
|
emptyKey: {
|
||||||
|
width: 70,
|
||||||
|
height: 70,
|
||||||
|
backgroundColor: 'transparent',
|
||||||
|
},
|
||||||
|
});
|
||||||
62
app/components/numericKeypad/numericKeypad.tsx
Normal file
62
app/components/numericKeypad/numericKeypad.tsx
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, TouchableOpacity } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './numericKeypad.styles';
|
||||||
|
|
||||||
|
interface NumericKeypadProps {
|
||||||
|
onPress: (digit: string) => void;
|
||||||
|
onBackspace: () => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NumericKeypad: React.FC<NumericKeypadProps> = ({
|
||||||
|
onPress,
|
||||||
|
onBackspace,
|
||||||
|
disabled = false,
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
const keys = [
|
||||||
|
['1', '2', '3'],
|
||||||
|
['4', '5', '6'],
|
||||||
|
['7', '8', '9'],
|
||||||
|
['', '0', '⌫'],
|
||||||
|
];
|
||||||
|
|
||||||
|
const handlePress = (val: string) => {
|
||||||
|
if (disabled) return;
|
||||||
|
if (val === '⌫') {
|
||||||
|
onBackspace();
|
||||||
|
} else if (val !== '') {
|
||||||
|
onPress(val);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{keys.map((row, rIndex) => (
|
||||||
|
<View key={rIndex} style={styles.row}>
|
||||||
|
{row.map((val, cIndex) => {
|
||||||
|
if (val === '') {
|
||||||
|
return <View key={cIndex} style={styles.emptyKey} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
key={cIndex}
|
||||||
|
style={styles.keyButton}
|
||||||
|
activeOpacity={0.7}
|
||||||
|
onPress={() => handlePress(val)}
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
<Text style={styles.keyText}>{val}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default NumericKeypad;
|
||||||
2
app/components/onlineToggleButton/index.ts
Normal file
2
app/components/onlineToggleButton/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './onlineToggleButton';
|
||||||
|
export { default } from './onlineToggleButton';
|
||||||
@ -0,0 +1,29 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, borderRadius } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
button: {
|
||||||
|
width: '100%',
|
||||||
|
paddingVertical: 16,
|
||||||
|
borderRadius: borderRadius.md,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: 4,
|
||||||
|
},
|
||||||
|
onlineBg: {
|
||||||
|
backgroundColor: colors.primary,
|
||||||
|
},
|
||||||
|
offlineBg: {
|
||||||
|
backgroundColor: colors.statusOffline,
|
||||||
|
},
|
||||||
|
text: {
|
||||||
|
fontSize: typography.fontSize.md,
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.white,
|
||||||
|
},
|
||||||
|
});
|
||||||
38
app/components/onlineToggleButton/onlineToggleButton.tsx
Normal file
38
app/components/onlineToggleButton/onlineToggleButton.tsx
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Text, TouchableOpacity, ActivityIndicator } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './onlineToggleButton.styles';
|
||||||
|
|
||||||
|
interface OnlineToggleButtonProps {
|
||||||
|
isOnline: boolean;
|
||||||
|
onToggle: () => void;
|
||||||
|
isLoading?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OnlineToggleButton: React.FC<OnlineToggleButtonProps> = ({
|
||||||
|
isOnline,
|
||||||
|
onToggle,
|
||||||
|
isLoading = false,
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
const buttonStyle = isOnline ? styles.offlineBg : styles.onlineBg;
|
||||||
|
const label = isOnline ? 'Go Offline' : 'Go Online';
|
||||||
|
|
||||||
|
return (
|
||||||
|
<TouchableOpacity
|
||||||
|
style={[styles.button, buttonStyle]}
|
||||||
|
activeOpacity={0.8}
|
||||||
|
onPress={onToggle}
|
||||||
|
disabled={isLoading}
|
||||||
|
>
|
||||||
|
{isLoading ? (
|
||||||
|
<ActivityIndicator color={colors.white} />
|
||||||
|
) : (
|
||||||
|
<Text style={styles.text}>{label}</Text>
|
||||||
|
)}
|
||||||
|
</TouchableOpacity>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default OnlineToggleButton;
|
||||||
2
app/components/orderCard/index.ts
Normal file
2
app/components/orderCard/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './orderCard';
|
||||||
|
export { default } from './orderCard';
|
||||||
49
app/components/orderCard/orderCard.styles.ts
Normal file
49
app/components/orderCard/orderCard.styles.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, spacing } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
amount: {
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.primary,
|
||||||
|
},
|
||||||
|
distance: {
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
color: colors.textSecondary,
|
||||||
|
fontWeight: typography.fontWeight.medium,
|
||||||
|
},
|
||||||
|
row: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
marginTop: spacing.sm,
|
||||||
|
},
|
||||||
|
detailsRow: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
marginTop: spacing.md,
|
||||||
|
paddingTop: spacing.md,
|
||||||
|
borderTopWidth: 1,
|
||||||
|
borderTopColor: colors.border,
|
||||||
|
},
|
||||||
|
spacer: {
|
||||||
|
height: 12,
|
||||||
|
},
|
||||||
|
centerCol: {
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
endCol: {
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
color: colors.textSecondary,
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.text,
|
||||||
|
marginTop: 2,
|
||||||
|
},
|
||||||
|
});
|
||||||
63
app/components/orderCard/orderCard.tsx
Normal file
63
app/components/orderCard/orderCard.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { Card } from '../card/card';
|
||||||
|
import { LocationRow } from '../locationRow/locationRow';
|
||||||
|
import { formatCurrency, formatDistance } from '@utils/formatters';
|
||||||
|
import { getStyles } from './orderCard.styles';
|
||||||
|
|
||||||
|
interface OrderCardProps {
|
||||||
|
pickupName: string;
|
||||||
|
pickupAddress: string;
|
||||||
|
dropName: string;
|
||||||
|
dropAddress: string;
|
||||||
|
amount: number;
|
||||||
|
distance: number;
|
||||||
|
itemCount?: number;
|
||||||
|
paymentType?: string;
|
||||||
|
onPress?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OrderCard: React.FC<OrderCardProps> = ({
|
||||||
|
pickupName,
|
||||||
|
pickupAddress,
|
||||||
|
dropName,
|
||||||
|
dropAddress,
|
||||||
|
amount,
|
||||||
|
distance,
|
||||||
|
itemCount,
|
||||||
|
paymentType,
|
||||||
|
onPress,
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card onPress={onPress}>
|
||||||
|
<LocationRow type="pickup" name={pickupName} address={pickupAddress} />
|
||||||
|
<View style={styles.spacer} />
|
||||||
|
<LocationRow type="drop" name={dropName} address={dropAddress} />
|
||||||
|
|
||||||
|
<View style={styles.detailsRow}>
|
||||||
|
<View>
|
||||||
|
<Text style={styles.label}>ESTIMATED EARNINGS</Text>
|
||||||
|
<Text style={styles.amount}>{formatCurrency(amount)}</Text>
|
||||||
|
</View>
|
||||||
|
<View style={styles.centerCol}>
|
||||||
|
<Text style={styles.label}>DISTANCE</Text>
|
||||||
|
<Text style={styles.distance}>{formatDistance(distance)}</Text>
|
||||||
|
</View>
|
||||||
|
{(itemCount !== undefined || paymentType !== undefined) && (
|
||||||
|
<View style={styles.endCol}>
|
||||||
|
<Text style={styles.label}>DETAILS</Text>
|
||||||
|
<Text style={styles.value}>
|
||||||
|
{itemCount ? `${itemCount} items` : ''}
|
||||||
|
{paymentType ? ` • ${paymentType.toUpperCase()}` : ''}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default OrderCard;
|
||||||
2
app/components/otpInput/index.ts
Normal file
2
app/components/otpInput/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './otpInput';
|
||||||
|
export { default } from './otpInput';
|
||||||
36
app/components/otpInput/otpInput.styles.ts
Normal file
36
app/components/otpInput/otpInput.styles.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, borderRadius } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
width: '100%',
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
marginVertical: 20,
|
||||||
|
},
|
||||||
|
inputContainer: {
|
||||||
|
width: 45,
|
||||||
|
height: 50,
|
||||||
|
borderWidth: 1.5,
|
||||||
|
borderColor: colors.border,
|
||||||
|
borderRadius: borderRadius.sm,
|
||||||
|
backgroundColor: colors.inputBg,
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
},
|
||||||
|
inputActive: {
|
||||||
|
borderColor: colors.primary,
|
||||||
|
backgroundColor: colors.background,
|
||||||
|
},
|
||||||
|
inputError: {
|
||||||
|
borderColor: colors.error,
|
||||||
|
},
|
||||||
|
inputText: {
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.text,
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
});
|
||||||
40
app/components/otpInput/otpInput.tsx
Normal file
40
app/components/otpInput/otpInput.tsx
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './otpInput.styles';
|
||||||
|
|
||||||
|
interface OtpInputProps {
|
||||||
|
length?: number;
|
||||||
|
value: string;
|
||||||
|
error?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const OtpInput: React.FC<OtpInputProps> = ({ length = 6, value, error }) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
const boxes = Array(length).fill(0);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
{boxes.map((_, index) => {
|
||||||
|
const char = value[index] || '';
|
||||||
|
const isFocused = index === value.length;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View
|
||||||
|
key={index}
|
||||||
|
style={[
|
||||||
|
styles.inputContainer,
|
||||||
|
isFocused && styles.inputActive,
|
||||||
|
error && styles.inputError,
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<Text style={styles.inputText}>{char}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default OtpInput;
|
||||||
2
app/components/screenHeader/index.ts
Normal file
2
app/components/screenHeader/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './screenHeader';
|
||||||
|
export { default } from './screenHeader';
|
||||||
34
app/components/screenHeader/screenHeader.styles.ts
Normal file
34
app/components/screenHeader/screenHeader.styles.ts
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, spacing } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
paddingHorizontal: spacing.lg,
|
||||||
|
paddingVertical: spacing.md,
|
||||||
|
backgroundColor: colors.background,
|
||||||
|
borderBottomWidth: 1,
|
||||||
|
borderBottomColor: colors.border,
|
||||||
|
},
|
||||||
|
leftContainer: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
backButton: {
|
||||||
|
marginRight: spacing.md,
|
||||||
|
padding: spacing.xs,
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: typography.fontSize.lg,
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
color: colors.text,
|
||||||
|
},
|
||||||
|
rightContainer: {
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'flex-end',
|
||||||
|
},
|
||||||
|
});
|
||||||
39
app/components/screenHeader/screenHeader.tsx
Normal file
39
app/components/screenHeader/screenHeader.tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, TouchableOpacity } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { BackIcon } from '@icons/backIcon';
|
||||||
|
import { getStyles } from './screenHeader.styles';
|
||||||
|
|
||||||
|
interface ScreenHeaderProps {
|
||||||
|
title: string;
|
||||||
|
onBack?: () => void;
|
||||||
|
showBack?: boolean;
|
||||||
|
rightElement?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ScreenHeader: React.FC<ScreenHeaderProps> = ({
|
||||||
|
title,
|
||||||
|
onBack,
|
||||||
|
showBack = true,
|
||||||
|
rightElement,
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<View style={styles.leftContainer}>
|
||||||
|
{showBack && onBack && (
|
||||||
|
<TouchableOpacity style={styles.backButton} onPress={onBack} activeOpacity={0.7}>
|
||||||
|
<BackIcon size={24} color={colors.text} />
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
<Text style={styles.title} numberOfLines={1}>
|
||||||
|
{title}
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
{rightElement && <View style={styles.rightContainer}>{rightElement}</View>}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default ScreenHeader;
|
||||||
2
app/components/sectionHeader/index.ts
Normal file
2
app/components/sectionHeader/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './sectionHeader';
|
||||||
|
export { default } from './sectionHeader';
|
||||||
24
app/components/sectionHeader/sectionHeader.styles.ts
Normal file
24
app/components/sectionHeader/sectionHeader.styles.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, spacing } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingVertical: spacing.sm,
|
||||||
|
marginVertical: spacing.xs,
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
title: {
|
||||||
|
fontSize: typography.fontSize.md,
|
||||||
|
fontWeight: typography.fontWeight.bold,
|
||||||
|
color: colors.text,
|
||||||
|
},
|
||||||
|
actionText: {
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
fontWeight: typography.fontWeight.medium,
|
||||||
|
color: colors.primary,
|
||||||
|
},
|
||||||
|
});
|
||||||
31
app/components/sectionHeader/sectionHeader.tsx
Normal file
31
app/components/sectionHeader/sectionHeader.tsx
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, TouchableOpacity } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './sectionHeader.styles';
|
||||||
|
|
||||||
|
interface SectionHeaderProps {
|
||||||
|
title: string;
|
||||||
|
actionText?: string;
|
||||||
|
onActionPress?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const SectionHeader: React.FC<SectionHeaderProps> = ({
|
||||||
|
title,
|
||||||
|
actionText,
|
||||||
|
onActionPress,
|
||||||
|
}) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text style={styles.title}>{title}</Text>
|
||||||
|
{actionText && onActionPress && (
|
||||||
|
<TouchableOpacity activeOpacity={0.7} onPress={onActionPress}>
|
||||||
|
<Text style={styles.actionText}>{actionText}</Text>
|
||||||
|
</TouchableOpacity>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default SectionHeader;
|
||||||
2
app/components/statusBadge/index.ts
Normal file
2
app/components/statusBadge/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './statusBadge';
|
||||||
|
export { default } from './statusBadge';
|
||||||
49
app/components/statusBadge/statusBadge.styles.ts
Normal file
49
app/components/statusBadge/statusBadge.styles.ts
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, borderRadius } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
paddingHorizontal: 10,
|
||||||
|
paddingVertical: 4,
|
||||||
|
borderRadius: borderRadius.full,
|
||||||
|
alignSelf: 'flex-start',
|
||||||
|
},
|
||||||
|
dot: {
|
||||||
|
width: 8,
|
||||||
|
height: 8,
|
||||||
|
borderRadius: 4,
|
||||||
|
marginRight: 6,
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
fontSize: typography.fontSize.xs,
|
||||||
|
fontWeight: typography.fontWeight.semibold,
|
||||||
|
textTransform: 'capitalize',
|
||||||
|
},
|
||||||
|
onlineBg: {
|
||||||
|
backgroundColor: '#E8F8F0',
|
||||||
|
},
|
||||||
|
offlineBg: {
|
||||||
|
backgroundColor: '#FEF0E6',
|
||||||
|
},
|
||||||
|
busyBg: {
|
||||||
|
backgroundColor: '#FEF9E7',
|
||||||
|
},
|
||||||
|
onDeliveryBg: {
|
||||||
|
backgroundColor: '#EBF5FB',
|
||||||
|
},
|
||||||
|
onlineText: {
|
||||||
|
color: colors.statusOnline,
|
||||||
|
},
|
||||||
|
offlineText: {
|
||||||
|
color: colors.statusOffline,
|
||||||
|
},
|
||||||
|
busyText: {
|
||||||
|
color: colors.statusBusy,
|
||||||
|
},
|
||||||
|
onDeliveryText: {
|
||||||
|
color: colors.statusOnDelivery,
|
||||||
|
},
|
||||||
|
});
|
||||||
60
app/components/statusBadge/statusBadge.tsx
Normal file
60
app/components/statusBadge/statusBadge.tsx
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './statusBadge.styles';
|
||||||
|
|
||||||
|
export type BadgeStatusType = 'online' | 'offline' | 'busy' | 'onDelivery';
|
||||||
|
|
||||||
|
interface StatusBadgeProps {
|
||||||
|
status: BadgeStatusType;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const StatusBadge: React.FC<StatusBadgeProps> = ({ status }) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
const getStyleConfig = () => {
|
||||||
|
switch (status) {
|
||||||
|
case 'online':
|
||||||
|
return {
|
||||||
|
bg: styles.onlineBg,
|
||||||
|
text: styles.onlineText,
|
||||||
|
dotColor: colors.statusOnline,
|
||||||
|
};
|
||||||
|
case 'offline':
|
||||||
|
return {
|
||||||
|
bg: styles.offlineBg,
|
||||||
|
text: styles.offlineText,
|
||||||
|
dotColor: colors.statusOffline,
|
||||||
|
};
|
||||||
|
case 'busy':
|
||||||
|
return {
|
||||||
|
bg: styles.busyBg,
|
||||||
|
text: styles.busyText,
|
||||||
|
dotColor: colors.statusBusy,
|
||||||
|
};
|
||||||
|
case 'onDelivery':
|
||||||
|
return {
|
||||||
|
bg: styles.onDeliveryBg,
|
||||||
|
text: styles.onDeliveryText,
|
||||||
|
dotColor: colors.statusOnDelivery,
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
return {
|
||||||
|
bg: styles.offlineBg,
|
||||||
|
text: styles.offlineText,
|
||||||
|
dotColor: colors.statusOffline,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = getStyleConfig();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, config.bg]}>
|
||||||
|
<View style={[styles.dot, { backgroundColor: config.dotColor }]} />
|
||||||
|
<Text style={[styles.label, config.text]}>{status}</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
export default StatusBadge;
|
||||||
2
app/components/toast/index.ts
Normal file
2
app/components/toast/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export * from './toast';
|
||||||
|
export { default } from './toast';
|
||||||
51
app/components/toast/toast.styles.ts
Normal file
51
app/components/toast/toast.styles.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
import { StyleSheet } from 'react-native';
|
||||||
|
import { typography, borderRadius, spacing } from '@theme';
|
||||||
|
|
||||||
|
export const getStyles = (colors: any) =>
|
||||||
|
StyleSheet.create({
|
||||||
|
container: {
|
||||||
|
position: 'absolute',
|
||||||
|
bottom: spacing.xxl + 20,
|
||||||
|
left: spacing.lg,
|
||||||
|
right: spacing.lg,
|
||||||
|
padding: spacing.md,
|
||||||
|
borderRadius: borderRadius.md,
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
shadowColor: '#000',
|
||||||
|
shadowOffset: { width: 0, height: 4 },
|
||||||
|
shadowOpacity: 0.1,
|
||||||
|
shadowRadius: 8,
|
||||||
|
elevation: 4,
|
||||||
|
zIndex: 99999,
|
||||||
|
},
|
||||||
|
successBg: {
|
||||||
|
backgroundColor: colors.successLight,
|
||||||
|
borderColor: colors.success,
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
errorBg: {
|
||||||
|
backgroundColor: colors.errorLight,
|
||||||
|
borderColor: colors.error,
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
infoBg: {
|
||||||
|
backgroundColor: colors.primaryLight,
|
||||||
|
borderColor: colors.primary,
|
||||||
|
borderWidth: 1,
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
flex: 1,
|
||||||
|
fontSize: typography.fontSize.sm,
|
||||||
|
fontWeight: typography.fontWeight.medium,
|
||||||
|
},
|
||||||
|
successText: {
|
||||||
|
color: colors.success,
|
||||||
|
},
|
||||||
|
errorText: {
|
||||||
|
color: colors.error,
|
||||||
|
},
|
||||||
|
infoText: {
|
||||||
|
color: colors.primary,
|
||||||
|
},
|
||||||
|
});
|
||||||
82
app/components/toast/toast.tsx
Normal file
82
app/components/toast/toast.tsx
Normal file
@ -0,0 +1,82 @@
|
|||||||
|
import React, { createContext, useContext, useState, useCallback } from 'react';
|
||||||
|
import { Animated, Text } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { getStyles } from './toast.styles';
|
||||||
|
|
||||||
|
export type ToastType = 'success' | 'error' | 'info';
|
||||||
|
|
||||||
|
interface ToastOptions {
|
||||||
|
message: string;
|
||||||
|
type?: ToastType;
|
||||||
|
duration?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface ToastContextType {
|
||||||
|
showToast: (options: ToastOptions) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToastContext = createContext<ToastContextType | undefined>(undefined);
|
||||||
|
|
||||||
|
export const ToastProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const styles = getStyles(colors);
|
||||||
|
|
||||||
|
const [toast, setToast] = useState<{ message: string; type: ToastType } | null>(null);
|
||||||
|
const [fadeAnim] = useState(new Animated.Value(0));
|
||||||
|
|
||||||
|
const showToast = useCallback(
|
||||||
|
({ message, type = 'info', duration = 3000 }: ToastOptions) => {
|
||||||
|
setToast({ message, type });
|
||||||
|
|
||||||
|
// Animate In
|
||||||
|
Animated.timing(fadeAnim, {
|
||||||
|
toValue: 1,
|
||||||
|
duration: 300,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}).start();
|
||||||
|
|
||||||
|
// Dismiss after duration
|
||||||
|
setTimeout(() => {
|
||||||
|
Animated.timing(fadeAnim, {
|
||||||
|
toValue: 0,
|
||||||
|
duration: 300,
|
||||||
|
useNativeDriver: true,
|
||||||
|
}).start(() => setToast(null));
|
||||||
|
}, duration);
|
||||||
|
},
|
||||||
|
[fadeAnim]
|
||||||
|
);
|
||||||
|
|
||||||
|
const getStyleConfig = () => {
|
||||||
|
if (!toast) return { bg: null, text: null };
|
||||||
|
switch (toast.type) {
|
||||||
|
case 'success':
|
||||||
|
return { bg: styles.successBg, text: styles.successText };
|
||||||
|
case 'error':
|
||||||
|
return { bg: styles.errorBg, text: styles.errorText };
|
||||||
|
case 'info':
|
||||||
|
return { bg: styles.infoBg, text: styles.infoText };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const config = getStyleConfig();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ToastContext.Provider value={{ showToast }}>
|
||||||
|
{children}
|
||||||
|
{toast ? (
|
||||||
|
<Animated.View style={[styles.container, config.bg, { opacity: fadeAnim }]}>
|
||||||
|
<Text style={[styles.message, config.text]}>{toast.message}</Text>
|
||||||
|
</Animated.View>
|
||||||
|
) : null}
|
||||||
|
</ToastContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const useToast = () => {
|
||||||
|
const context = useContext(ToastContext);
|
||||||
|
if (!context) {
|
||||||
|
throw new Error('useToast must be used within a ToastProvider');
|
||||||
|
}
|
||||||
|
return context;
|
||||||
|
};
|
||||||
@ -1 +0,0 @@
|
|||||||
export const DEFAULT_OTP = '111111';
|
|
||||||
@ -76,4 +76,14 @@ export const getStyles = (colors: any) => StyleSheet.create({
|
|||||||
color: colors.primary,
|
color: colors.primary,
|
||||||
fontWeight: typography.fontWeight.bold,
|
fontWeight: typography.fontWeight.bold,
|
||||||
},
|
},
|
||||||
|
otpWrapper: {
|
||||||
|
marginTop: 10,
|
||||||
|
},
|
||||||
|
otpText: {
|
||||||
|
flex: 1,
|
||||||
|
marginVertical: 10,
|
||||||
|
},
|
||||||
|
loginButton: {
|
||||||
|
marginBottom: 12,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@ -8,13 +8,18 @@ import {
|
|||||||
TouchableOpacity,
|
TouchableOpacity,
|
||||||
Alert,
|
Alert,
|
||||||
} from 'react-native';
|
} from 'react-native';
|
||||||
import { getStyles } from './LoginScreen.styles';
|
import { getStyles } from './loginScreen.styles';
|
||||||
import { CustomInput, PrimaryButton, SocialButton } from '@components';
|
import { CustomInput, PrimaryButton, SocialButton } from '@components';
|
||||||
import { useAppTheme } from '@theme';
|
import { useAppTheme } from '@theme';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||||
|
import { AuthStackParamList } from '@navigation/navigationTypes';
|
||||||
|
import { RouteNames } from '@utils/constants';
|
||||||
|
|
||||||
export const LoginScreen: React.FC = () => {
|
export const LoginScreen: React.FC = () => {
|
||||||
const { colors } = useAppTheme();
|
const { colors } = useAppTheme();
|
||||||
const styles = getStyles(colors);
|
const styles = getStyles(colors);
|
||||||
|
const navigation = useNavigation<NativeStackNavigationProp<AuthStackParamList>>();
|
||||||
|
|
||||||
const [mobileNumber, setMobileNumber] = useState('');
|
const [mobileNumber, setMobileNumber] = useState('');
|
||||||
// const [password, setPassword] = useState('');
|
// const [password, setPassword] = useState('');
|
||||||
@ -49,7 +54,7 @@ export const LoginScreen: React.FC = () => {
|
|||||||
// Simulate API request
|
// Simulate API request
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
Alert.alert('Success', 'Logged in successfully!');
|
navigation.navigate(RouteNames.Otp, { phone: mobileNumber });
|
||||||
}, 1500);
|
}, 1500);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -128,8 +133,8 @@ export const LoginScreen: React.FC = () => {
|
|||||||
<Text style={styles.checkboxText}>Remember me</Text>
|
<Text style={styles.checkboxText}>Remember me</Text>
|
||||||
</TouchableOpacity>
|
</TouchableOpacity>
|
||||||
</View> */}
|
</View> */}
|
||||||
<View style={{ marginTop: 10 }}>
|
<View style={styles.otpWrapper}>
|
||||||
<Text style={{ flex: 1, marginVertical: 10 }}>
|
<Text style={styles.otpText}>
|
||||||
OTP will send on this number
|
OTP will send on this number
|
||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
@ -138,7 +143,7 @@ export const LoginScreen: React.FC = () => {
|
|||||||
title="Login"
|
title="Login"
|
||||||
onPress={handleLogin}
|
onPress={handleLogin}
|
||||||
isLoading={isLoading}
|
isLoading={isLoading}
|
||||||
style={{ marginBottom: 12 }}
|
style={styles.loginButton}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<View style={styles.dividerContainer}>
|
<View style={styles.dividerContainer}>
|
||||||
@ -169,3 +174,5 @@ export const LoginScreen: React.FC = () => {
|
|||||||
</KeyboardAvoidingView>
|
</KeyboardAvoidingView>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default LoginScreen;
|
||||||
|
|||||||
@ -1,2 +1,3 @@
|
|||||||
export * from './LoginScreen';
|
export { default } from './loginScreen';
|
||||||
export * from './LoginScreen.styles';
|
export * from './loginScreen';
|
||||||
|
export * from './loginScreen.styles';
|
||||||
|
|||||||
@ -0,0 +1,32 @@
|
|||||||
|
import React, { useState } from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
import { CustomInput, PrimaryButton } from '@components';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||||
|
import { AuthStackParamList } from '@navigation/navigationTypes';
|
||||||
|
import { RouteNames } from '@utils/constants';
|
||||||
|
|
||||||
|
export const OtpScreen: React.FC<{ route: any }> = ({ route: _route }) => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const navigation = useNavigation<NativeStackNavigationProp<AuthStackParamList>>();
|
||||||
|
const [otp, setOtp] = useState('');
|
||||||
|
const handleVerify = () => {
|
||||||
|
navigation.navigate(RouteNames.SetLocation);
|
||||||
|
};
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, { backgroundColor: colors.background }]}
|
||||||
|
>
|
||||||
|
<Text style={styles.title}>Enter OTP</Text>
|
||||||
|
<CustomInput label="OTP" value={otp} onChangeText={setOtp} keyboardType="numeric" />
|
||||||
|
<PrimaryButton title="Verify" onPress={handleVerify} />
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1, justifyContent: 'center', padding: 20 },
|
||||||
|
title: { fontSize: 24, marginBottom: 20, textAlign: 'center' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default OtpScreen;
|
||||||
2
app/features/screens/OtpScreen/index.ts
Normal file
2
app/features/screens/OtpScreen/index.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export { default } from './otpScreen';
|
||||||
|
export * from './otpScreen';
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
|
||||||
|
const ArrivedAtCustomerScreen: React.FC = () => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
||||||
|
<Text>Arrived At Customer Screen</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ArrivedAtCustomerScreen;
|
||||||
1
app/features/screens/arrivedAtCustomerScreen/index.ts
Normal file
1
app/features/screens/arrivedAtCustomerScreen/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from "./arrivedAtCustomerScreen";
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
|
||||||
|
const ArrivedAtStoreScreen: React.FC = () => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
||||||
|
<Text>Arrived At Store Screen</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ArrivedAtStoreScreen;
|
||||||
1
app/features/screens/arrivedAtStoreScreen/index.ts
Normal file
1
app/features/screens/arrivedAtStoreScreen/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from "./arrivedAtStoreScreen";
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
import { PrimaryButton } from '@components';
|
||||||
|
import { useNavigation } from '@react-navigation/native';
|
||||||
|
import { NativeStackNavigationProp } from '@react-navigation/native-stack';
|
||||||
|
import { AuthStackParamList } from '@navigation/navigationTypes';
|
||||||
|
import { RouteNames } from '@utils/constants';
|
||||||
|
|
||||||
|
const CompleteProfileScreen: React.FC = () => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
const navigation = useNavigation<NativeStackNavigationProp<AuthStackParamList>>();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
||||||
|
<Text style={styles.title}>Complete Profile Screen</Text>
|
||||||
|
<PrimaryButton
|
||||||
|
title="Continue"
|
||||||
|
onPress={() => navigation.navigate(RouteNames.OnboardingComplete)}
|
||||||
|
style={{ marginTop: 20, width: '90%' }}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||||
|
title: { fontSize: 20 },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default CompleteProfileScreen;
|
||||||
1
app/features/screens/completeProfileScreen/index.ts
Normal file
1
app/features/screens/completeProfileScreen/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './completeProfileScreen';
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
|
||||||
|
const ConfirmPickupScreen: React.FC = () => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
||||||
|
<Text>Confirm Pickup Screen</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1, justifyContent: "center", alignItems: "center" },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default ConfirmPickupScreen;
|
||||||
1
app/features/screens/confirmPickupScreen/index.ts
Normal file
1
app/features/screens/confirmPickupScreen/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from "./confirmPickupScreen";
|
||||||
14
app/features/screens/dashboardScreen/dashboardScreen.tsx
Normal file
14
app/features/screens/dashboardScreen/dashboardScreen.tsx
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
|
||||||
|
const DashboardScreen: React.FC = () => (
|
||||||
|
<View style={styles.container}>
|
||||||
|
<Text>Dashboard Screen</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default DashboardScreen;
|
||||||
1
app/features/screens/dashboardScreen/index.ts
Normal file
1
app/features/screens/dashboardScreen/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from './dashboardScreen';
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
|
||||||
|
const DeliverOrderScreen: React.FC = () => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
||||||
|
<Text>Deliver Order Screen</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default DeliverOrderScreen;
|
||||||
1
app/features/screens/deliverOrderScreen/index.ts
Normal file
1
app/features/screens/deliverOrderScreen/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from "./deliverOrderScreen";
|
||||||
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
|
||||||
|
const DeliveryCompletedScreen: React.FC = () => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
||||||
|
<Text>Delivery Completed Screen</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default DeliveryCompletedScreen;
|
||||||
1
app/features/screens/deliveryCompletedScreen/index.ts
Normal file
1
app/features/screens/deliveryCompletedScreen/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from "./deliveryCompletedScreen";
|
||||||
18
app/features/screens/documentsScreen/documentsScreen.tsx
Normal file
18
app/features/screens/documentsScreen/documentsScreen.tsx
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { View, Text, StyleSheet } from 'react-native';
|
||||||
|
import { useAppTheme } from '@theme';
|
||||||
|
|
||||||
|
const DocumentsScreen: React.FC = () => {
|
||||||
|
const { colors } = useAppTheme();
|
||||||
|
return (
|
||||||
|
<View style={[styles.container, { backgroundColor: colors.background }]}>
|
||||||
|
<Text>Documents Screen</Text>
|
||||||
|
</View>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
container: { flex: 1, justifyContent: 'center', alignItems: 'center' },
|
||||||
|
});
|
||||||
|
|
||||||
|
export default DocumentsScreen;
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user