This commit is contained in:
amel
2025-02-25 15:09:41 +08:00
parent 46508b9f05
commit 31b85dad69
37 changed files with 421 additions and 699 deletions

View File

@@ -0,0 +1,14 @@
import { Headers } from "@/constants/Headers";
import { Stack } from "expo-router";
import { StatusBar } from 'expo-status-bar';
export default function RootLayout() {
return (
<>
<Stack screenOptions={Headers.shadow}>
<Stack.Screen name="home" options={{ title: 'Home' }} />
</Stack>
<StatusBar style="light" />
</>
)
}

View File

@@ -0,0 +1,22 @@
import CaraouselHome from "@/components/home/carouselHome";
import { HeaderRightHome } from "@/components/home/headerRightHome";
import { Stack } from "expo-router";
import { SafeAreaView, ScrollView } from "react-native";
export default function Home() {
return (
<SafeAreaView>
<Stack.Screen
options={{
title: 'Home',
headerLeft: () => <></>,
headerTitle: 'Darmasaba',
headerRight: () => <HeaderRightHome />
}}
/>
<ScrollView>
<CaraouselHome/>
</ScrollView>
</SafeAreaView>
)
}

View File

@@ -1,45 +0,0 @@
import { Tabs } from 'expo-router';
import React from 'react';
import { Platform } from 'react-native';
import { HapticTab } from '@/components/HapticTab';
import { IconSymbol } from '@/components/ui/IconSymbol';
import TabBarBackground from '@/components/ui/TabBarBackground';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
export default function TabLayout() {
const colorScheme = useColorScheme();
return (
<Tabs
screenOptions={{
tabBarActiveTintColor: Colors[colorScheme ?? 'light'].tint,
headerShown: false,
tabBarButton: HapticTab,
tabBarBackground: TabBarBackground,
tabBarStyle: Platform.select({
ios: {
// Use a transparent background on iOS to show the blur effect
position: 'absolute',
},
default: {},
}),
}}>
<Tabs.Screen
name="index"
options={{
title: 'Home',
tabBarIcon: ({ color }) => <IconSymbol size={28} name="house.fill" color={color} />,
}}
/>
<Tabs.Screen
name="explore"
options={{
title: 'Explore',
tabBarIcon: ({ color }) => <IconSymbol size={28} name="paperplane.fill" color={color} />,
}}
/>
</Tabs>
);
}

View File

