tosanoob commited on
Commit
fab48db
1 Parent(s): 22c310d

Feature: add authentication, feeds and menu page

Browse files
frontend/package-lock.json CHANGED
@@ -17,6 +17,7 @@
17
  "react-dom": "^18.3.1",
18
  "react-router-dom": "^6.27.0",
19
  "react-scripts": "5.0.1",
 
20
  "web-vitals": "^2.1.4"
21
  }
22
  },
@@ -18988,6 +18989,15 @@
18988
  "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
18989
  "license": "MIT"
18990
  },
 
 
 
 
 
 
 
 
 
18991
  "node_modules/vary": {
18992
  "version": "1.1.2",
18993
  "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
 
17
  "react-dom": "^18.3.1",
18
  "react-router-dom": "^6.27.0",
19
  "react-scripts": "5.0.1",
20
+ "validator": "^13.12.0",
21
  "web-vitals": "^2.1.4"
22
  }
23
  },
 
18989
  "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==",
18990
  "license": "MIT"
18991
  },
18992
+ "node_modules/validator": {
18993
+ "version": "13.12.0",
18994
+ "resolved": "https://registry.npmjs.org/validator/-/validator-13.12.0.tgz",
18995
+ "integrity": "sha512-c1Q0mCiPlgdTVVVIJIrBuxNicYE+t/7oKeI9MWLj3fh/uq2Pxh/3eeWbVZ4OcGW1TUf53At0njHw5SMdA3tmMg==",
18996
+ "license": "MIT",
18997
+ "engines": {
18998
+ "node": ">= 0.10"
18999
+ }
19000
+ },
19001
  "node_modules/vary": {
19002
  "version": "1.1.2",
19003
  "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
frontend/package.json CHANGED
@@ -12,6 +12,7 @@
12
  "react-dom": "^18.3.1",
13
  "react-router-dom": "^6.27.0",
14
  "react-scripts": "5.0.1",
 
15
  "web-vitals": "^2.1.4"
16
  },
17
  "scripts": {
 
12
  "react-dom": "^18.3.1",
13
  "react-router-dom": "^6.27.0",
14
  "react-scripts": "5.0.1",
15
+ "validator": "^13.12.0",
16
  "web-vitals": "^2.1.4"
17
  },
