import { Form, SubmitButton, FormField, ErrorMessage, } from "../components/forms"; import React, { useState, useEffect, useContext } from "react"; import { StyleSheet, View, Text, TouchableNativeFeedback, ScrollView, } from "react-native"; import Screen from "../components/Screen"; import { dimensions } from "../config/dimensions"; import { MaterialCommunityIcons } from "@expo/vector-icons"; import colors from "../config/colors"; import { KeyboardAwareScrollView } from "react-native-keyboard-aware-scroll-view"; import * as Yup from "yup"; import FormDatePicker from "../components/forms/FormDatePicker"; import moment from "moment"; import { Shadow } from "react-native-shadow-2"; import SearchablePicker from "../components/SearchablePicker"; import { states, statesToCities } from "../assets/cities_states"; import institutions from "../assets/institutions"; import { useFormikContext } from "formik"; import { signup, login, userPersonalData, existUsername, existNickname, } from "../api/auth"; import { AuthContext } from "../auth/context"; import authStorage from "../auth/storage"; import ConfirmationModal from "../components/ConfirmationModal"; import PasswordFormField from "../components/forms/PasswordFormField"; import constants from "../config/constants"; import CheckBox from "../components/forms/CheckBox"; import defaultStyles from "../config/styles"; import PhoneNumberFormField from "../components/forms/PhoneNumberFormField"; const phoneRegex = RegExp( /^\(?[\(]?([0-9]{2})?\)?[)\b]?([0-9]{4,5})[-. ]?([0-9]{4})$/ ); const validationSchema = Yup.object().shape({ name: Yup.string() .required("O nome é obrigatório") .matches(/[a-zA-Z]/, "O nome e só pode conter letras"), number: Yup.string() .matches(phoneRegex, "Número inválido") .required("O número de telefone é obrigatório"), password: Yup.string() .required("A senha é obrigatória") .min(8, "Senha muito curta, minimo 8 caracteres") .matches(/[a-zA-Z]/, "A senha só pode conter letras"), confirmPassword: Yup.string() .required("A senha é obrigatória") .min(8, "Senha muito curta, minimo 8 caracteres") .matches(/[a-zA-Z]/, "A senha só pode conter letras"), state: Yup.string().required("O estado é obrigatório"), city: Yup.string().required("A cidade é obrigatória"), institutionName: Yup.string(), secQuestion: Yup.string().required("Escolha a pergunta de segurança"), secQuestionAns: Yup.string() .required("A resposta da pergunta de segurança é obrigatória") .max(255), consent: Yup.bool().equals([true], "Este campo é obrigatório"), role: Yup.string(), }); function LocalDatePicker({ date, setDate, _moment }) { const formatDate = () => date.format("DD/MM/YYYY"); return ( <View flex={1}> <FormDatePicker onDateChange={(value) => setDate(value)} minimumDate={new Date(moment().subtract(110, "year"))} date={date} > <View style={[styles.dateInput, { flex: 1, paddingRight: 2 }]}> <View style={{ ...defaultStyles.shadow, height: 58, paddingLeft: 12, backgroundColor: colors.white, borderColor: colors.grayBG, borderWidth: 1, padding: 5, borderRadius: 6, flexDirection: "row", alignItems: "center", }} > <Text style={{ color: colors.medium, fontSize: 18, }} > {date != _moment ? formatDate() : "Selecione a data de nascimento"} </Text> </View> </View> </FormDatePicker> </View> ); } function GenderPicker({ name }) { const [items, setItems] = useState([ { value: "F", label: "Feminino" }, { value: "M", label: "Masculino" }, { value: "N", label: "Prefiro não dizer" }, ]); return ( <SearchablePicker name={name} items={items} setItems={setItems} formPlaceholder={"Selecione o seu gênero"} searchPlaceholder={"Busca..."} /> ); } function InstitutionPicker({ name }) { const [items, setItems] = useState([ { value: "E", label: "Escola" }, { value: "D", label: "Defesa civil" }, { value: "N", label: "Não governamental" }, { value: "O", label: "Outra" }, { value: "X", label: "Nenhuma" }, ]); return ( <SearchablePicker name={name} items={items} setItems={setItems} formPlaceholder={"Selecione o tipo da instituição"} searchPlaceholder={"Busca..."} /> ); } function StatePicker({ name }) { const [items, setItems] = useState(states); return ( <SearchablePicker name={name} items={items} setItems={setItems} formPlaceholder={"Selecione o seu estado"} searchPlaceholder={"Busca..."} /> ); } function CityPicker({ name }) { const { values } = useFormikContext(); const state = values["state"]; useEffect(() => { state && setItems(statesToCities[state].cities); }, [state]); const [items, setItems] = useState([]); return ( <SearchablePicker name={name} items={items} setItems={setItems} formPlaceholder={"Selecione a sua cidade"} nothingToShow={ state ? "Não encontramos nada com esse termo" : "Selecione o Estado primeiro" } searchPlaceholder={"Busca..."} /> ); } function InstitutionNamePicker({ name }) { const { values } = useFormikContext(); const state = values["state"]; const instType = values["institution"]; const [items, setItems] = useState([]); useEffect(() => { try { if (state && instType) { const insts = institutions[state] && institutions[state][instType]; console.log(insts); insts ? setItems(insts) : setItems([]); } } catch (e) { console.log(e); } }, [state, instType]); return ( <SearchablePicker name={name} items={items} setItems={setItems} formPlaceholder={"Selecione o nome da instituição"} nothingToShow={ institutions?.state?.instType ? "Não encontramos nada com esse termo" : state && instType ? `Nenhuma instituição do tipo ${constants.institutionMap[instType]} no ${constants.statesMap[state]}` : "Selecione o Estado e o tipo da instituição primeiro" } searchPlaceholder={"Busca..."} /> ); } function SecQuestionPicker({ name }) { const [items, setItems] = useState([ { value: "Qual a sua cor predileta?", label: "Qual a sua cor predileta?" }, { value: "Qual é seu livro predileto?", label: "Qual é seu livro predileto?", }, { value: "Qual o nome da rua em que você cresceu?", label: "Qual o nome da rua em que você cresceu?", }, { value: "Qual o nome do seu bicho de estimação predileto?", label: "Qual o nome do seu bicho de estimação predileto?", }, { value: "Qual a sua comida predileta?", label: "Qual a sua comida predileta?", }, { value: "Qual é o seu país preferido?", label: "Qual é o seu país preferido?", }, { value: "Qual é a sua marca de carro predileto?", label: "Qual é a sua marca de carro predileto?", }, ]); return ( <SearchablePicker name={name} items={items} setItems={setItems} formPlaceholder={"Selecione a pergunta de segurança"} searchPlaceholder={"Busca..."} marginLeft={2} /> ); } function RolePicker({ name }) { const [items, setItems] = useState([ { value: "ROLE_INSTITUTION", label: "Responsável" }, { value: "ROLE_CLIENT", label: "Não responsável" }, ]); return ( <SearchablePicker name={name} items={items} setItems={setItems} formPlaceholder={"Selecione o vínculo institucional"} searchPlaceholder={"Busca..."} /> ); } function MaterialCommunityIconsCustom({ name, color = colors.primary, size = 25, }) { return ( <View justifyContent={"center"} height={48}> <MaterialCommunityIcons name={name} size={size} color={color} /> </View> ); } export default function RegisterScreen(props) { const authContext = useContext(AuthContext); const _moment = moment(); const [date, setDate] = useState(_moment); const [scroll, setScroll] = useState(); const [showLog, setShowLog] = useState({ show: false, message: "" }); const comparePassword = (password, confirmPassword) => { return password !== confirmPassword; }; const automaticLogin = async (form) => { const result = await login(form.number, form.password); if (!result.ok) return; authStorage.setToken(result.data); const user = await userPersonalData(); user.ok && authContext.setUser(user.data); }; const handleSubmit = async (form) => { console.log(form); const formDate = date.format("DD/MM/yyyy") === moment().format("DD/MM/yyyy") ? "" : date; const result = await signup({ ...form, dateofborn: formDate }); // console.log(JSON.stringify(result)) switch (result.status) { case 200: automaticLogin(form); break; case 422: setShowLog({ show: true, message: "Campo obrigatório não informado", }); break; default: setShowLog({ show: true, message: "Um erro inesperado ocorreu. Tente novamente mais tarde", }); } }; const fieldsAreNotInUse = async (form, actions) => { var inUse = false; const ru = await existUsername(form.number); if (ru.data) { actions.setFieldError("number", "Este número de telefone já está em uso"); inUse = true; } const rn = await existNickname(form.name); if (rn.data) { actions.setFieldError("name", "Este apelido de usuário já está em uso"); inUse = true; } if (!ru.ok || !rn.ok) setShowLog({ show: true, message: "Um erro inesperado ocorreu. Tente novamente mais tarde", }); inUse && setShowLog({ show: true, message: "Apelido de usuário ou telefone em uso", }); return !inUse; }; return ( <Screen style={styles.containter}> <ConfirmationModal show={showLog.show} description={showLog.message} confirmationLabel="OK" onConfirm={() => setShowLog({ ...showLog, show: false })} /> <Form initialValues={{ name: "", number: "", password: "", confirmPassword: "", institutionName: "", gender: "", state: "", city: "", institution: "", secQuestion: "", secQuestionAns: "", role: "", consent: false, }} onSubmit={(form, actions) => { console.log(form); const psw_match = comparePassword( form.password, form.confirmPassword ); if (psw_match) { actions.setFieldError( "confirmPassword", "As senhas não correspondem" ); scroll.scrollTo({ x: 0, y: 0, animated: true }); } else { fieldsAreNotInUse(form, actions).then((isNotUsed) => { if (isNotUsed) { handleSubmit(form); } else { console.log("scrolll"); scroll.scrollTo({ x: 0, y: 0, animated: true }); } }); } }} validationSchema={validationSchema} > <View style={{ flexDirection: "column", justifyContent: "center", flex: 1 }} > <ScrollView scrollToOverflowEnabled={true} ref={(ref) => { setScroll(ref); }} scrollEnabled={true} > <Text style={{ color: colors.primary, fontSize: 20, fontWeight: "bold", alignSelf: "center", marginVertical: 24, }} > Cadastro do usuário </Text> <Text style={styles.labelStyle}>Apelido de usuário*</Text> <View style={styles.iconField}> <MaterialCommunityIconsCustom name="account" /> <FormField paddingRight={2} flex={1} maxLength={40} name="name" placeholder="Digite o apelido de usuário" /> </View> <Text style={styles.labelStyle}>Número do telefone:*</Text> <View style={styles.iconField}> <MaterialCommunityIconsCustom name="phone" /> <PhoneNumberFormField flex={1} name="number" paddingRight={2} maxLength={11} placeholder="(DD) XXXXX-XXXX" /> </View> <Text style={styles.labelStyle}>Senha:*</Text> <View style={styles.iconField}> <MaterialCommunityIconsCustom name="lock" /> <PasswordFormField flex={1} maxLength={20} name="password" placeholder="Digite a senha" paddingRight={2} /> </View> <Text style={styles.labelStyle}>Confirmar senha:*</Text> <View style={styles.iconField}> <MaterialCommunityIconsCustom name="lock" /> <PasswordFormField flex={1} maxLength={20} name="confirmPassword" placeholder="Repita a senha" paddingRight={2} /> </View> <Text style={styles.labelStyle}>Data de nascimento:</Text> <View style={styles.iconField}> <MaterialCommunityIconsCustom name="calendar-today" /> <LocalDatePicker date={date} setDate={setDate} _moment={_moment} /> </View> <Text style={styles.labelStyle}>Gênero:</Text> <View style={[styles.iconField]}> <MaterialCommunityIconsCustom name="account" /> <GenderPicker name="gender" /> </View> <Text style={styles.labelStyle}>Estado*:</Text> <View style={[styles.iconField]}> <MaterialCommunityIconsCustom name="map-marker" /> <StatePicker name="state" /> </View> <Text style={styles.labelStyle}>Cidade*:</Text> <View style={[styles.iconField]}> <MaterialCommunityIconsCustom name="map-marker" /> <CityPicker name={"city"} /> </View> <Text style={styles.labelStyle}>Tipo de instituição:</Text> <View style={[styles.iconField]}> <MaterialCommunityIconsCustom name="bank" /> <InstitutionPicker name="institution" /> </View> <Text style={styles.labelStyle}>Nome da instituição</Text> <View style={styles.iconField}> <MaterialCommunityIconsCustom name="bank" /> <InstitutionNamePicker name="institutionName" /> </View> <Text style={styles.labelStyle}>Vínculo institucional:</Text> <View style={[styles.iconField]}> <MaterialCommunityIconsCustom name="bank" /> <RolePicker name="role" /> </View> <Text style={styles.labelStyle}>Pergunta de segurança*:</Text> <View style={{ flexDirection: "row", marginTop: 12, marginBottom: 24, }} > <SecQuestionPicker name="secQuestion" /> </View> <Text style={styles.labelStyle}>Resposta*:</Text> <View style={styles.iconField}> <FormField flex={1} maxLength={255} name="secQuestionAns" placeholder="Digite a resposta à pergunta" paddingRight={2} paddingLeft={2} /> </View> <Text style={styles.labelStyle}>Termos de uso*</Text> <View flexDirection={"column"} alignItems={"flex-start"} marginBottom={24} marginTop={12} > <CheckBox name={"consent"} navigate={() => props.navigation.navigate("UserAgreement")} /> </View> <SubmitButton flex={1} title="cadastrar" backgroundColor={colors.primary} /> <TouchableNativeFeedback onPress={() => { props.navigation.goBack(); }} > <View style={{ flexDirection: "row", alignSelf: "center", paddingBottom: 34, marginTop: 6, }} > <Text>Já tem uma conta? </Text> <Text style={{ color: colors.lightBlue }}>Faça Login</Text> </View> </TouchableNativeFeedback> </ScrollView> </View> </Form> </Screen> ); } const styles = StyleSheet.create({ containter: { flex: 1, justifyContent: "center", textAlign: "center", paddingHorizontal: 10, }, labelStyle: { fontSize: dimensions.text.secondary, fontWeight: "bold", textAlign: "left", color: colors.secondary, }, iconField: { alignItems: "center", width: "100%", flex: 1, flexDirection: "row", marginTop: 12, marginBottom: 24, }, dateInput: { paddingLeft: 16, }, });