@@ -1,109 +0,0 @@
import { StyleSheet, Image, Platform } from 'react-native';
import { Collapsible } from '@/components/Collapsible';
import { ExternalLink } from '@/components/ExternalLink';
import ParallaxScrollView from '@/components/ParallaxScrollView';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
import { IconSymbol } from '@/components/ui/IconSymbol';
export default function TabTwoScreen() {
return (
<ParallaxScrollView
headerBackgroundColor={{ light: '#D0D0D0', dark: '#353636' }}
headerImage={
<IconSymbol
size={310}
color="#808080"
name="chevron.left.forwardslash.chevron.right"
style={styles.headerImage}
/>
}>
<ThemedView style={styles.titleContainer}>
<ThemedText type="title">Explore</ThemedText>
</ThemedView>
<ThemedText>This app includes example code to help you get started.</ThemedText>
<Collapsible title="File-based routing">
<ThemedText>
This app has two screens:{' '}
<ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> and{' '}
<ThemedText type="defaultSemiBold">app/(tabs)/explore.tsx</ThemedText>
</ThemedText>
<ThemedText>
The layout file in <ThemedText type="defaultSemiBold">app/(tabs)/_layout.tsx</ThemedText>{' '}
sets up the tab navigator.
</ThemedText>
<ExternalLink href="https://docs.expo.dev/router/introduction">
<ThemedText type="link">Learn more</ThemedText>
</ExternalLink>
</Collapsible>
<Collapsible title="Android, iOS, and web support">
<ThemedText>
You can open this project on Android, iOS, and the web. To open the web version, press{' '}
<ThemedText type="defaultSemiBold">w</ThemedText> in the terminal running this project.
</ThemedText>
</Collapsible>
<Collapsible title="Images">
<ThemedText>
For static images, you can use the <ThemedText type="defaultSemiBold">@2x</ThemedText> and{' '}
<ThemedText type="defaultSemiBold">@3x</ThemedText> suffixes to provide files for
different screen densities
</ThemedText>
<Image source={require('@/assets/images/react-logo.png')} style={{ alignSelf: 'center' }} />
<ExternalLink href="https://reactnative.dev/docs/images">
<ThemedText type="link">Learn more</ThemedText>
</ExternalLink>
</Collapsible>
<Collapsible title="Custom fonts">
<ThemedText>
Open <ThemedText type="defaultSemiBold">app/_layout.tsx</ThemedText> to see how to load{' '}
<ThemedText style={{ fontFamily: 'SpaceMono' }}>
custom fonts such as this one.
</ThemedText>
</ThemedText>
<ExternalLink href="https://docs.expo.dev/versions/latest/sdk/font">
<ThemedText type="link">Learn more</ThemedText>
</ExternalLink>
</Collapsible>
<Collapsible title="Light and dark mode components">
<ThemedText>
This template has light and dark mode support. The{' '}
<ThemedText type="defaultSemiBold">useColorScheme()</ThemedText> hook lets you inspect
what the user's current color scheme is, and so you can adjust UI colors accordingly.
</ThemedText>
<ExternalLink href="https://docs.expo.dev/develop/user-interface/color-themes/">
<ThemedText type="link">Learn more</ThemedText>
</ExternalLink>
</Collapsible>
<Collapsible title="Animations">
<ThemedText>
This template includes an example of an animated component. The{' '}
<ThemedText type="defaultSemiBold">components/HelloWave.tsx</ThemedText> component uses
the powerful <ThemedText type="defaultSemiBold">react-native-reanimated</ThemedText>{' '}
library to create a waving hand animation.
</ThemedText>
{Platform.select({
ios: (
<ThemedText>
The <ThemedText type="defaultSemiBold">components/ParallaxScrollView.tsx</ThemedText>{' '}
component provides a parallax effect for the header image.
</ThemedText>
),
})}
</Collapsible>
</ParallaxScrollView>
);
}
const styles = StyleSheet.create({
headerImage: {
color: '#808080',
bottom: -90,
left: -35,
position: 'absolute',
},
titleContainer: {
flexDirection: 'row',
gap: 8,
},
});

View File

@@ -1,74 +0,0 @@
import { Image, StyleSheet, Platform } from 'react-native';
import { HelloWave } from '@/components/HelloWave';
import ParallaxScrollView from '@/components/ParallaxScrollView';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
export default function HomeScreen() {
return (
<ParallaxScrollView
headerBackgroundColor={{ light: '#A1CEDC', dark: '#1D3D47' }}
headerImage={
<Image
source={require('@/assets/images/partial-react-logo.png')}
style={styles.reactLogo}
/>
}>
<ThemedView style={styles.titleContainer}>
<ThemedText type="title">Welcome!</ThemedText>
<HelloWave />
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 1: Try it</ThemedText>
<ThemedText>
Edit <ThemedText type="defaultSemiBold">app/(tabs)/index.tsx</ThemedText> to see changes.
Press{' '}
<ThemedText type="defaultSemiBold">
{Platform.select({
ios: 'cmd + d',
android: 'cmd + m',
web: 'F12'
})}
</ThemedText>{' '}
to open developer tools.
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 2: Explore</ThemedText>
<ThemedText>
Tap the Explore tab to learn more about what's included in this starter app.
</ThemedText>
</ThemedView>
<ThemedView style={styles.stepContainer}>
<ThemedText type="subtitle">Step 3: Get a fresh start</ThemedText>
<ThemedText>
When you're ready, run{' '}
<ThemedText type="defaultSemiBold">npm run reset-project</ThemedText> to get a fresh{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> directory. This will move the current{' '}
<ThemedText type="defaultSemiBold">app</ThemedText> to{' '}
<ThemedText type="defaultSemiBold">app-example</ThemedText>.
</ThemedText>
</ThemedView>
</ParallaxScrollView>
);
}
const styles = StyleSheet.create({
titleContainer: {
flexDirection: 'row',
alignItems: 'center',
gap: 8,
},
stepContainer: {
gap: 8,
marginBottom: 8,
},
reactLogo: {
height: 178,
width: 290,
bottom: 0,
left: 0,
position: 'absolute',
},
});

View File

@@ -1,32 +0,0 @@
import { Link, Stack } from 'expo-router';
import { StyleSheet } from 'react-native';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
export default function NotFoundScreen() {
return (
<>
<Stack.Screen options={{ title: 'Oops!' }} />
<ThemedView style={styles.container}>
<ThemedText type="title">This screen doesn't exist.</ThemedText>
<Link href="/" style={styles.link}>
<ThemedText type="link">Go to home screen!</ThemedText>
</Link>
</ThemedView>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
padding: 20,
},
link: {
marginTop: 15,
paddingVertical: 15,
},
});

