upload 04/08

This commit is contained in:
2025-08-04 10:17:58 +07:00
parent cc29aaabb4
commit 67939d2f54
24 changed files with 1917 additions and 655 deletions

10
App.tsx
View File

@@ -1,9 +1,17 @@
import React from "react";
import React, { useEffect } from "react";
import { NavigationContainer } from "@react-navigation/native";
import { Platform } from "react-native";
import { SafeAreaProvider } from "react-native-safe-area-context";
import AppNavigator from "./src/navigation/AppNavigator";
export default function App() {
useEffect(() => {
if (Platform.OS === "web") {
// Fix scroll bị chặn trên web
document.body.style.overflow = "auto";
document.documentElement.style.overflow = "auto";
}
}, []);
return (
<SafeAreaProvider>
<NavigationContainer>

View File

@@ -4,7 +4,7 @@ export default {
expo: {
name: "bestpc_mobile",
slug: "bestpc-mobile",
platforms: ["ios", "android"],
platforms: ["ios", "android", "web"],
version: "1.0.0",
orientation: "portrait",
splash: {

View File

@@ -14,5 +14,6 @@ module.exports = {
},
},
],
'react-native-reanimated/plugin'
],
};

93
package-lock.json generated
View File

@@ -18,12 +18,13 @@
"@react-navigation/stack": "^7.3.2",
"dotenv": "^16.5.0",
"expo-image-picker": "~16.1.4",
"expo-linear-gradient": "~14.1.4",
"expo-status-bar": "~2.2.3",
"hermes-engine": "^0.11.0",
"lodash": "^4.17.21",
"metro-runtime": "^0.82.3",
"react": "19.0.0",
"react-dom": "^19.0.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-native": "^0.79.2",
"react-native-collapsible": "^1.6.2",
"react-native-gesture-handler": "~2.24.0",
@@ -5203,16 +5204,16 @@
}
},
"node_modules/compression": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
"integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
"license": "MIT",
"dependencies": {
"bytes": "3.1.2",
"compressible": "~2.0.18",
"debug": "2.6.9",
"negotiator": "~0.6.4",
"on-headers": "~1.0.2",
"on-headers": "~1.1.0",
"safe-buffer": "5.2.1",
"vary": "~1.1.2"
},
@@ -5999,6 +6000,17 @@
"react": "*"
}
},
"node_modules/expo-linear-gradient": {
"version": "14.1.5",
"resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-14.1.5.tgz",
"integrity": "sha512-BSN3MkSGLZoHMduEnAgfhoj3xqcDWaoICgIr4cIYEx1GcHfKMhzA/O4mpZJ/WC27BP1rnAqoKfbclk1eA70ndQ==",
"license": "MIT",
"peerDependencies": {
"expo": "*",
"react": "*",
"react-native": "*"
}
},
"node_modules/expo-modules-autolinking": {
"version": "2.1.10",
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.1.10.tgz",
@@ -8951,9 +8963,9 @@
}
},
"node_modules/on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==",
"license": "MIT",
"engines": {
"node": ">= 0.8"
@@ -9584,9 +9596,9 @@
}
},
"node_modules/react": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==",
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -9624,17 +9636,23 @@
}
},
"node_modules/react-dom": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"license": "MIT",
"dependencies": {
"scheduler": "^0.25.0"
"scheduler": "^0.26.0"
},
"peerDependencies": {
"react": "^19.0.0"
"react": "^19.1.0"
}
},
"node_modules/react-dom/node_modules/scheduler": {
"version": "0.26.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
"license": "MIT"
},
"node_modules/react-freeze": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/react-freeze/-/react-freeze-1.0.4.tgz",
@@ -15619,15 +15637,15 @@
}
},
"compression": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.0.tgz",
"integrity": "sha512-k6WLKfunuqCYD3t6AsuPGvQWaKwuLLh2/xHNcX4qE+vIfDNXpSqnrhwA7O53R7WVQUnt8dVAIW+YHr7xTgOgGA==",
"version": "1.8.1",
"resolved": "https://registry.npmjs.org/compression/-/compression-1.8.1.tgz",
"integrity": "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w==",
"requires": {
"bytes": "3.1.2",
"compressible": "~2.0.18",
"debug": "2.6.9",
"negotiator": "~0.6.4",
"on-headers": "~1.0.2",
"on-headers": "~1.1.0",
"safe-buffer": "5.2.1",
"vary": "~1.1.2"
},
@@ -16136,6 +16154,12 @@
"integrity": "sha512-wU9qOnosy4+U4z/o4h8W9PjPvcFMfZXrlUoKTMBW7F4pLqhkkP/5G4EviPZixv4XWFMjn1ExQ5rV6BX8GwJsWA==",
"requires": {}
},
"expo-linear-gradient": {
"version": "14.1.5",
"resolved": "https://registry.npmjs.org/expo-linear-gradient/-/expo-linear-gradient-14.1.5.tgz",
"integrity": "sha512-BSN3MkSGLZoHMduEnAgfhoj3xqcDWaoICgIr4cIYEx1GcHfKMhzA/O4mpZJ/WC27BP1rnAqoKfbclk1eA70ndQ==",
"requires": {}
},
"expo-modules-autolinking": {
"version": "2.1.10",
"resolved": "https://registry.npmjs.org/expo-modules-autolinking/-/expo-modules-autolinking-2.1.10.tgz",
@@ -18140,9 +18164,9 @@
}
},
"on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz",
"integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A=="
},
"once": {
"version": "1.4.0",
@@ -18551,9 +18575,9 @@
}
},
"react": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz",
"integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ=="
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
"integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg=="
},
"react-devtools-core": {
"version": "6.1.2",
@@ -18573,11 +18597,18 @@
}
},
"react-dom": {
"version": "19.0.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz",
"integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==",
"version": "19.1.0",
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
"integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
"requires": {
"scheduler": "^0.25.0"
"scheduler": "^0.26.0"
},
"dependencies": {
"scheduler": {
"version": "0.26.0",
"resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
"integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA=="
}
}
},
"react-freeze": {

View File

@@ -24,8 +24,8 @@
"hermes-engine": "^0.11.0",
"lodash": "^4.17.21",
"metro-runtime": "^0.82.3",
"react": "19.0.0",
"react-dom": "^19.0.0",
"react": "^19.1.0",
"react-dom": "^19.1.0",
"react-native": "^0.79.2",
"react-native-collapsible": "^1.6.2",
"react-native-gesture-handler": "~2.24.0",

View File

@@ -221,7 +221,7 @@ export function CreateBuildpc() {
</View>
{/* Popup */}
{showPopup && (
<PopupBuildpc visible={showPopup} onClose={() => setShowPopup(false)} />
<PopupBuildpc show={showPopup} onClose={() => setShowPopup(false)} />
)}
</ScrollView>
);

View File

@@ -4,7 +4,7 @@ import {
Text,
TouchableOpacity,
StyleSheet,
ScrollView,
Platform,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { NavigationProp } from "@react-navigation/native";
@@ -33,7 +33,7 @@ const ButtonFooter: React.FC<Props> = ({ navigation, activeTab }) => {
onPress={() => navigation?.navigate(tab.route)}
>
<Ionicons
name={tab.icon}
name={tab?.icon}
size={22}
color={isActive ? "#4a00e0" : "#666"}
/>
@@ -54,9 +54,15 @@ const styles = StyleSheet.create({
backgroundColor: "#fff",
paddingVertical: 8,
justifyContent: "space-around",
position: "absolute",
bottom: 0,
left: 0,
width: "100%",
...Platform.select({
web: { position: "fixed" as any },
default: {
position: "absolute",
},
}),
},
navItem: {
alignItems: "center",

View File

@@ -82,7 +82,8 @@ const Footer: React.FC<Props> = ({ navigation, activeTab = "homepage" }) => {
/>
</TouchableOpacity>
<Collapsible collapsed={activeIndex !== index}>
{item.links?.map((link, linkIndex) => (
{item.links && item.links.length > 0 ? (
item.links.map((link, linkIndex) => (
<TouchableOpacity
key={linkIndex}
style={styles.linkItem}
@@ -90,7 +91,10 @@ const Footer: React.FC<Props> = ({ navigation, activeTab = "homepage" }) => {
>
<Text style={styles.linkText}>{link.title}</Text>
</TouchableOpacity>
))}
))
) : item.content ? (
<Text style={styles.accordionContent}>{item.content}</Text>
) : null}
</Collapsible>
</View>
))}

View File

@@ -7,6 +7,7 @@ import {
TextInput,
TouchableOpacity,
Dimensions,
Platform,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { Picker } from "@react-native-picker/picker";
@@ -14,25 +15,15 @@ import { NavigationContainer } from "@react-navigation/native";
import { createDrawerNavigator } from "@react-navigation/drawer";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
const Header = () => {
const Header = ({ toggleMenu }: { toggleMenu: () => void }) => {
const [openSort, setopenSort] = useState(true);
const [openFilter, setOpenFilter] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const toggleDrawer = () => {
//Props to open/close the drawer
setIsMenuOpen(!isMenuOpen);
};
const closeMenu = () => {
setIsMenuOpen(false); // Đóng menu
};
return (
<View>
<View style={styles.BoxHeader}>
<View style={styles.headerTop}>
<View style={styles.headerTopLeft}>
<TouchableOpacity onPress={toggleDrawer} style={styles.menu}>
<TouchableOpacity onPress={toggleMenu} style={styles.menu}>
<Ionicons style={styles.iconMenu} name="menu-outline" size={30} />
</TouchableOpacity>
<Image
@@ -58,6 +49,20 @@ const Header = () => {
name="caret-down-outline"
/>
</TouchableOpacity>
</View>
</View>
</View>
</View>
<View style={styles.searchContainer}>
<View style={styles.searchBar}>
<TextInput
style={styles.searchInput}
placeholder="Tìm kiếm khuyến mãi, cửa hàng, sản phẩm,..."
placeholderTextColor="rgba(169, 169, 169, 1)"
/>
<Ionicons style={styles.iconMenu} name="search-outline" size={19} />
</View>
</View>
<View
style={
openSort
@@ -68,12 +73,7 @@ const Header = () => {
<TouchableOpacity
style={[styles.boxSortItem, styles.boxSortItemActive]}
>
<Text
style={[
styles.boxSortItemText,
styles.boxSortItemTextActive,
]}
>
<Text style={[styles.boxSortItemText, styles.boxSortItemTextActive]}>
nội
</Text>
<Ionicons
@@ -89,115 +89,34 @@ const Header = () => {
</TouchableOpacity>
</View>
</View>
</View>
</View>
</View>
<View style={styles.searchContainer}>
<View style={styles.searchBar}>
<TextInput
style={styles.searchInput}
placeholder="Tìm kiếm khuyến mãi, cửa hàng, sản phẩm,..."
placeholderTextColor="rgba(169, 169, 169, 1)"
/>
<Ionicons style={styles.iconMenu} name="search-outline" size={19} />
</View>
</View>
{isMenuOpen && (
<View style={styles.overlay} onTouchStart={closeMenu}>
<MainMenu closeMenu={closeMenu} />
</View>
)}
</View>
);
};
const MainMenu = ({ closeMenu }: { closeMenu: () => void }) => {
return (
<View style={styles.menuFixed}>
<View style={styles.headerMenu}>
<Text style={styles.headerMenuText}>Menu</Text>
<TouchableOpacity onPress={closeMenu}>
<Ionicons style={styles.closeMenu} name="close-outline" size={20} />
</TouchableOpacity>
</View>
<View>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_buildpc.png")}
style={styles.iconBuild}
/>
<Text style={styles.itemText}>Build PC</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_store.png")}
style={styles.iconStore}
/>
<Text style={styles.itemText}>Tìm người bán</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_location.png")}
style={styles.iconLocation}
/>
<Text style={styles.itemText}>Đa chỉ sửa chữa</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_question.png")}
style={styles.iconProduct}
/>
<Text style={styles.itemText}>Hỏi đáp</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_box.png")}
style={styles.iconBox}
/>
<Text style={styles.itemText}>Sản phẩm đã lưu</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_article.png")}
style={styles.iconArticle}
/>
<Text style={styles.itemText}>Tin rao vặt</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
</View>
</View>
);
};
export { MainMenu };
let winWidth = Dimensions.get("window").width; //full width
let winHeight = Dimensions.get("window").height; //full height
const styles = StyleSheet.create({
BoxHeader: {
width: winWidth,
zIndex: 9999,
...Platform.select({
web: {
position: "fixed" as any,
top: 0,
},
default: {
position: "absolute",
top: 0,
left: 0,
},
}),
},
headerTop: {
flexDirection: "row",
alignItems: "center",
width: winWidth,
paddingRight: 10,
paddingLeft: 10,
paddingTop: 45,
paddingTop: 10,
paddingBottom: 10,
backgroundColor: "#462F91",
justifyContent: "space-between",
@@ -212,12 +131,14 @@ const styles = StyleSheet.create({
height: 20,
},
searchContainer: {
width: winWidth,
width: "100%",
paddingLeft: 10,
paddingRight: 10,
paddingTop: 9,
paddingBottom: 9,
backgroundColor: "#462F91",
position: "relative",
zIndex: 1,
},
searchBar: {
borderRadius: 4,
@@ -236,9 +157,8 @@ const styles = StyleSheet.create({
color: "#000000",
height: 32,
lineHeight: 32,
paddingTop: 7,
marginTop: 5,
width: winWidth - 60,
outlineWidth: 0,
},
searchIconContainer: {
flexDirection: "row",
@@ -270,6 +190,7 @@ const styles = StyleSheet.create({
alignItems: "center",
gap: 5,
color: "#FFFFFF",
position: "relative",
},
textLocation: {
color: "#FFFFFF",
@@ -298,10 +219,15 @@ const styles = StyleSheet.create({
paddingHorizontal: 10,
position: "absolute",
right: 0,
top: 40,
top: 60,
display: "none",
backgroundColor: "#fff",
width: 100,
zIndex: 999,
shadowColor: "#000",
shadowOffset: { width: 0, height: 4 },
shadowOpacity: 0.3,
shadowRadius: 5,
},
boxSortListActive: {
display: "flex",
@@ -343,91 +269,5 @@ const styles = StyleSheet.create({
justifyContent: "center",
display: "flex",
},
headerMenu: {
alignItems: "center",
justifyContent: "space-between",
flexDirection: "row",
padding: 10,
backgroundColor: "#462F91",
paddingTop: 55,
},
menuFixed: {
position: "absolute",
top: 0,
left: 0,
width: "85%",
height: winHeight,
backgroundColor: "#fff",
zIndex: 99,
},
headerMenuText: {
color: "#fff",
fontWeight: "bold",
},
closeMenu: {
backgroundColor: "#fff",
width: 20,
height: 20,
color: "#000",
borderRadius: 50,
},
iconBuild: {
width: 30,
objectFit: "contain",
},
iconStore: {
width: 20,
objectFit: "contain",
},
iconCart: {
width: 20,
objectFit: "contain",
},
iconProduct: {
width: 20,
objectFit: "contain",
},
iconArticle: {
width: 20,
objectFit: "contain",
},
iconLocation: {
width: 20,
objectFit: "contain",
},
iconBox: {
width: 20,
objectFit: "contain",
},
itemMenu: {
alignItems: "center",
flexDirection: "row",
justifyContent: "space-between",
gap: 10,
marginBottom: 10,
padding: 10,
borderBottomWidth: 1,
borderBottomColor: "#e1e1e1",
height: 50,
},
itemText: {
fontSize: 15,
color: "#000",
fontWeight: "bold",
},
textLeft: {
alignItems: "center",
gap: 10,
flexDirection: "row",
},
overlay: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0, 0, 0, 0.5)", // Lớp nền bán trong suốt
zIndex: 98, // Đảm bảo lớp overlay nằm dưới menu
},
});
export default Header;

View File

@@ -0,0 +1,197 @@
import React, { useState, useRef, useEffect } from "react";
import {
View,
StyleSheet,
Image,
Text,
TextInput,
TouchableOpacity,
Dimensions,
Modal,
Animated,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
const MainMenu = ({
visible,
onClose,
}: {
visible: boolean;
onClose: () => void;
}) => {
const [internalVisible, setInternalVisible] = useState(visible);
const slideAnim = useRef(new Animated.Value(-winWidth * 0.85)).current;
useEffect(() => {
if (visible) {
setInternalVisible(true);
Animated.timing(slideAnim, {
toValue: 0,
duration: 250,
useNativeDriver: false,
}).start();
} else {
Animated.timing(slideAnim, {
toValue: -winWidth * 0.85,
duration: 250,
useNativeDriver: false,
}).start(() => {
setInternalVisible(false); // Tắt Modal sau animation
});
}
}, [visible]);
if (!internalVisible) return null;
return (
<Modal visible={internalVisible} transparent animationType="none">
<TouchableOpacity
style={styles.overlay}
onPress={onClose}
activeOpacity={1}
/>
<Animated.View style={[styles.menuFixed, { left: slideAnim }]}>
<View style={styles.headerMenu}>
<Text style={styles.headerMenuText}>Menu</Text>
<TouchableOpacity onPress={onClose}>
<Ionicons name="close-outline" size={20} style={styles.closeMenu} />
</TouchableOpacity>
</View>
<View>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_buildpc.png")}
style={styles.iconFix}
resizeMode="contain"
/>
<Text style={styles.itemText}>Build PC</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_store.png")}
style={styles.iconFix}
resizeMode="contain"
/>
<Text style={styles.itemText}>Tìm người bán</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_location.png")}
style={styles.iconFix}
resizeMode="contain"
/>
<Text style={styles.itemText}>Đa chỉ sửa chữa</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_question.png")}
style={styles.iconFix}
resizeMode="contain"
/>
<Text style={styles.itemText}>Hỏi đáp</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_box.png")}
style={styles.iconFix}
resizeMode="contain"
/>
<Text style={styles.itemText}>Sản phẩm đã lưu</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
<TouchableOpacity style={styles.itemMenu}>
<View style={styles.textLeft}>
<Image
source={require("../../../assets/images/icon_article.png")}
style={styles.iconFix}
resizeMode="contain"
/>
<Text style={styles.itemText}>Tin rao vặt</Text>
</View>
<Ionicons name="chevron-forward-outline" size={20} />
</TouchableOpacity>
</View>
</Animated.View>
</Modal>
);
};
export { MainMenu };
let winWidth = Dimensions.get("window").width; //full width
let winHeight = Dimensions.get("window").height; //full height
const styles = StyleSheet.create({
headerMenu: {
alignItems: "center",
justifyContent: "space-between",
flexDirection: "row",
padding: 10,
backgroundColor: "#462F91",
},
menuFixed: {
width: "85%",
height: "100%",
backgroundColor: "#fff",
zIndex: 999,
},
headerMenuText: {
color: "#fff",
fontWeight: "bold",
},
closeMenu: {
backgroundColor: "#fff",
width: 20,
height: 20,
color: "#000",
borderRadius: 50,
},
iconFix: {
width: 25,
resizeMode: "contain",
},
itemMenu: {
alignItems: "center",
flexDirection: "row",
justifyContent: "space-between",
gap: 10,
marginBottom: 10,
padding: 10,
borderBottomWidth: 1,
borderBottomColor: "#e1e1e1",
height: 50,
},
itemText: {
fontSize: 15,
color: "#000",
fontWeight: "bold",
},
textLeft: {
alignItems: "center",
gap: 10,
flexDirection: "row",
},
overlay: {
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0, 0, 0, 0.5)", // Lớp nền bán trong suốt
zIndex: 98, // Đảm bảo lớp overlay nằm dưới menu
},
});

View File

@@ -99,7 +99,7 @@ const styles = StyleSheet.create({
backgroundColor: "#fff",
},
productImage: {
width: winWidth / 2 - 44,
width: "100%",
height: 150,
position: "relative",
marginBottom: 5,
@@ -112,8 +112,8 @@ const styles = StyleSheet.create({
},
boxSaleoff: {
position: "absolute",
right: 10,
top: 10,
right: 0,
top: 0,
width: 35,
height: 35,
borderRadius: 25,

View File

@@ -37,6 +37,7 @@ const ItemProductSave = ({ product }: { product: any }) => {
source={require("../../../assets/images/icon_heart.png")}
style={styles.imgsave}
alt="icon-heart"
resizeMode="contain"
/>
</View>
</TouchableOpacity>
@@ -74,10 +75,10 @@ const ItemProductSave = ({ product }: { product: any }) => {
const styles = StyleSheet.create({
productItem: {
width: winWidth / 2 - 20,
width: winWidth / 2 - 15,
},
productImage: {
width: winWidth / 2 - 20,
width: "100%",
height: 200,
position: "relative",
marginBottom: 5,
@@ -85,6 +86,7 @@ const styles = StyleSheet.create({
borderColor: "#d3d3d3",
backgroundColor: "#fff",
borderRadius: 8,
overflow: "hidden",
},
productImg: {
width: "100%",

View File

@@ -1,12 +1,24 @@
import React from "react";
import { View, StyleSheet, ScrollView } from "react-native";
import React, { useState } from "react";
import {
View,
StyleSheet,
ScrollView,
Dimensions,
Platform,
} from "react-native";
import {
useNavigation,
NavigationProp,
NavigationContainer,
} from "@react-navigation/native";
import { SafeAreaView } from "react-native-safe-area-context";
import Header from "../components/header/Header";
import { MainMenu } from "@components/header/Menu";
import Footer from "../components/footer/Footer";
import ButtonFooter from "../components/footer/ButtonFooter";
var winWidth = Dimensions.get("window").width; //full width
const winHeight = Dimensions.get("window").height;
type Props = {
children: React.ReactNode;
@@ -15,27 +27,49 @@ type Props = {
const AppLayout = ({ children, activeTab }: Props) => {
const navigation = useNavigation<NavigationProp<any>>();
const [isMenuOpen, setIsMenuOpen] = useState(false);
return (
<View style={styles.container}>
<Header />
<View style={styles.content}>{children}</View>
<SafeAreaView style={styles.wrapper}>
<Header toggleMenu={() => setIsMenuOpen(true)} />
<MainMenu visible={isMenuOpen} onClose={() => setIsMenuOpen(false)} />
<ScrollView
style={styles.scroll}
contentContainerStyle={styles.scrollContainer}
keyboardShouldPersistTaps="handled"
>
{children}
</ScrollView>
<Footer navigation={navigation} />
<ButtonFooter navigation={navigation} activeTab={activeTab} />
</View>
</SafeAreaView>
);
};
export default AppLayout;
const styles = StyleSheet.create({
container: {
marginLeft: "auto",
marginRight: "auto",
maxWidth: 480,
wrapper: {
flex: 1,
backgroundColor: "#fff",
},
content: {
scroll: {
flex: 1,
...Platform.select({
web: { marginTop: 113 },
default: { marginTop: 0 },
}),
},
scrollWrapper: {
flex: 1,
overflow: "scroll",
},
scrollContainer: {
flexGrow: 1,
},
webScrollArea: {
flex: 1,
overflow: "auto" as any,
},
});

View File

@@ -25,6 +25,7 @@ const AppNavigator: React.FC = () => {
<Stack.Screen name="buildpc" component={Buildpc} />
<Stack.Screen name="comparebuildpc" component={CompareBuildpc} />
<Stack.Screen name="buildpcdetail" component={BuildpcDeail} />
<Stack.Screen name="classifieds" component={BuildpcDeail} />
</Stack.Navigator>
);
};

View File

@@ -1,5 +1,5 @@
// src/screens/HomeScreen.tsx
import React, { useState } from "react";
import React, { useState, useRef } from "react";
import { useNavigation, NavigationProp } from "@react-navigation/native";
import {
View,
@@ -11,9 +11,9 @@ import {
ImageBackground,
TouchableOpacity,
SafeAreaView,
ScrollViewBase,
} from "react-native";
import { globalStyles } from "../styles/globalStyles";
import Swiper from "react-native-swiper";
import { Ionicons } from "@expo/vector-icons";
import { products } from "../data/product";
import ProductItem from "../components/product/ItemProduct";
@@ -23,17 +23,19 @@ import chunk from "lodash/chunk";
import { globalAgent } from "http";
import { WebView } from "react-native-webview";
import AppLayout from "../layouts/AppLayout";
import Footer from "../components/footer/Footer";
import Carousel, { ICarouselInstance } from "react-native-reanimated-carousel";
var winWidth = Dimensions.get("window").width; //full width
var winHeight = Dimensions.get("window").height; //full height
const ratio = winWidth / 930;
const HomeScreen: React.FC = () => {
const navigation = useNavigation<NavigationProp<any>>();
return (
<AppLayout activeTab="homepage">
<ScrollView>
<ScrollView
keyboardShouldPersistTaps="handled"
showsVerticalScrollIndicator={true}
>
<SliderHome />
<BoxMenuHome />
<BoxProductReviewTop />
@@ -45,67 +47,58 @@ const HomeScreen: React.FC = () => {
<BoxArticleClassifieds />
<BoxBannerSaleHome />
<BoxPromotionHome />
<Footer navigation={navigation} />
</ScrollView>
</AppLayout>
);
};
const SliderHome = () => {
const [currentIndex, setCurrentIndex] = useState(0);
const carouselRef = useRef<ICarouselInstance>(null);
const handleDotPress = (index: number) => {
if (carouselRef.current) {
carouselRef.current?.scrollTo({ index, animated: true });
}
};
const banner_list = [
require("../../assets/images/banner_slider.png"),
require("../../assets/images/banner_slider.png"),
require("../../assets/images/banner_slider.png"),
require("../../assets/images/banner_slider.png"),
];
return (
<View style={styles.slider}>
<Swiper
style={styles.sliderSwipper}
autoplay={false}
autoplayTimeout={3}
showsButtons={true}
showsPagination={false}
nextButton={
<Ionicons
name="chevron-forward-outline"
size={30}
color="white"
style={styles.buttonNext}
<Carousel
loop
autoPlay
autoPlayInterval={3000}
width={winWidth}
height={200}
data={banner_list}
scrollAnimationDuration={1000}
onSnapToItem={(index) => {
setCurrentIndex(index);
}}
renderItem={({ item }) => (
<Image source={item} style={styles.imgSlider} resizeMode="cover" />
)}
/>
}
prevButton={
<Ionicons
name="chevron-back-outline"
size={30}
color="white"
style={styles.buttonPrev}
/>
}
>
<View style={styles.imgContainer}>
<Image
style={styles.imgSlider}
source={require("../../assets/images/banner_slider.png")}
resizeMode="cover"
<View style={globalStyles.dotWrapper}>
{banner_list.map((_, index) => (
<TouchableOpacity key={index} onPress={() => handleDotPress(index)}>
<View
key={index}
style={[
globalStyles.dot,
currentIndex === index && globalStyles.activeDot,
]}
/>
</TouchableOpacity>
))}
</View>
<View style={styles.imgContainer}>
<Image
style={styles.imgSlider}
source={require("../../assets/images/banner_slider.png")}
resizeMode="cover"
/>
</View>
<View style={styles.imgContainer}>
<Image
style={styles.imgSlider}
source={require("../../assets/images/banner_slider.png")}
resizeMode="cover"
/>
</View>
<View style={styles.imgContainer}>
<Image
style={styles.imgSlider}
source={require("../../assets/images/banner_slider.png")}
resizeMode="cover"
/>
</View>
</Swiper>
</View>
);
};
@@ -252,124 +245,132 @@ const BoxProductReviewTop = () => {
const BoxCategoryHome = () => {
const chunkedItems = chunk(categories, 8);
const [activeIndex, setActiveIndex] = useState(0);
const carouselRef = useRef<ICarouselInstance>(null);
const handleDotPress = (index: number) => {
if (carouselRef.current) {
carouselRef.current?.scrollTo({ index, animated: true });
}
};
return (
<View style={globalStyles.boxCategoryHome}>
<Text style={globalStyles.textBoxCategoryHome}>Tìm theo danh mục</Text>
<Swiper
style={globalStyles.listCategoryBox}
<Carousel
loop={false}
showsPagination={true}
dot={<View style={globalStyles.dot} />}
activeDot={<View style={globalStyles.activeDot} />}
paginationStyle={{
bottom: 10,
}}
>
{chunkedItems.map((group, slideIndex) => (
width={winWidth}
height={300}
data={chunkedItems}
scrollAnimationDuration={500}
onSnapToItem={(index) => setActiveIndex(index)}
renderItem={({ item }) => (
<View
key={`slide-${slideIndex}`}
style={{
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "space-between",
paddingHorizontal: 10,
}}
>
{group.map((item) => (
<TouchableOpacity key={item.id} style={globalStyles.categoryItem}>
{item.map((cat) => (
<TouchableOpacity key={cat.id} style={globalStyles.categoryItem}>
<View style={globalStyles.boxImageCategory}>
<Image
source={require("../../assets/images/category-laptop.png")}
style={globalStyles.iconCategory}
/>
</View>
<Text style={globalStyles.categoryName}>{item.name}</Text>
<Text style={globalStyles.categoryName}>{cat.name}</Text>
</TouchableOpacity>
))}
</View>
)}
/>
<View style={globalStyles.dotWrapper}>
{chunkedItems.map((_, index) => (
<TouchableOpacity key={index} onPress={() => handleDotPress(index)}>
<View
key={index}
style={[
globalStyles.dot,
activeIndex === index && globalStyles.activeDot,
]}
/>
</TouchableOpacity>
))}
</Swiper>
</View>
</View>
);
};
const BoxBusiness = () => {
const [currentIndex, setCurrentIndex] = useState(0);
const carouselRef = useRef<ICarouselInstance>(null);
const handleDotPress = (index: number) => {
if (carouselRef.current) {
carouselRef.current?.scrollTo({ index, animated: true });
}
};
const slides = [
[
require("../../assets/images/logo-hacom.png"),
require("../../assets/images/logo-hacom.png"),
require("../../assets/images/logo-anphat.png"),
require("../../assets/images/logo-gearvn.png"),
],
[
require("../../assets/images/logo-hacom.png"),
require("../../assets/images/logo-hacom.png"),
require("../../assets/images/logo-anphat.png"),
require("../../assets/images/logo-gearvn.png"),
],
];
return (
<View style={globalStyles.BoxBusiness}>
<Text style={globalStyles.textBoxBusiness}>DOANH NGHIỆP NỔI BẬT</Text>
<Swiper
<Carousel
style={globalStyles.sliderBusinesses}
autoplay={false}
showsPagination={true}
dot={<View style={globalStyles.dot} />}
activeDot={<View style={globalStyles.activeDot} />}
paginationStyle={{
bottom: 10,
}}
>
loop={false}
width={winWidth}
height={45}
data={slides}
scrollAnimationDuration={500}
onSnapToItem={(index) => setCurrentIndex(index)}
renderItem={({ item }) => (
<View style={{ flexDirection: "row", paddingHorizontal: 10 }}>
{item.map((logo, idx) => (
<TouchableOpacity key={idx} style={globalStyles.logoItem}>
<Image source={logo} style={globalStyles.logoImage} />
</TouchableOpacity>
))}
</View>
)}
/>
{/* Custom Dots */}
<View
style={{
flexDirection: "row",
paddingHorizontal: 10,
justifyContent: "center",
marginTop: 10,
}}
>
<TouchableOpacity style={globalStyles.logoItem}>
<Image
source={require("../../assets/images/logo-hacom.png")}
style={globalStyles.logoImage}
/>
</TouchableOpacity>
<TouchableOpacity style={globalStyles.logoItem}>
<Image
source={require("../../assets/images/logo-hacom.png")}
style={globalStyles.logoImage}
/>
</TouchableOpacity>
<TouchableOpacity style={globalStyles.logoItem}>
<Image
source={require("../../assets/images/logo-anphat.png")}
style={globalStyles.logoImage}
/>
</TouchableOpacity>
<TouchableOpacity style={globalStyles.logoItem}>
<Image
source={require("../../assets/images/logo-gearvn.png")}
style={globalStyles.logoImage}
/>
</TouchableOpacity>
</View>
{slides.map((_, index) => (
<TouchableOpacity key={index} onPress={() => handleDotPress(index)}>
<View
style={{
flexDirection: "row",
paddingHorizontal: 10,
}}
>
<TouchableOpacity style={globalStyles.logoItem}>
<Image
source={require("../../assets/images/logo-hacom.png")}
style={globalStyles.logoImage}
/>
</TouchableOpacity>
<TouchableOpacity style={globalStyles.logoItem}>
<Image
source={require("../../assets/images/logo-hacom.png")}
style={globalStyles.logoImage}
/>
</TouchableOpacity>
<TouchableOpacity style={globalStyles.logoItem}>
<Image
source={require("../../assets/images/logo-anphat.png")}
style={globalStyles.logoImage}
/>
</TouchableOpacity>
<TouchableOpacity style={globalStyles.logoItem}>
<Image
source={require("../../assets/images/logo-gearvn.png")}
style={globalStyles.logoImage}
key={index}
style={[
globalStyles.dot,
currentIndex === index && globalStyles.activeDot,
]}
/>
</TouchableOpacity>
))}
</View>
</Swiper>
</View>
);
};
@@ -738,67 +739,65 @@ const BoxArticleClassifieds = () => {
};
const BoxBannerSaleHome = () => {
const [activeIndex, setActiveIndex] = useState(0);
const carouselRef = useRef<ICarouselInstance>(null);
const slides = [
[
require("../../assets/images/banner-sale-1.png"),
require("../../assets/images/banner-sale-2.png"),
],
[
require("../../assets/images/banner-sale-2.png"),
require("../../assets/images/banner-sale-3.png"),
],
[
require("../../assets/images/banner-sale-3.png"),
require("../../assets/images/banner-sale-1.png"),
],
];
const handleDotPress = (index: number) => {
if (carouselRef.current) {
carouselRef.current.scrollTo({ index, animated: true });
}
};
return (
<View style={globalStyles.BoxBannerSaleHome}>
<Swiper
style={globalStyles.sliderBannerSale}
autoplay={false}
showsPagination={true}
dot={<View style={globalStyles.dot} />}
activeDot={<View style={globalStyles.activeDot} />}
paginationStyle={{
bottom: 10,
}}
>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity style={globalStyles.itemBannerSale}>
<Image
source={require("../../assets/images/banner-sale-1.png")}
style={globalStyles.imgBannerSale}
alt="anh tin tuc"
/>
</TouchableOpacity>
<TouchableOpacity style={globalStyles.itemBannerSale}>
<Image
source={require("../../assets/images/banner-sale-2.png")}
style={globalStyles.imgBannerSale}
alt="anh tin tuc"
/>
<Carousel
ref={carouselRef}
width={winWidth}
height={220}
data={slides}
loop
autoPlay
autoPlayInterval={5000}
scrollAnimationDuration={600}
onSnapToItem={(index) => setActiveIndex(index)}
renderItem={({ item }) => (
<View style={{ flexDirection: "row", paddingHorizontal: 10 }}>
{item.map((logo, idx) => (
<TouchableOpacity key={idx} style={globalStyles.itemBannerSale}>
<Image source={logo} style={globalStyles.imgBannerSale} />
</TouchableOpacity>
))}
</View>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity style={globalStyles.itemBannerSale}>
<Image
source={require("../../assets/images/banner-sale-2.png")}
style={globalStyles.imgBannerSale}
alt="anh tin tuc"
/>
</TouchableOpacity>
<TouchableOpacity style={globalStyles.itemBannerSale}>
<Image
source={require("../../assets/images/banner-sale-3.png")}
style={globalStyles.imgBannerSale}
alt="anh tin tuc"
)}
/>
<View style={globalStyles.dotWrapper}>
{slides.map((_, index) => (
<TouchableOpacity key={index} onPress={() => handleDotPress(index)}>
<View
style={
index === activeIndex
? globalStyles.activeDot
: globalStyles.dot
}
/>
</TouchableOpacity>
))}
</View>
<View style={{ flexDirection: "row" }}>
<TouchableOpacity style={globalStyles.itemBannerSale}>
<Image
source={require("../../assets/images/banner-sale-3.png")}
style={globalStyles.imgBannerSale}
alt="anh tin tuc"
/>
</TouchableOpacity>
<TouchableOpacity style={globalStyles.itemBannerSale}>
<Image
source={require("../../assets/images/banner-sale-1.png")}
style={globalStyles.imgBannerSale}
alt="anh tin tuc"
/>
</TouchableOpacity>
</View>
</Swiper>
</View>
);
};
@@ -899,6 +898,7 @@ const styles = StyleSheet.create({
overflow: "hidden",
borderRadius: 10,
margin: 10,
flex: 1,
},
sliderSwipper: {
height: 210,
@@ -913,11 +913,12 @@ const styles = StyleSheet.create({
objectFit: "cover",
},
imgSlider: {
width: winWidth,
width: winWidth - 20,
height: "100%",
objectFit: "cover",
overflow: "hidden",
borderRadius: 10,
marginRight: 10,
},
buttonNext: {
color: "#fff", // Màu chữ cho các nút chuyển
@@ -930,15 +931,19 @@ const styles = StyleSheet.create({
fontWeight: "bold",
},
boxMenuHome: {
marginTop: 10,
flexDirection: "row",
flexWrap: "wrap",
justifyContent: "flex-start",
marginLeft: 20,
marginRight: 20,
gap: 20,
gap: 10,
backgroundColor: "#fff",
},
MenuItem: {
width: winWidth / 4 - 25,
width: winWidth / 4 - 20,
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
},
boxIconMenu: {
width: 50,
@@ -948,14 +953,15 @@ const styles = StyleSheet.create({
justifyContent: "center",
backgroundColor: "#eeeeee",
borderRadius: 25,
marginLeft: "auto",
marginRight: "auto",
flexDirection: "row",
margin: "auto",
},
textMenu: {
textAlign: "center",
fontSize: 14,
color: "#333",
fontWeight: "bold",
height: 35,
},
iconMenuItem: {
width: 20,

View File

@@ -92,7 +92,6 @@ export default function Buildpc() {
{/* Buildpc Content */}
<CreateBuildpc />
</View>
<Footer navigation={navigation} />
</ScrollView>
</AppLayout>
);
@@ -136,6 +135,7 @@ const styles = StyleSheet.create({
marginTop: 12,
backgroundColor: "#fff",
borderRadius: 8,
marginBottom: 10,
},
inputBox: {
flexDirection: "row",

View File

@@ -81,7 +81,6 @@ export function BuildpcDeail() {
{/* đánh giá */}
<ReviewList />
</View>
<Footer navigation={navigation} />
</ScrollView>
</AppLayout>
);

View File

@@ -58,7 +58,6 @@ export function CompareBuildpc() {
<ListCompare />
</View>
<Footer navigation={navigation} />
</ScrollView>
</AppLayout>
);

View File

@@ -0,0 +1,76 @@
import React from "react";
import {
View,
Text,
Image,
TouchableOpacity,
ScrollView,
StyleSheet,
Dimensions,
} from "react-native";
const { width } = Dimensions.get("window");
import { useNavigation, NavigationProp } from "@react-navigation/native";
import Footer from "@components/footer/Footer";
import AppLayout from "@layouts/AppLayout";
import { Ionicons } from "@expo/vector-icons"; // hoặc icon_2025 nếu bạn có icon font riêng
export default function ListCompare() {
const navigation = useNavigation<NavigationProp<any>>();
return (
<AppLayout activeTab="classifieds">
<ScrollView>
<View style={styles.container}>
{/* Breadcrumb */}
<View style={styles.breadcrumb}>
<View style={styles.breadcrumbItem}>
<TouchableOpacity>
<Ionicons
name="home"
size={16}
color="#637381"
style={styles.icon}
/>
</TouchableOpacity>
<Text style={styles.angle}></Text>
</View>
<View style={styles.breadcrumbItem}>
<Text style={styles.text}>Rao vặt</Text>
</View>
</View>
<View style={styles.boxCategory}>
<Text>Danh mục</Text>
</View>
</View>
<Footer navigation={navigation} />
</ScrollView>
</AppLayout>
);
}
const styles = StyleSheet.create({
container: {
paddingHorizontal: 10,
},
breadcrumb: {
flexDirection: "row",
alignItems: "center",
paddingVertical: 12,
flexWrap: "wrap",
},
breadcrumbItem: {
flexDirection: "row",
alignItems: "center",
marginRight: 8,
},
text: {
color: "#000",
},
icon: {
marginRight: 5,
},
angle: {
marginLeft: 12,
color: "#888",
},
});

View File

@@ -24,7 +24,6 @@ import ProductInformation from "./ProductInformation";
import ProductSpecification from "./ProductSpecification";
import { products } from "../../data/product";
import ProductItem from "@components/product/ItemProduct";
import Footer from "@components/footer/Footer";
const ProductDetail = () => {
const navigation = useNavigation<NavigationProp<any>>();
@@ -192,7 +191,6 @@ const ProductDetail = () => {
</TouchableOpacity>
</View>
</View>
<Footer navigation={navigation} />
</ScrollView>
</AppLayout>
);
@@ -358,7 +356,7 @@ const styles = StyleSheet.create({
flexDirection: "row",
flexWrap: "wrap",
gap: 5,
marginLeft: 7,
marginTop: 10,
marginLeft: 7,
},
});

View File

@@ -70,7 +70,6 @@ const ProductList = () => {
))}
</View>
</View>
<Footer navigation={navigation} />
</ScrollView>
</AppLayout>
);

View File

@@ -67,7 +67,6 @@ const ProductListBig = () => {
showsVerticalScrollIndicator={false}
/>
</View>
<Footer navigation={navigation} />
</ScrollView>
</AppLayout>
);

View File

@@ -51,7 +51,7 @@ export const globalStyles = StyleSheet.create({
marginBottom: 10,
flexDirection: 'row',
flexWrap: 'wrap',
gap: 10
gap: 10,
},
moreAll: {
width: 110,
@@ -85,8 +85,11 @@ export const globalStyles = StyleSheet.create({
height: 300
},
categoryItem: {
width: '25%',
marginBottom: 25
width: winWidth / 4 - 15,
marginBottom: 25,
flexDirection: 'column',
alignItems: 'center',
justifyContent: 'center',
},
boxImageCategory: {
width: 70,
@@ -99,7 +102,8 @@ export const globalStyles = StyleSheet.create({
alignItems: 'center',
},
iconCategory: {
width: 50,
width: 45,
height: 45,
objectFit: 'contain',
},
categoryName: {
@@ -107,6 +111,15 @@ export const globalStyles = StyleSheet.create({
fontWeight: '500',
marginTop: 5,
textAlign: 'center',
height: 40
},
dotWrapper: {
position: 'absolute',
bottom: 10,
left: 0,
right: 0,
flexDirection: 'row',
justifyContent: 'center',
},
dot: {
backgroundColor: 'rgba(0,0,0,.2)',
@@ -116,11 +129,7 @@ export const globalStyles = StyleSheet.create({
margin: 3,
},
activeDot: {
backgroundColor: '#ff7a00',
width: 40,
height: 10,
borderRadius: 5,
margin: 3,
backgroundColor: '#f9ef06',
},
BoxBusiness: {
marginTop: 25,
@@ -135,10 +144,11 @@ export const globalStyles = StyleSheet.create({
},
sliderBusinesses: {
marginTop: 20,
height: 85
width: winWidth - 10,
marginBottom: 10
},
logoItem: {
width: itemWidth,
width: itemWidth - 3,
height: 40,
borderRadius: 8,
backgroundColor: '#fff',
@@ -159,7 +169,7 @@ export const globalStyles = StyleSheet.create({
marginTop: 20,
width: winWidth,
paddingLeft: 10,
paddingRight: 10,
paddingRight: 10
},
textBoxProductSaveHome: {
fontSize: 20,
@@ -289,18 +299,13 @@ export const globalStyles = StyleSheet.create({
imgBannerSale: {
width: '100%',
height: '100%',
objectFit: 'cover',
resizeMode: 'cover',
borderRadius: 12
},
sliderBannerSale: {
flexDirection: 'row',
alignItems: 'center',
height: 200
},
itemBannerSale: {
width: winWidth - 50,
height: 160,
objectFit: 'cover',
width: winWidth - 80,
height: 180,
resizeMode: 'cover',
marginRight: 10
},
BoxPromotionHome: {

1349
yarn.lock

File diff suppressed because it is too large Load Diff