This commit is contained in:
2025-06-05 10:09:29 +07:00
parent eb69d8bbc4
commit bee191d7c6
40 changed files with 69930 additions and 823 deletions

View File

@@ -0,0 +1,102 @@
import * as React from "react";
import { View, Text, StyleSheet, Image, TouchableOpacity } from "react-native";
type FooterItemProps = {
label: string;
icon: string;
};
const FooterItem = ({ label, icon }: FooterItemProps) => (
<TouchableOpacity style={styles.footerItem}>
<Text style={styles.footerItemText}>{label}</Text>
<Image
source={{ uri: icon }}
style={styles.footerItemIcon}
resizeMode="contain"
/>
</TouchableOpacity>
);
const Footer = () => {
return (
<View style={styles.container}>
<View style={styles.spacer} />
<View style={styles.content}>
<FooterItem
label="Về chúng tôi"
icon="https://cdn.builder.io/api/v1/image/assets/TEMP/0f5cda0be55412240422e2274330e3f6e64023d3?placeholderIfAbsent=true&apiKey=1fa9f06a1e81406d92148011750a3756"
/>
<FooterItem
label="Tuyển dụng"
icon="https://cdn.builder.io/api/v1/image/assets/TEMP/0f5cda0be55412240422e2274330e3f6e64023d3?placeholderIfAbsent=true&apiKey=1fa9f06a1e81406d92148011750a3756"
/>
<FooterItem
label="Liên kết"
icon="https://cdn.builder.io/api/v1/image/assets/TEMP/0f5cda0be55412240422e2274330e3f6e64023d3?placeholderIfAbsent=true&apiKey=1fa9f06a1e81406d92148011750a3756"
/>
<FooterItem
label="Giới thiệu"
icon="https://cdn.builder.io/api/v1/image/assets/TEMP/5263024b89e68e21be091071a684ede939b2cbf7?placeholderIfAbsent=true&apiKey=1fa9f06a1e81406d92148011750a3756"
/>
<Image
source={{
uri: "https://cdn.builder.io/api/v1/image/assets/TEMP/e7727a56c76a2e7c02cd1646e68e8b20cefb30e1?placeholderIfAbsent=true&apiKey=1fa9f06a1e81406d92148011750a3756",
}}
style={styles.logo}
resizeMode="contain"
/>
</View>
</View>
);
};
const styles = StyleSheet.create({
container: {
alignSelf: "stretch",
marginTop: 28,
width: "100%",
paddingBottom: 21,
},
spacer: {
display: "flex",
minHeight: 10,
width: "100%",
},
content: {
marginTop: 17,
width: "100%",
paddingLeft: 10,
paddingRight: 10,
},
footerItem: {
borderRadius: 4,
display: "flex",
paddingLeft: 9,
paddingRight: 9,
paddingTop: 6,
paddingBottom: 13,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 8,
},
footerItemText: {
color: "#444",
fontFamily: "Shopee Display, -apple-system, Roboto, Helvetica, sans-serif",
fontSize: 13,
fontWeight: "700",
},
footerItemIcon: {
width: 11,
height: 6,
marginTop: 7,
},
logo: {
width: 95,
height: 40,
marginTop: 17,
marginLeft: 10,
},
});
export default Footer;

View File