View File

@@ -1,4 +1,3 @@
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
@@ -6,13 +5,10 @@ import { StatusBar } from 'expo-status-bar';
import { useEffect } from 'react';
import 'react-native-reanimated';
import { useColorScheme } from '@/hooks/useColorScheme';
// Prevent the splash screen from auto-hiding before asset loading is complete.
SplashScreen.preventAutoHideAsync();
export default function RootLayout() {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
@@ -28,12 +24,14 @@ export default function RootLayout() {
}
return (
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
<Stack.Screen name="index" options={{ headerShown: false }} />
<Stack.Screen name="verification" options={{ headerShown: false }} />
<Stack.Screen name="(application)" options={{ headerShown: false }} />
</Stack>
<StatusBar style="auto" />
</ThemeProvider>
</>
);
}

26
app/index.tsx Normal file
View File

@@ -0,0 +1,26 @@
import { ButtonForm } from "@/components/buttonForm";
import { InputForm } from "@/components/inputForm";
import Styles from "@/constants/Styles";
import { router } from "expo-router";
import { Image, Text, View } from "react-native";
export default function Index() {
return (
<View style={Styles.wrapLogin} >
<View style={{ alignItems: "center", marginVertical: 50 }}>
<Image
source={require("../assets/images/splash-icon.png")}
style={{ width: 130, height: 130 }}
/>
<Text style={[Styles.textSubtitle]}>PERBEKEL DARMASABA</Text>
</View>
<InputForm
type="numeric"
placeholder="XXX-XXX-XXXX"
round
itemLeft={<Text>+62</Text>}
info="Kami akan mengirim kode verifikasi melalui WhatsApp, guna mengonfirmasikan nomor Anda." />
<ButtonForm text="MASUK" onPress={() => { router.push("/verification") }} />
</View>
);
}

54
app/verification.tsx Normal file
View File

