diff --git a/App.web.tsx b/App.web.tsx index 0edc6f34..9fd96927 100644 --- a/App.web.tsx +++ b/App.web.tsx @@ -11,7 +11,7 @@ import { startMockedServer } from '@baca/services' import 'expo-router/entry' // FIXME: moking not working on mobile app - follow this discussion https://github.com/mswjs/msw/issues/2026 -const ENABLE_MOCKED_SERVER = true +const ENABLE_MOCKED_SERVER = false if (ENABLE_MOCKED_SERVER) { startMockedServer() diff --git a/app/(app)/(not-authorized)/forgot-password.tsx b/app/(app)/(not-authorized)/forgot-password.tsx new file mode 100644 index 00000000..caedadac --- /dev/null +++ b/app/(app)/(not-authorized)/forgot-password.tsx @@ -0,0 +1,3 @@ +import { ForgotPasswordScreen } from '@baca/screens' + +export default ForgotPasswordScreen diff --git a/app/(app)/(not-authorized)/reset-password-complete.tsx b/app/(app)/(not-authorized)/reset-password-complete.tsx new file mode 100644 index 00000000..6714dff0 --- /dev/null +++ b/app/(app)/(not-authorized)/reset-password-complete.tsx @@ -0,0 +1,3 @@ +import { ResetPasswordCompleteScreen } from '@baca/screens' + +export default ResetPasswordCompleteScreen diff --git a/app/(app)/(not-authorized)/reset-password-link-sent.tsx b/app/(app)/(not-authorized)/reset-password-link-sent.tsx new file mode 100644 index 00000000..dcfec1d5 --- /dev/null +++ b/app/(app)/(not-authorized)/reset-password-link-sent.tsx @@ -0,0 +1,2 @@ +import { ResetPasswordLinkSentScreen } from '@baca/screens' +export default ResetPasswordLinkSentScreen diff --git a/app/(app)/(not-authorized)/reset-password.tsx b/app/(app)/(not-authorized)/reset-password.tsx new file mode 100644 index 00000000..cba7da09 --- /dev/null +++ b/app/(app)/(not-authorized)/reset-password.tsx @@ -0,0 +1,3 @@ +import { ResetPasswordScreen } from '@baca/screens' + +export default ResetPasswordScreen diff --git a/assets/logo/logo-full-dark.png b/assets/logo/logo-full-dark.png deleted file mode 100644 index a14668ad..00000000 Binary files a/assets/logo/logo-full-dark.png and /dev/null differ diff --git a/assets/logo/logo-full-light.png b/assets/logo/logo-full-light.png deleted file mode 100644 index 5f767a5a..00000000 Binary files a/assets/logo/logo-full-light.png and /dev/null differ diff --git a/src/api/axios/custom-instance.ts b/src/api/axios/custom-instance.ts index 4a8ced25..e289f077 100644 --- a/src/api/axios/custom-instance.ts +++ b/src/api/axios/custom-instance.ts @@ -8,13 +8,26 @@ import qs from 'qs' import { injectTokenToRequest } from './interceptors' -export type ApiError = { - error?: string +type ApiErrorType = { + error: string + message: string + statusCode: number + + errors: never +} + +type FormErrorType = { errors?: { [key: string]: string[] } + statusCode: number + + error: never + message: never } +export type ApiError = ApiErrorType | FormErrorType + export const baseURL = ENV.API_URL export const AXIOS_INSTANCE = Axios.create({ @@ -35,16 +48,24 @@ AXIOS_INSTANCE.interceptors.response.use( return response }, async (error: AxiosError) => { - const errorMessage = error?.response?.data?.error + // handle FormErrorType const formErrors = error?.response?.data?.errors if (formErrors) { throw formErrors } + // handle ApiErrorType + const errorMessage = error?.response?.data?.message + + if (error.response?.status === 429) { + showErrorToast({ description: i18n.t('errors.to_may_requests') }) + return + } + // TODO: we should handle certain error type if (errorMessage) { - showErrorToast({ title: 'ERROR', description: i18n.t('errors.something_went_wrong') }) + showErrorToast({ description: errorMessage }) //CONFIG: Add errors in getApiError const api_error = getApiError(errorMessage) diff --git a/src/components/CompanyLogo.tsx b/src/components/CompanyLogo.tsx new file mode 100644 index 00000000..831725cf --- /dev/null +++ b/src/components/CompanyLogo.tsx @@ -0,0 +1,37 @@ +import { darkBinarLogo, darkLogoSygnet, lightBinarLogo, lightLogoSygnet } from '@baca/constants' +import { ColorSchemeName, useColorScheme } from '@baca/contexts' +import { Image, ImageProps, ImageStyle } from 'react-native' + +type LogoTypes = 'binarSygnet' | 'binar' + +const LOGO: { + [key in ColorSchemeName]: { [key in LogoTypes]: ImageProps['source'] } +} = { + light: { binarSygnet: lightLogoSygnet, binar: lightBinarLogo }, + dark: { binarSygnet: darkLogoSygnet, binar: darkBinarLogo }, +} + +export const CompanyLogo = ({ + height = 100, + type = 'binar', + width = '100%', + style, +}: { + height?: ImageStyle['height'] + type?: LogoTypes + width?: ImageStyle['width'] + style?: ImageStyle +}) => { + const { colorScheme } = useColorScheme() + + const source = LOGO[colorScheme][type] + + return ( + + ) +} diff --git a/src/components/FeaturedIcon.tsx b/src/components/FeaturedIcon.tsx new file mode 100644 index 00000000..22005967 --- /dev/null +++ b/src/components/FeaturedIcon.tsx @@ -0,0 +1,35 @@ +import { Box, Icon } from '@baca/design-system' +import { IconNames } from '@baca/types/icon' + +type FeatureIconSize = 'sm' | 'md' | 'lg' | 'xl' + +const featuredIconSizeVariants: { + [key in FeatureIconSize]: { iconSize: number; borderRadius: number; padding: number } +} = { + // FIXME: add values according to design theme + sm: { iconSize: 16, borderRadius: 6, padding: 2.5 }, + md: { iconSize: 20, borderRadius: 8, padding: 2.5 }, + lg: { iconSize: 24, borderRadius: 10, padding: 3 }, + xl: { iconSize: 28, borderRadius: 12, padding: 14 }, +} + +// FIXME: Add different variants for featuredIcon according to design https://www.figma.com/file/Zk5v95NgasXz7o84UABvH3/%E2%9D%96-BACA---Untitled-UI-(v4.0---plik-madka)?type=design&node-id=1102-5338&mode=design&t=sc8jQ2o3oHFoeslg-4 +export const FeaturedIcon = ({ + iconName, + size = 'md', +}: { + iconName: IconNames + size?: FeatureIconSize +}) => { + const icon = featuredIconSizeVariants[size] + return ( + + + + ) +} diff --git a/src/components/KeyboardAwareScrollView.tsx b/src/components/KeyboardAwareScrollView.tsx index e1e0eb9d..c137f118 100644 --- a/src/components/KeyboardAwareScrollView.tsx +++ b/src/components/KeyboardAwareScrollView.tsx @@ -3,45 +3,49 @@ import { forwardRef, useMemo } from 'react' import { Platform, StyleProp, ViewStyle } from 'react-native' import { KeyboardAwareScrollView as KeyboardAwareScroll, - KeyboardAwareScrollViewProps, + KeyboardAwareScrollViewProps as KeyboardAwareScrollProps, } from 'react-native-keyboard-aware-scroll-view' const keyboardDismissMode = Platform.OS === 'android' ? 'on-drag' : 'interactive' -type Props = Omit & { +export type KeyboardAwareScrollViewProps = Omit< + KeyboardAwareScrollProps, + 'contentContainerStyle' +> & { contentContainerStyle?: Omit, 'false'> } -export const KeyboardAwareScrollView = forwardRef( - ({ children, contentContainerStyle = {}, ...rest }, ref) => { - const { navigationTheme } = useNavigationTheme() +export const KeyboardAwareScrollView = forwardRef< + KeyboardAwareScroll, + KeyboardAwareScrollViewProps +>(({ children, contentContainerStyle = {}, ...rest }, ref) => { + const { navigationTheme } = useNavigationTheme() - const scrollViewContentContainerStyle = useMemo( - () => [ - { - backgroundColor: navigationTheme.colors.background, - flexGrow: 1, - }, - contentContainerStyle, - ], - [contentContainerStyle, navigationTheme.colors.background] - ) + const scrollViewContentContainerStyle = useMemo( + () => [ + { + backgroundColor: navigationTheme.colors.background, + flexGrow: 1, + }, + contentContainerStyle, + ], + [contentContainerStyle, navigationTheme.colors.background] + ) - return ( - - {children} - - ) - } -) + return ( + + {children} + + ) +}) diff --git a/src/components/LanguagePicker.tsx b/src/components/LanguagePicker.tsx index 984d0f17..cf98c6e9 100644 --- a/src/components/LanguagePicker.tsx +++ b/src/components/LanguagePicker.tsx @@ -1,6 +1,7 @@ +import { theme } from '@baca/design-system' import { Icon, Row, Text } from '@baca/design-system/components' import { Touchable, TouchableProps } from '@baca/design-system/components/Touchables/Touchable' -import { useCallback, useTranslation, useTheme } from '@baca/hooks' +import { useCallback, useTranslation } from '@baca/hooks' import { StyleSheet } from 'react-native' import Animated, { useAnimatedStyle, @@ -9,12 +10,18 @@ import Animated, { withTiming, } from 'react-native-reanimated' -import { Menu } from './organisms/Menu' +import { Menu, MenuProps } from './organisms/Menu' import languages from '../../assets/languages.json' -export const LanguagePicker: React.FC = () => { - const { size } = useTheme() +type LanguagePickerProps = { + isWeb?: boolean + pickerPlacement?: MenuProps['placement'] +} +export const LanguagePicker: React.FC = ({ + isWeb = false, + pickerPlacement, +}) => { const { i18n } = useTranslation() const language = i18n?.language?.slice?.(0, 2).toUpperCase() as keyof typeof languages const isOpen = useSharedValue(false) @@ -24,10 +31,6 @@ export const LanguagePicker: React.FC = () => { transform: [{ rotateZ: `${rotateZ.value}deg` }], })) - const styles = StyleSheet.create({ - icon: { height: size['8'], justifyContent: 'center' }, - }) - const iconColor = 'text.brand.primary' const renderTrigger = useCallback( @@ -43,7 +46,9 @@ export const LanguagePicker: React.FC = () => { return ( - {languages?.[language]?.emoji} + + {languages?.[language]?.[isWeb ? 'language' : 'emoji']} + @@ -51,7 +56,7 @@ export const LanguagePicker: React.FC = () => { ) }, - [animatedIconStyle, isOpen, language, styles.icon, iconColor] + [isOpen, language, isWeb, animatedIconStyle] ) const handleItemPress = useCallback( @@ -62,7 +67,7 @@ export const LanguagePicker: React.FC = () => { ) return ( - + {Object.entries(languages).map(([key, languageData]) => ( { ) } + +const styles = StyleSheet.create({ + icon: { height: theme.dark.size['8'], justifyContent: 'center' }, +}) diff --git a/src/components/Version.tsx b/src/components/Version.tsx index 7330a418..38972d5a 100644 --- a/src/components/Version.tsx +++ b/src/components/Version.tsx @@ -11,7 +11,7 @@ const appVersion = Application?.nativeApplicationVersion ?? Constants?.expoConfi export const Version = ({ onPress }: { onPress: () => void }) => { const version = `${appName}: ${appVersion} (${Application?.nativeBuildVersion ?? '-'}) ${ - Updates.updateId ? 'update: ' + Updates.updateId : '' + Updates.updateId ? '\nupdate: ' + Updates.updateId : '' }` const handleShortPress = useCallback(async () => { @@ -20,7 +20,9 @@ export const Version = ({ onPress }: { onPress: () => void }) => { return ( - {version} + + {version} + ) diff --git a/src/components/index.ts b/src/components/index.ts index f33b3c77..129263d8 100644 --- a/src/components/index.ts +++ b/src/components/index.ts @@ -1,8 +1,12 @@ export * from './molecules' export * from './organisms' +export * from './wrappers' export * from './AppLoading' +export * from './CompanyLogo' +export * from './FeaturedIcon' export * from './KeyboardAwareScrollView' +export * from './LandingHeader' export * from './LanguagePicker' export * from './Modal' export * from './StatusBar' diff --git a/src/components/molecules/Field/Checkbox.tsx b/src/components/molecules/Field/Checkbox.tsx index 9401d07a..c8585be8 100644 --- a/src/components/molecules/Field/Checkbox.tsx +++ b/src/components/molecules/Field/Checkbox.tsx @@ -52,7 +52,7 @@ export const Checkbox = ({ }, [checkboxes, value, props, onChange]) return ( - + {checkboxes ? ( renderCheckboxes diff --git a/src/components/molecules/Field/Input.tsx b/src/components/molecules/Field/Input.tsx index 071f54d8..648199ed 100644 --- a/src/components/molecules/Field/Input.tsx +++ b/src/components/molecules/Field/Input.tsx @@ -88,7 +88,7 @@ export const Input = forwardRef, FieldInputProps>( ) return ( - + JSX.Element onOpen?: () => void onClose?: () => void @@ -47,6 +48,7 @@ const Menu = memo( placement = 'bottomLeft', ...props }) => { + const { colorScheme } = useColorScheme() const { shadows } = useTheme() const _modalRef = useRef(null) const _triggerContainer = useRef(null) @@ -120,12 +122,19 @@ const Menu = memo( visible={isOpen} onRequestClose={handleClose} > - + {triggerPosition && ( > = ({ + children, + edges, + headerShown = false, + keyboardAwareProps = {}, +}) => { + useScreenOptions({ headerShown }) + return ( + + + {children} + + + ) +} + +const styles = StyleSheet.create({ + contentContainerStyle: { + alignSelf: 'center', + flexGrow: 1, + maxWidth: 360 + 2 * 32, // contentWidth + 2 * paddingHorizontal + paddingHorizontal: 32, + width: '100%', + }, + safeAreaContainer: { flex: 1 }, +}) diff --git a/src/components/wrappers/index.ts b/src/components/wrappers/index.ts new file mode 100644 index 00000000..719fcfb6 --- /dev/null +++ b/src/components/wrappers/index.ts @@ -0,0 +1 @@ +export * from './FormWrapper' diff --git a/src/constants/environments.ts b/src/constants/environments.ts index 9e44fbfc..12d2ad22 100644 --- a/src/constants/environments.ts +++ b/src/constants/environments.ts @@ -1,5 +1,8 @@ import Constants, { AppOwnership } from 'expo-constants' +import { Platform } from 'react-native' export const isExpoGo = Constants.appOwnership === AppOwnership.Expo export const isDevelopment = __DEV__ || process.env.NODE_ENV === 'development' export const isProduction = !isDevelopment || process.env.NODE_ENV === 'production' + +export const isWeb = Platform.OS === 'web' diff --git a/src/constants/images.ts b/src/constants/images.ts index f4320fc4..055dda66 100644 --- a/src/constants/images.ts +++ b/src/constants/images.ts @@ -5,8 +5,5 @@ export const lightLogo = require('@baca/assets/logo/logo-light.png') export const darkBinarLogo = require('@baca/assets/logo/logo-dark.png') export const lightBinarLogo = require('@baca/assets/logo/logo-light.png') -export const darkLogoFull = require('@baca/assets/logo/logo-full-dark.png') -export const lightLogoFull = require('@baca/assets/logo/logo-full-light.png') - export const darkLogoSygnet = require('@baca/assets/logo/logo-sygnet-dark.png') export const lightLogoSygnet = require('@baca/assets/logo/logo-sygnet-light.png') diff --git a/src/constants/regex.ts b/src/constants/regex.ts index d89c9c67..f703ae7a 100644 --- a/src/constants/regex.ts +++ b/src/constants/regex.ts @@ -1,5 +1,5 @@ export const REGEX = { EMAIL: /^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$/g, // PASSWORD - minimum: at least 8 chars, 1 small letter, 1 big letter, 1 special char, 1 number - REGISTRATION_PASSWORD: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/g, + REGISTRATION_PASSWORD: /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@#$%^&*!])[A-Za-z\d@#$%^&*!]{8,}$/, } as const diff --git a/src/design-system/components/BoxWithShadow.tsx b/src/design-system/components/BoxWithShadow.tsx index 2cfe4f9f..2da06cc2 100644 --- a/src/design-system/components/BoxWithShadow.tsx +++ b/src/design-system/components/BoxWithShadow.tsx @@ -18,7 +18,7 @@ export const BoxWithShadow: FC> = ({ ...rest }) => { const shadowProps = useMemo( - () => (isInvalid ? errorShadow : isFocused ? focusShadow : {}), + () => (isInvalid && isFocused ? errorShadow : isFocused ? focusShadow : {}), [isInvalid, isFocused] ) diff --git a/src/design-system/components/Button/Button.tsx b/src/design-system/components/Button/Button.tsx index e9af61ef..c7bb0518 100644 --- a/src/design-system/components/Button/Button.tsx +++ b/src/design-system/components/Button/Button.tsx @@ -1,4 +1,4 @@ -import { useColorScheme } from '@baca/contexts' +import { useTheme } from '@baca/hooks' import { IconNames } from '@baca/types/icon' import { getColorValue } from '@baca/utils' import { @@ -28,7 +28,6 @@ import { buttonSizeVariants, buttonVariants, getButtonShadowStyle, - theme, } from '../../config' import { generateStyledComponent } from '../../utils' import { Icon } from '../Icon' @@ -61,7 +60,6 @@ const styles = StyleSheet.create({ baseText: { fontStyle: 'normal', fontWeight: '400', - letterSpacing: 0, lineHeight: 21, }, }) @@ -84,7 +82,7 @@ const RawButton = memo( }, ref ) => { - const { colorScheme } = useColorScheme() + const { colors } = useTheme() const { hoverProps, isHovered } = useHover() const { hoveredStyle, defaultStyle, disabledStyle } = useMemo( () => buttonVariants[variant], @@ -99,76 +97,66 @@ const RawButton = memo( () => ({ backgroundColor: getColorValue({ color: hoveredStyle.backgroundColor, - colors: colorScheme === 'light' ? theme.light.colors : theme.dark.colors, + colors, }), borderColor: getColorValue({ color: hoveredStyle.borderColor!, - colors: colorScheme === 'light' ? theme.light.colors : theme.dark.colors, + colors, }), borderWidth: hoveredStyle.borderWidth, }), - [ - colorScheme, - hoveredStyle.backgroundColor, - hoveredStyle.borderColor, - hoveredStyle.borderWidth, - ] + [colors, hoveredStyle.backgroundColor, hoveredStyle.borderColor, hoveredStyle.borderWidth] ) const hoverColorStyle = useMemo( () => ({ color: getColorValue({ color: hoveredStyle.color!, - colors: colorScheme === 'light' ? theme.light.colors : theme.dark.colors, + colors, }), }), - [colorScheme, hoveredStyle.color] + [colors, hoveredStyle.color] ) const defaultStyles = useMemo( () => ({ backgroundColor: getColorValue({ color: defaultStyle.backgroundColor, - colors: colorScheme === 'light' ? theme.light.colors : theme.dark.colors, + colors, }), borderColor: getColorValue({ color: defaultStyle.borderColor!, - colors: colorScheme === 'light' ? theme.light.colors : theme.dark.colors, + colors, }), borderWidth: defaultStyle.borderWidth, }), - [ - colorScheme, - defaultStyle.backgroundColor, - defaultStyle.borderColor, - defaultStyle.borderWidth, - ] + [colors, defaultStyle.backgroundColor, defaultStyle.borderColor, defaultStyle.borderWidth] ) const defaultColorStyle = useMemo( () => ({ color: getColorValue({ color: defaultStyle.color!, - colors: colorScheme === 'light' ? theme.light.colors : theme.dark.colors, + colors, }), }), - [colorScheme, defaultStyle.color] + [colors, defaultStyle.color] ) const disabledStyles = useMemo( () => ({ backgroundColor: getColorValue({ color: disabledStyle.backgroundColor, - colors: colorScheme === 'light' ? theme.light.colors : theme.dark.colors, + colors, }), borderColor: getColorValue({ color: disabledStyle.borderColor!, - colors: colorScheme === 'light' ? theme.light.colors : theme.dark.colors, + colors, }), borderWidth: disabledStyle.borderWidth, }), [ - colorScheme, + colors, disabledStyle.backgroundColor, disabledStyle.borderColor, disabledStyle.borderWidth, @@ -179,10 +167,10 @@ const RawButton = memo( () => ({ color: getColorValue({ color: disabledStyle.color!, - colors: colorScheme === 'light' ? theme.light.colors : theme.dark.colors, + colors, }), }), - [colorScheme, disabledStyle.color] + [colors, disabledStyle.color] ) const buttonSizeVariant = buttonSizeVariants[size] @@ -261,10 +249,11 @@ const RawButton = memo( if (title) { return ( {title} @@ -273,10 +262,11 @@ const RawButton = memo( if (typeof children === 'string') { return ( {children} @@ -298,13 +288,17 @@ const RawButton = memo( {...{ ...hoverProps, ref, ...props }} > {loading ? ( - + ) : ( (props: PressableStateCallbackType) => ( {leftIconName && iconElement(props, leftIconName)} {childrenElement(props)} - {rightIconName && iconElement(props, leftIconName)} + {rightIconName && iconElement(props, rightIconName)} ) )} @@ -326,7 +320,7 @@ const Button = generateStyledComponent( ) as ButtonComposition const generateButtonVariant = (variant: ButtonVariant) => - forwardRef((props, ref) => - - {t('sign_in_screen.do_not_have_an_account')} - - - {t('sign_in_screen.sign_up')} - - - - - {/* TODO: Remove this after implementing signing in with backend */} - Correct credentials - - Email: test@example.com{'\n'}Password: 123456 - - - - - ) -} - -const styles = StyleSheet.create({ - logo: { - height: 50, - width: '100%', - }, -}) diff --git a/src/screens/ConfirmEmail.tsx b/src/screens/auth/ConfirmEmail.tsx similarity index 93% rename from src/screens/ConfirmEmail.tsx rename to src/screens/auth/ConfirmEmail.tsx index d2b918fa..52827720 100644 --- a/src/screens/ConfirmEmail.tsx +++ b/src/screens/auth/ConfirmEmail.tsx @@ -9,7 +9,7 @@ const navigateToSignIn = () => router.replace('/sign-in') export const ConfirmEmail = () => { const { t } = useTranslation() - const { code } = useLocalSearchParams<{ code: string }>() + const { hash } = useLocalSearchParams<{ hash: string }>() const { isError, @@ -30,8 +30,8 @@ export const ConfirmEmail = () => { } } } - confirmFn(code) - }, [code, confirmEmailMutation]) + confirmFn(hash) + }, [hash, confirmEmailMutation]) return (
diff --git a/src/screens/auth/ForgotPasswordScreen.tsx b/src/screens/auth/ForgotPasswordScreen.tsx new file mode 100644 index 00000000..29d24311 --- /dev/null +++ b/src/screens/auth/ForgotPasswordScreen.tsx @@ -0,0 +1,67 @@ +import { CompanyLogo, ControlledField, FeaturedIcon, FormWrapper } from '@baca/components' +import { REGEX } from '@baca/constants' +import { Button, Center, Display, Spacer, Text } from '@baca/design-system' +import { useForgotPasswordForm, useTranslation, useEffect } from '@baca/hooks' +import { router, useLocalSearchParams } from 'expo-router' + +export const ForgotPasswordScreen = () => { + const { t } = useTranslation() + + const { email } = useLocalSearchParams<{ email?: string }>() + + const { control, errors, isSubmitting, reset, submit } = useForgotPasswordForm({}) + + useEffect(() => { + if (email) { + reset({ email: decodeURIComponent(email) }) + } + }, [email, reset]) + + return ( + +
+ + + + + + {t('forgot_password_screen.forgot_password')} + + + {t('forgot_password_screen.no_worries')} + + + + + + + +
+
+ ) +} diff --git a/src/screens/auth/ResetPasswordCompleteScreen.tsx b/src/screens/auth/ResetPasswordCompleteScreen.tsx new file mode 100644 index 00000000..ec7baecd --- /dev/null +++ b/src/screens/auth/ResetPasswordCompleteScreen.tsx @@ -0,0 +1,31 @@ +import { CompanyLogo, FeaturedIcon, FormWrapper } from '@baca/components' +import { Button, Center, Display, Spacer, Text } from '@baca/design-system' +import { useTranslation } from '@baca/hooks' +import { router } from 'expo-router' + +const navigateToLogin = () => { + router.replace('/sign-in') +} + +export const ResetPasswordCompleteScreen = () => { + const { t } = useTranslation() + + return ( + +
+ + + + + + {t('reset_password_complete.password_reset')} + + + {t('reset_password_complete.password_successfully_reset')} + + +
+
+ ) +} diff --git a/src/screens/auth/ResetPasswordLinkSentScreen.tsx b/src/screens/auth/ResetPasswordLinkSentScreen.tsx new file mode 100644 index 00000000..e1bf7858 --- /dev/null +++ b/src/screens/auth/ResetPasswordLinkSentScreen.tsx @@ -0,0 +1,65 @@ +import { CompanyLogo, FeaturedIcon, FormWrapper } from '@baca/components' +import { Button, Center, Display, Row, Spacer, Text } from '@baca/design-system' +import { useEffect, useForgotPasswordForm, useTranslation } from '@baca/hooks' +import { showSuccessToast } from '@baca/utils' +import { router, useLocalSearchParams } from 'expo-router' + +const navigateToLogin = () => { + router.navigate('/sign-in') +} + +export const ResetPasswordLinkSentScreen = () => { + const { t } = useTranslation() + + const { email } = useLocalSearchParams<{ email?: string }>() + + const { isSubmitting, reset, submit } = useForgotPasswordForm({ + onSuccess: () => { + showSuccessToast({ description: 'Password link resend' }) + }, + }) + + useEffect(() => { + if (email) { + reset({ email }) + } + }, [email, reset]) + + return ( + +
+ + + + + + {t('reset_password_link_sent_screen.check_email')} + + + {t('reset_password_link_sent_screen.we_sent_link')} + + {email} + + + + + {t('reset_password_link_sent_screen.did_not_receive')} + + + + +
+
+ ) +} diff --git a/src/screens/auth/ResetPasswordScreen.tsx b/src/screens/auth/ResetPasswordScreen.tsx new file mode 100644 index 00000000..978657c2 --- /dev/null +++ b/src/screens/auth/ResetPasswordScreen.tsx @@ -0,0 +1,96 @@ +import { CompanyLogo, ControlledField, FeaturedIcon, FormWrapper } from '@baca/components' +import { REGEX } from '@baca/constants' +import { Button, Center, Display, Spacer, Text } from '@baca/design-system' +import { useEffect, useResetPasswordForm, useTranslation } from '@baca/hooks' +import { router, useLocalSearchParams } from 'expo-router' + +const navigateToLogin = () => { + router.replace('/sign-in') +} + +export const ResetPasswordScreen = () => { + const { t } = useTranslation() + const { hash } = useLocalSearchParams<{ hash: string }>() + + const { control, errors, isSubmitting, reset, submit } = useResetPasswordForm() + + useEffect(() => { + if (hash) { + reset({ hash }) + } + }, [hash, reset]) + + return ( + +
+ + + + + + {t('reset_password_screen.set_new_password')} + + + {t('reset_password_screen.welcome_back')} + + + + + + { + if (formValues.password !== inputValue) { + return t('form.validation.passwords_does_not_match') + } + }, + }, + }} + testID="passwordInput" + type="password" + /> + + + + +
+
+ ) +} diff --git a/src/screens/auth/SignInScreen.tsx b/src/screens/auth/SignInScreen.tsx new file mode 100644 index 00000000..87d7f898 --- /dev/null +++ b/src/screens/auth/SignInScreen.tsx @@ -0,0 +1,121 @@ +import { + CompanyLogo, + ControlledField, + FormWrapper, + LanguagePicker, + Version, +} from '@baca/components' +import { REGEX, isWeb } from '@baca/constants' +import { Box, Button, Center, Display, Row, Spacer, Text } from '@baca/design-system' +import { useCallback, useSignInForm, useTranslation } from '@baca/hooks' +import { useRouter } from 'expo-router' + +export const SignInScreen = (): JSX.Element => { + const { push } = useRouter() + const { t } = useTranslation() + + const { control, errors, submit, getValues, isSubmitting, setFocus } = useSignInForm() + + const navigateToSignUp = useCallback(() => push('/sign-up'), [push]) + const navigateToAppInfo = useCallback(() => push('/application-info'), [push]) + const navigateToForgotPassword = useCallback( + () => push(`/forgot-password?email=${encodeURIComponent(getValues('email'))}`), + [getValues, push] + ) + const focusPasswordInput = useCallback(() => setFocus('password'), [setFocus]) + + return ( + + {isWeb ? ( + + + + + + + ) : null} +
+ {!isWeb ? ( + + + + ) : null} + + + + {t('sign_in_screen.welcome_back')} + + + {t('sign_in_screen.welcome_back_enter_details')} + + + + + + + + {t('sign_in_screen.forgot_password')} + + + + + + {t('sign_in_screen.do_not_have_an_account')} + + + {t('sign_in_screen.sign_up')} + + +
+ + + +
+ ) +} diff --git a/src/screens/SignUpScreen.tsx b/src/screens/auth/SignUpScreen.tsx similarity index 52% rename from src/screens/SignUpScreen.tsx rename to src/screens/auth/SignUpScreen.tsx index dee3915d..412d27c7 100644 --- a/src/screens/SignUpScreen.tsx +++ b/src/screens/auth/SignUpScreen.tsx @@ -1,30 +1,35 @@ -import { ControlledField, KeyboardAwareScrollView } from '@baca/components' -import { Button, Center, Spacer } from '@baca/design-system' -import { useScreenOptions, useSignUpForm, useTranslation } from '@baca/hooks' -import { useCallback, useEffect } from 'react' +import { CompanyLogo, ControlledField, FormWrapper } from '@baca/components' +import { Box, Button, Center, Display, Row, Spacer, Text } from '@baca/design-system' +import { useSignUpForm, useTranslation } from '@baca/hooks' +import { router } from 'expo-router' +import { useCallback } from 'react' +import { Keyboard } from 'react-native' + +const navigateToLogIn = () => { + router.navigate('/sign-in') +} export const SignUpScreen = () => { const { t } = useTranslation() - useScreenOptions({ - title: t('navigation.screen_titles.sign_up'), - }) - const { control, errors, register, isSubmitting, setFocus } = useSignUpForm() - useEffect(() => { - setTimeout(() => { - setFocus('firstName') - }, 500) - }, [setFocus]) - const focusLastNameInput = useCallback(() => setFocus('lastName'), [setFocus]) const focusEmailInput = useCallback(() => setFocus('email'), [setFocus]) const focusPasswordInput = useCallback(() => setFocus('password'), [setFocus]) return ( - -
+ +
+ + + + {t('sign_up_screen.sign_up')} + + + {t('sign_up_screen.start_free_trail')} + + { enterKeyHint="next" isRequired label={t('form.labels.password')} - mb={16} name="password" - onSubmitEditing={register} + onSubmitEditing={Keyboard.dismiss} placeholder={t('form.placeholders.create_password')} rules={{ required: t('form.validation.required'), }} type="password" /> - - - + + + + + - + + + + {t('sign_in_screen.do_not_have_an_account')} + + + {t('sign_up_screen.log_in')} + +
- +
) } diff --git a/src/screens/auth/index.ts b/src/screens/auth/index.ts new file mode 100644 index 00000000..78212110 --- /dev/null +++ b/src/screens/auth/index.ts @@ -0,0 +1,7 @@ +export * from './ConfirmEmail' +export * from './ForgotPasswordScreen' +export * from './ResetPasswordCompleteScreen' +export * from './ResetPasswordLinkSentScreen' +export * from './ResetPasswordScreen' +export * from './SignInScreen' +export * from './SignUpScreen' diff --git a/src/screens/index.ts b/src/screens/index.ts index 12a7fc75..c15d255e 100644 --- a/src/screens/index.ts +++ b/src/screens/index.ts @@ -1,8 +1,9 @@ +export * from './auth' + export * from './ApplicationInfoScreen' export * from './CategoriesScreen' export * from './ColorsScreen' export * from './ComponentsScreen' -export * from './ConfirmEmail' export * from './DataFromBeScreen_EXAMPLE' export * from './DetailsScreen' export * from './ExamplesScreen' @@ -10,7 +11,5 @@ export * from './HomeScreen' export * from './NotFoundScreen' export * from './ProfileScreen' export * from './SettingsScreen' -export * from './SignInScreen' -export * from './SignUpScreen' export * from './TestFormScreen' export * from './TypographyScreen' diff --git a/src/styles/root-layout.module.scss b/src/styles/root-layout.module.scss index c65cb1bc..6046563b 100644 --- a/src/styles/root-layout.module.scss +++ b/src/styles/root-layout.module.scss @@ -15,8 +15,6 @@ $ui-sidebar: 244px; // Header Logo .headerContainer { padding-top: 0px; - min-height: 96px; - height: 96px; } .headerLink { @@ -87,5 +85,5 @@ $ui-sidebar: 244px; } .sideBarHeader { - align-items: center; + align-items: stretch; } diff --git a/src/utils/removeFalsyProperties.ts b/src/utils/removeFalsyProperties.ts index 94f51195..e1031436 100644 --- a/src/utils/removeFalsyProperties.ts +++ b/src/utils/removeFalsyProperties.ts @@ -1,10 +1,10 @@ export const removeFalsyProperties = (obj: O) => { - const filterdObject = Object.entries(obj).reduce>((acc, curr) => { - if (curr[1]) { + const filteredObject = Object.entries(obj).reduce>((acc, curr) => { + if (curr[1] || curr[1] === 0) { acc[curr[0]] = curr[1] } return acc }, {}) - return filterdObject as O + return filteredObject } diff --git a/src/utils/showToast.ts b/src/utils/showToast.ts index 74113dd3..9872acc4 100644 --- a/src/utils/showToast.ts +++ b/src/utils/showToast.ts @@ -24,7 +24,7 @@ const showCustomToast = ({ error: i18n.t('toast.title.error'), info: i18n.t('toast.title.info'), warning: i18n.t('toast.title.warning'), - success: i18n.t('toast.title.warning'), + success: i18n.t('toast.title.success'), }[variant] notify(variant, {