Магазин на JavaScript, часть 11 из 19. Компоненты Signup и Login, компоненты Shop и Basket

24.12.2021

Теги: BackendExpress.jsFrontendJavaScriptNode.jsORMReact.jsWeb-разработкаБазаДанныхИнтернетМагазинКаталогТоваровКорзинаФреймворк

Теперь будет немного верстки — нам нужно создать все страницы приложения. Для начала страницы ренистрации и авторизации, которые будут очень похожи. Потом главную страницу магазина со списком всех товаров каталога. Наконец, страницу просмотра одного товара и страницу корзины покупателя.

Компоненты Signup и Login

Файл pages/Signup.js:

import { AppContext } from '../components/AppContext.js'
import { useContext } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { Container, Row, Card, Form, Button } from 'react-bootstrap'

const Signup = () => {
    const { user } = useContext(AppContext)
    const navigate = useNavigate()

    // если пользователь авторизован — ему здесь делать нечего
    useEffect(() => {
        if (user.isAdmin) navigate('/admin', {replace: true})
        if (user.isAuth) navigate('/user', {replace: true})
    }, [])

    return (
        <Container className="d-flex justify-content-center">
            <Card style={{width: '50%'}} className="p-2 mt-5 bg-light">
                <h3 className="m-auto">Регистрация</h3>
                <Form className="d-flex flex-column">
                    <Form.Control
                        className="mt-3"
                        placeholder="Введите ваш email..."
                        value=""
                    />
                    <Form.Control
                        className="mt-3"
                        placeholder="Введите ваш пароль..."
                        value=""
                    />
                    <div className="d-flex justify-content-between mt-3 pl-3 pr-3">
                        <Button type="submit">Регистрация</Button>
                        <p>Уже есть аккаунт? <Link to="/login">Войдите!</Link></p>
                    </div>
                </Form>
            </Card>
        </Container>
    )
}

export default Signup

Файл pages/Login.js:

import { AppContext } from '../components/AppContext.js'
import { useContext } from 'react'
import { Link, useNavigate } from 'react-router-dom'
import { Container, Row, Card, Form, Button } from 'react-bootstrap'

const Login = () => {
    const { user } = useContext(AppContext)
    const navigate = useNavigate()

    // если пользователь авторизован — ему здесь делать нечего
    useEffect(() => {
        if (user.isAdmin) navigate('/admin', {replace: true})
        if (user.isAuth) navigate('/user', {replace: true})
    }, [])

    return (
        <Container className="d-flex justify-content-center">
            <Card style={{width: '50%'}} className="p-2 mt-5 bg-light">
                <h3 className="m-auto">Авторизация</h3>
                <Form className="d-flex flex-column">
                    <Form.Control
                        className="mt-3"
                        placeholder="Введите ваш email..."
                        value=""
                    />
                    <Form.Control
                        className="mt-3"
                        placeholder="Введите ваш пароль..."
                        value=""
                    />
                    <Row className="d-flex justify-content-between mt-3 pl-3 pr-3">
                        <Button type="submit">Войти</Button>
                        <p>Нет аккаунта? <Link to="/signup">Зарегистрирутесь!</Link></p>
                    </Row>
                </Form>
            </Card>
        </Container>
    )
}

export default Login

Компонент Shop

Это будет очень большой компонент — слева будет меню со списком всех категорий. Справа — меню со списком всех брендов, а под ним — список всех товаров. При клике на категорию и/или бренд — будет происходить фильтрация товаров. Но прежде добавим в контекст товары, категории и бренды — чтобы было проще верстать.

import React from 'react'

const AppContext = React.createContext()

// контекст, который будем передавать
const context = {
    user: {
        email: 'ivanov@mail.ru',
        isAuth: true,
        isAdmin: false,
    },
    products: [
        {id: 1, name: 'Холодильник раз', price: 12345, rating: 0, image: '', categoryId: 1, brandId: 1},
        {id: 2, name: 'Холодильник два', price: 23456, rating: 0, image: '', categoryId: 1, brandId: 2},
        {id: 3, name: 'Телевизор раз', price: 34567, rating: 0, image: '', categoryId: 2, brandId: 1},
        {id: 4, name: 'Телевизор два', price: 45678, rating: 0, image: '', categoryId: 2, brandId: 2},
        {id: 5, name: 'Смартфон раз', price: 56789, rating: 0, image: '', categoryId: 3, brandId: 3},
        {id: 6, name: 'Смартфон два', price: 67890, rating: 0, image: '', categoryId: 3, brandId: 4},
        {id: 7, name: 'Планшет раз', price: 78901, rating: 0, image: '', categoryId: 4, brandId: 3},
        {id: 8, name: 'Планшет два', price: 89012, rating: 0, image: '', categoryId: 4, brandId: 4},
    ],
    categories: [
        {id: 1, name: 'Холодильники'},
        {id: 2, name: 'Телевизоры'},
        {id: 3, name: 'Смартфоны'},
        {id: 4, name: 'Планшеты'},
    ],
    brands: [
        {id: 1, name: 'Samsung'},
        {id: 2, name: 'Philips'},
        {id: 3, name: 'Siemens'},
        {id: 4, name: 'Xiaomi'},
    ],
}

const AppContextProvider = (props) => {
    return (
        <AppContext.Provider value={context}>
            {props.children}
        </AppContext.Provider>
    );
}

export {AppContext, AppContextProvider}

Файл pages/Shop.js:

import { Container, Row, Col } from 'react-bootstrap'
import CategoryBar from '../components/CategoryBar.js'
import BrandBar from '../components/BrandBar.js'
import ProductList from '../components/ProductList.js'

const Shop = () => {
    return (
        <Container>
            <Row className="mt-2">
                <Col md={3}>
                    <CategoryBar />
                </Col>
                <Col md={9}>
                    <BrandBar />
                    <ProductList />
                </Col>
            </Row>
        </Container>
    )
}

export default Shop

Файл components/CategoryBar.js:

import { useContext } from 'react'
import { AppContext } from './AppContext.js'
import { ListGroup } from 'react-bootstrap'

const CategoryBar = () => {
    const { categories } = useContext(AppContext)
    return (
        <ListGroup>
            {categories.map(item =>
                <ListGroup.Item
                    key={item.id}
                    active={false}
                    onClick={() => alert('Фильтрация, только товары категории')}
                    style={{cursor: 'pointer'}}
                >
                    {item.name}
                </ListGroup.Item>
            )}
        </ListGroup>
    )
}

export default CategoryBar

Файл components/BrandBar.js:

import { useContext } from 'react'
import { AppContext } from './AppContext.js'
import { ListGroup } from 'react-bootstrap';

const BrandBar = () => {
    const { brands } = useContext(AppContext)
    return (
        <ListGroup horizontal>
            {brands.map(item =>
                <ListGroup.Item
                    key={item.id}
                    active={false}
                    onClick={() => alert('Фильтрация, только товары бренда')}
                    style={{cursor: 'pointer'}}
                >
                    {item.name}
                </ListGroup.Item>
            )}
        </ListGroup>
    )
}

export default BrandBar

Файл components/ProductList.js:

import { useContext } from 'react'
import { AppContext } from './AppContext.js'
import { Row } from 'react-bootstrap'
import ProductItem from './ProductItem.js'

const ProductList = () => {
    const { products } = useContext(AppContext)

    return (
        <Row className="d-flex">
            {products.map(item =>
                <ProductItem key={item.id} data={item}/>
            )}
        </Row>
    );
}

export default ProductList

Файл components/ProductItem.js:

import { Card, Col } from 'react-bootstrap'

const ProductItem = ({data}) => {
    return (
        <Col md={3} className="mt-3" onClick={() => alert('Переход на страницу товара')}>
            <Card style={{width: 200, cursor: 'pointer'}}>
                <Card.Img variant="top" src="http://via.placeholder.com/200" />
                <Card.Body style={{height: 100, overflow: 'hidden'}}>
                    <p>Бренд: Samsung</p>
                    <strong>{data.name}</strong>
                </Card.Body>
            </Card>
        </Col>
    )
}

export default ProductItem

Компонент Basket

Будет во многом похож на компонент ProductList, потому что тоже показывает список, только в виде таблицы. Для начала добавим в контекст содержимое корзины.

import React from 'react'

const AppContext = React.createContext()

// контекст, который будем передавать
const context = {
    user: {
        email: 'ivanov@mail.ru',
        isAuth: true,
        isAdmin: false,
    },
    /* .......... */
    basket: [
        {product_id: 1, name: 'Смартфон раз', price: 56789, quantity: 2},
        {product_id: 8, name: 'Планшет два', price: 89012, quantity: 1},
    ],
}

const AppContextProvider = (props) => {
    return (
        <AppContext.Provider value={context}>
            {props.children}
        </AppContext.Provider>
    );
}

export {AppContext, AppContextProvider}

Файл pages/Basket.js:

import BasketList from '../components/BasketList.js'
import { Container } from 'react-bootstrap'

const Basket = () => {
    return (
        <Container>
            <h1>Корзина</h1>
            <BasketList />
        </Container>
    )
}

export default Basket

Файл components/BasketList.js:

import { useContext } from 'react'
import { AppContext } from './AppContext.js'
import { Table } from 'react-bootstrap'
import BasketItem from './BasketItem.js'

const BasketList = () => {
    const { basket } = useContext(AppContext)
    // общая стоимость товаров в корзине
    const cost = basket.reduce((sum, item) => sum + item.price * item.quantity, 0) 
    return (
        <>
            {basket.length ? (
                <Table bordered hover size="sm" className="mt-3">
                    <thead>
                        <tr>
                            <th>Наименование</th>
                            <th>Количество</th>
                            <th>Цена</th>
                            <th>Сумма</th>
                            <th>Удалить</th>
                        </tr>
                    </thead>
                    <tbody>
                        {basket.map(item => <BasketItem key={item.product_id} {...item} />)}
                        <tr>
                            <th colSpan="3">Итого</th>
                            <th>{cost}</th>
                            <th>руб.</th>
                        </tr>
                    </tbody>
                </Table>
            ) : (
                <p>Ваша корзина пуста</p>
            )}
        </>
    )
}

export default BasketList

Файл components/BasketItem.js:

const BasketItem = (props) => {
    return (
        <tr>
            <td>{props.name}</td>
            <td>{props.quantity}</td>
            <td>{props.price}</td>
            <td>{props.price * props.quantity}</td>
            <td>Удалить</td>
        </tr>
    );
}

export default BasketItem

Поиск: Backend • Express.js • Frontend • JavaScript • Node.js • ORM • React.js • Web-разработка • База данных • Интернет магазин • Каталог товаров • Корзина • Фреймворк

Каталог оборудования
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Производители
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Функциональные группы
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.