@@ -0,0 +1,54 @@
import { ButtonForm } from "@/components/buttonForm";
import Styles from "@/constants/Styles";
import { router } from "expo-router";
import React, { useState } from "react";
import { Image, SafeAreaView, Text, View } from "react-native";
import { CodeField, Cursor, useBlurOnFulfill, useClearByFocusCell, } from 'react-native-confirmation-code-field';
export default function Index() {
const [value, setValue] = useState('');
const ref = useBlurOnFulfill({ value, cellCount: 4 });
const [props, getCellOnLayoutHandler] = useClearByFocusCell({
value,
setValue,
});
return (
<SafeAreaView style={Styles.wrapLogin} >
<View style={{ alignItems: "center", marginVertical: 50 }}>
<Image
source={require("../assets/images/splash-icon.png")}
style={{ width: 130, height: 130 }}
/>
<Text style={[Styles.textSubtitle]}>PERBEKEL DARMASABA</Text>
</View>
<View style={[Styles.mb30]}>
<Text style={[Styles.textDefaultSemiBold]}>Verifikasi Nomor Telepon</Text>
<Text style={[Styles.textMediumNormal]}>Masukkan kode yang kami kirimkan melalui WhatsApp</Text>
<Text style={[Styles.textMediumSemiBold]}>+628980185458</Text>
</View>
<CodeField
ref={ref}
{...props}
value={value}
rootStyle={{ width: '80%', alignSelf: 'center' }}
onChangeText={setValue}
cellCount={4}
keyboardType="number-pad"
renderCell={({ index, symbol, isFocused }) => (
<Text
key={index}
style={[Styles.verificationCell, isFocused && Styles.verificationFocusCell]}
onLayout={getCellOnLayoutHandler(index)}>
{symbol || (isFocused ? <Cursor /> : null)}
</Text>
)}
/>
<ButtonForm text="SUBMIT" onPress={() => { router.push("/home") }} />
<Text style={[Styles.textInformation, Styles.cDefault, { textAlign: 'center' }]}>
Tidak Menerima kode verifikasi? Kirim Ulang
</Text>
</SafeAreaView>
);
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 37 KiB

BIN
assets/images/user.jpeg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 128 KiB

BIN
bun.lockb

Binary file not shown.

View File

@@ -1,45 +0,0 @@
import { PropsWithChildren, useState } from 'react';
import { StyleSheet, TouchableOpacity } from 'react-native';
import { ThemedText } from '@/components/ThemedText';
import { ThemedView } from '@/components/ThemedView';
import { IconSymbol } from '@/components/ui/IconSymbol';
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
export function Collapsible({ children, title }: PropsWithChildren & { title: string }) {
const [isOpen, setIsOpen] = useState(false);
const theme = useColorScheme() ?? 'light';
return (
<ThemedView>
<TouchableOpacity
style={styles.heading}
onPress={() => setIsOpen((value) => !value)}
activeOpacity={0.8}>
<IconSymbol
name="chevron.right"
size={18}
weight="medium"
color={theme === 'light' ? Colors.light.icon : Colors.dark.icon}
style={{ transform: [{ rotate: isOpen ? '90deg' : '0deg' }] }}
/>
<ThemedText type="defaultSemiBold">{title}</ThemedText>
</TouchableOpacity>
{isOpen && <ThemedView style={styles.content}>{children}</ThemedView>}
</ThemedView>
);
}
const styles = StyleSheet.create({
heading: {
flexDirection: 'row',
alignItems: 'center',
gap: 6,
},
content: {
marginTop: 6,
marginLeft: 24,
},
});

View File

@@ -1,24 +0,0 @@
import { Link } from 'expo-router';
import { openBrowserAsync } from 'expo-web-browser';
import { type ComponentProps } from 'react';
import { Platform } from 'react-native';
type Props = Omit<ComponentProps<typeof Link>, 'href'> & { href: string };
export function ExternalLink({ href, ...rest }: Props) {
return (
<Link
target="_blank"
{...rest}
href={href}
onPress={async (event) => {
if (Platform.OS !== 'web') {
// Prevent the default behavior of linking to the default browser on native.
event.preventDefault();
// Open the link in an in-app browser.
await openBrowserAsync(href);
}
}}
/>
);
}

View File

@@ -1,18 +0,0 @@
import { BottomTabBarButtonProps } from '@react-navigation/bottom-tabs';
import { PlatformPressable } from '@react-navigation/elements';
import * as Haptics from 'expo-haptics';
export function HapticTab(props: BottomTabBarButtonProps) {
return (
<PlatformPressable
{...props}
onPressIn={(ev) => {
if (process.env.EXPO_OS === 'ios') {
// Add a soft haptic feedback when pressing down on the tabs.
Haptics.impactAsync(Haptics.ImpactFeedbackStyle.Light);
}
props.onPressIn?.(ev);
}}
/>
);
}

View File

@@ -1,40 +0,0 @@
import { useEffect } from 'react';
import { StyleSheet } from 'react-native';
import Animated, {
useSharedValue,
useAnimatedStyle,
withTiming,
withRepeat,
withSequence,
} from 'react-native-reanimated';
import { ThemedText } from '@/components/ThemedText';
export function HelloWave() {
const rotationAnimation = useSharedValue(0);
useEffect(() => {
rotationAnimation.value = withRepeat(
withSequence(withTiming(25, { duration: 150 }), withTiming(0, { duration: 150 })),
4 // Run the animation 4 times
);
}, []);
const animatedStyle = useAnimatedStyle(() => ({
transform: [{ rotate: `${rotationAnimation.value}deg` }],
}));
return (
<Animated.View style={animatedStyle}>
<ThemedText style={styles.text}>👋</ThemedText>
</Animated.View>
);
}
const styles = StyleSheet.create({
text: {
fontSize: 28,
lineHeight: 32,
marginTop: -6,
},
});

View File

@@ -1,82 +0,0 @@
import type { PropsWithChildren, ReactElement } from 'react';
import { StyleSheet } from 'react-native';
import Animated, {
interpolate,
useAnimatedRef,
useAnimatedStyle,
useScrollViewOffset,
} from 'react-native-reanimated';
import { ThemedView } from '@/components/ThemedView';
import { useBottomTabOverflow } from '@/components/ui/TabBarBackground';
import { useColorScheme } from '@/hooks/useColorScheme';
const HEADER_HEIGHT = 250;
type Props = PropsWithChildren<{
headerImage: ReactElement;
headerBackgroundColor: { dark: string; light: string };
}>;
export default function ParallaxScrollView({
children,
headerImage,
headerBackgroundColor,
}: Props) {
const colorScheme = useColorScheme() ?? 'light';
const scrollRef = useAnimatedRef<Animated.ScrollView>();
const scrollOffset = useScrollViewOffset(scrollRef);
const bottom = useBottomTabOverflow();
const headerAnimatedStyle = useAnimatedStyle(() => {
return {
transform: [
{
translateY: interpolate(
scrollOffset.value,
[-HEADER_HEIGHT, 0, HEADER_HEIGHT],
[-HEADER_HEIGHT / 2, 0, HEADER_HEIGHT * 0.75]
),
},
{
scale: interpolate(scrollOffset.value, [-HEADER_HEIGHT, 0, HEADER_HEIGHT], [2, 1, 1]),
},
],
};
});
return (
<ThemedView style={styles.container}>
<Animated.ScrollView
ref={scrollRef}
scrollEventThrottle={16}
scrollIndicatorInsets={{ bottom }}
contentContainerStyle={{ paddingBottom: bottom }}>
<Animated.View
style={[
styles.header,
{ backgroundColor: headerBackgroundColor[colorScheme] },
headerAnimatedStyle,
]}>
{headerImage}
</Animated.View>
<ThemedView style={styles.content}>{children}</ThemedView>
</Animated.ScrollView>
</ThemedView>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
header: {
height: HEADER_HEIGHT,
overflow: 'hidden',
},
content: {
flex: 1,
padding: 32,
gap: 16,
overflow: 'hidden',
},
});

View File

@@ -1,60 +0,0 @@
import { Text, type TextProps, StyleSheet } from 'react-native';
import { useThemeColor } from '@/hooks/useThemeColor';
export type ThemedTextProps = TextProps & {
lightColor?: string;
darkColor?: string;
type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link';
};
export function ThemedText({
style,
lightColor,
darkColor,
type = 'default',
...rest
}: ThemedTextProps) {
const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text');
return (
<Text
style={[
{ color },
type === 'default' ? styles.default : undefined,
type === 'title' ? styles.title : undefined,
type === 'defaultSemiBold' ? styles.defaultSemiBold : undefined,
type === 'subtitle' ? styles.subtitle : undefined,
type === 'link' ? styles.link : undefined,
style,
]}
{...rest}
/>
);
}
const styles = StyleSheet.create({
default: {
fontSize: 16,
lineHeight: 24,
},
defaultSemiBold: {
fontSize: 16,
lineHeight: 24,
fontWeight: '600',
},
title: {
fontSize: 32,
fontWeight: 'bold',
lineHeight: 32,
},
subtitle: {
fontSize: 20,
fontWeight: 'bold',
},
link: {
lineHeight: 30,
fontSize: 16,
color: '#0a7ea4',
},
});

View File

@@ -1,14 +0,0 @@
import { View, type ViewProps } from 'react-native';
import { useThemeColor } from '@/hooks/useThemeColor';
export type ThemedViewProps = ViewProps & {
lightColor?: string;
darkColor?: string;
};
export function ThemedView({ style, lightColor, darkColor, ...otherProps }: ThemedViewProps) {
const backgroundColor = useThemeColor({ light: lightColor, dark: darkColor }, 'background');
return <View style={[{ backgroundColor }, style]} {...otherProps} />;
}

15
components/buttonForm.tsx Normal file
View File

@@ -0,0 +1,15 @@
import Styles from "@/constants/Styles";
import { Text, TouchableOpacity } from "react-native";
type Props = {
text: string;
onPress: () => void;
};
export function ButtonForm({ text, onPress }: Props) {
return (
<TouchableOpacity style={[Styles.btnRound, { marginTop: 30 }]} onPress={onPress}>
<Text style={[Styles.textDefaultSemiBold, Styles.cWhite]}>{text}</Text>
</TouchableOpacity>
);
}

View File

@@ -0,0 +1,17 @@
import Styles from "@/constants/Styles";
import { TouchableWithoutFeedback, View } from "react-native";
type PropsBtnHeader = {
onPress?: () => void;
item: React.ReactNode;
};
export function ButtonHeader({ onPress, item }: PropsBtnHeader) {
return (
<TouchableWithoutFeedback onPress={onPress}>
<View style={[Styles.btnIconHeader]}>
{item}
</View>
</TouchableWithoutFeedback>
)
}

View File

@@ -0,0 +1,32 @@
import Styles from "@/constants/Styles";
import React from "react";
import { Dimensions, Text, View } from "react-native";
import { useSharedValue } from "react-native-reanimated";
import Carousel, { ICarouselInstance } from "react-native-reanimated-carousel";
export default function CaraouselHome() {
const ref = React.useRef<ICarouselInstance>(null);
const width = Dimensions.get("window").width;
const data = [...new Array(6).keys()];
const progress = useSharedValue<number>(0);
return (
<View style={{ marginVertical: 20 }}>
<Carousel
ref={ref}
width={width}
height={width / 2.5}
data={data}
loop={true}
autoPlay={true}
autoPlayInterval={5000}
onProgressChange={progress}
renderItem={({ index }) => (
<View style={Styles.caraoselContent} >
<Text style={{ textAlign: "center", color: "white", fontWeight: 'bold' }}>BANNER DARMASABA</Text>
</View>
)}
/>
</View>
)
}

View File

@@ -0,0 +1,3 @@
export default function FiturHome(){
}

View File

@@ -0,0 +1,18 @@
import { View } from "react-native"
import { router } from "expo-router"
import Feather from '@expo/vector-icons/Feather';
import { ButtonHeader } from "../buttonHeader";
export function HeaderRightHome() {
return (
<View style={{
flexDirection: 'row',
justifyContent: 'space-between',
width: 140,
}}>
<ButtonHeader item={<Feather name="search" size={20} color="white" />} onPress={() => { router.push('/') }} />
<ButtonHeader item={<Feather name="bell" size={20} color="white" />} onPress={() => { router.push('/') }} />
<ButtonHeader item={<Feather name="user" size={20} color="white" />} onPress={() => { router.push('/') }} />
</View>
)
}

68
components/inputForm.tsx Normal file
View File

@@ -0,0 +1,68 @@
import Styles from "@/constants/Styles";
import { View, TextInput, Text } from "react-native";
type Props = {
label?: string;
placeholder?: string;
onChange?: (val: string) => void;
info?: string;
itemLeft?: React.ReactNode;
itemRight?: React.ReactNode;
error?: boolean;
errorText?: string;
required?: boolean;
type: 'default' | 'visible-password' | 'numeric'
round?: boolean
};
export function InputForm({ label, placeholder, onChange, info, error, errorText, required, itemLeft, itemRight, type, round }: Props) {
if (itemLeft != undefined || itemRight != undefined) {
return (
<View style={{ marginBottom: 10 }}>
{
label != undefined && (
<Text style={[{ marginBottom: 5, textTransform: "capitalize" }, error && Styles.cError]}>
{label}
{required && (<Text style={Styles.cError}>*</Text>)}
</Text>
)
}
<View style={[Styles.inputRoundForm, itemRight != undefined ? Styles.inputRoundFormRight : Styles.inputRoundFormLeft, round && Styles.round30]}>
{itemRight != undefined ? itemRight : itemLeft}
<TextInput
placeholder={placeholder}
keyboardType={type}
onChangeText={onChange}
/>
</View>
{error && (<Text style={[Styles.textInformation, Styles.cError]}>{errorText}</Text>)}
{info != undefined && (<Text style={[Styles.textInformation, Styles.cGray]}>{info}</Text>)}
</View>
)
}
return (
<View style={{ marginBottom: 10 }}>
{
label != undefined && (
<Text style={[{ marginBottom: 5, textTransform: "capitalize" }, error && Styles.cError]}>
{label}
{required && (<Text style={Styles.cError}>*</Text>)}
</Text>
)
}
<TextInput
placeholder={placeholder}
keyboardType={type}
style={[Styles.inputRoundForm, error && { borderColor: "red" }, round && Styles.round30]}
onChangeText={onChange}
/>
{error && (<Text style={[Styles.textInformation, Styles.cError]}>{errorText}</Text>)}
{info != undefined && (<Text style={[Styles.textInformation, , Styles.cGray]}>{info}</Text>)}
</View>
)
}

View File

@@ -1,32 +0,0 @@
import { SymbolView, SymbolViewProps, SymbolWeight } from 'expo-symbols';
import { StyleProp, ViewStyle } from 'react-native';
export function IconSymbol({
name,
size = 24,
color,
style,
weight = 'regular',
}: {
name: SymbolViewProps['name'];
size?: number;
color: string;
style?: StyleProp<ViewStyle>;
weight?: SymbolWeight;
}) {
return (
<SymbolView
weight={weight}
tintColor={color}
resizeMode="scaleAspectFit"
name={name}
style={[
{
width: size,
height: size,
},
style,
]}
/>
);
}

View File

@@ -1,43 +0,0 @@
// This file is a fallback for using MaterialIcons on Android and web.
import MaterialIcons from '@expo/vector-icons/MaterialIcons';
import { SymbolWeight } from 'expo-symbols';
import React from 'react';
import { OpaqueColorValue, StyleProp, ViewStyle } from 'react-native';
// Add your SFSymbol to MaterialIcons mappings here.
const MAPPING = {
// See MaterialIcons here: https://icons.expo.fyi
// See SF Symbols in the SF Symbols app on Mac.
'house.fill': 'home',
'paperplane.fill': 'send',
'chevron.left.forwardslash.chevron.right': 'code',
'chevron.right': 'chevron-right',
} as Partial<
Record<
import('expo-symbols').SymbolViewProps['name'],
React.ComponentProps<typeof MaterialIcons>['name']
>
>;
export type IconSymbolName = keyof typeof MAPPING;
/**
* An icon component that uses native SFSymbols on iOS, and MaterialIcons on Android and web. This ensures a consistent look across platforms, and optimal resource usage.
*
* Icon `name`s are based on SFSymbols and require manual mapping to MaterialIcons.
*/
export function IconSymbol({
name,
size = 24,
color,
style,
}: {
name: IconSymbolName;
size?: number;
color: string | OpaqueColorValue;
style?: StyleProp<ViewStyle>;
weight?: SymbolWeight;
}) {
return <MaterialIcons color={color} size={size} name={MAPPING[name]} style={style} />;
}

View File

@@ -1,22 +0,0 @@
import { useBottomTabBarHeight } from '@react-navigation/bottom-tabs';
import { BlurView } from 'expo-blur';
import { StyleSheet } from 'react-native';
import { useSafeAreaInsets } from 'react-native-safe-area-context';
export default function BlurTabBarBackground() {
return (
<BlurView
// System chrome material automatically adapts to the system's theme
// and matches the native tab bar appearance on iOS.
tint="systemChromeMaterial"
intensity={100}
style={StyleSheet.absoluteFill}
/>
);
}
export function useBottomTabOverflow() {
const tabHeight = useBottomTabBarHeight();
const { bottom } = useSafeAreaInsets();
return tabHeight - bottom;
}

View File

@@ -1,6 +0,0 @@
// This is a shim for web and Android where the tab bar is generally opaque.
export default undefined;
export function useBottomTabOverflow() {
return 0;
}

View File

@@ -3,13 +3,13 @@
* There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc.
*/
const tintColorLight = '#0a7ea4';
const tintColorLight = '#19345E';
const tintColorDark = '#fff';
export const Colors = {
light: {
text: '#11181C',
background: '#fff',
background: '#f4f9fd',
tint: tintColorLight,
icon: '#687076',
tabIconDefault: '#687076',

18
constants/Headers.ts Normal file
View File

@@ -0,0 +1,18 @@
const bgColorDefault = '#19345E';
export const Headers = {
shadow: {
headerStyle: {
backgroundColor: bgColorDefault,
},
headerTintColor: '#fff',
headerShadowVisible: true
},
noShadow: {
headerStyle: {
backgroundColor: bgColorDefault,
},
headerTintColor: '#fff',
headerShadowVisible: false
}
}

124
constants/Styles.ts Normal file
View File

@@ -0,0 +1,124 @@
import { StyleSheet } from "react-native";
const Styles = StyleSheet.create({
wrapLogin: {
flex: 1,
justifyContent: "flex-start",
alignItems: "stretch",
padding: 20,
},
textDefault: {
fontSize: 16,
lineHeight: 24,
},
textDefaultSemiBold: {
fontSize: 16,
lineHeight: 24,
fontWeight: '600',
},
textMediumNormal: {
fontSize: 13,
lineHeight: 24,
},
textMediumSemiBold: {
fontSize: 13,
lineHeight: 24,
fontWeight: '600',
},
textTitle: {
fontSize: 32,
fontWeight: 'bold',
lineHeight: 32,
},
textSubtitle: {
fontSize: 20,
fontWeight: 'bold',
},
textLink: {
lineHeight: 30,
fontSize: 16,
color: '#0a7ea4',
},
textInformation: {
fontSize: 12,
fontWeight: 'light',
marginTop: 5,
},
cError: {
color: '#DB1514'
},
cGray: {
color: 'gray'
},
cWhite: {
color: 'white'
},
cDefault: {
color: '#19345E'
},
mb15: {
marginBottom: 15
},
mb30: {
marginBottom: 30
},
round30: {
borderRadius: 30
},
btnIconHeader: {
backgroundColor: '#384288',
padding: 7,
borderRadius: 100,
},
btnRound: {
backgroundColor: '#19345E',
borderWidth: 0,
borderColor: '#19345E',
alignItems: 'center',
borderRadius: 30,
marginTop: 15,
paddingVertical: 10
},
inputRoundForm: {
borderRadius: 10,
borderColor: '#19345E',
borderWidth: 1,
paddingVertical: 10,
paddingHorizontal: 15,
},
inputRoundFormLeft: {
flexDirection: 'row',
alignItems: 'center',
paddingVertical: 0,
},
inputRoundFormRight: {
flexDirection: 'row-reverse',
alignItems: 'center',
paddingVertical: 0,
justifyContent: 'space-between'
},
verificationCell: {
width: 50,
height: 50,
lineHeight: 45,
fontSize: 24,
borderWidth: 1,
borderRadius: 15,
borderColor: 'gray',
textAlign: 'center',
},
verificationFocusCell: {
borderColor: '#19345E',
},
caraoselContent: {
flex: 1,
borderWidth: 1,
justifyContent: "center",
marginHorizontal: 15,
borderRadius: 15,
backgroundColor: '#19345E',
display: 'flex',
}
})
export default Styles;

View File

@@ -1 +0,0 @@
export { useColorScheme } from 'react-native';

View File

@@ -1,21 +0,0 @@
import { useEffect, useState } from 'react';
import { useColorScheme as useRNColorScheme } from 'react-native';
/**
* To support static rendering, this value needs to be re-calculated on the client side for web
*/
export function useColorScheme() {
const [hasHydrated, setHasHydrated] = useState(false);
useEffect(() => {
setHasHydrated(true);
}, []);
const colorScheme = useRNColorScheme();
if (hasHydrated) {
return colorScheme;
}
return 'light';
}

View File

@@ -1,21 +0,0 @@
/**
* Learn more about light and dark modes:
* https://docs.expo.dev/guides/color-schemes/
*/
import { Colors } from '@/constants/Colors';
import { useColorScheme } from '@/hooks/useColorScheme';
export function useThemeColor(
props: { light?: string; dark?: string },
colorName: keyof typeof Colors.light & keyof typeof Colors.dark
) {
const theme = useColorScheme() ?? 'light';
const colorFromProps = props[theme];
if (colorFromProps) {
return colorFromProps;
} else {
return Colors[theme][colorName];
}
}

View File

@@ -33,8 +33,10 @@
"react": "18.3.1",
"react-dom": "18.3.1",
"react-native": "0.76.7",
"react-native-confirmation-code-field": "^7.4.0",
"react-native-gesture-handler": "~2.20.2",
"react-native-reanimated": "~3.16.1",
"react-native-reanimated-carousel": "^4.0.2",
"react-native-safe-area-context": "4.12.0",
"react-native-screens": "~4.4.0",
"react-native-web": "~0.19.13",