This commit is contained in:
2025-07-21 09:00:48 +07:00
parent 19f99ca33f
commit cc29aaabb4
11 changed files with 1565 additions and 140 deletions

View File

@@ -13,8 +13,11 @@ import PopupBuildpc from "@components/buildpc/PopupBuildpc"; // Bạn cần tự
import Feather from "@expo/vector-icons/Feather";
import AntDesign from "@expo/vector-icons/AntDesign";
const { width } = Dimensions.get("window");
import { useNavigation, NavigationProp } from "@react-navigation/native";
export function CreateBuildpc() {
const navigation = useNavigation<NavigationProp<any>>();
const [showPopup, setShowPopup] = useState(false);
return (
@@ -30,14 +33,17 @@ export function CreateBuildpc() {
style={styles.productImage}
resizeMode="contain"
/>
<Text style={styles.productName}>
<Text
style={styles.productName}
onPress={() => navigation?.navigate("buildpcdetail")}
>
AMD Ryzen 7 9800x3D 4.7 GHz 8-Core Processor
</Text>
</View>
<View style={styles.boxQlt}>
<Text>Số lượng</Text>
<TextInput
value="111"
value="1"
style={styles.inputQl}
keyboardType="numeric"
placeholder="Nhập số"
@@ -93,6 +99,91 @@ export function CreateBuildpc() {
</TouchableOpacity>
</View>
</View>
{/* Thêm lựa chọn CPU */}
<TouchableOpacity
style={styles.addButton}
onPress={() => setShowPopup(true)}
>
<Text style={styles.addButtonText}>+ Chọn thêm CPU</Text>
</TouchableOpacity>
</View>
{/* Item CPU */}
<View style={styles.itemRow}>
<Text style={styles.componentTitle}>CPU</Text>
<View style={styles.productInfo}>
<View style={styles.productLeft}>
<View style={styles.infolinhkien}>
<Image
source={require("../../../assets/images/lienkien-ram.png")}
style={styles.productImage}
resizeMode="contain"
/>
<Text
style={styles.productName}
onPress={() => navigation?.navigate("buildpcdetail")}
>
AMD Ryzen 7 9800x3D 4.7 GHz 8-Core Processor
</Text>
</View>
<View style={styles.boxQlt}>
<Text>Số lượng</Text>
<TextInput
value="1"
style={styles.inputQl}
keyboardType="numeric"
placeholder="Nhập số"
/>
</View>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginBottom: 10,
}}
>
<Text style={{ marginRight: 10, fontSize: 13 }}>Giá bán:</Text>
<Text style={styles.oldPrice}>4.700.000 Vnđ</Text>
</View>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginBottom: 10,
}}
>
<Text style={{ marginRight: 10, fontSize: 13 }}>Khuyến mãi:</Text>
<Text style={styles.discount}>20%</Text>
</View>
<View
style={{
flexDirection: "row",
alignItems: "center",
marginBottom: 10,
}}
>
<Text style={{ marginRight: 10, fontSize: 13 }}>Thành tiền:</Text>
<Text style={styles.totalPrice}>4.000.000đ</Text>
</View>
<View style={styles.supplierSection}>
<Text style={{ marginRight: 10, fontSize: 13 }}>
Nhà cung cấp
</Text>
<Image
source={require("../../../assets/images/logo-hacom.png")}
style={styles.supplierLogo}
resizeMode="contain"
/>
</View>
</View>
<View style={styles.buttonGroup}>
<TouchableOpacity style={styles.buyButton}>
<Feather name="edit" size={24} color="#1877f2" />
</TouchableOpacity>
<TouchableOpacity style={styles.removeButton}>
<AntDesign name="delete" size={24} color="#ff0000" />
</TouchableOpacity>
</View>
</View>
{/* Thêm lựa chọn CPU */}
<TouchableOpacity
@@ -101,6 +192,21 @@ export function CreateBuildpc() {
>
<Text style={styles.addButtonText}>+ Chọn thêm CPU</Text>
</TouchableOpacity>
</View>
{/* item gcu */}
<View style={styles.itemRow}>
<Text style={styles.componentTitle}>CPU</Text>
{/* Thêm lựa chọn CPU */}
<TouchableOpacity
style={styles.addButton}
onPress={() => setShowPopup(true)}
>
<Text style={styles.addButtonText}>+ Chọn sản phẩm</Text>
</TouchableOpacity>
</View>
{/* Tổng tiền */}
<View style={styles.totalSection}>
<Text style={styles.totalText}>Tổng tiền 2 sản phẩm: 8.000.000đ</Text>
@@ -140,6 +246,7 @@ const styles = StyleSheet.create({
},
productInfo: {
width: "100%",
flexDirection: "row",
alignItems: "flex-start",
justifyContent: "space-between",
@@ -210,6 +317,7 @@ const styles = StyleSheet.create({
paddingVertical: 2,
},
addButton: {
width: 115,
marginTop: 12,
backgroundColor: "#d4d4d4",
borderRadius: 4,

View File

@@ -0,0 +1,115 @@
import React, { useRef, useState, useCallback } from "react";
import {
View,
Image,
FlatList,
TouchableOpacity,
Dimensions,
StyleSheet,
} from "react-native";
import Carousel from "react-native-reanimated-carousel";
const { width } = Dimensions.get("window");
const THUMB_SIZE = 64;
const SPACING = 10;
const images = [
require("../../../assets/images/big-product-detail.png"),
require("../../../assets/images/big-product-detail.jpg"),
require("../../../assets/images/big-product-detail-2.jpg"),
require("../../../assets/images/big-product-detail-3.jpg"),
require("../../../assets/images/big-product-detail-4.jpg"),
];
const ImageGallery = () => {
const [activeIndex, setActiveIndex] = useState(0);
const carouselRef = useRef<Carousel<any>>(null);
const thumbnailListRef = useRef<FlatList>(null);
const scrollToThumbnail = useCallback((index: number) => {
const offset = index * (THUMB_SIZE + SPACING) - width / 2 + THUMB_SIZE / 2;
thumbnailListRef.current?.scrollToOffset({
offset: Math.max(0, offset),
animated: true,
});
}, []);
const onSnapToItem = useCallback(
(index: number) => {
setActiveIndex(index);
scrollToThumbnail(index);
},
[scrollToThumbnail]
);
const onThumbnailPress = useCallback((index: number) => {
carouselRef.current?.scrollTo({ index, animated: true });
}, []);
const renderThumbnail = useCallback(
({ item, index }: { item: any; index: number }) => (
<TouchableOpacity onPress={() => onThumbnailPress(index)}>
<Image
source={item}
style={[
styles.thumbnail,
activeIndex === index && styles.activeThumbnail,
]}
/>
</TouchableOpacity>
),
[activeIndex, onThumbnailPress]
);
return (
<View style={styles.container}>
<Carousel
ref={carouselRef}
loop={false}
width={width}
height={300}
data={images}
scrollAnimationDuration={400}
renderItem={({ item }) => (
<Image source={item} style={styles.bigImage} resizeMode="contain" />
)}
onSnapToItem={onSnapToItem}
/>
<FlatList
ref={thumbnailListRef}
data={images}
horizontal
keyExtractor={(_, i) => i.toString()}
renderItem={renderThumbnail}
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.thumbList}
/>
</View>
);
};
export default ImageGallery;
const styles = StyleSheet.create({
container: {},
bigImage: {
width: width - 40,
height: 280,
borderWidth: 1,
borderColor: "#b1b1b1",
borderRadius: 12,
},
thumbnail: {
width: THUMB_SIZE,
height: THUMB_SIZE,
marginRight: SPACING,
borderRadius: 6,
borderWidth: 1,
borderColor: "#b1b1b1",
},
activeThumbnail: {
borderColor: "#1877F2",
},
thumbList: {},
});

View File

@@ -1,98 +1,334 @@
import React from "react";
import React, { useState } from "react";
import {
Modal,
View,
Text,
TouchableOpacity,
StyleSheet,
TextInput,
ScrollView,
Image,
FlatList,
StyleSheet,
Dimensions,
} from "react-native";
import { Ionicons } from "@expo/vector-icons"; // hoặc react-native-vector-icons
import { Ionicons } from "@expo/vector-icons";
const { width } = Dimensions.get("window");
import FilterDropdown from "@components/product/FilterDropdown";
interface PopupBuildpcProps {
visible: boolean;
onClose: () => void;
}
export function PopupBuildpc({ show, onClose }) {
const productList = Array(10).fill({
name: "AMD Ryzen 7 9800x3D 4.7 GHz 8-Core Processor",
capacity: "8bg",
generation: "5",
memory: "GDDR5",
noise: "5db",
color: "Đen",
rating: 4,
reviews: 125,
price: "3.400.000đ",
image: require("../../../assets/images/lienkien-ram.png"),
});
export default function PopupBuildpc({ visible, onClose }: PopupBuildpcProps) {
const [selectedAddress, setSelectedAddress] = useState("");
const [selectedBrand, setSelectedBrand] = useState("");
const renderStars = (count: number) => {
return (
<Modal
animationType="fade"
transparent={true}
visible={visible}
onRequestClose={onClose}
>
<View style={styles.overlay}>
<View style={styles.modalContent}>
{/* Close button */}
<TouchableOpacity style={styles.closeBtn} onPress={onClose}>
<Ionicons name="close" size={24} color="#000" />
</TouchableOpacity>
<Text style={styles.title}>Chọn linh kiện thay thế</Text>
{/* List of products (giả lập) */}
<ScrollView>
{[1, 2, 3].map((item) => (
<View key={item} style={styles.item}>
<Text style={styles.itemText}>Linh kiện {item}</Text>
<TouchableOpacity style={styles.selectButton}>
<Text style={styles.selectButtonText}>Chọn</Text>
</TouchableOpacity>
</View>
<View style={{ flexDirection: "row" }}>
{Array.from({ length: 5 }).map((_, index) => (
<Ionicons
key={index}
name="star"
size={10}
color={index < count ? "#ff7a00" : "#d9d9d9"}
/>
))}
</ScrollView>
</View>
);
};
return (
<Modal visible={show} transparent animationType="fade">
<TouchableOpacity
style={styles.overlay}
onPress={onClose}
activeOpacity={1}
/>
<View style={styles.modalContent}>
<View style={styles.header}>
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
width: "100%",
marginBottom: 15,
}}
>
<Text style={styles.title}>Chọn linh kiện</Text>
<TouchableOpacity onPress={onClose}>
<Ionicons name="close" size={24} color="#fff" />
</TouchableOpacity>
</View>
<View style={styles.searchBox}>
<TextInput
style={styles.input}
placeholder="Bạn cần tìm linh kiện gì..."
/>
<TouchableOpacity style={styles.searchButton}>
<Ionicons name="search" size={24} color="#000" />
</TouchableOpacity>
</View>
</View>
<ScrollView>
<View style={styles.body}>
<View style={styles.sidebar}>
<Text style={{ fontWeight: 700 }}>230 sản phẩm</Text>
<View style={styles.actionsRow}>
<Text style={styles.actionText}>Chọn tất cả</Text>
<Text style={styles.actionText}>Bỏ chọn tất cả</Text>
<Text style={styles.actionText}>So sánh sản phẩm đã chọn</Text>
</View>
<View style={styles.BoxFilter}>
<View style={styles.titleFilter}>
<Text>Bộ lọc</Text>
<Ionicons name="filter" size={24} color="black" />
</View>
</View>
<View style={styles.listFilter}>
<FilterDropdown
placeholder="Địa chỉ"
options={["Hà Nội", "TP.HCM", "Đà Nẵng"]}
selected={selectedAddress}
onSelect={setSelectedAddress}
/>
<FilterDropdown
placeholder="Thương hiệu"
options={["Dell", "HP", "Lenovo"]}
selected={selectedBrand}
onSelect={setSelectedBrand}
/>
</View>
</View>
<View style={styles.mainContent}>
<FlatList
data={productList}
keyExtractor={(_, index) => index.toString()}
style={styles.productList}
renderItem={({ item }) => (
<View style={styles.productItem}>
<View style={styles.productInfo}>
<View
style={{ flexDirection: "row", alignItems: "center" }}
>
<Image
source={item.image}
style={styles.productImage}
/>
<Text style={styles.productName}>{item.name}</Text>
</View>
<Text style={styles.proPrice}>{item.price}</Text>
<View
style={{
flexDirection: "row",
alignItems: "center",
flexWrap: "wrap",
}}
>
<View style={styles.itemTs}>
<Text>Dung lượng:</Text>
<Text style={{ marginLeft: 5, fontWeight: 700 }}>
{item.capacity}
</Text>
</View>
<View style={styles.itemTs}>
<Text>Đ òn</Text>
<Text style={{ marginLeft: 5, fontWeight: 700 }}>
{item.generation}
</Text>
</View>
<View style={styles.itemTs}>
<Text>Thế hệ</Text>
<Text style={{ marginLeft: 5, fontWeight: 700 }}>
{item.memory}
</Text>
</View>
<View style={styles.itemTs}>
<Text>Màu sắc</Text>
<Text style={{ marginLeft: 5, fontWeight: 700 }}>
{item.color}
</Text>
</View>
<View style={styles.itemTs}>
<Text>Bộ nhớ</Text>
<Text style={{ marginLeft: 5, fontWeight: 700 }}>
{item.noise}
</Text>
</View>
</View>
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<View
style={{
flexDirection: "row",
alignItems: "center",
}}
>
<Text style={{ marginRight: 5 }}>Đánh giá</Text>
{renderStars(item.rating)}
<Text style={{ fontWeight: 700, marginLeft: 5 }}>
({item.reviews})
</Text>
</View>
</View>
</View>
<TouchableOpacity style={styles.addButton}>
<Text style={styles.addButtonText}>Add</Text>
</TouchableOpacity>
</View>
)}
/>
</View>
</View>
</ScrollView>
</View>
</Modal>
);
}
export default PopupBuildpc;
const styles = StyleSheet.create({
overlay: {
flex: 1,
backgroundColor: "rgba(0,0,0,0.4)",
justifyContent: "center",
alignItems: "center",
backgroundColor: "rgba(0,0,0,0.5)",
},
modalContent: {
width: "90%",
maxHeight: "80%",
backgroundColor: "#fff",
borderRadius: 12,
padding: 20,
position: "relative",
},
closeBtn: {
position: "absolute",
right: 16,
top: 16,
zIndex: 10,
top: "5%",
left: "2.5%",
width: "95%",
height: "90%",
backgroundColor: "#fff",
borderRadius: 8,
overflow: "hidden",
},
header: {
backgroundColor: "#462f91",
padding: 10,
flexDirection: "row",
flexWrap: "wrap",
alignItems: "center",
justifyContent: "space-between",
},
title: {
fontSize: 20,
fontWeight: "bold",
marginBottom: 16,
textAlign: "center",
},
item: {
flexDirection: "row",
justifyContent: "space-between",
paddingVertical: 12,
borderBottomWidth: 1,
borderColor: "#e5e7eb",
},
itemText: {
fontSize: 16,
},
selectButton: {
backgroundColor: "#2563eb",
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 4,
},
selectButtonText: {
color: "#fff",
fontWeight: "bold",
fontSize: 16,
},
searchBox: {
flex: 1,
flexDirection: "row",
marginHorizontal: 10,
backgroundColor: "#fff",
borderRadius: 8,
alignItems: "center",
width: width,
marginLeft: 0,
marginRight: 5,
},
listFilter: {
flexDirection: "row",
flexWrap: "wrap",
marginRight: -5,
},
input: {
flex: 1,
padding: 10,
},
searchButton: {
padding: 10,
},
body: {},
sidebar: {
padding: 10,
},
filterList: {
marginTop: 10,
},
filterTitle: {
fontWeight: "bold",
marginBottom: 5,
},
filterItem: {
paddingVertical: 5,
},
mainContent: {
padding: 10,
},
actionsRow: {
marginTop: 10,
flexDirection: "row",
marginBottom: 10,
borderBottomWidth: 1,
borderColor: "#e3e3e3",
paddingBottom: 5,
},
actionText: {
color: "#004aad",
fontSize: 12,
marginRight: 10,
},
BoxFilter: {},
titleFilter: { flexDirection: "row", alignItems: "center", gap: 5 },
productList: {
flex: 1,
},
productItem: {
borderBottomWidth: 1,
borderBottomColor: "#ddd",
flexDirection: "row",
alignItems: "flex-start",
justifyContent: "space-between",
paddingBottom: 10,
},
productInfo: {
width: width - 120,
},
productImage: {
width: 50,
height: 50,
marginBottom: 5,
},
productName: {
fontWeight: "bold",
marginBottom: 5,
},
proPrice: {
fontWeight: 700,
color: "#ff0000",
},
itemTs: {
width: "50%",
flexDirection: "row",
alignItems: "center",
marginBottom: 10,
},
addButton: {
marginTop: 5,
backgroundColor: "#004aad",
padding: 8,
borderRadius: 4,
},
addButtonText: {
color: "#fff",
textAlign: "center",
},
});

View File

@@ -0,0 +1,84 @@
import React from "react";
import { View, Text, FlatList, StyleSheet } from "react-native";
const specs = [
{ key: "Manufacturer", value: "AMD" },
{
key: "Part #",
value: ["100-1000001084WOF", "AMD Ryzen 7 9800X3D", "100-100001084WOF"],
},
{ key: "Series", value: "AMD Ryzen 7" },
{ key: "Microarchitecture", value: "Zen 5" },
{ key: "Core Family", value: "Granite Ridge" },
{ key: "Socket", value: "AM5" },
{ key: "Core", value: "8" },
{ key: "Thread Count", value: "16" },
{ key: "Performance Core Clock", value: "4.7 GHz" },
{ key: "Performance Core Boost Clock", value: "5.2 GHz" },
{ key: "L2 Cache", value: "8 MB" },
{ key: "L3 Cache", value: "96 MB" },
{ key: "TDP", value: "120 W" },
{ key: "Integrated Graphics", value: "Radeon" },
];
export default function ProductSpecs() {
return (
<View style={styles.container}>
<Text style={styles.title}>Thông số kỹ thuật</Text>
<FlatList
data={specs}
keyExtractor={(item, index) => index.toString()}
renderItem={({ item, index }) => (
<View
style={[styles.item, index < specs.length - 1 && styles.itemBorder]}
>
<Text style={styles.label}>{item.key}</Text>
{Array.isArray(item.value) ? (
<View style={styles.list}>
{item.value.map((v, i) => (
<Text key={i} style={styles.value}>
{v}
</Text>
))}
</View>
) : (
<Text style={styles.value}>{item.value}</Text>
)}
</View>
)}
/>
</View>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: "#fff",
padding: 12,
},
title: {
fontSize: 20,
fontWeight: "bold",
paddingBottom: 10,
borderBottomWidth: 1,
borderColor: "#ababab",
},
item: {
paddingVertical: 10,
},
itemBorder: {
borderBottomWidth: 1,
borderColor: "#e5e5e5",
},
label: {
fontWeight: "bold",
marginBottom: 4,
fontSize: 14,
},
value: {
fontSize: 14,
color: "#444",
},
list: {
gap: 2,
},
});

View File

@@ -0,0 +1,140 @@
import React from "react";
import { View, Text, Image, TouchableOpacity, StyleSheet } from "react-native";
const providers = [
{
logo: require("../../../assets/images/logo-hacom.png"),
old: "3.700.000 Vnđ",
deal: "20%",
status: "Còn hàng",
ship: "Liên hệ",
total: "3.000.000 Vnđ",
},
{
logo: require("../../../assets/images/logo-hacom.png"),
old: "3.700.000 Vnđ",
deal: "20%",
status: "Còn hàng",
ship: "free",
total: "3.000.000 Vnđ",
},
];
export function ProvidersList() {
return (
<View style={styles.container}>
{providers.map((p, i) => (
<View key={i} style={styles.row}>
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 10,
}}
>
<Image source={p.logo} style={styles.logo} resizeMode="contain" />
<TouchableOpacity style={styles.buyBtn}>
<Text style={styles.buyText}>Mua ngay</Text>
</TouchableOpacity>
</View>
<View style={styles.flexBox}>
<Text style={styles.textFlex}>Giá sản phẩm:</Text>
<Text style={styles.price}>4.700.000 Vnđ</Text>
<Text style={styles.oldPrice}>4.700.000 Vnđ</Text>
</View>
<View style={styles.flexBox}>
<Text style={styles.textFlex}>Khuyến mãi:</Text>
<Text style={styles.discount}>20%</Text>
</View>
<View style={styles.flexBox}>
<Text style={styles.textFlex}>Giao hàng:</Text>
<Text style={styles.ship}>Liên hệ</Text>
</View>
</View>
))}
</View>
);
}
export default ProvidersList;
const styles = StyleSheet.create({
container: {
backgroundColor: "#fff",
padding: 10,
},
row: {
paddingVertical: 10,
borderBottomWidth: 1,
borderColor: "#eee",
},
logo: {
width: 60,
height: 30,
resizeMode: "contain",
},
oldPrice: {
flex: 1,
textDecorationLine: "line-through",
color: "#999",
fontSize: 13,
marginLeft: 10,
},
deal: {
flex: 1,
color: "#e11d48",
fontWeight: "bold",
fontSize: 13,
},
status: {
flex: 1,
fontSize: 13,
fontWeight: "500",
},
green: {
color: "#22c55e",
},
price: {
color: "#ff0000",
fontWeight: 700,
},
ship: {
flex: 1,
fontSize: 13,
fontWeight: 700,
},
cellWide: {
flex: 1.5,
alignItems: "center",
justifyContent: "center",
},
total: {
fontWeight: "bold",
fontSize: 14,
color: "#111",
marginBottom: 4,
},
buyBtn: {
backgroundColor: "#1877f2",
paddingHorizontal: 12,
paddingVertical: 6,
borderRadius: 6,
},
buyText: {
color: "#fff",
fontSize: 13,
fontWeight: "bold",
},
flexBox: {
flexDirection: "row",
alignItems: "center",
marginBottom: 5,
},
textFlex: {
width: 100,
},
discount: {
fontWeight: 700,
},
});

View File

@@ -0,0 +1,175 @@
import React from "react";
import {
View,
Text,
Image,
StyleSheet,
FlatList,
TouchableOpacity,
} from "react-native";
import { Ionicons } from "@expo/vector-icons";
import MaterialCommunityIcons from "@expo/vector-icons/MaterialCommunityIcons";
const reviews = [
{
id: 1,
name: "Dino",
avatar: require("../../../assets/images/avartar-review-1.png"),
time: "10:00pm 20/02/2025",
content: `Lorem Ipsum is simply dummy text of the printing and typesetting industry...`,
views: 120,
comments: 120,
},
// Thêm các review khác nếu có
];
const renderStars = (count: number) => {
return (
<View style={{ flexDirection: "row" }}>
{Array.from({ length: 5 }).map((_, index) => (
<Ionicons
key={index}
name="star"
size={13}
color={index < count ? "#ff7a00" : "#d9d9d9"}
/>
))}
</View>
);
};
export default function ReviewList() {
return (
<View style={styles.container}>
{/* Header */}
<View style={styles.header}>
<Text style={styles.title}>Đánh giá</Text>
</View>
{/* List */}
<FlatList
style={styles.list}
data={reviews}
keyExtractor={(item) => item.id.toString()}
renderItem={({ item }) => (
<View style={styles.item}>
<View style={styles.avatarBox}>
<Image source={item.avatar} style={styles.avatar} />
<Text style={styles.name}>{item.name}</Text>
<Text style={styles.time}>{item.time}</Text>
{renderStars(4)}
</View>
<View style={styles.contentBox}>
<Text style={styles.content}>{item.content}</Text>
<View style={styles.stats}>
<View style={styles.statItem}>
<Text style={styles.statNumber}>{item.views}</Text>
<Text style={styles.icon}>
<Ionicons name="eye-outline" size={20} color="black" />
</Text>
</View>
<View style={styles.statItem}>
<Text style={styles.statNumber}>{item.comments}</Text>
<Text style={styles.icon}>
<MaterialCommunityIcons
name="comment-processing-outline"
size={20}
color="black"
/>
</Text>
</View>
</View>
</View>
</View>
)}
/>
<TouchableOpacity style={styles.button}>
<Text style={styles.buttonText}>Xem tất cả bình luận</Text>
</TouchableOpacity>
</View>
);
}
const styles = StyleSheet.create({
container: {
backgroundColor: "#fff",
padding: 10,
},
header: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingBottom: 10,
borderBottomWidth: 1,
borderColor: "#ababab",
},
title: {
fontSize: 18,
fontWeight: "bold",
},
button: {
backgroundColor: "#2563eb",
paddingHorizontal: 16,
paddingVertical: 6,
borderRadius: 4,
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
},
buttonText: {
color: "#fff",
fontWeight: "bold",
},
list: {
marginTop: 12,
},
item: {
marginBottom: 15,
paddingBottom: 15,
borderBottomWidth: 1,
borderColor: "#e4e4e4",
},
avatarBox: {
marginBottom: 10,
alignItems: "center",
flexDirection: "row",
},
avatar: {
width: 25,
height: 25,
borderRadius: 40,
},
starBox: {},
contentBox: {
flex: 1,
},
name: {
fontWeight: "bold",
marginLeft: 6,
},
time: {
color: "#747474",
marginLeft: 6,
marginRight: 6,
},
content: {
marginBottom: 5,
color: "#333",
},
stats: {
flexDirection: "row",
gap: 10,
},
statItem: {
flexDirection: "row",
alignItems: "center",
marginRight: 10,
},
statNumber: {
marginRight: 5,
fontWeight: "bold",
},
icon: {
fontSize: 14,
},
});

View File

@@ -7,6 +7,8 @@ import ProductListBig from "../screens/product/ProductListBig";
import ProductList from "../screens/product/ProductList";
import ProductDetail from "../screens/product/ProductDetail";
import Buildpc from "../screens/buildpc/Buildpc";
import CompareBuildpc from "../screens/buildpc/CompareBuildpc";
import BuildpcDeail from "../screens/buildpc/BuildpcDetail";
const Stack = createStackNavigator();
@@ -21,6 +23,8 @@ const AppNavigator: React.FC = () => {
<Stack.Screen name="productlistmain" component={ProductList} />
<Stack.Screen name="productdetail" component={ProductDetail} />
<Stack.Screen name="buildpc" component={Buildpc} />
<Stack.Screen name="comparebuildpc" component={CompareBuildpc} />
<Stack.Screen name="buildpcdetail" component={BuildpcDeail} />
</Stack.Navigator>
);
};

View File

@@ -8,16 +8,20 @@ import {
StyleSheet,
Dimensions,
} from "react-native";
import { useNavigation, NavigationProp } from "@react-navigation/native";
import AppLayout from "@layouts/AppLayout";
import { Ionicons } from "@expo/vector-icons"; // hoặc icon_2025 nếu bạn có icon font riêng
import CreateBuildpc from "@components/buildpc/CreateBuildpc"; // component con bạn tự tạo
const { width } = Dimensions.get("window");
import Octicons from "@expo/vector-icons/Octicons";
import Footer from "@components/footer/Footer";
export default function Buildpc() {
const navigation = useNavigation<NavigationProp<any>>();
return (
<AppLayout activeTab="buildpc">
<ScrollView style={styles.container}>
<ScrollView>
<View style={styles.container}>
{/* Breadcrumb */}
<View style={styles.breadcrumb}>
<View style={styles.breadcrumbItem}>
@@ -38,10 +42,16 @@ export default function Buildpc() {
{/* Buttons Bắt đầu/So sánh */}
<View style={styles.buttonWrapper}>
<TouchableOpacity style={styles.primaryButton}>
<TouchableOpacity
style={styles.primaryButton}
onPress={() => navigation?.navigate("buildpc")}
>
<Text style={styles.primaryButtonText}>Bắt đu tạo</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.secondaryButton}>
<TouchableOpacity
style={styles.secondaryButton}
onPress={() => navigation?.navigate("comparebuildpc")}
>
<Text style={styles.secondaryButtonText}>
So sánh giá tại các cửa hàng
</Text>
@@ -81,6 +91,8 @@ export default function Buildpc() {
{/* Buildpc Content */}
<CreateBuildpc />
</View>
<Footer navigation={navigation} />
</ScrollView>
</AppLayout>
);
@@ -97,7 +109,6 @@ function ActionButton({ icon, label }: { icon: string; label: string }) {
const styles = StyleSheet.create({
container: {
paddingBottom: 100,
paddingHorizontal: 10,
},
breadcrumb: {

View File

@@ -0,0 +1,126 @@
import React from "react";
import {
View,
Text,
TextInput,
TouchableOpacity,
ScrollView,
StyleSheet,
Dimensions,
} from "react-native";
import { globalStyles } from "styles/globalStyles";
import { useNavigation, NavigationProp } from "@react-navigation/native";
import AppLayout from "@layouts/AppLayout";
import { Ionicons } from "@expo/vector-icons";
import ImageGallery from "@components/buildpc/ImageGallery";
import Footer from "@components/footer/Footer";
import ProvidersList from "@components/buildpc/ProvidersList";
import ProductSpecs from "@components/buildpc/ProductSpecs";
import ReviewList from "@components/buildpc/ReviewList";
const stars = [5, 4, 3, 2, 1];
export function BuildpcDeail() {
const navigation = useNavigation<NavigationProp<any>>();
return (
<AppLayout activeTab="buildpcdetail">
<ScrollView>
<View style={styles.container}>
{/* Breadcrumb */}
<View style={globalStyles.breadcrumb}>
<TouchableOpacity
style={globalStyles.breadcrumbItem}
onPress={() => navigation.navigate("homepage" as never)}
>
<Ionicons name="home-outline" size={20} color="#637381" />
</TouchableOpacity>
<Ionicons name="chevron-forward-outline" size={14} color="#999" />
<Text style={[globalStyles.breadcrumbText, { fontWeight: "600" }]}>
Màn hình máy tính
</Text>
</View>
<View
style={{
padding: 10,
backgroundColor: "#fff",
marginTop: 5,
}}
>
<ImageGallery />
<View style={{ marginTop: 10 }}>
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
marginBottom: 10,
}}
>
<Text style={{ marginRight: 5, fontWeight: 700 }}>
Nguời dùng đánh giá
</Text>
<Text>(40 ratings, 4.9)</Text>
</View>
{stars.map((star) => (
<View key={star} style={styles.row}>
<Text style={styles.starLabel}>{star} sao</Text>
<View style={styles.barBackground}>
<View style={[styles.barFill, { width: "90%" }]} />
</View>
<Text style={styles.percent}>90%</Text>
</View>
))}
</View>
</View>
{/* nhà cung cấp */}
<ProvidersList />
{/* thông số kỹ thuật */}
<ProductSpecs />
{/* đánh giá */}
<ReviewList />
</View>
<Footer navigation={navigation} />
</ScrollView>
</AppLayout>
);
}
export default BuildpcDeail;
const styles = StyleSheet.create({
container: {
flex: 1,
paddingHorizontal: 10,
backgroundColor: "#efefef",
paddingBottom: 10,
},
row: {
flexDirection: "row",
alignItems: "center",
marginBottom: 8,
},
starLabel: {
width: 50,
fontSize: 14,
color: "#1877f2",
},
barBackground: {
flex: 1,
height: 8,
backgroundColor: "#eee",
borderRadius: 4,
marginHorizontal: 8,
},
barFill: {
height: 8,
backgroundColor: "#ff960b",
},
percent: {
width: 40,
fontSize: 12,
textAlign: "right",
color: "#1877f2",
},
});

View File

@@ -0,0 +1,210 @@
import React from "react";
import {
View,
Text,
TextInput,
TouchableOpacity,
ScrollView,
StyleSheet,
Dimensions,
} from "react-native";
import { useNavigation, NavigationProp } from "@react-navigation/native";
import AppLayout from "@layouts/AppLayout";
import Footer from "@components/footer/Footer";
import { Ionicons } from "@expo/vector-icons";
import ListCompare from "./ListCompare";
export function CompareBuildpc() {
const navigation = useNavigation<NavigationProp<any>>();
return (
<AppLayout activeTab="buildpc">
<ScrollView style={styles.page}>
<View style={styles.wrapper}>
{/* 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}>Tạo máy tính riêng của bạn</Text>
</View>
</View>
{/* Buttons Bắt đầu/So sánh */}
<View style={styles.buttonWrapper}>
<TouchableOpacity
style={styles.primaryButton}
onPress={() => navigation?.navigate("buildpc")}
>
<Text style={styles.primaryButtonText}>Bắt đu tạo</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.secondaryButton}
onPress={() => navigation?.navigate("comparebuildpc")}
>
<Text style={styles.secondaryButtonText}>
So sánh giá tại các cửa hàng
</Text>
</TouchableOpacity>
</View>
<ListCompare />
</View>
<Footer navigation={navigation} />
</ScrollView>
</AppLayout>
);
}
export default CompareBuildpc;
const styles = StyleSheet.create({
page: {
backgroundColor: "#F4F4F4",
},
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",
},
wrapper: {
paddingHorizontal: 12,
},
actionBar: {
backgroundColor: "#fff",
paddingVertical: 10,
paddingHorizontal: 12,
borderRadius: 8,
marginTop: 12,
},
actionRow: {
flexDirection: "row",
gap: 10,
marginBottom: 10,
},
urlInput: {
flex: 4,
height: 48,
borderRadius: 6,
borderWidth: 1,
borderColor: "#ddd",
flexDirection: "row",
alignItems: "center",
},
iconWrapper: {
width: 24,
marginLeft: 10,
},
input: {
flex: 1,
padding: 10,
},
nameInput: {
flex: 3,
height: 48,
borderRadius: 6,
borderWidth: 1,
borderColor: "#ddd",
flexDirection: "row",
alignItems: "center",
},
label: {
marginLeft: 10,
},
buttonWrapper: {
backgroundColor: "#fff",
flexDirection: "row",
gap: 10,
alignItems: "center",
},
primaryButton: {
backgroundColor: "#ddd",
padding: 10,
borderRadius: 4,
marginRight: 5,
},
primaryButtonText: {
color: "#666",
fontWeight: "bold",
textAlign: "center",
},
secondaryButton: {
backgroundColor: "#5B21B6",
padding: 10,
borderRadius: 4,
},
secondaryButtonText: {
color: "#fff",
fontWeight: "bold",
},
buttonRow: {
flexDirection: "row",
gap: 10,
},
iconButton: {
flex: 1,
height: 48,
borderRadius: 6,
borderWidth: 1,
borderColor: "#ddd",
justifyContent: "center",
alignItems: "center",
flexDirection: "row",
},
iconButtonText: {
marginLeft: 5,
},
bottomPanel: {
backgroundColor: "#fff",
padding: 15,
marginTop: 15,
},
buttonGroup: {
flexDirection: "row",
gap: 10,
marginBottom: 10,
},
btnGrey: {
backgroundColor: "#f4f4f4",
paddingHorizontal: 24,
paddingVertical: 10,
borderRadius: 8,
},
btnGreyText: {
fontWeight: "bold",
color: "#6b7280",
},
btnPrimary: {
backgroundColor: "#6D28D9",
paddingHorizontal: 24,
paddingVertical: 10,
borderRadius: 8,
},
btnPrimaryText: {
fontWeight: "bold",
color: "#fff",
},
});

View File

@@ -0,0 +1,216 @@
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";
export function ListCompare() {
const navigation = useNavigation<NavigationProp<any>>();
const storeLogo = require("../../../assets/images/logo-hacom.png");
const productImg = require("../../../assets/images/lienkien-ram.png");
const stores = [1, 2]; // giả lập 2 nhà cung cấp
const products = [1, 2]; // giả lập 2 sản phẩm mỗi nhà cung cấp
return (
<ScrollView style={styles.boxList}>
{stores.map((store, storeIdx) => (
<View style={styles.storeBox} key={storeIdx}>
{/* Header */}
<View style={styles.storeHeader}>
<View style={styles.logoBox}>
<Image
source={storeLogo}
style={styles.logo}
resizeMode="contain"
/>
</View>
</View>
{/* Products */}
{products.map((product, index) => (
<View style={styles.productRow} key={index}>
<Text style={styles.productName}>CPU Cooler</Text>
<View style={styles.productGrid}>
{/* Product Info */}
<View style={styles.productInfo}>
<View style={styles.infoRow}>
<Image source={productImg} style={styles.productImg} />
<Text
style={styles.productTitle}
onPress={() => navigation?.navigate("buildpcdetail")}
>
AMD Ryzen 7 9800x3D 4.7 GHz 8-Core Processor
</Text>
</View>
<TouchableOpacity style={styles.buyNowBtn}>
<Text style={styles.buyNowText}>Mua ngay</Text>
</TouchableOpacity>
</View>
<View style={styles.flexBox}>
<Text style={styles.textFlex}>Giá sản phẩm:</Text>
<Text style={styles.price}>4.700.000 Vnđ</Text>
<Text style={styles.oldPrice}>4.700.000 Vnđ</Text>
</View>
<View style={styles.flexBox}>
<Text style={styles.textFlex}>Khuyến mãi:</Text>
<Text style={styles.discount}>20%</Text>
</View>
<View style={styles.flexBox}>
<Text style={styles.textFlex}>Giao hàng:</Text>
<Text style={styles.ship}>Liên hệ</Text>
</View>
</View>
</View>
))}
{/* Footer tổng tiền */}
<View style={styles.totalRow}>
<Text style={styles.totalLabel}>Tổng tiền (2 sản phẩm) :</Text>
<Text style={styles.totalAmount}>6.000.000 Vnđ</Text>
</View>
</View>
))}
</ScrollView>
);
}
export default ListCompare;
const styles = StyleSheet.create({
boxList: {
padding: 10,
backgroundColor: "#fff",
marginTop: 10,
borderRadius: 4,
},
storeBox: {
marginBottom: 20,
},
storeHeader: {
borderBottomWidth: 1,
borderColor: "#e5e7eb",
paddingBottom: 12,
},
logoBox: {
width: 90,
},
logo: {
width: 90,
height: 32,
},
headerGrid: {
flex: 1,
flexDirection: "row",
flexWrap: "wrap",
},
gridTitle: {
width: "12.5%",
fontWeight: "bold",
},
gridTitleTotal: {
width: "25%",
fontWeight: "bold",
},
productRow: {
paddingVertical: 10,
borderBottomWidth: 1,
borderColor: "#e5e7eb",
},
productName: {
width: 200,
fontWeight: 700,
marginBottom: 10,
},
productGrid: {},
productInfo: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
marginBottom: 10,
},
infoRow: {
width: width - 120,
flexDirection: "row",
alignItems: "center",
},
productImg: {
width: 60,
height: 60,
borderWidth: 1,
borderColor: "#e5e7eb",
marginRight: 10,
padding: 5,
},
productTitle: {
flex: 1,
color: "#111",
fontWeight: 700,
},
oldPrice: {
textDecorationLine: "line-through",
marginLeft: 5,
color: "#f5f5f5",
},
price: {
color: "#ff0000",
fontWeight: "bold",
},
discount: { fontWeight: "bold" },
ship: {
textDecorationLine: "underline",
color: "#33c600",
},
totalBox: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
},
total: {
fontWeight: "bold",
},
buyNowBtn: {
backgroundColor: "#2563eb",
borderRadius: 6,
width: 80,
height: 32,
lineHeight: 32,
textAlign: "center",
flexDirection: "row",
alignItems: "center",
justifyContent: "center",
},
buyNowText: {
color: "white",
fontWeight: "bold",
},
totalRow: {
flexDirection: "row",
justifyContent: "flex-end",
alignItems: "center",
marginTop: 20,
},
totalLabel: {
fontWeight: "bold",
marginRight: 10,
},
totalAmount: {
fontWeight: "bold",
},
flexBox: {
flexDirection: "row",
alignItems: "center",
marginBottom: 5,
},
textFlex: {
width: 100,
},
});