diff --git a/package-lock.json b/package-lock.json index fd14586..f944d0e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@expo-google-fonts/alex-brush": "^0.2.3", "@expo-google-fonts/montserrat": "^0.2.3", + "@react-native-community/datetimepicker": "^7.6.2", "@react-native-firebase/analytics": "^19.0.0", "@react-native-firebase/app": "^19.0.0", "@react-native-firebase/auth": "^19.0.0", @@ -25,6 +26,7 @@ "expo-font": "^11.10.3", "expo-status-bar": "~1.11.1", "react": "18.2.0", + "react-hook-form": "^7.51.0", "react-native": "0.73.4", "react-native-modal": "^13.0.1", "react-native-popup-menu": "^0.16.1", @@ -5533,6 +5535,14 @@ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, + "node_modules/@react-native-community/datetimepicker": { + "version": "7.6.2", + "resolved": "https://registry.npmjs.org/@react-native-community/datetimepicker/-/datetimepicker-7.6.2.tgz", + "integrity": "sha512-ogZnvCmNG/lGHhEQypqz/mPymiIWD5jv6uKczg/l/aWgiKs1RDPQH1abh+R0I26aKoXmgamJJPuXKeC5eRWtZg==", + "dependencies": { + "invariant": "^2.2.4" + } + }, "node_modules/@react-native-firebase/analytics": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/@react-native-firebase/analytics/-/analytics-19.0.0.tgz", @@ -12159,6 +12169,21 @@ "react": ">=17.0.0" } }, + "node_modules/react-hook-form": { + "version": "7.51.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.51.0.tgz", + "integrity": "sha512-BggOy5j58RdhdMzzRUHGOYhSz1oeylFAv6jUSG86OvCIvlAvS7KvnRY7yoAf2pfEiPN7BesnR0xx73nEk3qIiw==", + "engines": { + "node": ">=12.22.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18" + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", diff --git a/package.json b/package.json index dcc7429..389afac 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,7 @@ "dependencies": { "@expo-google-fonts/alex-brush": "^0.2.3", "@expo-google-fonts/montserrat": "^0.2.3", + "@react-native-community/datetimepicker": "^7.6.2", "@react-native-firebase/analytics": "^19.0.0", "@react-native-firebase/app": "^19.0.0", "@react-native-firebase/auth": "^19.0.0", @@ -26,6 +27,7 @@ "expo-font": "^11.10.3", "expo-status-bar": "~1.11.1", "react": "18.2.0", + "react-hook-form": "^7.51.0", "react-native": "0.73.4", "react-native-modal": "^13.0.1", "react-native-popup-menu": "^0.16.1", diff --git a/src/components/ClientDebit.js b/src/components/ClientDebit.js index 0880c0f..2d5d12c 100644 --- a/src/components/ClientDebit.js +++ b/src/components/ClientDebit.js @@ -5,6 +5,7 @@ import { theme } from "../../constants"; import { useFocusEffect } from "@react-navigation/native"; import Modal from "../modals/SimpleModal"; import { StatusBar } from "expo-status-bar"; +import { useNavigation } from "@react-navigation/native"; const screenWidth = Dimensions.get('window').width; const ClientDebit = ({ clientInfo }) => { @@ -47,13 +48,15 @@ const ClientDebit = ({ clientInfo }) => { setModalVisible(!modalVisible); }; + const navigation = useNavigation(); + return ( {vBalance} Bs - {}} style={clientDebitStyles.button}> + navigation.navigate("AutomaticPayScreen")} style={clientDebitStyles.button}> Automático { + + const [selectedDate, setSelectedDate] = useState(new Date()); + const [showDatePicker, setShowDatePicker] = useState(false); + + const handleDateChange = (event, selectedDate) => { + const currentDate = selectedDate; + setShowDatePicker(false); + setSelectedDate(currentDate); + console.log('selectedDate', selectedDate); + }; + + return ( + + {title} + ( + setShowDatePicker(true)} + onChangeText={onChange} + value={formatDate(selectedDate, 'dd/MM/yyyy', { awareOfUnicodeTokens: true })} + keyboardType={type === 'numeric' ? 'numeric' : 'default'} + /> + + )} + /> + {showDatePicker && ( + + )} + + ); +}; + +const styles = StyleSheet.create({ + container: { + marginHorizontal: 20, + marginBottom: 10, + width: 145, + }, + label: { + color: 'gray', + fontSize: 18, + marginBottom: 5, + paddingLeft: 10 + }, + input: { + height: 46, + borderColor: 'gray', + paddingHorizontal: 10, + backgroundColor: theme.colors.otherWhite, + borderRadius: 22, + fontSize: 18, + fontWeight: "bold", + textAlign: 'center', + }, + error: { + color: 'red', + fontSize: 12, + marginTop: 1, + paddingLeft: 10 + }, +}); + +export default DateInputField; \ No newline at end of file diff --git a/src/components/DropdownSelector2.js b/src/components/DropdownSelector2.js new file mode 100644 index 0000000..0bcb0e3 --- /dev/null +++ b/src/components/DropdownSelector2.js @@ -0,0 +1,113 @@ +// DropdownSelector.js +import React, { useState, useCallback } from "react"; +import { View, Text } from "react-native"; +import { FontAwesome5 } from "@expo/vector-icons"; +import { Menu, MenuOptions, MenuOption, MenuTrigger } from "react-native-popup-menu"; +import { theme } from '../../constants'; +import { StyleSheet, Dimensions } from 'react-native'; +const screenWidth = Dimensions.get('window').width; + +const DropdownSelector = ({ title, options, selectedOption, onOptionChange, size=200 }) => { + const [menuVisible, setMenuVisible] = useState(false); + + const toggleMenu = () => { + setMenuVisible(prevState => !prevState); + }; + + return ( + + {title} + setMenuVisible(false)}> + + + + + {selectedOption.charAt(0).toUpperCase() + selectedOption.slice(1)} + + + + + + + {options.map((option) => ( + { onOptionChange(option); setMenuVisible(false); }}> + + {option} + {selectedOption === option && ( + + )} + + + ))} + + + + ); +}; + +const styles = StyleSheet.create({ + title: { + color: 'gray', + fontSize: 18, + marginBottom: 5, + paddingLeft: 10 + }, + container: { + marginHorizontal: 20, + borderRadius: 20, + backgroundColor: theme.colors.skyBlue, + padding: 7, + flexDirection: "row", + justifyContent: 'flex-end', + }, + label: { + flexDirection: "row", + alignItems: "center", + justifyContent: 'center', + borderRadius: 20, + flex:1 + }, + optionText: { + fontSize: 16, + fontWeight: "bold", + }, + optionsText: { + color: theme.colors.primary, + fontSize: 16, + fontWeight: "bold", + }, + menuTrigger: { + flexDirection: "row", + alignItems: 'center', + justifyContent: 'space-between', + paddingVertical: 12, + paddingHorizontal: 25, + backgroundColor: theme.colors.tertiary, + borderRadius: 22, + width: "auto", + maxWidth: screenWidth - 40, + alignSelf: "center", + }, + menuTriggerInter: { + flexDirection: "row", + alignItems: 'center', + justifyContent: 'center', + }, + triggerText: { + color: theme.colors.primary, + fontSize: 16, + fontWeight: "bold", + marginRight: 12, + }, + optionsContainer: { + paddingVertical: 15, + marginTop: 55, + marginLeft: 0, + borderRadius: 20, + backgroundColor: theme.colors.tertiary, + }, + optionsWrapper: { + marginLeft: 20, + }, +}); +export default DropdownSelector; \ No newline at end of file diff --git a/src/components/InputField.js b/src/components/InputField.js new file mode 100644 index 0000000..fd5e839 --- /dev/null +++ b/src/components/InputField.js @@ -0,0 +1,81 @@ +import React, {useState} from 'react'; +import { Text, TextInput, StyleSheet, View } from 'react-native'; +import { theme } from '../../constants'; +import { Controller } from 'react-hook-form'; + +const InputField = ({ control, name, title, type = 'default', rules = {}, errors ={} }) => { + + const [isFocused, setIsFocused] = useState(false); + + const InputStyle = isFocused ? styles.inputFocused : styles.input; + return ( + + {title} + ( + setIsFocused(true)} + onBlur={() => setIsFocused(false)} + value={value} + keyboardType={type === 'numeric' ? 'numeric' : 'default'} + /> + )} + /> + {errors[name] && ( + {errors[name].message} + )} + + ); +}; + + +const styles = StyleSheet.create({ + container: { + marginHorizontal: 20, + marginBottom: 10, + }, + label: { + color: 'gray', + fontSize: 18, + marginBottom: 5, + paddingLeft: 10 + }, + input: { + height: 46, + width: "auto", + minWidth: 245, + paddingHorizontal: 10, + backgroundColor: theme.colors.otherWhite, + borderRadius: 22, + fontSize: 18, + fontWeight: "bold", + + }, + inputFocused:{ + height: 46, + width: "auto", + minWidth: 245, + paddingHorizontal: 10, + backgroundColor: theme.colors.otherWhite, + borderRadius: 22, + fontSize: 18, + fontWeight: "bold", + borderWidth: 2, + borderColor: theme.colors.black, + + }, + error: { + color: 'red', + fontSize: 12, + marginTop: 1, + paddingLeft: 10 + }, +}); + +export default InputField; diff --git a/src/components/NoteItem.js b/src/components/NoteItem.js index 66c429c..2f210c0 100644 --- a/src/components/NoteItem.js +++ b/src/components/NoteItem.js @@ -10,6 +10,7 @@ import { import { theme } from "../../constants"; import BorderBox from "../utils/BorderBox"; import StyledText from "../utils/StyledText"; +import { useNavigation } from "@react-navigation/native"; const NoteItem = ({ note, onSelect }) => { const [modalVisible, setModalVisible] = useState(false); @@ -25,6 +26,8 @@ const NoteItem = ({ note, onSelect }) => { }).start(); }; + const navigation = useNavigation(); + return ( { {note.Saldo_pendiente} - {}} style={noteItemstyles.button}> + navigation.navigate("PayScreen")} style={noteItemstyles.button}> Pagar diff --git a/src/components/RadioButtonGroup.js b/src/components/RadioButtonGroup.js new file mode 100644 index 0000000..25162de --- /dev/null +++ b/src/components/RadioButtonGroup.js @@ -0,0 +1,68 @@ +import React, { useState } from 'react'; +import { View, Text, TouchableOpacity, StyleSheet } from 'react-native'; + +const RadioButtonGroup = ({ title, options, onSelect }) => { + const [selectedOption, setSelectedOption] = useState(null); + + const handleSelectOption = (option) => { + setSelectedOption(option); + onSelect(option); + }; + + return ( + + {title} + {options.map((option) => ( + handleSelectOption(option)} + > + + {selectedOption === option && } + + {option} + + ))} + + ); +}; + +const styles = StyleSheet.create({ + title: { + color: 'gray', + fontSize: 18, + marginBottom: 5, + }, + container: { + flexDirection: 'column', + marginHorizontal: 36, + }, + optionContainer: { + flexDirection: 'row', + alignItems: 'center', + marginVertical: 2, + marginHorizontal: 6 + }, + radioButton: { + width: 24, + height: 24, + borderRadius: 12, + borderWidth: 2, + borderColor: 'black', + justifyContent: 'center', + alignItems: 'center', + marginRight: 8, + }, + radioInnerCircle: { + width: 12, + height: 12, + borderRadius: 6, + backgroundColor: 'black', + }, + optionText: { + fontSize: 16, + }, +}); + +export default RadioButtonGroup; diff --git a/src/layouts/FormLayout.js b/src/layouts/FormLayout.js new file mode 100644 index 0000000..e69de29 diff --git a/src/navigation/AppNavigator.js b/src/navigation/AppNavigator.js index 7afebac..29df0c0 100644 --- a/src/navigation/AppNavigator.js +++ b/src/navigation/AppNavigator.js @@ -5,6 +5,8 @@ import NewScreen from '../screens/HomeScreen'; import ClientSearchScreen from '../screens/ClientSearchScreen'; import BillScreen from '../screens/BillScreen' import ClientPaymentScreen from '../screens/ClientPaymentScreen'; +import PayScreen from '../screens/PayScreen'; +import AutomaticPayScreen from '../screens/AutomaticPayScreen'; const Stack = createNativeStackNavigator(); @@ -38,6 +40,20 @@ function AppNavigator() { headerShown: false, }} /> + + ); diff --git a/src/screens/AutomaticPayScreen.js b/src/screens/AutomaticPayScreen.js new file mode 100644 index 0000000..db61e5f --- /dev/null +++ b/src/screens/AutomaticPayScreen.js @@ -0,0 +1,263 @@ +import React, { useState, useCallback } from "react"; +import { SafeAreaView, StyleSheet, Text, TextInput, TouchableOpacity, View, Dimensions, ScrollView } from 'react-native'; +import { useForm } from "react-hook-form"; +import Icon from "react-native-vector-icons/AntDesign"; +import { useNavigation, useFocusEffect } from '@react-navigation/native'; +import { StatusBar } from 'expo-status-bar'; +import Cascading from "../animation/CascadingFadeInView"; +import { theme } from "../../constants.js"; +import InputField from "../components/InputField.js"; +import DateInputField from "../components/DateInputField.js"; +import DropdownSelector2 from "../components/DropdownSelector2.js"; +import RadioButtonGroup from "../components/RadioButtonGroup.js"; + +const screenWidth = Dimensions.get("window").width; +const screenHeight = Dimensions.get("window").height; + +const PayScreen = ({ route }) => { + const navigation = useNavigation(); + //const { itemClient } = route.params; + const [animationKey, setAnimationKey] = useState(Date.now()); + useFocusEffect( + useCallback(() => { + setAnimationKey(Date.now()); + }, []) + ); + + + const [selectedCurrency, setSelectedCurrency] = useState('Bs'); + const handleCurrencyChange = (option) => { + setSelectedCurrency(option); + }; + + const [selectedBank, setSelectedBank] = useState('BNB 1213434789'); + const banks = ['BNB 1213434789', 'BCP 4432765343', 'CTA 1239123234']; + + const [selectedPay, setSelectedPay] = useState('PEPS'); + const payMode = ['Primera Nota en entrar Primera en Pagar', 'Ultima Nota en entrar Primera en Pagar', 'De mayor importe a menor', 'De menor importe a mayor' ]; + + const { + control, + handleSubmit, + formState: { errors }, + } = useForm({ + defaultValues: { + amount: "", + currency: "", + deposit: "", + advancePaymentNumber: "", + checkBankNumber: "", + checkBankDate: "", + bankAccount: "", + reference: "", + observations: "", + }, + }); + + const onSubmit = (data) => { + console.log(data); + // Aquí puedes agregar la lógica para guardar los datos + //navigation.navigate("ClientPaymentScreen", { itemClient: item }); + }; + + return ( + + + + + + + navigation.goBack()} + > + + + + Pago Automático + + + + + + + + + + + + + + + {/* El input de abajo necesita usar un datetime picker para la fecha */} + + + + + + + + + + console.log('Opción seleccionada:', option)} +/> + + + + + Registrar Pago + + + + + ) +}; + +const styles = StyleSheet.create({ + cover: { + backgroundColor: theme.colors.primary, + zIndex: 1, + }, + up: { + backgroundColor: theme.colors.primary, + }, + container: { + flex: 1, + paddingTop: 15, + backgroundColor: theme.colors.primary, + }, + header: { + flexDirection: "row", + paddingHorizontal: 20, + paddingVertical: 20, + alignItems: "center", + }, + back: { + justifyContent: "center", + alignItems: "center", + backgroundColor: theme.colors.otherWhite, + borderRadius: 20, + width: 60, + height: 60, + }, + aviContainer: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + avi: { + fontWeight: "bold", + fontSize: 22, + marginRight: 60, + }, + buttonContainer: { + marginTop: 20, + flexDirection: "row", + justifyContent: "center", + alignSelf: "center", + width: screenWidth - 240, + }, + button: { + justifyContent: "center", + alignItems: "center", + elevation: 5, + paddingVertical: 15, + paddingHorizontal: 15, + backgroundColor: theme.colors.tertiary, + borderRadius: 22, + flex: 1, + }, + buttonText: { + color: theme.colors.primary, + fontSize: 16, + fontWeight: "bold", + }, + firstLineForm: { + flexDirection: "row", + }, +}); + +export default PayScreen; diff --git a/src/screens/PayScreen.js b/src/screens/PayScreen.js new file mode 100644 index 0000000..4dab2b9 --- /dev/null +++ b/src/screens/PayScreen.js @@ -0,0 +1,251 @@ +import React, { useState, useCallback } from "react"; +import { SafeAreaView, StyleSheet, Text, TextInput, TouchableOpacity, View, Dimensions, ScrollView } from 'react-native'; +import { useForm } from "react-hook-form"; +import Icon from "react-native-vector-icons/AntDesign"; +import { useNavigation, useFocusEffect } from '@react-navigation/native'; +import { StatusBar } from 'expo-status-bar'; +import Cascading from "../animation/CascadingFadeInView"; +import { theme } from "../../constants.js"; +import InputField from "../components/InputField.js"; +import DateInputField from "../components/DateInputField.js"; +import DropdownSelector2 from "../components/DropdownSelector2.js"; + +const screenWidth = Dimensions.get("window").width; +const screenHeight = Dimensions.get("window").height; + +const PayScreen = ({ route }) => { + const navigation = useNavigation(); + //const { itemClient } = route.params; + const [animationKey, setAnimationKey] = useState(Date.now()); + useFocusEffect( + useCallback(() => { + setAnimationKey(Date.now()); + }, []) + ); + + const [selectedCurrency, setSelectedCurrency] = useState('Bs'); + const handleCurrencyChange = (option) => { + setSelectedCurrency(option); + }; + + const [selectedBank, setSelectedBank] = useState('BNB 1213434789'); + const banks = ['BNB 1213434789', 'BCP 4432765343', 'CTA 1239123234']; + + const { + control, + handleSubmit, + formState: { errors }, + } = useForm({ + defaultValues: { + amount: "", + currency: "", + deposit: "", + advancePaymentNumber: "", + checkBankNumber: "", + checkBankDate: "", + bankAccount: "", + reference: "", + observations: "", + }, + }); + + const onSubmit = (data) => { + console.log(data); + // Aquí puedes agregar la lógica para guardar los datos + //navigation.navigate("ClientPaymentScreen", { itemClient: item }); + }; + + return ( + + + + + + + navigation.goBack()} + > + + + + Nº 150 + 130. Bs + + + + + + + + + + + + + + + {/* El input de abajo necesita usar un datetime picker para la fecha */} + + + + + + + + + + + Registrar Pago + + + + + ) +}; + +const styles = StyleSheet.create({ + cover: { + backgroundColor: theme.colors.primary, + zIndex: 1, + }, + up: { + backgroundColor: theme.colors.primary, + }, + container: { + flex: 1, + paddingTop: 15, + backgroundColor: theme.colors.primary, + }, + header: { + flexDirection: "row", + paddingHorizontal: 20, + paddingVertical: 20, + alignItems: "center", + }, + back: { + justifyContent: "center", + alignItems: "center", + backgroundColor: theme.colors.otherWhite, + borderRadius: 20, + width: 60, + height: 60, + }, + aviContainer: { + flex: 1, + justifyContent: "center", + alignItems: "center", + }, + avi: { + fontWeight: "bold", + fontSize: 22, + marginRight: 60, + }, + buttonContainer: { + marginTop: 20, + flexDirection: "row", + justifyContent: "center", + alignSelf: "center", + width: screenWidth - 240, + }, + button: { + justifyContent: "center", + alignItems: "center", + elevation: 5, + paddingVertical: 15, + paddingHorizontal: 15, + backgroundColor: theme.colors.tertiary, + borderRadius: 22, + flex: 1, + }, + buttonText: { + color: theme.colors.primary, + fontSize: 16, + fontWeight: "bold", + }, + firstLineForm: { + flexDirection: "row", + }, +}); + +export default PayScreen; diff --git a/src/unused/NoteBelongs.js b/src/unused/NoteBelongs.js index ca7a58e..d9be443 100644 --- a/src/unused/NoteBelongs.js +++ b/src/unused/NoteBelongs.js @@ -2,8 +2,12 @@ import React from "react"; import { View, Text, TouchableOpacity, StyleSheet, Dimensions} from "react-native"; import { theme } from '../../constants'; +import { useNavigation } from "@react-navigation/native"; const NoteBelongs = ({ note, onSelect }) => { + + const navigation = useNavigation(); + return ( onSelect(note.name)} style={noteBelongstyles}> @@ -15,7 +19,7 @@ const NoteBelongs = ({ note, onSelect }) => { {note.amount} Bs - {}} style={noteBelongstyles.button}> + NavigationPreloadManager.navigate("PayScreen")} style={noteBelongstyles.button}> Pagar