Skip to content

Commit

Permalink
feat: datepicker, sign up, hookform
Browse files Browse the repository at this point in the history
  • Loading branch information
mgramigna committed Dec 11, 2023
1 parent 566465e commit 2c37bad
Show file tree
Hide file tree
Showing 6 changed files with 274 additions and 23 deletions.
8 changes: 7 additions & 1 deletion apps/expo/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
"@expo-google-fonts/open-sans": "^0.2.3",
"@expo/metro-config": "^0.10.7",
"@expo/vector-icons": "^13.0.0",
"@hookform/resolvers": "^3.3.2",
"@react-native-community/datetimepicker": "7.2.0",
"@tanstack/react-query": "^5.8.7",
"@trpc/client": "next",
"@trpc/react-query": "next",
Expand All @@ -39,15 +41,19 @@
"nativewind": "^4.0.13",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hook-form": "^7.49.0",
"react-native": "0.72.7",
"react-native-date-picker": "^4.3.4",
"react-native-gesture-handler": "~2.12.0",
"react-native-modal-datetime-picker": "^17.1.0",
"react-native-reanimated": "~3.3.0",
"react-native-safe-area-context": "4.6.3",
"react-native-screens": "~3.22.1",
"react-native-ui-datepicker": "^1.0.10",
"superjson": "2.2.0",
"tailwind-merge": "^2.1.0",
"ts-pattern": "^5.0.5"
"ts-pattern": "^5.0.5",
"zod": "^3.22.2"
},
"devDependencies": {
"@babel/core": "^7.23.2",
Expand Down
60 changes: 48 additions & 12 deletions apps/expo/src/app/(auth)/sign-in.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React, { useCallback, useState } from 'react';
import React, { useCallback } from 'react';
import { Keyboard, TouchableWithoutFeedback, View } from 'react-native';
import { Link, Redirect } from 'expo-router';
import { Button } from '@/components/atoms/Button';
Expand All @@ -7,12 +7,35 @@ import { TextInput } from '@/components/atoms/TextInput';
import { ScreenView } from '@/components/molecules/ScreenView';
import { useAuth } from '@/context/AuthContext';
import { api } from '@/utils/api';
import { zodResolver } from '@hookform/resolvers/zod';
import { Controller, useForm } from 'react-hook-form';
import { z } from 'zod';

import { type PatientBundle } from '@canvas-challenge/canvas';

const SignInFormSchema = z.object({
email: z.string().email().min(1),
});

type SignInFormType = z.infer<typeof SignInFormSchema>;

const SignIn = () => {
const { signIn, patientId } = useAuth();
const [email, setEmail] = useState('');

const {
control,
formState: { errors, isValid },
handleSubmit,
watch,
} = useForm<SignInFormType>({
defaultValues: {
email: '',
},
resolver: zodResolver(SignInFormSchema),
mode: 'onChange',
});

const email = watch('email');

const { isLoading: patientSearchLoading, refetch } = api.patient.search.useQuery(
{
Expand Down Expand Up @@ -54,18 +77,31 @@ const SignIn = () => {
Log in to start owning your health data
</Text>
<View className="mt-28">
<TextInput
autoCapitalize="none"
textContentType="emailAddress"
keyboardType="email-address"
autoComplete="email"
value={email}
onChangeText={setEmail}
placeholder="Enter your email..."
onSubmitEditing={searchForPatient}
<Text className="mb-2 pl-1 text-xl">Email</Text>
<Controller
control={control}
name="email"
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
hasError={!!errors.email}
autoCapitalize="none"
textContentType="emailAddress"
keyboardType="email-address"
autoComplete="email"
onBlur={onBlur}
onChangeText={onChange}
value={value}
placeholder="Enter your email..."
/>
)}
/>
<View className="mt-10">
<Button isLoading={patientSearchLoading} text="Login" onPress={searchForPatient} />
<Button
disabled={!isValid}
isLoading={patientSearchLoading}
text="Login"
onPress={handleSubmit(searchForPatient)}
/>
</View>
<View className="mt-10">
<Text className="text-xl">
Expand Down
149 changes: 142 additions & 7 deletions apps/expo/src/app/(auth)/sign-up.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,150 @@
import React from 'react';
import { View } from 'react-native';
import React, { useCallback, useState } from 'react';
import { Alert, ScrollView, TouchableOpacity, View } from 'react-native';
import DateTimePickerModal from 'react-native-modal-datetime-picker';
import { Stack } from 'expo-router';
import { Button } from '@/components/atoms/Button';
import { Text } from '@/components/atoms/Text';
import { TextInput } from '@/components/atoms/TextInput';
import { ScreenView } from '@/components/molecules/ScreenView';
import { zodResolver } from '@hookform/resolvers/zod';
import dayjs from 'dayjs';
import { Controller, useForm } from 'react-hook-form';
import { z } from 'zod';

const SignUpFormSchema = z.object({
firstName: z.string().min(1, {
message: 'hello??',
}),
lastName: z.string().min(1),
email: z.string().email().min(1),
dateOfBirth: z.date(),
});

type SignUpFormType = z.infer<typeof SignUpFormSchema>;

const SignUp = () => {
const [datePickerOpen, setDatePickerOpen] = useState(false);
const {
control,
formState: { errors, isValid },
handleSubmit,
watch,
getFieldState,
} = useForm<SignUpFormType>({
defaultValues: {
firstName: '',
lastName: '',
email: '',
dateOfBirth: undefined,
},
resolver: zodResolver(SignUpFormSchema),
mode: 'onChange',
});

const currentBirthDate = watch('dateOfBirth');
const dobState = getFieldState('dateOfBirth');

const onSubmit = useCallback((form: SignUpFormType) => {
Alert.alert(JSON.stringify(form, null, 2));
}, []);

return (
<ScreenView>
<View className="h-full">
<Text>TODO: sign up</Text>
</View>
</ScreenView>
<>
<Stack.Screen />
<ScreenView>
<ScrollView showsVerticalScrollIndicator={false} className="h-full">
<Text className="text-3xl" weight="bold">
Sign Up
</Text>
<View className="mt-8 flex gap-8">
<View>
<Text className="mb-2 pl-1 text-xl">First Name</Text>
<Controller
control={control}
name="firstName"
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
hasError={!!errors.firstName}
placeholder="Enter first name..."
onBlur={onBlur}
onChangeText={onChange}
value={value}
/>
)}
/>
</View>
<View>
<Text className="mb-2 pl-1 text-xl">Last Name</Text>
<Controller
control={control}
name="lastName"
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
hasError={!!errors.lastName}
placeholder="Enter last name..."
onBlur={onBlur}
onChangeText={onChange}
value={value}
/>
)}
/>
</View>
<View>
<Text className="mb-2 pl-1 text-xl">Email</Text>
<Controller
control={control}
name="email"
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
hasError={!!errors.email}
autoCapitalize="none"
textContentType="emailAddress"
keyboardType="email-address"
autoComplete="email"
placeholder="Enter email..."
onBlur={onBlur}
onChangeText={onChange}
value={value}
/>
)}
/>
</View>
<View>
<Text className="mb-2 pl-1 text-xl">Date of Birth</Text>
<TouchableOpacity onPress={() => setDatePickerOpen((curr) => !curr)}>
{!dobState.isDirty ? (
<Text className="bg-coolGray-100 text-coolGray-300 rounded-md p-6">
__ / __ / ____
</Text>
) : (
<Text className="bg-coolGray-100 rounded-md p-6">
{dayjs(currentBirthDate).format('MM/DD/YYYY')}
</Text>
)}
</TouchableOpacity>
<Controller
control={control}
name="dateOfBirth"
render={({ field: { onChange, value } }) => (
<DateTimePickerModal
isVisible={datePickerOpen}
date={value}
mode="date"
onConfirm={(date) => {
onChange(date);
setDatePickerOpen(false);
}}
onCancel={() => setDatePickerOpen(false)}
maximumDate={dayjs().add(-18, 'years').toDate()}
/>
)}
/>
</View>
<Button disabled={!isValid} text="Continue" onPress={handleSubmit(onSubmit)} />
</View>
</ScrollView>
</ScreenView>
</>
);
};

