diff --git a/App.js b/App.js index 45a48d1..80ae699 100644 --- a/App.js +++ b/App.js @@ -1,10 +1,10 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { Button, View } from 'react-native'; import { MenuProvider } from 'react-native-popup-menu'; import AppNavigator from './src/navigation/AppNavigator'; +import AsyncStorage from '@react-native-async-storage/async-storage'; const App = () => { - return ( diff --git a/config.js b/config.js new file mode 100644 index 0000000..b51fdfa --- /dev/null +++ b/config.js @@ -0,0 +1,2 @@ +// config.js +export const BASE_URL = "http://192.168.1.2:3000"; // Will diff --git a/package-lock.json b/package-lock.json index 4a62df4..48836e3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@expo-google-fonts/alex-brush": "^0.2.3", "@expo-google-fonts/montserrat": "^0.2.3", "@firebase/firestore": "^4.6.0", + "@react-native-async-storage/async-storage": "^1.23.1", "@react-native-community/datetimepicker": "8.0.1", "@react-native-firebase/analytics": "^19.0.0", "@react-native-firebase/app": "^19.0.0", @@ -4569,6 +4570,17 @@ "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, + "node_modules/@react-native-async-storage/async-storage": { + "version": "1.23.1", + "resolved": "https://registry.npmjs.org/@react-native-async-storage/async-storage/-/async-storage-1.23.1.tgz", + "integrity": "sha512-Qd2kQ3yi6Y3+AcUlrHxSLlnBvpdCEMVGFlVBneVOjaFaPU61g1huc38g339ysXspwY1QZA2aNhrk/KlHGO+ewA==", + "dependencies": { + "merge-options": "^3.0.4" + }, + "peerDependencies": { + "react-native": "^0.0.0-0 || >=0.60 <1.0" + } + }, "node_modules/@react-native-community/cli": { "version": "13.6.6", "resolved": "https://registry.npmjs.org/@react-native-community/cli/-/cli-13.6.6.tgz", @@ -10956,6 +10968,14 @@ "node": ">=8" } }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-object": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", @@ -12268,6 +12288,17 @@ "resolved": "https://registry.npmjs.org/memory-cache/-/memory-cache-0.2.0.tgz", "integrity": "sha512-OcjA+jzjOYzKmKS6IQVALHLVz+rNTMPoJvCztFaZxwG14wtAW7VRZjwTQu06vKCYOxh4jVnik7ya0SXTB0W+xA==" }, + "node_modules/merge-options": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/merge-options/-/merge-options-3.0.4.tgz", + "integrity": "sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==", + "dependencies": { + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", diff --git a/package.json b/package.json index 548ab4e..f5e0b28 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "@expo-google-fonts/alex-brush": "^0.2.3", "@expo-google-fonts/montserrat": "^0.2.3", "@firebase/firestore": "^4.6.0", + "@react-native-async-storage/async-storage": "^1.23.1", "@react-native-community/datetimepicker": "8.0.1", "@react-native-firebase/analytics": "^19.0.0", "@react-native-firebase/app": "^19.0.0", diff --git a/src/navigation/AppNavigator.js b/src/navigation/AppNavigator.js index 157b9fc..edc5264 100644 --- a/src/navigation/AppNavigator.js +++ b/src/navigation/AppNavigator.js @@ -3,7 +3,7 @@ import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { createBottomTabNavigator } from '@react-navigation/bottom-tabs'; import { NavigationContainer } from '@react-navigation/native'; import NewScreen from '../screens/HomeScreen'; -import ClientSearchScreen from '../screens/ClientSearchScreen'; +import ClientSearchScreen from '../screens/ClientSearch/ClientSearchScreen'; import BillScreen from '../screens/BillScreen' import ClientPaymentScreen from '../screens/ClientPaymentScreen'; import PayScreen from '../screens/PayScreen'; diff --git a/src/screens/ClientPaymentScreen.js b/src/screens/ClientPaymentScreen.js index c174126..066abac 100644 --- a/src/screens/ClientPaymentScreen.js +++ b/src/screens/ClientPaymentScreen.js @@ -9,7 +9,6 @@ import NoteItem from "../components/NoteItem"; import DropdownSelector from "../components/DropdownSelector"; import Cascading from "../animation/CascadingFadeInView"; import { useFocusEffect } from "@react-navigation/native"; -import ClientItem from "../components/ClientItem"; import StyledText from "../utils/StyledText"; const windowWidth = Dimensions.get('window').width; import useStore from '../stores/store'; diff --git a/src/components/ClientItem.js b/src/screens/ClientSearch/ClientItem.js similarity index 64% rename from src/components/ClientItem.js rename to src/screens/ClientSearch/ClientItem.js index 95a17aa..4cdcece 100644 --- a/src/components/ClientItem.js +++ b/src/screens/ClientSearch/ClientItem.js @@ -2,40 +2,43 @@ import React, { useEffect, useState } from "react"; import { View, Text, TouchableOpacity, StyleSheet, Dimensions } from "react-native"; import { MaterialCommunityIcons } from "@expo/vector-icons"; import { FontAwesome5 } from "@expo/vector-icons"; -import { theme } from '../assets/Theme'; -import StyledText from "../utils/StyledText"; -import BorderBox from "../utils/BorderBox"; -import useStore from "../stores/store"; +import { theme } from '../../assets/Theme'; +import StyledText from "../../utils/StyledText"; +import BorderBox from "../../utils/BorderBox"; +import useStore from "../../stores/store"; const windowWidth = Dimensions.get('window').width; const ClientItem = ({ client, onSelect }) => { const vNombre = client.Nombre; - const vCuenta =client.Cuenta; - const vBalance = parseFloat(client.NotasPendientes.reduce((total, nota) => total + nota.Saldo_pendiente, 0).toFixed(2)); - const vNotasPendientes = client.NotasPendientes.length; + const vCuenta = client.Cuenta; + + // Asegurarse de que NotasPendientes esté definido y sea un array + const notasPendientes = Array.isArray(client.NotasPendientes) ? client.NotasPendientes : []; + const vBalance = parseFloat(notasPendientes.reduce((total, nota) => { + const saldoPendiente = parseFloat(nota.Saldo_pendiente); + return total + (isNaN(saldoPendiente) ? 0 : saldoPendiente); + }, 0).toFixed(2)); + + const vNotasPendientes = notasPendientes.length; const pagosRealizados = useStore(state => state.pagosRealizados); const [vUltimoPago, setUltimoPago] = useState("2020-06-10"); - useEffect( () => { - if(pagosRealizados.length > 0){ - setUltimoPago(pagosRealizados.reduce((mayor, pago)=> pago.fecha > mayor && pago.cuenta === client.Cuenta? pago.fecha : mayor, "2020-06-10")); + useEffect(() => { + if (pagosRealizados.length > 0) { + setUltimoPago(pagosRealizados.reduce((mayor, pago) => pago.fecha > mayor && pago.cuenta === client.Cuenta ? pago.fecha : mayor, "2020-06-10")); } }, [pagosRealizados]); + return ( - onSelect(client.id)} style={{marginVertical: 10}}> + onSelect(client.cliente_ID)} style={{ marginVertical: 10 }}> - {/* {vNombre.charAt(0)} */} {vNombre.charAt(0)} {vNombre} {vCuenta} - {/* - - {vCuenta} - */} @@ -43,17 +46,17 @@ const ClientItem = ({ client, onSelect }) => { - notas Pendientes : + Notas Pendientes: {vNotasPendientes} {vNotasPendientes === 1 ? 'nota' : 'notas'} - saldo total : + Saldo Total: {vBalance} Bs - ultimo pago : + Último Pago: {vUltimoPago} @@ -84,16 +87,15 @@ const styles = StyleSheet.create({ }, notesContainer: { flexDirection: 'column', - // alignItems: 'flex-start', }, lineContainer: { justifyContent: 'center', alignItems: 'center', }, line: { - marginVertical: 3 , + marginVertical: 3, backgroundColor: theme.colors.otherWhite, - width: windowWidth*0.8, + width: windowWidth * 0.8, height: 2, }, textLine: { @@ -102,4 +104,5 @@ const styles = StyleSheet.create({ alignItems: "center", }, }); + export default ClientItem; diff --git a/src/screens/ClientSearchScreen.js b/src/screens/ClientSearch/ClientSearchScreen.js similarity index 74% rename from src/screens/ClientSearchScreen.js rename to src/screens/ClientSearch/ClientSearchScreen.js index 690edff..6cb2ac7 100644 --- a/src/screens/ClientSearchScreen.js +++ b/src/screens/ClientSearch/ClientSearchScreen.js @@ -1,23 +1,16 @@ -// ClientSearchScreen.js -import React, { useState, useCallback, useEffect } from "react"; -import { - SafeAreaView, - TouchableOpacity, - Text, - FlatList, - StyleSheet, - View, -} from "react-native"; -import SearchBar from "../components/SearchBar"; -import ClientItem from "../components/ClientItem"; +import React, { useState, useCallback, useEffect, useMemo } from "react"; +import { SafeAreaView, TouchableOpacity, FlatList, StyleSheet, View, } from "react-native"; +import SearchBar from "./SearchBar"; +import ClientItem from "./ClientItem"; import { StatusBar } from "expo-status-bar"; -import { theme } from "../assets/Theme"; +import { theme } from "../../assets/Theme"; import Icon from "react-native-vector-icons/AntDesign"; import { useNavigation } from "@react-navigation/native"; -import Cascading from "../animation/CascadingFadeInView"; +import Cascading from "../../animation/CascadingFadeInView"; import { useFocusEffect } from "@react-navigation/native"; -import StyledText from "../utils/StyledText"; +import StyledText from "../../utils/StyledText"; import axios from "axios"; +import { BASE_URL } from "../../../config"; const secondary = theme.colors.secondary; @@ -30,24 +23,25 @@ const ClientSearchScreen = () => { const [animationKey, setAnimationKey] = useState(Date.now()); const [visibleItemCount, setVisibleItemCount] = useState(10); - const fetchClientes = async () => { + const fetchClientes = useCallback(async () => { try { - const response = await axios.get("http://192.168.1.2:3000/empresa/1/clientes"); + const empresaId = 1; // Hardcoded empresa ID + const response = await axios.get(`${BASE_URL}/empresa/${empresaId}/clientes`); setClientesConNotas(response.data); - console.log(response.data); - // setFilteredData(response.data); + setFilteredData(response.data); + console.log(JSON.stringify(response.data, null, 2)); } catch (error) { console.error("Error fetching clientes: ", error); } - }; + }, []); useEffect(() => { fetchClientes(); - }, []); + }, [fetchClientes]); - const loadMoreItems = () => { + const loadMoreItems = useCallback(() => { setVisibleItemCount((prevItemCount) => prevItemCount + 10); - }; + }, []); useFocusEffect( useCallback(() => { @@ -56,7 +50,7 @@ const ClientSearchScreen = () => { }, []) ); - const handleSearch = (text) => { + const handleSearch = useCallback((text) => { setSearchQuery(text); const formattedQuery = text.toLowerCase(); const newData = clientesConNotas.filter((item) => { @@ -67,13 +61,13 @@ const ClientSearchScreen = () => { } }); setFilteredData(newData); - }; + }, [clientesConNotas, selectedOption]); - const handleOptionChange = (option) => { + const handleOptionChange = useCallback((option) => { setSelectedOption(option); - }; + }, []); - const renderItem = ({ item, index }) => ( + const renderItem = useCallback(({ item, index }) => ( 9 ? 0 : 400 + 100 * index} animationKey={animationKey} @@ -85,7 +79,15 @@ const ClientSearchScreen = () => { } /> - ); + ), [animationKey, navigation]); + + const keyExtractor = useCallback((item) => item.cliente_ID.toString(), []); + + const getItemLayout = useCallback((data, index) => ({ + length: 70, + offset: 70 * index, + index, + }), []); return ( @@ -121,7 +123,8 @@ const ClientSearchScreen = () => { item.cliente_ID} + keyExtractor={keyExtractor} + getItemLayout={getItemLayout} ListHeaderComponent={} ListFooterComponent={} onEndReached={loadMoreItems} @@ -179,24 +182,3 @@ const styles = StyleSheet.create({ }); export default ClientSearchScreen; - -// Empresa_ID: 2, -// sucursal_ID: 1, -// cliente_ID: "00C", -// Cuenta: "11201010013", -// Nombre: "ARANCIBIA HEBERTO", -// Direccion: "CHIQUICOLLO", -// Telefono: "4248174 - 75467019", -// cobrador_ID: "01" -// NotasPendientes[{ -// "Empresa_ID": 2, -// "sucursal_ID": 1, -// "Cuenta": "11201010011", -// "Fecha": "2024-01-01", -// "nro_nota": "R01225066", -// "importe_nota": 696.0, -// "Monto_pagado": 0.0, -// "Saldo_pendiente": 696.0, -// "Fecha_venta": "2022-10-26", -// "Fecha_vence": "2022-12-25" -// }] \ No newline at end of file diff --git a/src/components/SearchBar.js b/src/screens/ClientSearch/SearchBar.js similarity index 97% rename from src/components/SearchBar.js rename to src/screens/ClientSearch/SearchBar.js index 81007a9..2b6bbe4 100644 --- a/src/components/SearchBar.js +++ b/src/screens/ClientSearch/SearchBar.js @@ -4,8 +4,8 @@ import { View, TextInput, Text, TouchableOpacity, StyleSheet, Dimensions } from import { Ionicons } from "@expo/vector-icons"; import { FontAwesome5 } from "@expo/vector-icons"; import { Menu, MenuOptions, MenuOption, MenuTrigger } from "react-native-popup-menu"; -import { theme } from "../assets/Theme"; -import StyledText from "../utils/StyledText"; +import { theme } from "../../assets/Theme"; +import StyledText from "../../utils/StyledText"; const windowWidth = Dimensions.get("window").width; const { height } = Dimensions.get('window'); const regularTextSize = height * 0.021