diff --git a/package-lock.json b/package-lock.json index cbc977b..8c3d90e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12,6 +12,7 @@ "@react-native-async-storage/async-storage": "^1.19.5", "@react-native-community/masked-view": "^0.1.11", "@react-native-community/netinfo": "^9.4.1", + "@react-navigation/drawer": "^6.6.6", "@react-navigation/native": "^6.1.9", "@react-navigation/native-stack": "^6.9.16", "@reduxjs/toolkit": "^1.9.7", @@ -19,6 +20,7 @@ "axios": "^1.6.0", "react": "18.2.0", "react-native": "0.72.6", + "react-native-elements": "^3.4.3", "react-native-gesture-handler": "^2.9.0", "react-native-modal": "^13.0.1", "react-native-reanimated": "^3.5.4", @@ -27,7 +29,7 @@ "react-native-simple-toast": "^3.0.2", "react-native-svg": "^13.14.0", "react-native-svg-transformer": "^1.1.0", - "react-native-vector-icons": "^10.0.1", + "react-native-vector-icons": "^10.0.2", "react-query": "^3.39.3", "react-redux": "^8.1.3", "redux": "^4.2.1", @@ -41,6 +43,7 @@ "@react-native/metro-config": "^0.72.11", "@tsconfig/react-native": "^3.0.0", "@types/react": "^18.0.24", + "@types/react-native-vector-icons": "^6.4.18", "@types/react-redux": "^7.1.28", "@types/react-test-renderer": "^18.0.0", "babel-jest": "^29.2.1", @@ -4216,6 +4219,25 @@ "react": "*" } }, + "node_modules/@react-navigation/drawer": { + "version": "6.6.6", + "resolved": "https://registry.npmjs.org/@react-navigation/drawer/-/drawer-6.6.6.tgz", + "integrity": "sha512-DW/oNRisSOGOqvZfCzfhKBxnzT97Teqtg1Gal85g+K3gnVbM1jOBE2PdnYsKU0fULfFtDwvp/QZSbcgjDpr12A==", + "dependencies": { + "@react-navigation/elements": "^1.3.21", + "color": "^4.2.3", + "warn-once": "^0.1.0" + }, + "peerDependencies": { + "@react-navigation/native": "^6.0.0", + "react": "*", + "react-native": "*", + "react-native-gesture-handler": ">= 1.0.0", + "react-native-reanimated": ">= 1.0.0", + "react-native-safe-area-context": ">= 3.0.0", + "react-native-screens": ">= 3.0.0" + } + }, "node_modules/@react-navigation/elements": { "version": "1.3.21", "resolved": "https://registry.npmjs.org/@react-navigation/elements/-/elements-1.3.21.tgz", @@ -4838,6 +4860,23 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-native": { + "version": "0.70.18", + "resolved": "https://registry.npmjs.org/@types/react-native/-/react-native-0.70.18.tgz", + "integrity": "sha512-M06e2opOY8PyLVfVSWd70X2yrIDZrgiOw677HsOrI/fT27aqvKGuFE44tDUBLEvQ8XEPC+T5pW2h5Az6gX7hdA==", + "dependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/react-native-vector-icons": { + "version": "6.4.18", + "resolved": "https://registry.npmjs.org/@types/react-native-vector-icons/-/react-native-vector-icons-6.4.18.tgz", + "integrity": "sha512-YGlNWb+k5laTBHd7+uZowB9DpIK3SXUneZqAiKQaj1jnJCZM0x71GDim5JCTMi4IFkhc9m8H/Gm28T5BjyivUw==", + "dependencies": { + "@types/react": "*", + "@types/react-native": "^0.70" + } + }, "node_modules/@types/react-redux": { "version": "7.1.30", "resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.30.tgz", @@ -6186,6 +6225,18 @@ "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -6199,6 +6250,31 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/color/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, "node_modules/colorette": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/colorette/-/colorette-1.4.0.tgz", @@ -11065,6 +11141,11 @@ "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==" }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -12466,6 +12547,14 @@ "node": ">=8" } }, + "node_modules/opencollective-postinstall": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/opencollective-postinstall/-/opencollective-postinstall-2.0.3.tgz", + "integrity": "sha512-8AV/sCtuzUeTo8gQK5qDZzARrulB3egtLzFgteqB2tcT4Mw7B8Kt7JcDHmltjz6FOAHsvTevk70gZEbhM4ZS9Q==", + "bin": { + "opencollective-postinstall": "index.js" + } + }, "node_modules/optionator": { "version": "0.9.3", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", @@ -13090,6 +13179,35 @@ "prop-types": "^15.7.2" } }, + "node_modules/react-native-elements": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/react-native-elements/-/react-native-elements-3.4.3.tgz", + "integrity": "sha512-VtZc25EecPZyUBER85zFK9ZbY6kkUdcm1ZwJ9hdoGSCr1R/GFgxor4jngOcSYeMvQ+qimd5No44OVJW3rSJECA==", + "hasInstallScript": true, + "dependencies": { + "@types/react-native-vector-icons": "^6.4.6", + "color": "^3.1.2", + "deepmerge": "^4.2.2", + "hoist-non-react-statics": "^3.3.2", + "lodash.isequal": "^4.5.0", + "opencollective-postinstall": "^2.0.3", + "react-native-ratings": "8.0.4", + "react-native-size-matters": "^0.3.1" + }, + "peerDependencies": { + "react-native-safe-area-context": ">= 3.0.0", + "react-native-vector-icons": ">7.0.0" + } + }, + "node_modules/react-native-elements/node_modules/color": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/color/-/color-3.2.1.tgz", + "integrity": "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA==", + "dependencies": { + "color-convert": "^1.9.3", + "color-string": "^1.6.0" + } + }, "node_modules/react-native-gesture-handler": { "version": "2.13.4", "resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-2.13.4.tgz", @@ -13119,6 +13237,18 @@ "react-native": ">=0.65.0" } }, + "node_modules/react-native-ratings": { + "version": "8.0.4", + "resolved": "https://registry.npmjs.org/react-native-ratings/-/react-native-ratings-8.0.4.tgz", + "integrity": "sha512-Xczu5lskIIRD6BEdz9A0jDRpEck/SFxRqiglkXi0u67yAtI1/pcJC76P4MukCbT8K4BPVl+42w83YqXBoBRl7A==", + "dependencies": { + "lodash": "^4.17.15" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-reanimated": { "version": "3.5.4", "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-3.5.4.tgz", @@ -13174,6 +13304,14 @@ "react-native": ">=0.71.0" } }, + "node_modules/react-native-size-matters": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/react-native-size-matters/-/react-native-size-matters-0.3.1.tgz", + "integrity": "sha512-mKOfBLIBFBcs9br1rlZDvxD5+mAl8Gfr5CounwJtxI6Z82rGrMO+Kgl9EIg3RMVf3G855a85YVqHJL2f5EDRlw==", + "peerDependencies": { + "react-native": "*" + } + }, "node_modules/react-native-svg": { "version": "13.14.0", "resolved": "https://registry.npmjs.org/react-native-svg/-/react-native-svg-13.14.0.tgz", @@ -14015,6 +14153,19 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", diff --git a/package.json b/package.json index 6cfe13d..1e12dd7 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@react-native-async-storage/async-storage": "^1.19.5", "@react-native-community/masked-view": "^0.1.11", "@react-native-community/netinfo": "^9.4.1", + "@react-navigation/drawer": "^6.6.6", "@react-navigation/native": "^6.1.9", "@react-navigation/native-stack": "^6.9.16", "@reduxjs/toolkit": "^1.9.7", @@ -21,6 +22,7 @@ "axios": "^1.6.0", "react": "18.2.0", "react-native": "0.72.6", + "react-native-elements": "^3.4.3", "react-native-gesture-handler": "^2.9.0", "react-native-modal": "^13.0.1", "react-native-reanimated": "^3.5.4", @@ -29,7 +31,7 @@ "react-native-simple-toast": "^3.0.2", "react-native-svg": "^13.14.0", "react-native-svg-transformer": "^1.1.0", - "react-native-vector-icons": "^10.0.1", + "react-native-vector-icons": "^10.0.2", "react-query": "^3.39.3", "react-redux": "^8.1.3", "redux": "^4.2.1", @@ -43,6 +45,7 @@ "@react-native/metro-config": "^0.72.11", "@tsconfig/react-native": "^3.0.0", "@types/react": "^18.0.24", + "@types/react-native-vector-icons": "^6.4.18", "@types/react-redux": "^7.1.28", "@types/react-test-renderer": "^18.0.0", "babel-jest": "^29.2.1", diff --git a/res/menu_black_24dp.svg b/res/menu_black_24dp.svg new file mode 100644 index 0000000..16fbd60 --- /dev/null +++ b/res/menu_black_24dp.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/application/App.tsx b/src/application/App.tsx index 3ea4f22..09f6d2a 100644 --- a/src/application/App.tsx +++ b/src/application/App.tsx @@ -1,28 +1,96 @@ +import {createDrawerNavigator} from '@react-navigation/drawer'; import {NavigationContainer} from '@react-navigation/native'; import { createNativeStackNavigator, NativeStackNavigationProp, } from '@react-navigation/native-stack'; import React from 'react'; -import {Text, useColorScheme} from 'react-native'; +import {StyleSheet, Text, TouchableOpacity, useColorScheme} from 'react-native'; import {GestureHandlerRootView} from 'react-native-gesture-handler'; import {SafeAreaProvider} from 'react-native-safe-area-context'; import {Colors} from 'react-native/Libraries/NewAppScreen'; import {Provider} from 'react-redux'; import {PersistGate} from 'redux-persist/integration/react'; -import CartScreen from '../features/cart/CartScreen'; +import ArrowBack from '../../res/arrow_back.svg'; +import MenuIcon from '../../res/menu_black_24dp.svg'; +import CartScreen, {MARGIN_HORIZONTAL} from '../features/cart/CartScreen'; +import CartButton from '../features/products/components/CartButton'; import ProductsScreen from '../features/products/ProductsScreen'; +import PurchasesScreen from '../features/purchases/PurchasesScreen'; import {colors} from '../features/shared/colors'; import {QueryProvider} from '../infrastructure/query'; +import {useAppSelector} from '../infrastructure/store/hooks/hooks'; import store, {persistor} from '../infrastructure/store/store'; -type AppStackParamList = { +export type AppStackParamList = { Products: undefined; Cart: undefined; }; +type PurchasesParamList = { + Purchases: undefined; +}; + +const StoreNavigator = createNativeStackNavigator(); +const PurchasesNavigator = createNativeStackNavigator(); +const AppNavigator = createDrawerNavigator(); + +const StoreFlow = () => ( + + + + +); + +const PurchasesFlow = () => { + return ( + + + + ); +}; + export type NavigationProp = NativeStackNavigationProp; -const appNavigator = createNativeStackNavigator(); + +const StoreFlowNavigationOptions = ({navigation}) => { + const state = navigation.getState(); + const isCartEmpty = useAppSelector(state => state.cart.cart.length === 0); + + const stackState = state.routes.find(route => route.state)?.state; + const currentScreen = stackState?.routes[stackState?.index]?.name; + console.log('currentScreen: ', currentScreen); + const isCartScreen = currentScreen === 'Cart'; + return { + headerRight: () => + !isCartScreen && ( + navigation.navigate('Cart')} + isEnabled={isCartEmpty} + /> + ), + headerLeft: () => + isCartScreen ? ( + { + navigation.goBack(); + }}> + + + ) : ( + { + navigation.openDrawer(); + }}> + + + ), + }; +}; function App(): JSX.Element { const isDarkMode = useColorScheme() === 'dark'; @@ -32,28 +100,40 @@ function App(): JSX.Element { }; return ( - - - - - - + + + + ); } +const styles = StyleSheet.create({ + headerButton: { + marginHorizontal: MARGIN_HORIZONTAL, + }, +}); + export default () => { return ( - - - Loading...} persistor={persistor}> - - - - - - + + + + Loading...} + persistor={persistor}> + + + + + + + ); }; diff --git a/src/features/cart/CartScreen.tsx b/src/features/cart/CartScreen.tsx index c218ded..1fa61b4 100644 --- a/src/features/cart/CartScreen.tsx +++ b/src/features/cart/CartScreen.tsx @@ -1,16 +1,8 @@ import {BottomSheetModal, BottomSheetModalProvider} from '@gorhom/bottom-sheet'; import {useNavigation} from '@react-navigation/native'; import React, {useEffect, useMemo, useRef, useState} from 'react'; -import { - Platform, - SafeAreaView, - StyleSheet, - Text, - TouchableOpacity, - View, -} from 'react-native'; +import {Platform, SafeAreaView, StyleSheet, Text, View} from 'react-native'; import Toast from 'react-native-simple-toast'; -import ArrowBack from '../../../res/arrow_back.svg'; import {NavigationProp} from '../../application/App'; import {cleanCart, editQuantity} from '../../infrastructure/store/cartSlice'; import { @@ -90,13 +82,6 @@ const CartScreen = () => { - { - navigation.goBack(); - }}> - - Shopping Cart void; -}) => { + onPress?: () => void; +}; + +const CartItem: React.FC = ({product, style, onPress}) => { return ( {product.name} - {'$' + product.price.toFixed(2)} + {'$' + product.price?.toFixed(2) || '0'} {product.quantity + ' units'} diff --git a/src/features/cart/queries.ts b/src/features/cart/queries.ts index d65b769..4f42af4 100644 --- a/src/features/cart/queries.ts +++ b/src/features/cart/queries.ts @@ -1,7 +1,14 @@ -import {useMutation} from '@tanstack/react-query'; +import {useMutation, useQueryClient} from '@tanstack/react-query'; import {makeCheckout} from '../../infrastructure/api'; +import {purchasesKeys} from '../purchases/queries'; -export const useCheckoutMutation = () => - useMutation({ +export const useCheckoutMutation = () => { + const queryClient = useQueryClient(); + + return useMutation({ mutationFn: makeCheckout, + onSuccess: () => { + queryClient.invalidateQueries({queryKey: purchasesKeys}); + }, }); +}; diff --git a/src/features/products/ProductsScreen.tsx b/src/features/products/ProductsScreen.tsx index 25b93ab..45debfa 100644 --- a/src/features/products/ProductsScreen.tsx +++ b/src/features/products/ProductsScreen.tsx @@ -1,35 +1,16 @@ -import {useNavigation} from '@react-navigation/native'; import React, {useState} from 'react'; -import {StyleSheet, TouchableOpacity, View} from 'react-native'; +import {StyleSheet, View} from 'react-native'; import {SafeAreaView} from 'react-native-safe-area-context'; -import EnabledShoppingCart from '../../../res/shopping-cart-enabled.svg'; -import ShoppingCart from '../../../res/shopping-cart.svg'; -import {NavigationProp} from '../../application/App'; -import {useAppSelector} from '../../infrastructure/store/hooks/hooks'; import Carrousel from './components/Carrousel'; import {ProductsList} from './components/ProductsList'; import SearchBar from './components/SearchBar'; const ProductsScreen = () => { - const isEmpty = useAppSelector(state => state.cart.cart.length === 0); const [query, setQuery] = useState(''); - const navigation = useNavigation(); - - const cartImage = isEmpty ? ( - - ) : ( - { - navigation.navigate('Cart'); - }}> - - - ); return ( - + - {cartImage} { const styles = StyleSheet.create({ container: { flex: 1, + marginTop: 12, }, carrousel: { width: '100%', height: 250, }, - cartButton: { - marginEnd: 29, - marginVertical: 16, - alignSelf: 'flex-end', - }, searchBar: { marginTop: 24, }, diff --git a/src/features/products/components/CartButton.tsx b/src/features/products/components/CartButton.tsx new file mode 100644 index 0000000..222521c --- /dev/null +++ b/src/features/products/components/CartButton.tsx @@ -0,0 +1,30 @@ +import React from 'react'; +import {StyleSheet, TouchableOpacity, View} from 'react-native'; +import EnabledShoppingCart from '../../../../res/shopping-cart-enabled.svg'; +import ShoppingCart from '../../../../res/shopping-cart.svg'; + +const CartButton = ({ + isEnabled, + onPress, +}: { + isEnabled: boolean; + onPress: () => void; +}) => { + const cartImage = isEnabled ? ( + + ) : ( + + + + ); + + return {cartImage}; +}; + +const styles = StyleSheet.create({ + cartButton: { + marginEnd: 29, + }, +}); + +export default CartButton; diff --git a/src/features/products/components/ProductsList.tsx b/src/features/products/components/ProductsList.tsx index f1719d8..d61286b 100644 --- a/src/features/products/components/ProductsList.tsx +++ b/src/features/products/components/ProductsList.tsx @@ -6,6 +6,7 @@ import { } from '../../../infrastructure/store/cartSlice'; import {useAppDispatch} from '../../../infrastructure/store/hooks/hooks'; import {colors} from '../../shared/colors'; +import {productsSeparator} from '../../shared/components/ProductsSeparator'; import {useDisplayList} from '../hooks/useDisplayList'; import {Product} from '../types/Product'; import HeaderListItem from './HeaderListItem'; @@ -48,12 +49,6 @@ export const ProductsList = ({query}: {query: string}) => { ); }; -const productsSeparator = () => { - return ( - - ); -}; - const styles = StyleSheet.create({ productContainer: { marginHorizontal: 16, diff --git a/src/features/purchases/PurchasesScreen.tsx b/src/features/purchases/PurchasesScreen.tsx new file mode 100644 index 0000000..a91f046 --- /dev/null +++ b/src/features/purchases/PurchasesScreen.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import {SectionList, StyleSheet} from 'react-native'; +import {SafeAreaView} from 'react-native-safe-area-context'; +import HeaderListItem from '../products/components/HeaderListItem'; +import {productsSeparator} from '../shared/components/ProductsSeparator'; +import {PurchaseListItem} from './components/PurchaseListItem'; +import {usePurchases} from './queries'; + +const PurchaseScreen = () => { + const {data: purchases} = usePurchases(); + const purchasesList = purchases ?? []; + + return ( + + } + ItemSeparatorComponent={productsSeparator} + keyExtractor={(item, index) => item.date + index} + renderSectionHeader={({ + section: {title}, + }: { + section: {title: string}; + }) => } + /> + + ); +}; + +const styles = StyleSheet.create({ + container: { + flex: 1, + }, +}); + +export default PurchaseScreen; diff --git a/src/features/purchases/components/PurchaseListItem.tsx b/src/features/purchases/components/PurchaseListItem.tsx new file mode 100644 index 0000000..2e4afb5 --- /dev/null +++ b/src/features/purchases/components/PurchaseListItem.tsx @@ -0,0 +1,43 @@ +import React from 'react'; +import {FlatList, StyleSheet, View} from 'react-native'; +import {MARGIN_BETWEEN_COLUMNS, MARGIN_HORIZONTAL} from '../../cart/CartScreen'; +import CartItem from '../../cart/components/CartItem'; +import {colors} from '../../shared/colors'; +import {Purchase} from '../types/Purchase'; + +export const PurchaseListItem = ({purchase}: {purchase: Purchase}) => { + return ( + + { + return {...purchaseItem.product, quantity: purchaseItem.quantity}; + })} + contentContainerStyle={styles.contentContainer} + renderItem={({item, index}) => ( + + )} + /> + + ); +}; + +const styles = StyleSheet.create({ + container: { + borderColor: colors.purple, + marginBottom: 16, + borderWidth: 2, + }, + contentContainer: { + paddingTop: 24, + alignItems: 'stretch', + marginHorizontal: MARGIN_HORIZONTAL, + }, +}); diff --git a/src/features/purchases/index.ts b/src/features/purchases/index.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/features/purchases/queries.ts b/src/features/purchases/queries.ts new file mode 100644 index 0000000..7f45569 --- /dev/null +++ b/src/features/purchases/queries.ts @@ -0,0 +1,26 @@ +import {useQuery} from '@tanstack/react-query'; +import {getPurchases} from '../../infrastructure/api'; +import {Purchase} from './types/Purchase'; + +export const purchasesKeys = ['all']; + +export const usePurchases = () => { + return useQuery({ + queryKey: purchasesKeys, + queryFn: getPurchases, + select: data => { + const datesPurchasesMap = data.reduce((acc, purchase) => { + const date = purchase.date.split('T')[0]; + const group = acc.get(date) || []; + group.push(purchase); + acc.set(date, group); + return acc; + }, new Map()); + + return Array.from(datesPurchasesMap, ([date, purchases]) => ({ + title: date, + data: purchases, + })).reverse(); + }, + }); +}; diff --git a/src/features/purchases/types/Purchase.ts b/src/features/purchases/types/Purchase.ts new file mode 100644 index 0000000..ef2bf15 --- /dev/null +++ b/src/features/purchases/types/Purchase.ts @@ -0,0 +1,6 @@ +import {ApiProduct} from '../../../infrastructure/api'; + +export interface Purchase { + date: string; + items: {product: ApiProduct; quantity: number}[]; +} diff --git a/src/features/shared/components/ProductsSeparator.tsx b/src/features/shared/components/ProductsSeparator.tsx new file mode 100644 index 0000000..1d03327 --- /dev/null +++ b/src/features/shared/components/ProductsSeparator.tsx @@ -0,0 +1,11 @@ +import React from 'react'; +import {StyleSheet, View} from 'react-native'; +import {colors} from '../colors'; + +export const productsSeparator = () => { + return ; +}; + +const styles = StyleSheet.create({ + lineStyle: {height: 1, width: '90%', backgroundColor: colors.gray}, +}); diff --git a/src/infrastructure/api/endpoints.ts b/src/infrastructure/api/endpoints.ts index 3ae989c..7fe7cca 100644 --- a/src/infrastructure/api/endpoints.ts +++ b/src/infrastructure/api/endpoints.ts @@ -1,3 +1,4 @@ +import {Purchase} from '../../features/purchases/types/Purchase'; import {CartProduct} from '../store/cartSlice'; import {instance} from './instance'; @@ -44,6 +45,22 @@ export const makeCheckout = (cart: CartProduct[]) => { }); }; +export const getPurchases = (): Promise => { + return instance + .get('/purchases') + .then(response => { + if (response.status === 200) { + return response.data; + } else { + throw new Error('Error fetching products'); + } + }) + .catch(error => { + console.log(error); + throw error; + }); +}; + export interface Banner { id: number; name: string;