Expand Down
3 changes: 2 additions & 1 deletion apps/expo/src/components/atoms/Button.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ActivityIndicator, TouchableOpacity, type TouchableOpacityProps } from 'react-native';
import { cn } from '@/utils/cn';
import { cva, type VariantProps } from 'class-variance-authority';

import { Text } from './Text';
Expand All @@ -24,7 +25,7 @@ export const Button = ({ variant, text, isLoading, className, ...props }: Button
return (
<TouchableOpacity
{...props}
className={buttonVariants({ variant, className })}
className={cn(buttonVariants({ variant, className }), props.disabled && 'bg-coolGray-400/50')}
disabled={isLoading ?? props.disabled}
>
<Text className="text-coolGray-50 text-center" weight="bold">
Expand Down
14 changes: 12 additions & 2 deletions apps/expo/src/components/atoms/TextInput.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,18 @@ import { cn } from '@/utils/cn';

export interface TextInputProps extends RNTextInputProps {
withClear?: boolean;
hasError?: boolean;
}

export const TextInput = ({ className, ...props }: TextInputProps) => {
return <RNTextInput className={cn('bg-coolGray-100 rounded-md p-6', className)} {...props} />;
export const TextInput = ({ className, hasError, ...props }: TextInputProps) => {
return (
<RNTextInput
className={cn(
'text-coolGray-800 bg-coolGray-100 placeholder-coolGray-300 rounded-md p-6',
className,
hasError && 'border border-red-500',
)}
{...props}
/>
);
};
Loading

0 comments on commit 2c37bad

Please sign in to comment.