hoc-lap-trinh-18

Lab 1 + Lab 2: Xây dựng Giao diện Trang Homepage + Product Detail

  • Trang Hompage (/): có header, footer, danh sách sản phẩm, click vào product sang trang chi tiết sản phẩm – 3 điểm
  • Trang ProductDetail (/product/:id): có header, footer, có thông tin chi tiết sản phẩm (tiêu đề, mô tả, giá cả, đánh giá, category …) – 3 điểm
  • Có show lỗi khi call API Error và khi id product không hợp lệ hoặc không tìm thì hiện thị thông tin Product Not Found (hoặc chuyển trang Not Found) – 2 điểm
  • Định nghĩa type/interface Product đầy đủ theo response API trả về – 1 điểm
  • Giao diện UI đẹp mắt  – 1 điểm

Create Project React + Typescript

npm create vite@latest
Đặt tên project_name
Chọn React
Chọn Typescript
cd project_name
npm i
npm i axios react-router-dom
npm run dev

File package.json: Thêm script server để chạy API: npm run server

 "scripts": {
    "start": "vite --open",
    "server": "npx json-server db.json",
    "dev": "vite",
     ...
}

Tạo file db.json để chạy API

{
  "products": [
    {
      "id": 1,
      "title": "Fjallraven - Foldsack No. 1 Backpack, Fits 15 Laptops",
      "price": 109.95,
      "description": "Your perfect pack for everyday use and walks in the forest. Stash your laptop (up to 15 inches) in the padded sleeve, your everyday",
      "category": "men's clothing",
      "image": "https://fakestoreapi.com/img/81fPKd-2AYL._AC_SL1500_.jpg",
      "rating": {
        "rate": 3.9,
        "count": 120
      }
    },
    {
      "id": 2,
      "title": "Mens Casual Premium Slim Fit T-Shirts ",
      "price": 22.3,
      "description": "Slim-fitting style, contrast raglan long sleeve, three-button henley placket, light weight & soft fabric for breathable and comfortable wearing. And Solid stitched shirts with round neck made for durability and a great fit for casual fashion wear and diehard baseball fans. The Henley style round neckline includes a three-button placket.",
      "category": "men's clothing",
      "image": "https://fakestoreapi.com/img/71-3HjGNDUL._AC_SY879._SX._UX._SY._UY_.jpg",
      "rating": {
        "rate": 4.1,
        "count": 259
      }
    }
  ]
}

Add routeConfig sử dụng useRoutes vào file App.tsx

import { useRoutes } from "react-router-dom";
import Homepage from "./pages/Homepage";
import ProductDetail from "./pages/ProductDetail";

const routeConfig = [
  { path: "/", element: <Homepage /> },
  { path: "/product/:id", element: <ProductDetail /> },
];

function App() {
  const routes = useRoutes(routeConfig);
  return <div>{routes}</div>;
}

export default App;

Thêm BrowserRouter vào file main.tsx

import React from "react";
import ReactDOM from "react-dom/client";
import App from "./App.tsx";
import "./index.css";
import { BrowserRouter } from "react-router-dom";

ReactDOM.createRoot(document.getElementById("root")!).render(
  <React.StrictMode>
    <BrowserRouter>
      <App />
    </BrowserRouter>
  </React.StrictMode>
);

Tạo folder pages: tạo file pages/Homepage.tsx

import { useEffect, useState } from "react";
import axios from "axios";

type Product = {
  id: string;
  title: string;
  image: string;
  description: string;
};
function Homepage() {
  const [products, setProducts] = useState<Product[]>([]);
  const getAllProduct = async () => {
    const { data } = await axios.get("http://localhost:3000/products");
    setProducts(data);
  };
  useEffect(() => {
    getAllProduct();
  }, []);
  return (
    <>
      {products.map((product, index) => (
        <div key={index} className="card" style={{ width: "18rem" }}>
          <img
            src={product.image}
            className="card-img-top"
            alt="..."
            width={"100px"}
          />
          <div className="card-body">
            <h5 className="card-title">{product.title}</h5>
            <p className="card-text">{product.description}</p>
            <a href={`/product/${product.id}`} className="btn btn-primary">
              Detail
            </a>
          </div>
        </div>
      ))}
    </>
  );
}

export default Homepage;

Tạo file pages/ProductDetail.tsx

function ProductDetail() {
  return <h1>ProductDetail</h1>;
}

export default ProductDetail;

By hoadv