@@ -0,0 +1,431 @@
import React, { useState } from "react";
import {
View,
StyleSheet,
Image,
Text,
TextInput,
TouchableOpacity,
Dimensions,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import { Picker } from "@react-native-picker/picker";
import { NavigationContainer } from "@react-navigation/native";
import { createDrawerNavigator } from "@react-navigation/drawer";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
const Header = () => {
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.headerTop}>
<View style={styles.headerTopLeft}>
<TouchableOpacity onPress={toggleDrawer} style={styles.menu}>
<Ionicons style={styles.iconMenu} name="menu-outline" size={30} />
</TouchableOpacity>
<Image
source={require("../../../assets/images/logo.png")}
style={styles.logo}
resizeMode="contain"
/>
</View>
<View style={styles.boxLocation}>
<Ionicons style={styles.location} name="location-outline" size={34} />
<View>
<Text style={styles.textLocation}>Bạn đang </Text>
<View style={styles.boxSort}>
<TouchableOpacity
style={styles.boxSortButton}
onPress={() => setopenSort(!openSort)}
>
<Text style={styles.boxSortButtonActive}> nội</Text>
<Ionicons
style={styles.boxSortButtonIcon}
name="caret-down-outline"
/>
</TouchableOpacity>
<View
style={
openSort
? styles.boxSortList
: [styles.boxSortList, styles.boxSortListActive]
}
>
<TouchableOpacity
style={[styles.boxSortItem, styles.boxSortItemActive]}
>
<Text
style={[
styles.boxSortItemText,
styles.boxSortItemTextActive,
]}
>
nội
</Text>
<Ionicons
style={styles.boxSortItemTextIcon}
name="checkmark-outline"
/>
</TouchableOpacity>
<TouchableOpacity style={styles.boxSortItem}>
<Text style={styles.boxSortItemText}>HCM</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.boxSortItem}>
<Text style={styles.boxSortItemText}>Đà nẵng</Text>
</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({
headerTop: {
flexDirection: "row",
alignItems: "center",
width: winWidth,
paddingRight: 10,
paddingLeft: 10,
paddingTop: 45,
paddingBottom: 10,
backgroundColor: "#462F91",
justifyContent: "space-between",
},
headerTopLeft: {
flexDirection: "row",
alignItems: "center",
gap: 5,
},
logo: {
width: 99,
height: 20,
},
searchContainer: {
width: winWidth,
paddingLeft: 10,
paddingRight: 10,
paddingTop: 9,
paddingBottom: 9,
backgroundColor: "#462F91",
},
searchBar: {
borderRadius: 4,
paddingLeft: 10,
paddingRight: 10,
paddingTop: 6,
paddingBottom: 6,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
backgroundColor: "#FFFFFF",
},
searchInput: {
flex: 1,
fontSize: 12,
fontFamily: "Roboto, -apple-system, Roboto, Helvetica, sans-serif",
fontWeight: "500",
color: "#000000",
height: 32,
},
searchIconContainer: {
flexDirection: "row",
alignItems: "center",
gap: 9,
},
divider: {
borderColor: "rgba(179, 179, 179, 1)",
borderStyle: "solid",
borderWidth: 1,
width: 1,
height: 20,
},
iconMenu: {
color: "#462f91",
},
location: {
color: "#FFFFFF",
width: 34,
height: 34,
},
boxlocalistion: {
height: 20,
width: 40,
color: "#fff",
},
boxLocation: {
flexDirection: "row",
alignItems: "center",
gap: 5,
color: "#FFFFFF",
},
textLocation: {
color: "#FFFFFF",
fontSize: 10,
},
boxSort: {
zIndex: 1,
},
boxSortButton: {
flexDirection: "row",
alignItems: "center",
height: 30,
},
boxSortButtonTextBold: {
fontSize: 14,
fontWeight: "bold",
},
boxSortButtonActive: {
color: "#fff",
},
boxSortButtonIcon: {
marginLeft: 5,
color: "#fff",
},
boxSortList: {
paddingHorizontal: 10,
position: "absolute",
right: 0,
top: 40,
display: "none",
backgroundColor: "#fff",
width: 100,
},
boxSortListActive: {
display: "flex",
},
boxSortItem: {
paddingVertical: 10,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
},
boxSortItemActive: {
borderBottomColor: "#D8262F",
borderBottomWidth: 1,
},
boxSortItemText: {
fontSize: 14,
},
boxSortItemTextActive: {
color: "#D8262F",
},
boxSortItemTextIcon: {
color: "#D8262F",
},
boxFilter: {
width: 70,
height: 30,
alignItems: "center",
flexDirection: "row",
justifyContent: "flex-end",
borderLeftWidth: 1,
borderLeftColor: "#e1e1e1",
},
menu: {
backgroundColor: "#fff",
width: 30,
height: 30,
borderRadius: 4,
alignItems: "center",
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,198 @@
import React, { useState } from "react";
import {
View,
StyleSheet,
Image,
Text,
TextInput,
TouchableOpacity,
Dimensions,
} from "react-native";
var winWidth = Dimensions.get("window").width; //full width
var winHeight = Dimensions.get("window").height;
const ItemProduct = ({ product }: { product: any }) => {
function formatCurrency(a: number | string): string {
let b = parseFloat(a.toString())
.toFixed(2)
.replace(/(\d)(?=(\d{3})+\.)/g, "$1.")
.toString();
var len = b.length;
b = b.substring(0, len - 3);
return b;
}
return (
<View style={styles.productItem}>
<TouchableOpacity style={styles.productImage}>
<Image
source={{ uri: product.productImage.large }}
style={styles.productImg}
alt={product.productName}
/>
<View style={styles.boxSaleoff}>
<View style={styles.beforeSaleoff}></View>
<Text style={styles.saleoffText}>{product.saleOff}</Text>
</View>
</TouchableOpacity>
<View>
<TouchableOpacity>
<Text numberOfLines={2} style={styles.productName}>
{product.productName}
</Text>
</TouchableOpacity>
<View style={styles.BoxPriceRating}>
<View>
<Text style={styles.price}>{formatCurrency(product.price)}đ</Text>
<Text style={styles.oldPrice}>
{formatCurrency(product.oldPrice)}đ
</Text>
</View>
<View>
<Image
source={
product.review.rate !== undefined
? require(`../../../assets/images/icon_star_${product.review.rate}.png`)
: require("../../../assets/images/icon_star_0.png")
}
style={styles.iconReviewRating}
alt="icon review"
resizeMode="contain"
/>
</View>
</View>
<View style={styles.boxStore}>
<View style={styles.boxIconStore}>
<Image
source={require("../../../assets/images/icon_store_white.png")}
style={styles.iconStore}
alt="icon store"
resizeMode="contain"
/>
</View>
<Text style={styles.storeName}>
{product.toltalStore} cửa hàng bán
</Text>
</View>
</View>
</View>
);
};
const styles = StyleSheet.create({
productItem: {
width: winWidth / 2 - 20,
marginRight: 10,
marginBottom: 10,
padding: 12,
},
productImage: {
width: winWidth / 2 - 20,
height: 150,
position: "relative",
marginBottom: 5,
borderWidth: 1,
borderColor: "#d3d3d3",
backgroundColor: "#fff",
borderRadius: 8,
},
productImg: {
width: "100%",
height: "100%",
objectFit: "contain",
},
boxSaleoff: {
position: "absolute",
right: 10,
top: 10,
width: 35,
height: 35,
borderRadius: 25,
backgroundColor: "#da251c",
justifyContent: "center",
alignItems: "center",
},
beforeSaleoff: {
position: "absolute",
top: "50%",
left: "50%",
transform: [{ translateY: "-50%" }, { translateX: "-50%" }],
width: 30,
height: 30,
borderWidth: 1,
borderStyle: "dashed",
borderColor: "#fff",
borderRadius: 25,
},
saleoffText: {
fontSize: 8,
fontWeight: 700,
backgroundColor: "#da251c",
borderRadius: 25,
color: "#fff",
paddingLeft: 2,
},
productName: {
fontWeight: 700,
fontSize: 13,
color: "#000",
marginBottom: 5,
},
summary: {
marginTop: 5,
color: "#595959",
},
locahostPro: {
flexDirection: "row",
alignItems: "center",
},
iconMap: {},
BoxPriceRating: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "flex-end",
},
price: {
fontSize: 15,
fontWeight: "bold",
color: "#d80a00",
},
oldPrice: {
fontSize: 13,
textDecorationLine: "line-through",
color: "#595959",
fontWeight: "bold",
},
iconReviewRating: {
width: 58,
height: 11,
marginBottom: 2,
},
boxStore: {
flexDirection: "row",
alignItems: "center",
marginTop: 5,
},
boxIconStore: {
width: 24,
height: 24,
borderRadius: 25,
backgroundColor: "#FF7A00",
flexDirection: "row",
justifyContent: "center",
alignItems: "center",
marginRight: 5,
},
iconStore: {
width: 18,
height: 18,
},
storeName: {
fontSize: 13,
fontWeight: "bold",
color: "#000",
},
});
export default ItemProduct;