18
  "scripts": {
frontend/src/App.js CHANGED
@@ -1,6 +1,7 @@
1
  import React from 'react';
2
  import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
3
  import HomePage from './pages/HomePage';
 
4
  import './styles/App.css';
5
  // index.js hoặc App.js
6
  import 'bootstrap/dist/css/bootstrap.min.css';
@@ -11,7 +12,9 @@ function App() {
11
  <Router>
12
  <Routes>
13
  {/* Định tuyến trang chủ */}
14
- <Route path="/" element={<HomePage />} />
 
 
15
  {/* Có thể thêm các route khác nếu cần */}
16
  </Routes>
17
  </Router>
 
1
  import React from 'react';
2
  import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
3
  import HomePage from './pages/HomePage';
4
+ import BasicTemplate from './templates/BasicTemplate';
5
  import './styles/App.css';
6
  // index.js hoặc App.js
7
  import 'bootstrap/dist/css/bootstrap.min.css';
 
12
  <Router>
13
  <Routes>
14
  {/* Định tuyến trang chủ */}
15
+ <Route path="/" element={<BasicTemplate/>} >
16
+ <Route path="*" element={<HomePage/>} />
17
+ </Route>
18
  {/* Có thể thêm các route khác nếu cần */}
19
  </Routes>
20
  </Router>
frontend/src/index.js CHANGED
@@ -1,13 +1,47 @@
1
  import React from 'react';
2
  import ReactDOM from 'react-dom/client';
 
3
  import './styles/index.css';
4
- import App from './App';
 
 
5
  import reportWebVitals from './reportWebVitals';
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6
 
7
  const root = ReactDOM.createRoot(document.getElementById('root'));
8
  root.render(
9
  <React.StrictMode>
10
- <App />
11
  </React.StrictMode>
12
  );
13
 
 
1
  import React from 'react';
2
  import ReactDOM from 'react-dom/client';
3
+ import { createBrowserRouter, RouterProvider } from 'react-router-dom';
4
  import './styles/index.css';
5
+ import 'bootstrap/dist/css/bootstrap.min.css';
6
+ import ErrorPage from './pages/ErrorPage';
7
+ import HomePage from './pages/HomePage';
8
  import reportWebVitals from './reportWebVitals';
9
+ import LoginPage from './pages/LoginPage';
10
+ import RegisterPage from './pages/RegisterPage';
11
+ import NewsPage from './pages/NewsPage';
12
+ import MenuPage from './pages/MenuPage';
13
+
14
+ const router = createBrowserRouter([
15
+ {
16
+ path: "/",
17
+ element: <HomePage />,
18
+ errorElement: <ErrorPage />,
19
+ },
20
+ {
21
+ path: "/login",
22
+ element: <LoginPage />,
23
+ errorElement: <ErrorPage />
24
+ },
25
+ {
26
+ path: "/register",
27
+ element: <RegisterPage/>,
28
+ errorElement: <ErrorPage/>
29
+ },
30
+ {
31
+ path: "/news",
32
+ element: <NewsPage/>,
33
+ errorElement: <ErrorPage/>
34
+ },
35
+ {
36
+ path: "/menu",
37
+ element: <MenuPage/>
38
+ }
39
+ ]);
40
 
41
  const root = ReactDOM.createRoot(document.getElementById('root'));
42
  root.render(
43
  <React.StrictMode>
44
+ <RouterProvider router={router}/>
45
  </React.StrictMode>
46
  );
47
 
frontend/src/molecules/AboutUsSection.js CHANGED
@@ -2,8 +2,8 @@ import { Container, Row, Col } from "react-bootstrap";
2
 
3
  export default function AboutUsSection () {
4
  return (<>
5
- <Container id="about-us">
6
- <h1 className="my-4">CATS Shop
7
  <small> - We code for fun </small>
8
  </h1>
9
 
 
2
 
3
  export default function AboutUsSection () {
4
  return (<>
5
+ <Container id="about-us" className="my-5">
6
+ <h1 className="my-4 text-cen">CATS Shop
7
  <small> - We code for fun </small>
8
  </h1>
9
 
frontend/src/molecules/ContactSection.js CHANGED
@@ -2,8 +2,8 @@ import { Container, Row, Col } from "react-bootstrap";
2
 
3
  export default function ContactSection() {
4
  return (
5
- <Container id="contact" className="align-items-center">
6
- <h1>Thông tin liên hệ</h1>
7
  <Row md={3}>
8
  {/* Cột 1: Số điện thoại liên hệ */}
9
  <Col>
 
2
 
3
  export default function ContactSection() {
4
  return (
5
+ <Container id="contact" className="my-5 align-items-center">
6
+ <h1 className="text-center mb-5">Thông tin liên hệ</h1>
7
  <Row md={3}>
8
  {/* Cột 1: Số điện thoại liên hệ */}
9
  <Col>
frontend/src/molecules/Navbar.js CHANGED
@@ -3,18 +3,31 @@ import Nav from 'react-bootstrap/Nav';
3
  import Navbar from 'react-bootstrap/Navbar';
4
  import Button from 'react-bootstrap/Button';
5
  import { Stack } from 'react-bootstrap';
 
6
 
7
- export default function ANavbar( {isLoggedIn = false, user} ) {
 
 
 
 
 
 
 
 
 
 
 
 
8
 
9
  let userContent;
10
- if (isLoggedIn === true) {
11
  userContent = <>
12
  <Stack direction='horizontal' gap={2}>
13
  <Button href="/userinfo" variant='primary'>
14
- Xin chào, {user.name}
15
  </Button>
16
  {' '}
17
- <Button href="/logout" variant='outline-primary'>
18
  Đăng xuất
19
  </Button>
20
  </Stack>
@@ -34,7 +47,7 @@ export default function ANavbar( {isLoggedIn = false, user} ) {
34
  }
35
 
36
  return (
37
- <Navbar expand="lg" className="bg-body-tertiary" sticky='top' >
38
  <Container>
39
  <Navbar.Brand href="/">
40
  <img
@@ -50,12 +63,12 @@ export default function ANavbar( {isLoggedIn = false, user} ) {
50
  <Navbar.Collapse id="basic-navbar-nav">
51
  <Nav className="me-auto">
52
  {/* These are the navigators */}
53
- <Nav.Link href="#home">Trang chủ</Nav.Link>
54
- <Nav.Link href="#about-us">Về chúng tôi</Nav.Link>
55
- <Nav.Link href="#news">Tin tức</Nav.Link>
56
- <Nav.Link href="#store">Chi nhánh</Nav.Link>
57
- <Nav.Link href="#menu">Menu</Nav.Link>
58
- <Nav.Link href="#contact">Liên hệ</Nav.Link>
59
  </Nav>
60
  {userContent}
61
  </Navbar.Collapse>
 
3
  import Navbar from 'react-bootstrap/Navbar';
4
  import Button from 'react-bootstrap/Button';
5
  import { Stack } from 'react-bootstrap';
6
+ import { useNavigate } from 'react-router-dom';
7
 
8
+ export default function ANavbar() {
9
+
10
+ const navigate = useNavigate();
11
+
12
+ function handleLogout() {
13
+ sessionStorage.setItem('isLoggedIn','false');
14
+ sessionStorage.removeItem('username');
15
+ sessionStorage.removeItem('cart');
16
+ navigate('/');
17
+ }
18
+
19
+ let username = sessionStorage.getItem('username');
20
+ let isLoggedIn = sessionStorage.getItem('isLoggedIn');
21
 
22
  let userContent;
23
+ if (isLoggedIn === 'true') {
24
  userContent = <>
25
  <Stack direction='horizontal' gap={2}>
26
  <Button href="/userinfo" variant='primary'>
27
+ Xin chào, {username}
28
  </Button>
29
  {' '}
30
+ <Button onClick={handleLogout} variant='outline-primary'>
31
  Đăng xuất
32
  </Button>
33
  </Stack>
 
47
  }
48
 
49
  return (
50
+ <Navbar id="home" expand="lg" className="bg-body-tertiary" sticky='top' >
51
  <Container>
52
  <Navbar.Brand href="/">
53
  <img
 
63
  <Navbar.Collapse id="basic-navbar-nav">
64
  <Nav className="me-auto">
65
  {/* These are the navigators */}
66
+ <Nav.Link href="/#home">Trang chủ</Nav.Link>
67
+ <Nav.Link href="/#about-us">Về chúng tôi</Nav.Link>
68
+ <Nav.Link href="/#news">Tin tức</Nav.Link>
69
+ <Nav.Link href="/#store">Chi nhánh</Nav.Link>
70
+ <Nav.Link href="/#menu">Menu</Nav.Link>
71
+ <Nav.Link href="/#contact">Liên hệ</Nav.Link>
72
  </Nav>
73
  {userContent}
74
  </Navbar.Collapse>
frontend/src/organisms/MenuSection.js CHANGED
@@ -18,16 +18,16 @@ function MenuSection() {
18
  const countCarouselSlides = 4;
19
 
20
  return (
21
- <Container id="menu" className="text-center justify-content-center align-items-center">
22
- <h1>Menu</h1>
23
- <Carousel activeIndex={index} onSelect={handleSelect} data-bs-theme="dark">
24
  {Array.from({ length: countCarouselSlides }).map((_, slideIndex) => (
25
  <Carousel.Item key={slideIndex}>
26
  <div className="d-flex justify-content-center align-items-center" style={{ width: '100%' }}>
27
  <img
28
  className="img-fluid"
29
  src="/placeholder4.jpg"
30
- alt="a placeholder"
31
  style={{ maxHeight: '100%', maxWidth: '100%', objectFit: 'contain' }}
32
  />
33
  </div>
 
18
  const countCarouselSlides = 4;
19
 
20
  return (
21
+ <Container id="menu" className="text-center justify-content-center align-items-center my-5">
22
+ <h1 className='mb-5'>Menu</h1>
23
+ <Carousel as='a' href='/menu' activeIndex={index} onSelect={handleSelect} data-bs-theme="dark">
24
  {Array.from({ length: countCarouselSlides }).map((_, slideIndex) => (
25
  <Carousel.Item key={slideIndex}>
26
  <div className="d-flex justify-content-center align-items-center" style={{ width: '100%' }}>
27
  <img
28
  className="img-fluid"
29
  src="/placeholder4.jpg"
30
+ alt="a placeholder"
31
  style={{ maxHeight: '100%', maxWidth: '100%', objectFit: 'contain' }}
32
  />
33
  </div>
frontend/src/organisms/NewsSection.js CHANGED
@@ -1,17 +1,26 @@
1
  import { Container, Col, Row } from 'react-bootstrap';
2
  import NewsItem from '../molecules/NewsItem';
3
 
4
- function NewsSection( {newsFeeds} ) {
 
 
 
 
 
 
 
5
  return (
6
- <Container id="news" className="text-center justify-content-center align-items-center">
7
- <h1>Tin tức</h1>
8
  <Row xs={1} md={2} xl={3} className="g-4">
9
  {Array.from(newsFeeds).map((feed, idx) => (
10
  <Col key={idx}>
11
  <NewsItem title={feed.title}
12
  text={feed.text}
13
  imageSrc={feed.imageSrc}
14
- feedHref={feed.feedHref}>
 
 
15
  </NewsItem>
16
  </Col>
17
  ))}
 
1
  import { Container, Col, Row } from 'react-bootstrap';
2
  import NewsItem from '../molecules/NewsItem';
3
 
4
+ function NewsSection() {
5
+
6
+ const newsFeeds = [
7
+ { title: 'Feed 1', text: 'This is the first feed', imageSrc: '/placeholder1.jpg', feedHref: '' },
8
+ { title: 'Feed 2', text: 'This is the second feed', imageSrc: '/placeholder1.jpg', feedHref: '' },
9
+ { title: 'Feed 3', text: 'This is the third feed', imageSrc: '/placeholder1.jpg', feedHref: ''}
10
+ ];
11
+
12
  return (
13
+ <Container id="news" className="text-center justify-content-center align-items-center my-5">
14
+ <h1 className='mb-5'>Tin tức</h1>
15
  <Row xs={1} md={2} xl={3} className="g-4">
16
  {Array.from(newsFeeds).map((feed, idx) => (
17
  <Col key={idx}>
18
  <NewsItem title={feed.title}
19
  text={feed.text}
20
  imageSrc={feed.imageSrc}
21
+ // feedHref={feed.feedHref}
22
+ feedHref="/news" //demo
23
+ >
24
  </NewsItem>
25
  </Col>
26
  ))}
frontend/src/organisms/StoreSection.js CHANGED
@@ -1,10 +1,17 @@
1
  import { Container, Col, Row } from 'react-bootstrap';
2
  import StoreItem from '../molecules/StoreItem';
3
 
4
- function StoreSection({ stores }) {
 
 
 
 
 
 
 
5
  return (
6
- <Container id="store" className="text-center justify-content-center align-items-center">
7
- <h1>Các chi nhánh</h1>
8
  <Row className="align-items-center">
9
  <Col xs={12} md={4} className="d-flex justify-content-center align-items-center">
10
  Đây là các chi nhánh đang mở, chưa mở, sắp mở và có thể là sẽ không mở
 
1
  import { Container, Col, Row } from 'react-bootstrap';
2
  import StoreItem from '../molecules/StoreItem';
3
 
4
+ function StoreSection() {
5
+
6
+ const stores = [
7
+ { storeName: 'Store 1', address: 'Address 1', imageSrc: '/placeholder2.jpg' },
8
+ { storeName: 'Store 2', address: 'Address 2', imageSrc: '/placeholder2.jpg' },
9
+ { storeName: 'Store 3', address: 'Address 3', imageSrc: '/placeholder2.jpg' },
10
+ ];
11
+
12
  return (
13
+ <Container id="store" className="text-center justify-content-center align-items-center my-5">
14
+ <h1 className='mb-5'>Các chi nhánh</h1>
15
  <Row className="align-items-center">
16
  <Col xs={12} md={4} className="d-flex justify-content-center align-items-center">
17
  Đây là các chi nhánh đang mở, chưa mở, sắp mở và có thể là sẽ không mở
frontend/src/pages/ErrorPage.js ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useRouteError } from "react-router-dom";
2
+
3
+ export default function ErrorPage() {
4
+ const error = useRouteError();
5
+ console.error(error);
6
+
7
+ return (
8
+ <div id="error-page">
9
+ <h1>Oops!</h1>
10
+ <p>Sorry, an unexpected error has occurred.</p>
11
+ <p>
12
+ <i>{error.statusText || error.message}</i>
13
+ </p>
14
+ </div>
15
+ );
16
+ }
frontend/src/pages/HomePage.js CHANGED
@@ -1,41 +1,28 @@
1
  // pages/HomePage.js
2
  // import React, { useState } from 'react';
3
- import ANavbar from '../molecules/Navbar';
4
  import AboutUsSection from '../molecules/AboutUsSection';
5
  import NewsSection from '../organisms/NewsSection';
6
  import StoreSection from '../organisms/StoreSection';
7
  import MenuSection from '../organisms/MenuSection';
8
- import ContactSection from '../molecules/ContactSection';
9
 
10
  function HomePage () {
11
  // const [isLoggedIn, setIsLoggedIn] = useState(false);
12
  // const [user, setUser] = useState({ name: 'User' });
13
 
14
- const newsFeeds = [
15
- { title: 'Feed 1', text: 'This is the first feed', imageSrc: '/placeholder1.jpg', feedHref: '' },
16
- { title: 'Feed 2', text: 'This is the second feed', imageSrc: '/placeholder1.jpg', feedHref: '' },
17
- { title: 'Feed 3', text: 'This is the third feed', imageSrc: '/placeholder1.jpg', feedHref: ''}
18
- ];
19
- const stores = [
20
- { storeName: 'Store 1', address: 'Address 1', imageSrc: '/placeholder2.jpg' },
21
- { storeName: 'Store 2', address: 'Address 2', imageSrc: '/placeholder2.jpg' },
22
- { storeName: 'Store 3', address: 'Address 3', imageSrc: '/placeholder2.jpg' },
23
- ];
24
-
25
-
26
  return (
27
- <>
28
- <ANavbar id="home"></ANavbar>
29
  <AboutUsSection></AboutUsSection>
30
  <br></br>
31
- <NewsSection newsFeeds={newsFeeds}></NewsSection>
32
- <br></br>
33
- <StoreSection stores={stores}></StoreSection>
34
  <br></br>
35
- <MenuSection></MenuSection>
36
  <br></br>
37
- <ContactSection></ContactSection>
38
- </>
 
 
39
  );
40
  };
41
 
 
1
  // pages/HomePage.js
2
  // import React, { useState } from 'react';
 
3
  import AboutUsSection from '../molecules/AboutUsSection';
4
  import NewsSection from '../organisms/NewsSection';
5
  import StoreSection from '../organisms/StoreSection';
6
  import MenuSection from '../organisms/MenuSection';
7
+ import BasicTemplate from '../templates/BasicTemplate';
8
 
9
  function HomePage () {
10
  // const [isLoggedIn, setIsLoggedIn] = useState(false);
11
  // const [user, setUser] = useState({ name: 'User' });
12
 
 
 
 
 
 
 
 
 
 
 
 
 
13
  return (
14
+ <BasicTemplate content={
15
+ (<>
16
  <AboutUsSection></AboutUsSection>
17
  <br></br>
18
+ <NewsSection />
 
 
19
  <br></br>
20
+ <StoreSection />
21
  <br></br>
22
+ <MenuSection />
23
+ </>)
24
+ }>
25
+ </BasicTemplate>
26
  );
27
  };
28
 
frontend/src/pages/LoginPage.js ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from "react";
2
+ import { Alert, Container, Form, Row, Col, Button, Card } from "react-bootstrap";
3
+ import BasicTemplate from "../templates/BasicTemplate";
4
+ import { useNavigate } from "react-router-dom";
5
+
6
+ export default function LoginPage() {
7
+
8
+ const [username, setUsername] = useState('');
9
+ const [password, setPassword] = useState('');
10
+ const [error, setError] = useState('');
11
+
12
+ const navigate = useNavigate();
13
+
14
+ const handleSubmit = (e) => {
15
+ e.preventDefault();
16
+ // Validate password and confirm password match
17
+ if (password.length === 0) {
18
+ setError('Hãy nhập mật khẩu');
19
+ } else if (username.length === 0) {
20
+ setError('Tên đăng nhập không thể trống')
21
+ } else {
22
+ setError('');
23
+ sessionStorage.setItem('username','testUser');
24
+ sessionStorage.setItem('isLoggedIn','true');
25
+ sessionStorage.setItem('cart','{}');
26
+ // Xử lý submit form ở đây (ví dụ: gọi API đăng nhập)
27
+ console.log('Đăng nhập thành công', { username, password });
28
+ navigate('/');
29
+ }
30
+ };
31
+
32
+ return (
33
+ <BasicTemplate content={
34
+ (<Container fluid className="d-flex justify-content-center mt-5">
35
+ <Row>
36
+ <Col xs={1} md={2}></Col>
37
+ <Col xs={10} md={8}>
38
+ <Card style={{ width: '30rem' }} className='justify-content-center'>
39
+ <Card.Header>
40
+ <Card.Title className='mt-1 text-center'>Đăng nhập</Card.Title>
41
+ </Card.Header>
42
+ <Card.Body>
43
+ <Form onSubmit={handleSubmit} >
44
+ {error && <Alert variant="danger">{error}</Alert>}
45
+
46
+ <Form.Group controlId="username" className='mb-3'>
47
+ <Form.Label>Tên đăng nhập</Form.Label>
48
+ <Form.Control
49
+ type="text"
50
+ placeholder="Tên đăng nhập"
51
+ onChange={(e) => setUsername(e.target.value)}
52
+ />
53
+ </Form.Group>
54
+
55
+ <Form.Group controlId="password" className='mb-3'>
56
+ <Form.Label>Mật khẩu</Form.Label>
57
+ <Form.Control
58
+ type="password"
59
+ placeholder="Mật khẩu"
60
+ onChange={(e) => setPassword(e.target.value)}
61
+ />
62
+ </Form.Group>
63
+ <div className='d-flex justify-content-between align-items-center'>
64
+ <a href="/register" className='me-2'>Đăng ký</a>
65
+ <Button type="submit" className='me-2'>Đăng nhập</Button>
66
+ <a href="/forgot-password" className='me-2'>Quên mật khẩu</a>
67
+ </div>
68
+
69
+ </Form>
70
+ </Card.Body>
71
+ </Card>
72
+
73
+ </Col>
74
+ <Col xs={1} md={2}></Col>
75
+ </Row>
76
+ </Container>)
77
+ }>
78
+ </BasicTemplate>
79
+ );
80
+ }
frontend/src/pages/MenuPage.js ADDED
@@ -0,0 +1,127 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import { useState } from 'react';
2
+ import { Modal, Button, Container, Row, Col, Tab, Tabs } from 'react-bootstrap';
3
+ import MenuItem from '../molecules/MenuItem';
4
+ import BasicTemplate from '../templates/BasicTemplate';
5
+ function MenuPage() {
6
+ const [key, setKey] = useState('cat1');
7
+ const [selectedDish, setSelectedDish] = useState(null);
8
+ const [show, setShow] = useState(false);
9
+
10
+ const handleClose = () => setShow(false);
11
+ function handleShow(dish) {
12
+ setSelectedDish(dish); // Set the selected dish to state
13
+ setShow(true); // Show the modal
14
+ }
15
+
16
+ const menuItems1 = [
17
+ { name: 'Món 1 thể loại 1', description: 'Mô tả món 1', imageSrc: '/placeholder3.jpg' },
18
+ { name: 'Món 2 thể loại 1', description: 'Mô tả món 2', imageSrc: '/placeholder3.jpg' },
19
+ { name: 'Món 3 thể loại 1', description: 'Mô tả món 3', imageSrc: '/placeholder3.jpg' }
20
+ ];
21
+
22
+ const menuItems2 = [
23
+ { name: 'Món 1 thể loại 2', description: 'Mô tả món 1', imageSrc: '/placeholder3.jpg' },
24
+ { name: 'Món 2 thể loại 2', description: 'Mô tả món 2', imageSrc: '/placeholder3.jpg' },
25
+ { name: 'Món 3 thể loại 2', description: 'Mô tả món 3', imageSrc: '/placeholder3.jpg' },
26
+ { name: 'Món 3 thể loại 2', description: 'Mô tả món 3', imageSrc: '/placeholder3.jpg' },
27
+ ];
28
+
29
+ const menuItems3 = [
30
+ { name: 'Món 1 thể loại 3', description: 'Mô tả món 1', imageSrc: '/placeholder3.jpg' },
31
+ { name: 'Món 2 thể loại 3', description: 'Mô tả món 2', imageSrc: '/placeholder3.jpg' },
32
+ { name: 'Món 3 thể loại 3', description: 'Mô tả món 3', imageSrc: '/placeholder3.jpg' },
33
+ { name: 'Món 4 thể loại 3', description: 'Mô tả món 1', imageSrc: '/placeholder3.jpg' },
34
+ { name: 'Món 5 thể loại 3', description: 'Mô tả món 2', imageSrc: '/placeholder3.jpg' },
35
+ { name: 'Món 6 thể loại 3', description: 'Mô tả món 3', imageSrc: '/placeholder3.jpg' }
36
+ ];
37
+
38
+ return <BasicTemplate content={(
39
+ <Container fluid className='my-5'>
40
+ <>
41
+ <Modal show={show} onHide={handleClose}>
42
+ <Modal.Header closeButton>
43
+ <Modal.Title>{selectedDish?.name}</Modal.Title> {/* Dish name in the title */}
44
+ </Modal.Header>
45
+ <Modal.Body>
46
+ <p>{selectedDish?.description}</p> {/* Dish description */}
47
+ <img src={selectedDish?.imageSrc} alt={selectedDish?.name} style={{ width: '100%' }} /> {/* Dish image */}
48
+ </Modal.Body>
49
+ <Modal.Footer>
50
+ <Button variant="secondary" onClick={handleClose}>
51
+ Close
52
+ </Button>
53
+ </Modal.Footer>
54
+ </Modal>
55
+ </>
56
+ <h1 className='text-center mb-5'>Thực đơn</h1>
57
+ <Row>
58
+ <Col xs={1} md={2}></Col>
59
+ <Col xs={10} md={8}>
60
+ <Tabs
61
+ id="controlled-tab-example"
62
+ activeKey={key}
63
+ onSelect={(k) => setKey(k)}
64
+ className="mb-3"
65
+ >
66
+ <Tab eventKey="cat1" title="Thể loại 1">
67
+ <Container fluid className='my-5'>
68
+ <Row md={3} className="g-4">
69
+ {menuItems1.map((item, idx) => (
70
+ <Col key={idx} >
71
+ <div onClick={() => handleShow(item)}>
72
+ <MenuItem
73
+ dishName={item.name}
74
+ description={item.description}
75
+ imageSrc={item.imageSrc}
76
+ />
77
+ </div>
78
+
79
+ </Col>
80
+ ))}
81
+ </Row>
82
+ </Container>
83
+ </Tab>
84
+ <Tab eventKey="cat2" title="Thể loại 2">
85
+ <Container fluid className='my-5'>
86
+ <Row md={3} className="g-4">
87
+ {menuItems2.map((item, idx) => (
88
+ <Col key={idx}>
89
+ <div onClick={() => handleShow(item)}>
90
+ <MenuItem
91
+ dishName={item.name}
92
+ description={item.description}
93
+ imageSrc={item.imageSrc}
94
+ />
95
+ </div>
96
+ </Col>
97
+ ))}
98
+ </Row>
99
+ </Container>
100
+ </Tab>
101
+ <Tab eventKey="cat3" title="Thể loại 3">
102
+ <Container fluid className='my-5'>
103
+ <Row md={3} className="g-4">
104
+ {menuItems3.map((item, idx) => (
105
+ <Col key={idx}>
106
+ <div onClick={() => handleShow(item)}>
107
+ <MenuItem
108
+ dishName={item.name}
109
+ description={item.description}
110
+ imageSrc={item.imageSrc}
111
+ />
112
+ </div>
113
+ </Col>
114
+ ))}
115
+ </Row>
116
+ </Container>
117
+ </Tab>
118
+ </Tabs>
119
+ </Col>
120
+ <Col xs={1} md={2}></Col>
121
+ </Row>
122
+ </Container>
123
+
124
+ )} />
125
+ }
126
+
127
+ export default MenuPage;
frontend/src/pages/NewsPage.js ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import {Container, Row, Col} from "react-bootstrap";
2
+ import BasicTemplate from "../templates/BasicTemplate";
3
+
4
+ export default function NewsPage({newsTitle, newsContent, newsImageSrc}) {
5
+
6
+ newsTitle = "This is a demo newstitle"
7
+ newsContent = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
8
+ newsImageSrc = "/placeholder3.jpg"
9
+
10
+ return (
11
+ <BasicTemplate content={
12
+ (<Container id="about-us" className="my-5">
13
+ <h1 className="mb-5 text-center">{newsTitle}
14
+ </h1>
15
+
16
+ <Row className="align-items-center">
17
+ <Col xs={12} md={6}>
18
+ <img className="img-fluid" src={newsImageSrc} alt="" style={{width: "100%", height: "auto"}}></img>
19
+ </Col>
20
+
21
+ <Col xs={12} md={6} className="d-flex justify-content-center align-items-center">
22
+ {newsContent}
23
+ </Col>
24
+ </Row>
25
+ </Container>)
26
+ }/>
27
+ )
28
+ }
frontend/src/pages/RegisterPage.js ADDED
@@ -0,0 +1,128 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useState } from 'react';
2
+ import validator from 'validator';
3
+ import { useNavigate } from 'react-router-dom';
4
+ import { Container, Form, Button, Alert, Row, Col, Card } from 'react-bootstrap';
5
+ import BasicTemplate from '../templates/BasicTemplate';
6
+
7
+ const RegisterPage = () => {
8
+ const [full_name, setFullname] = useState('');
9
+ const [phone_number, setPhoneNumber] = useState('');
10
+ const [email, setEmail] = useState('');
11
+ const [password, setPassword] = useState('');
12
+ const [confirmPassword, setConfirmPassword] = useState('');
13
+ const [error, setError] = useState('');
14
+
15
+ const navigator = useNavigate();
16
+
17
+ const handleSubmit = (e) => {
18
+ e.preventDefault();
19
+ // Validate password and confirm password match
20
+ if (full_name.length === 0) {
21
+ setError('Họ và tên không thể để trống');
22
+ } else if (!validator.isMobilePhone(phone_number)) {
23
+ setError('Số điện thoại không hợp lệ');
24
+ } else if (!validator.isEmail(email)) {
25
+ setError('Email không hợp lệ');
26
+ } else if (password.length < 8) {
27
+ setError('Mật khẩu phải có tối thiểu 8 ký tự');
28
+ } else if (password !== confirmPassword) {
29
+ setError('Mật khẩu và xác nhận mật khẩu không khớp.');
30
+ } else {
31
+ setError('');
32
+ // gọi API đăng ký ở đây
33
+ console.log('Đăng ký thành công:', {full_name, password });
34
+ navigator('/');
35
+ }
36
+ };
37
+
38
+ return (
39
+ <BasicTemplate content={
40
+ (<Container fluid className="d-flex justify-content-center mt-5">
41
+ <Row>
42
+ <Col xs={1} md={2}></Col>
43
+ <Col xs={10} md={8}>
44
+ <Card style={{ width: '30rem' }} className='justify-content-center'>
45
+ <Card.Header>
46
+ <Card.Title className='mt-1 text-center'>Đăng ký</Card.Title>
47
+ </Card.Header>
48
+ <Card.Body>
49
+ <Form onSubmit={handleSubmit}>
50
+ {/* full_name, phone_number, email, password */}
51
+ {error && <Alert variant="danger">{error}</Alert>}
52
+
53
+ <Form.Group controlId="full_name" className='mb-3'>
54
+ <Form.Label>Họ và tên</Form.Label>
55
+ <Form.Control
56
+ // id='full_name'
57
+ type="text"
58
+ placeholder="Họ và tên"
59
+ onChange={(e) => setFullname(e.target.value)}
60
+
61
+ />
62
+ </Form.Group>
63
+
64
+ <Form.Group controlId="phone_number" className='mb-3'>
65
+ <Form.Label>Số điện thoại</Form.Label>
66
+ <Form.Control
67
+ // id='phone_number'
68
+ type="text"
69
+ placeholder="Số điện thoại"
70
+ onChange={(e) => setPhoneNumber(e.target.value)}
71
+
72
+ />
73
+ </Form.Group>
74
+
75
+ <Form.Group controlId="email" className='mb-3'>
76
+ <Form.Label>Email</Form.Label>
77
+ <Form.Control
78
+ // id='email'
79
+ type="text"
80
+ placeholder="Email"
81
+ onChange={(e) => setEmail(e.target.value)}
82
+
83
+ />
84
+ </Form.Group>
85
+
86
+ <Form.Group controlId="password" className='mb-3'>
87
+ <Form.Label>Mật khẩu</Form.Label>
88
+ <Form.Control
89
+ // id='password'
90
+ type="password"
91
+ placeholder="Nhập mật khẩu"
92
+ onChange={(e) => setPassword(e.target.value)}
93
+
94
+ />
95
+ </Form.Group>
96
+
97
+ <Form.Group controlId="confirm_password" className='mb-3'>
98
+ <Form.Label>Xác nhận mật khẩu</Form.Label>
99
+ <Form.Control
100
+ type="password"
101
+ placeholder="Xác nhận mật khẩu"
102
+ onChange={(e) => setConfirmPassword(e.target.value)}
103
+
104
+ />
105
+ </Form.Group>
106
+ <div className='d-flex justify-content-between align-items-center'>
107
+ <Button variant="primary" type="submit" className='me-2'>
108
+ Đăng ký
109
+ </Button>
110
+ <a href="/login" className='me-2'>Đăng nhập</a>
111
+
112
+ </div>
113
+
114
+ </Form>
115
+ </Card.Body>
116
+ </Card>
117
+
118
+ </Col>
119
+ <Col xs={1} md={2}></Col>
120
+ </Row>
121
+ </Container>
122
+ )
123
+ }>
124
+ </BasicTemplate>
125
+ );
126
+ };
127
+
128
+ export default RegisterPage;
frontend/src/templates/BasicTemplate.js ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // pages/HomePage.js
2
+ // import React, { useState } from 'react';
3
+ import ANavbar from '../molecules/Navbar';
4
+ import ContactSection from '../molecules/ContactSection';
5
+
6
+ function BasicTemplate ({content}) {
7
+ // const [isLoggedIn, setIsLoggedIn] = useState(false);
8
+ // const [user, setUser] = useState({ name: 'User' });
9
+
10
+ return (
11
+ <>
12
+ <ANavbar></ANavbar>
13
+ {content}
14
+ <ContactSection></ContactSection>
15
+ </>
16
+ );
17
+ };
18
+
19
+ export default BasicTemplate;