Как создать платформу электронной коммерции Web3 с помощью React и Solidity: (ЧАСТЬ II)
27 марта 2022 г.Что вы будете создавать, см. [живую демонстрацию] (https://fresher-a5113.web.app/) и [репозиторий GitHub] (https://github.com/Daltonic/freshers) для получения дополнительной информации.
Введение
В ПЕРВОЙ ЧАСТИ этого руководства мы создали смарт-контракт, на котором работает наше приложение. Теперь давайте создадим интерфейс для взаимодействия с ним, как вы можете видеть выше.
Не будем много говорить, давайте займемся кодированием… Начнем с установки остальных зависимостей для этого приложения.
Установка зависимостей приложений
На вашем терминале выполните следующие команды…
пряжа добавить firebase #Database SDK
пряжа добавить @cometchat-pro/chat #Chat SDK
пряжа добавить @material-tailwind/react #UI Kit
Если вы успешно выполнили приведенные выше команды, давайте перейдем к созданию закрытых ключей для Firebase и CometChat.
Создание закрытых ключей
Чтобы использовать Firebase или CometChat SDK, нам нужно создать приложение с их услугами. Не волнуйтесь, это не будет стоить вам ни копейки. Firebase ограничен, но бесплатен, этого более чем достаточно, чтобы помочь вам изучить разработку с полным стеком. CometChat предлагает своим пользователям пробную версию для тестирования своего SDK и ознакомления с тем, как работает их технология.
Создание приложения с помощью Firebase
Используйте этот пример. Если у вас еще нет учетной записи Firebase, создайте ее для себя. После этого перейдите в Firebase и создайте новый проект под названием freshers, затем активируйте службу аутентификации Google, как описано ниже.
Firebase поддерживает аутентификацию через различных провайдеров. Например, социальная аутентификация, номера телефонов и традиционный метод электронной почты и пароля. Поскольку в этом руководстве мы будем использовать аутентификацию Google, нам нужно включить ее для проекта, который мы создали в Firebase, поскольку по умолчанию она отключена. Щелкните метод входа на вкладке аутентификации для вашего проекта, и вы должны увидеть список поставщиков, которые в настоящее время поддерживаются Firebase.
Супер, это все для аутентификации Firebase, давайте сгенерируем ключи конфигурации Firebase SDK.
Вам нужно пойти и зарегистрировать свое приложение в рамках вашего проекта Firebase.
На странице обзора проекта выберите вариант добавления приложения и выберите web в качестве платформы.
!)
!)
Вернитесь на страницу обзора проекта после завершения регистрации конфигурации SDK, как показано на изображении ниже.
Теперь вы нажимаете на настройки проекта, чтобы скопировать настройки конфигурации SDK.
Ключи конфигурации, показанные на изображении выше, необходимо скопировать в файл .env. Позже мы будем использовать его в этом проекте.
Создайте файл с именем firebase.js в папке src этого проекта и вставьте в него следующие коды перед сохранением.
```javascript
импортировать {initializeApp} из 'firebase/app'
импортировать {setAlert} из './store'
импорт {
получитьАут,
вход с электронной почтой и паролем,
создать пользователя с электронной почтой и паролем,
выход,
onAuthStateChanged,
} из 'firebase/auth'
импорт {
получитьFirestore,
запрос,
получитьДокументы,
обновлениеДок,
коллекция,
коллекцияГруппа,
Сортировать по,
удалитьдок,
добавитьдок,
док,
сетдок,
серверВременная метка,
} из 'firebase/firestore'
константа firebaseConfig = {
APIKey: процесс.env.REACT_APP_FB_AUTH_KEY,
authDomain: 'fresher-a5113.firebaseapp.com',
идентификатор проекта: 'свежее-a5113',
storageBucket: 'fresher-a5113.appspot.com',
идентификатор отправителя сообщений: '443136794867',
идентификатор приложения: process.env.REACT_APP_FB_APP_ID,
константное приложение = initializeApp (firebaseConfig)
константная авторизация = getAuth (приложение)
const db = getFirestore (приложение)
const logInWithEmailAndPassword = async (адрес электронной почты, пароль) => {
пытаться {
return await signInWithEmailAndPassword (авторизация, электронная почта, пароль). затем (
(рез) => рез.пользователь
} поймать (ошибка) {
setAlert (JSON.stringify (ошибка), «красный»)
const registerWithEmailAndPassword = async (
Эл. адрес,
пароль,
полное имя,
Телефон,
учетная запись,
адрес
пытаться {
const res = await createUserWithEmailAndPassword(авторизация, электронная почта, пароль)
постоянный пользователь = res.user
const userDocRef = doc(db, 'users', user.email)
ожидание setDoc(userDocRef, {
uid: user.uid,
полное имя,
Эл. адрес,
Телефон,
учетная запись,
адрес,
вернуть пользователя
} поймать (ошибка) {
setAlert (JSON.stringify (ошибка), «красный»)
константный выход = асинхронный () => {
пытаться {
ожидание выхода (авторизация)
вернуть истину
} поймать (ошибка) {
setAlert (JSON.stringify (ошибка), «красный»)
const addToOrders = async (корзина) => {
пытаться {
постоянный порядок = {
порядок: Math.random().toString(36).substring(2, 9).toUpperCase(),
отметка времени: serverTimestamp(),
тележка,
жду аддок(
коллекция (БД, пользователи/${auth.currentUser.email}
, 'заказы'),
заказ
возврат заказа
} поймать (ошибка) {
setAlert (JSON.stringify (ошибка), «красный»)
const addProduct = async (продукт) => {
пытаться {
жду аддок(
коллекция (db, users/${auth.currentUser.email}
, 'products'),
имя: product.name,
UID: auth.currentUser.uid,
электронная почта: auth.currentUser.email,
цена: продукт.цена,
описание: описание продукта,
учетная запись: product.account,
imgURL: продукт.imgURL,
запас: ((Math.random() * 10) | 0) + 1,
отметка времени: serverTimestamp(),
} поймать (ошибка) {
setAlert (JSON.stringify (ошибка), «красный»)
const getProducts = асинхронный () => {
пытаться {
константные продукты = запрос (
collectionGroup (дб, «продукты»),
orderBy('отметка времени', 'описание')
const snapshot = ожидание getDocs (продукты)
вернуть snapshot.docs.map((doc) => ({
идентификатор: doc.id,
...док.данные(),
цена: число(doc.data().price),
} поймать (ошибка) {
setAlert (JSON.stringify (ошибка), «красный»)
const getProduct = асинхронный (идентификатор) => {
пытаться {
константные продукты = запрос (
collectionGroup (дб, «продукты»),
orderBy('отметка времени', 'описание')
const snapshot = ожидание getDocs (продукты)
const product = snapshot.docs.find((doc) => doc.id == id)
вернуть {
идентификатор: product.id,
...Данные продукта(),
цена: число (продукт.данные().цена),
} поймать (ошибка) {
setAlert (JSON.stringify (ошибка), «красный»)
const updateProduct = async (продукт) => {
const productRef = doc(db, users/${product.email}/products
, product.id)
пытаться {
ждать updateDoc(productRef, продукт)
} поймать (ошибка) {
setAlert (JSON.stringify (ошибка), «красный»)
const deleteProduct = async (продукт) => {
const productRef = doc(db, users/${product.email}/products
, product.id)
пытаться {
ожидание удаления документа (productRef)
} поймать (ошибка) {
setAlert (JSON.stringify (ошибка), «красный»)
экспорт {
авторизация,
дБ,
войти с электронной почтой и паролем,
зарегистрироваться с электронной почтой и паролем,
выйти,
onAuthStateChanged,
добавитьПродукт,
добавить в заказы,
получитьПродукты,
получитьПродукт,
обновлениеПродукт,
удалитьПродукт,
Вы молодец, если все правильно сделали. Далее мы сделаем что-то подобное для CometChat.
Создание приложения с CometChat
Перейдите на [CometChat] (https://app.cometchat.com/app/) и зарегистрируйтесь, если у вас нет учетной записи. Затем войдите в систему, и вы увидите экран ниже.
Используйте это в качестве примера, чтобы создать новое приложение с названием freshers, нажав кнопку Добавить новое приложение. Вам будет представлено модальное окно, где вы можете ввести данные приложения. На изображении ниже показан пример.
После создания приложения вы будете перенаправлены на панель инструментов, которая должна выглядеть примерно так.
Вы также должны скопировать эти ключи в файл .env.
Наконец, удалите предварительно загруженных пользователей и группы, как показано на изображениях ниже.
Круто, этого будет достаточно для настроек. Используйте этот шаблон, чтобы убедиться, что ваш файл .env соответствует нашему соглашению.
ENDPOINT_URL=
SECRET_KEY=<СЕКРЕТ_ФРАЗА>
DEPLOYER_KEY=<ВАШ_ЧАСТНЫЙ_КЛЮЧ>
REACT_APP_COMET_CHAT_REGION=<ВАШ_COMET_CHAT_REGION>
REACT_APP_COMET_CHAT_APP_ID=
REACT_APP_COMET_CHAT_AUTH_KEY=<ВАШ_COMET_CHAT_AUTH_KEY>
REACT_APP_FB_AUTH_KEY=<ВАШ_FIREBASE_AUTH_KEY>
REACT_APP_FB_APP_ID=<ВАШ_FIREBASE_APP_ID>
Наконец, создайте файл с именем cometChat.js в папке src этого проекта и вставьте в него приведенный ниже код.
```javascript
импортировать {CometChat} из '@cometchat-pro/chat'
const КОНСТАНТЫ = {
APP_ID: process.env.REACT_APP_COMET_CHAT_APP_ID,
РЕГИОН: process.env.REACT_APP_COMET_CHAT_REGION,
Auth_Key: process.env.REACT_APP_COMET_CHAT_AUTH_KEY,
константа initCometChat = асинхронная () => {
пытаться {
постоянный идентификатор приложения = CONSTANTS.APP_ID
константная область = CONSTANTS.REGION
const appSetting = новый CometChat.AppSettingsBuilder()
.subscribePresenceForAllUsers()
.setRegion(регион)
.строить()
await CometChat.init(appID, appSetting).then(() =>
console.log('Инициализация успешно завершена')
} поймать (ошибка) {
console.log(ошибка)
const loginWithCometChat = async (UID) => {
пытаться {
const authKey = CONSTANTS.Auth_Key
await CometChat.login(UID, authKey).then((user) =>
console.log('Вход выполнен успешно:', {пользователь})
} поймать (ошибка) {
console.log(ошибка)
const signInWithCometChat = async (UID, имя) => {
пытаться {
пусть authKey = CONSTANTS.Auth_Key
постоянный пользователь = новый CometChat.User(UID)
user.setName(имя)
return await CometChat.createUser(пользователь, authKey).then((пользователь) => пользователь)
} поймать (ошибка) {
console.log(ошибка)
const logOutWithCometChat = async () => {
пытаться {
await CometChat.logout().then(() => console.log('Выход из системы выполнен успешно'))
} поймать (ошибка) {
console.log(ошибка)
const getMessages = асинхронный (UID) => {
пытаться {
постоянный предел = 30
const messagesRequest = ожидание нового CometChat.MessagesRequestBuilder()
.setUID(UID)
.setLimit(лимит)
.строить()
return await messagesRequest.fetchPrevious().then((сообщения) => сообщения)
} поймать (ошибка) {
console.log(ошибка)
const sendMessage = async (receiverID, messageText) => {
пытаться {
константный приемникType = CometChat.RECEIVER_TYPE.USER
const textMessage = ожидание нового CometChat.TextMessage(
идентификатор получателя,
текст сообщения,
Тип приемника
return await CometChat.sendMessage(textMessage).then((сообщение) => сообщение)
} поймать (ошибка) {
console.log(ошибка)
const getConversations = асинхронный () => {
пытаться {
постоянный предел = 30
константные разговорыRequest = новый CometChat.ConversationsRequestBuilder()
.setLimit(лимит)
.строить()
возврат ожидания разговоровЗапрос
.fetchNext()
.then((список_разговоров) => список_разговоров)
} поймать (ошибка) {
console.log(ошибка)
экспорт {
initCometChat,
логин с CometChat,
знакInWithCometChat,
logOutWithCometChat,
получить сообщения,
Отправить сообщение,
получитьразговоры,
Круто, давайте приступим к интеграции их всех в наше приложение, начнем мы с компонентов.
Сборка компонентов
Давайте начнем создавать все компоненты один за другим, всегда обращайтесь к git repo, если у вас возникнут какие-либо проблемы.
Регистрационный компонент
Этот компонент отвечает за сохранение новых пользователей в Firebase. Перейдите к компонентам src >> и создайте файл с именем Register.jsx.
```javascript
импортировать {useState} из «реагировать»
импортировать кнопку из '@material-tailwind/react/Button'
импортировать {Link, useNavigate} из 'react-router-dom'
импорт {registerWithEmailAndPassword, выход} из '../firebase'
импортировать {signInWithCometChat} из '../cometChat'
импортировать {setAlert} из '../store'
константный регистр = () => {
const [полное имя, setFullname] = useState ('')
const [электронная почта, setEmail] = useState('')
const [пароль, setPassword] = useState('')
const [телефон, setPhone] = useState('')
const [адрес, setAddress] = useState ('')
const [учетная запись, setAccount] = useState ('')
константная навигация = useNavigate()
const handleRegister = async (e) => {
e.preventDefault()
если (
электронная почта == '' ||
пароль == '' ||
полное имя == '' ||
телефон == '' ||
счет == '' ||
адрес == ''
вернуть
Зарегистрируйтесь с электронной почтой и паролем (
Эл. адрес,
пароль,
полное имя,
Телефон,
учетная запись,
адрес
).then((пользователь) => {
если (пользователь) {
выход из системы (). затем (() => {
signInWithCometChat(user.uid, полное имя).then(() => {
Сброс форма()
setAlert('Регистрация прошла успешно')
навигация('/вход')
константа resetForm = () => {
установитьПолное имя('')
установить электронную почту ('')
Установка пароля('')
установить телефон ('')
установить учетную запись ('')
установитьАдрес('')
вернуть (
<форма
onSubmit={handleRegister}
className="относительный flex w-полный flex-wrap items-stretch w-96 px-8"
<ввод
тип = "текст"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
заполнитель = "Полное имя"
значение = {полное имя}
onChange={(e) => setFullname(e.target.value)}
требуется
<ввод
тип = "электронная почта"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
заполнитель = "Электронная почта"
значение = {электронная почта}
onChange={(e) => setEmail(e.target.value)}
требуется
<ввод
тип = "пароль"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
заполнитель = "**"
значение = {пароль}
onChange={(e) => setPassword(e.target.value)}
требуется
<ввод
тип = "число"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
заполнитель = "081 056 8262"
значение = {телефон}
onChange={(e) => setPhone(e.target.value)}
требуется
<ввод
тип = "текст"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
placeholder="Адрес кошелька"
значение = {аккаунт}
onChange={(e) => setAccount(e.target.value)}
требуется
<ввод
тип = "текст"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
заполнитель = "Адрес"
значение = {адрес}
onChange={(e) => setAddress(e.target.value)}
требуется
Уже участник? войти
Зарегистрироваться
экспорт по умолчанию Регистрация
Потрясающий!!!
Компонент входа
Давайте также создадим еще один компонент с именем Login.jsx в папке src >> components и вставим в него приведенный ниже код.
```javascript
импортировать {useState} из «реагировать»
импортировать {Link, useNavigate} из 'react-router-dom'
импортировать {logInWithEmailAndPassword} из '../firebase'
импортировать {loginWithCometChat} из '../cometChat'
импортировать {setAlert} из '../store'
импортировать кнопку из '@material-tailwind/react/Button'
постоянный Логин = () => {
const [электронная почта, setEmail] = useState('')
const [пароль, setPassword] = useState('')
константная навигация = useNavigate()
const handleLogin = async (e) => {
e.preventDefault()
если (электронная почта == '' || пароль == '') вернуть
logInWithEmailAndPassword(email, password).then((user) => {
если (пользователь) {
loginWithCometChat(user.uid).then(() => {
Сброс форма()
setAlert('Вход выполнен успешно')
навигация('/')
константа resetForm = () => {
установить электронную почту ('')
Установка пароля('')
вернуть (
<форма
onSubmit={handleLogin}
className="относительный flex w-полный flex-wrap items-stretch w-96 px-8"
Войти
<ввод
тип = "электронная почта"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
заполнитель = "Электронная почта"
значение = {электронная почта}
onChange={(e) => setEmail(e.target.value)}
требуется
<ввод
тип = "пароль"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
заполнитель = "**"
значение = {пароль}
onChange={(e) => setPassword(e.target.value)}
требуется
Регистрация нового пользователя
Войти
экспорт по умолчанию Логин
Круто, эти два компонента составляют аспект аутентификации этого приложения. Позже мы объединим их в соответствующие представления.
Компонент заголовка
Этот компонент инкапсулирует страницы нашего приложения. Он был создан с помощью бесплатного набора Creative TIm Tailwind-Material UI Kit. Создайте файл с именем Header.jsx в каталоге src >> component и вставьте в него приведенные ниже коды.
```javascript
импортировать {useState} из «реагировать»
импортировать {Link, useNavigate} из 'react-router-dom'
импортировать {setAlert, useGlobalState} из '../store'
импортировать {выход} из '../firebase'
импортировать {logOutWithCometChat} из '../cometChat'
импортировать {connectWallet} из '../shared/Freshers'
импортировать панель навигации из '@material-tailwind/react/Navbar'
импортировать NavbarContainer из '@material-tailwind/react/NavbarContainer'
импортировать NavbarWrapper из '@material-tailwind/react/NavbarWrapper'
импортировать NavbarBrand из '@material-tailwind/react/NavbarBrand'
импортировать NavbarToggler из '@material-tailwind/react/NavbarToggler'
импортировать NavbarCollapse из '@material-tailwind/react/NavbarCollapse'
импортировать Nav из '@material-tailwind/react/Nav'
импортировать NavItem из '@material-tailwind/react/NavItem'
константный заголовок = () => {
const [openNavbar, setOpenNavbar] = useState (ложь)
const [корзина] = useGlobalState('корзина')
const [isLoggedIn] = useGlobalState('isLoggedIn')
const [connectedAccount] = useGlobalState('connectedAccount')
константная навигация = useNavigate()
константа handleSignOut = () => {
выход().затем((рез) => {
если (рез) {
logOutWithCometChat().then(() => {
setAlert('Выход из системы выполнен успешно')
навигация('/вход')
вернуть (
<Ссылка на ="/">
<NavbarToggler
цвет = "белый"
onClick={() => setOpenNavbar(!openNavbar)}
рябь = "белый"
{Вошел в систему? (
<Навигация слева>
клиенты
Добавить продукт
<Навигация справа>
{Вошел в систему? (
{подключенная учетная запись? нулевой : (
<NavItem
onClick={connectWallet}
активный = "свет"
рябь = "свет"
Подключить кошелек
Выйти
Авторизоваться
Корзина {cart.length}
экспорт заголовка по умолчанию
Пищевой компонент
Этот компонент визуализирует определенные свойства продуктов питания, чтобы отображать их в красиво оформленной карточке с помощью CSS Tailwind и дизайна материалов. Создайте файл с именем Food.jsx в папке компонентов и вставьте в него следующие коды.
Каждая карточка отображает название, изображение, описание, цену и остатки продуктов питания. Вот код для него.
```javascript
импортировать React из «реагировать»
импортировать карту из '@material-tailwind/react/Card'
импортировать CardImage из '@material-tailwind/react/CardImage'
импортировать CardBody из '@material-tailwind/react/CardBody'
импортировать CardFooter из '@material-tailwind/react/CardFooter'
импортировать H6 из '@material-tailwind/react/Heading6'
импортировать абзац из '@material-tailwind/реагировать/абзац'
импортировать кнопку из '@material-tailwind/react/Button'
импортировать {setAlert, setGlobalState, useGlobalState} из '../store'
импортировать {Link} из 'react-router-dom'
const Food = ({item}) => {
const [корзина] = useGlobalState('корзина')
const addToCart = (элемент) => {
элемент.добавлено = правда
пусть cartItems = [...корзина]
const newItem = { ...item, qty: (item.qty += 1), stock: (item.stock -= 1) }
если (cart.find((_item) => _item.id == item.id)) {
cartItems[item] = новыйItem
setGlobalState('корзина', [...cartItems])
} еще {
setGlobalState('корзина', [...cartItems, newItem])
setAlert(${item.name} добавлено в корзину!
)
const toCurrency = (число) =>
num.toLocaleString('en-US', {
стиль: 'валюта',
валюта: 'доллар США',
вернуть (
<Карта>
/product/ + item.id}>
<Карточка>
/product/ + item.id}>
{item.name}
<Цвет абзаца="серый">
Не бойтесь правды, потому что нам нужно...
<дел
цвет = "черный"
className="flex flex-row justify-between items-center"
{toCurrency (цена товара)}
{item.stock} в наличии
{item.stock > 0 ? (
<Кнопка
onClick={() => addToCart(элемент)}
цвет = "зеленый"
размер = "мд"
рябь = "свет"
отключено={item.stock == 0}
Добавить в корзину
<Кнопка
цвет = "зеленый"
размер = "мд"
тип кнопки = "контур"
рябь = "свет"
инвалид
Нет на складе
экспортировать еду по умолчанию
Далее, давайте посмотрим на пищевой компонент.
Пищевые компоненты
Этот компонент отвечает за отображение всей коллекции данных о продуктах питания в нашей базе данных. Давайте посмотрим на его фрагмент кода.
Тем не менее, в каталоге компонентов создайте еще один файл с именем Foods.jsx и вставьте в него приведенные ниже коды.
```javascript
импортировать еду из './Food'
const Продукты = ({ продукты }) => {
вернуть (
{products.map((item, i) => (
<Продукт питания={item} key={i} />
экспорт продуктов по умолчанию
Наконец, давайте посмотрим на компонент CartItem.
Компонент CartItem
Этот компонент отвечает за отображение одного элемента в нашей коллекции корзины. Вот код, отвечающий за это.
```javascript
импортировать {useState} из «реагировать»
импортировать карту из '@material-tailwind/react/Card'
импортировать CardStatusFooter из '@material-tailwind/react/CardStatusFooter'
импортировать {Link} из 'react-router-dom'
импортировать {изображение, кнопку} из '@material-tailwind/реагировать'
импортировать {setGlobalState, useGlobalState} из '../store'
const CartItem = ({ элемент }) => {
const [кол-во, setQty] = useState (item.qty)
const [корзина] = useGlobalState('корзина')
const toCurrency = (число) =>
num.toLocaleString('en-US', {
стиль: 'валюта',
валюта: 'доллар США',
константное увеличениеQty = () => {
пусть cartItems = [...корзина]
const newItem = { ...item, qty: (item.qty += 1), stock: (item.stock -= 1) }
cartItems[item] = новыйItem
setGlobalState('корзина', cartItems)
setQty (новый элемент. количество)
константное уменьшениеQty = () => {
пусть cartItems = [...корзина]
если (кол-во == 1) {
константный индекс = cartItems.indexOf (элемент)
cartItems.splice (индекс, 1)
} еще {
константа newItem = {
...пункт,
кол-во: (шт.кол-во -= 1),
сток: (арт.сток += 1),
cartItems[item] = новыйItem
setQty (новый элемент. количество)
setGlobalState('корзина', cartItems)
вернуть (
<Ссылка
to={'/product/' + item.id}
className="h-12 w-12 объект-содержат MR-4"
<Изображение
источник={item.imgURL}
alt={item.name}
округлено={ложь}
поднял = {правда}
<CardStatusFooter
цвет = "зеленый"
сумма={toCurrency(item.price)}
дата = {item.name}
<Кнопка
цвет = "зеленый"
тип кнопки = "заполнено"
размер = "см"
округлено={ложь}
блок = {ложь}
iconOnly={ложь}
рябь = "темный"
onClick={уменьшениеQty}
{количество}
<Кнопка
цвет = "зеленый"
тип кнопки = "заполнено"
размер = "см"
округлено={ложь}
блок = {ложь}
iconOnly={ложь}
рябь = "темный"
onClick={увеличениеQty}
отключено={item.stock == 0}
Итого: {toCurrency(item.price * qty)}
экспортировать CarItem по умолчанию
Поздравляем, вы только что закончили программировать компоненты, давайте перейдем к созданию представлений…
Виды
Теперь, когда мы создали компоненты, поддерживающие различные представления, давайте приступим к созданию отдельных страниц.
Главный вид
Этот вид визуализирует структуру компонента Food. То есть домашний вид извлекает всю коллекцию продуктов из firebase и показывает их на экране. Давайте посмотрим на коды, отвечающие за это.
Перейдите в каталог представлений и создайте файл с именем Home.jsx, затем вставьте в него приведенный ниже код. На самом деле все эти файлы вы будете создавать в папке views.
```javascript
импортировать {useEffect, useState} из «реагировать»
импортировать заголовок из «../components/Header»
импортировать продукты из «../components/Foods»
импортировать {getProducts} из '../firebase'
const Главная = () => {
const [продукты, setProducts] = useState([])
использоватьЭффект(() => {
getProducts().then((products) => {
products.filter((item) => {
item.price = Число(item.price)
товар.кол-во = 0
setProducts(продукты)
вернуть (
<Заголовок/>
<Продукты питания={продукты} />
экспорт по умолчанию Главная
Вид продукта
Это представление отвечает за детальное отображение информации о продукте. На этой странице пользователи могут просматривать, редактировать и удалять продукты, а также общаться с продавцом или быстро покупать продукты питания с помощью Ethereum.
Вот код для него…
```javascript
импортировать заголовок из «../components/Header»
импортировать {useEffect, useState} из «реагировать»
импортировать {useParams, useNavigate} из 'react-router-dom'
импортировать {Button, CardImage} из '@material-tailwind/реагировать'
импортировать {getProduct, deleteProduct, auth} из '../firebase'
импортировать {setGlobalState, useGlobalState, setAlert} из '../store'
импортировать {payWithEthers} из '../shared/Freshers'
константа Продукт = () => {
const {идентификатор} = useParams ()
константная навигация = useNavigate()
const [продукт, setProduct] = useState (ноль)
const [корзина] = useGlobalState('корзина')
const [isLoggedIn] = useGlobalState('isLoggedIn')
const [покупатель] = useGlobalState('connectedAccount')
const [ethToUsd] = useGlobalState('ethToUsd')
константа addToCart = () => {
постоянный элемент = продукт
элемент.добавлено = правда
пусть cartItems = [...корзина]
const newItem = { ...item, qty: (item.qty += 1), stock: (item.stock -= 1) }
если (cart.find((_item) => _item.id == item.id)) {
cartItems[item] = новыйItem
setGlobalState('корзина', [...cartItems])
} еще {
setGlobalState('корзина', [...cartItems, newItem])
setAlert('Товар добавлен в корзину')
const handlePayWithEthers = () => {
const item = {...товар, покупатель, цена: (product.price / ethToUsd).toFixed(4) }
payWithEthers(item).then((res) => {
if (res) setAlert('Товар куплен!')
const handleDeleteProduct = () => {
удалитьПродукт(продукт).затем(() => {
setAlert('Товар удален!')
навигация('/')
const toCurrency = (число) =>
num.toLocaleString('en-US', {
стиль: 'валюта',
валюта: 'доллар США',
использоватьЭффект(() => {
getProduct(id).then((data) => setProduct({...data, qty: 1 }))
}, [я бы])
вернуть (
<Заголовок/>
{!!товар ? (
<дел>
{наименование товара}
Информация о продукте
{toCurrency(product.price)}
{product.stock} осталось в наличии
{product.description}
<Кнопка
onClick={добавить в корзину}
цвет = "зеленый"
размер = "мд"
рябь = "свет"
Добавить в корзину
{Вошел в систему? (
{auth.currentUser.uid != product.uid &&
product.account != покупатель ? (
<Кнопка
onClick={handlePayWithEthers}
цвет = "янтарный"
размер = "мд"
рябь = "свет"
Купить с ETH
) : нулевой}
{auth.currentUser.uid == product.uid ? нулевой : (
<Кнопка
onClick={() => navigation('/chat/' + product.uid)}
тип кнопки = "ссылка"
цвет = "зеленый"
размер = "мд"
рябь = "свет"
Чат с продавцом
) : нулевой}
{isLoggedIn && auth.currentUser.uid == product.uid ? (
<Кнопка
onClick={() => navigation('/product/edit/' + id)}
тип кнопки = "ссылка"
цвет = "зеленый"
размер = "мд"
рябь = "свет"
Редактировать продукт
<Кнопка
onClick={handleDeleteProduct}
тип кнопки = "ссылка"
цвет = "красный"
размер = "мд"
рябь = "свет"
Удалить
) : нулевой}
) : нулевой}
экспорт продукта по умолчанию
Вид «Добавить продукт»
!)
Как следует из названия, это представление отвечает за хранение новых продуктов питания в нашей коллекции Firestore. Обратите внимание на фрагмент кода ниже…
```javascript
импортировать {useState} из «реагировать»
импортировать {Link, useNavigate} из 'react-router-dom'
импортировать {addProduct} из '../firebase'
импортировать {setAlert} из '../store'
импортировать {useGlobalState} из '../store'
импортировать кнопку из '@material-tailwind/react/Button'
импортировать заголовок из «../components/Header»
константа AddProduct = () => {
const [имя, setName] = useState('')
const [цена, setPrice] = useState('')
const [imgURL, setImgURL] = useState('')
const [описание, setDescription] = useState('')
const [учетная запись] = useGlobalState («подключенная учетная запись»)
константная навигация = useNavigate()
const handleAddProduct = (e) => {
e.preventDefault()
если (!аккаунт) {
setAlert('Пожалуйста, подключите свою учетную запись метамаски!', 'красный')
вернуть
если (имя == '' || цена == '' || imgURL == '' || описание == '') вернуть
addProduct({ название, цена, imgURL, описание, учетная запись }).then(() => {
setAlert('Продукт успешно создан')
навигация('/')
вернуть (
<Заголовок/>
<форма
onSubmit={handleAddProduct}
className="относительный flex w-полный flex-wrap items-stretch w-96 px-8"
Добавить продукт
<ввод
тип = "текст"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
заполнитель = "Название продукта"
значение = {имя}
onChange={(e) => setName(e.target.value)}
требуется
<ввод
тип = "число"
мин={1}
шаг={0,01}
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
placeholder="Цена товара"
значение = {цена}
onChange={(e) => setPrice(e.target.value)}
требуется
<ввод
тип = "ссылка"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
placeholder="URL-адрес изображения продукта"
значение={imgURL}
onChange={(e) => setImgURL(e.target.value)}
требуется
<ввод
тип = "текст"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
placeholder="Описание продукта"
значение = {описание}
onChange={(e) => setDescription(e.target.value)}
требуется
Вернуться домой
Сохранить продукт
экспорт по умолчанию AddProduct
Круто, мы движемся вперед, давайте посмотрим, как изменить вид продукта…
Вид редактирования продукта
Этот вид позволяет нам редактировать наши существующие продукты питания. Конечно, вы должны быть тем, кто изначально добавил продукт питания в магазин, прежде чем вы сможете редактировать. Редактировать могут только владельцы продукта, давайте посмотрим на коды, выполняющие это действие.
```javascript
импортировать заголовок из «../components/Header»
импортировать кнопку из '@material-tailwind/react/Button'
импортировать {useEffect, useState} из «реагировать»
импортировать {Link, useParams, useNavigate} из 'react-router-dom'
импортировать {updateProduct, getProduct, auth} из '../firebase'
импортировать {setAlert} из '../store'
импортировать {useGlobalState} из '../store'
константа EditProduct = () => {
const {идентификатор} = useParams ()
константная навигация = useNavigate()
const [продукт, setProduct] = useState (ноль)
const [имя, setName] = useState('')
const [цена, setPrice] = useState('')
const [imgURL, setImgURL] = useState('')
const [описание, setDescription] = useState('')
const [учетная запись] = useGlobalState («подключенная учетная запись»)
использоватьЭффект(() => {
getProduct(id).then((данные) => {
если (auth.currentUser.uid != data.uid) перейти('/')
setProduct (данные)
setName(данные.имя)
setPrice (Число (данные. Цена))
setImgURL(данные.imgURL)
setDescription (данные.описание)
}, [я бы])
const handleProductUpdate = (e) => {
e.preventDefault()
если (!аккаунт) {
setAlert('Пожалуйста, подключите свою учетную запись метамаски!', 'красный')
вернуть
если (имя == '' || цена == '' || imgURL == '' || описание == '') вернуть
обновить продукт ({
...товар,
имя,
цена,
imgURL,
описание,
учетная запись,
}).тог(() => {
setAlert('Продукт успешно обновлен')
перейти('/продукт/' + product.id)
вернуть (
<Заголовок/>
<форма
onSubmit={handleProductUpdate}
className="относительный flex w-полный flex-wrap items-stretch w-96 px-8"
Обновить продукт
<ввод
тип = "текст"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
заполнитель = "Название продукта"
значение = {имя}
onChange={(e) => setName(e.target.value)}
требуется
<ввод
тип = "число"
мин={1}
шаг={0,01}
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
placeholder="Цена товара"
значение = {цена}
onChange={(e) => setPrice(e.target.value)}
требуется
<ввод
тип = "ссылка"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
placeholder="URL-адрес изображения продукта"
значение={imgURL}
onChange={(e) => setImgURL(e.target.value)}
требуется
<ввод
тип = "текст"
className="px-3 py-3 заполнитель-blueGray-300 text-blueGray-600 относительный bg-white bg-white закругленный текст-sm граница border-blueGray-300 контур-нет фокус: контур-нет фокус: кольцо w-полный пл-10"
placeholder="Описание продукта"
значение = {описание}
onChange={(e) => setDescription(e.target.value)}
требуется
/product/ + id}>
Назад к продукту
Обновлять
экспортировать по умолчанию EditProduct
Наконец, для случаев, связанных с продуктами, давайте посмотрим на вид корзины…
Вид корзины
В этом представлении вы можете изменять и размещать свои заказы. Как только вы размещаете свой заказ, он сразу же сохраняется в Firestore. Ниже показано, как написан код.
```javascript
импортировать CartItem из '../components/CartItem'
импортировать заголовок из «../components/Header»
импортировать {Link} из 'react-router-dom'
импортировать {кнопку} из '@material-tailwind/реагировать'
импортировать {useEffect, useState} из «реагировать»
импортировать {addToOrders} из '../firebase'
импортировать {setAlert, setGlobalState, useGlobalState} из '../store'
const Корзина = () => {
const [корзина] = useGlobalState('корзина')
const [isLoggedIn] = useGlobalState('isLoggedIn')
const [всего, setTotal] = useState (0)
константа getTotal = () => {
пусть сумма = 0
cart.forEach((товар) => (всего += товар.кол-во * товар.цена))
setTotal (всего)
const placeOrder = () => {
если (!isLoggedIn) вернуть
addToOrders(корзина).then((данные) => {
setGlobalState('корзина', [])
setAlert(Заказ размещен с идентификатором: ${data.order}
)
const clearCart = () => {
setGlobalState('корзина', [])
const toCurrency = (число) =>
num.toLocaleString('en-US', {
стиль: 'валюта',
валюта: 'доллар США',
useEffect(() => getTotal(), [корзина])
вернуть (
<Заголовок/>
{корзина.длина > 0? (
{cart.map((элемент, я) => (
Общая сумма:
{в валюте (всего)}
<Кнопка
onClick={clearCart}
цвет = "красный"
рябь = "свет"
тип = "отправить"
Очистить корзину
{Вошел в систему? (
<Кнопка
onClick={placeOrder}
цвет = "зеленый"
рябь = "свет"
тип = "отправить"
Разместить заказ
) : нулевой}
Корзина пуста, добавьте несколько товаров в корзину
Выберите продукт
экспорт корзины по умолчанию
Далее, давайте позаботимся о последних четырех представлениях в нашем трее…
Просмотр списка чатов
В этом представлении просто перечислены недавние разговоры с вашими клиентами. Это возможно с помощью CometChat SDK, приведенные ниже коды показывают, как это было реализовано.
```javascript
импортировать заголовок из «../components/Header»
импортировать {getConversations} из '../cometChat'
импортировать {useEffect, useState} из «реагировать»
импортировать {Link} из 'react-router-dom'
импортировать {auth} из '../firebase'
const ChatList = () => {
const [клиенты, setCustomers] = useState([])
const [загружено, setLoaded] = useState (false)
использоватьЭффект(() => {
getConversations().then((разговор) => {
console.log(разговор)
setCustomers(разговор)
setLoaded (истина)
вернуть (
<Заголовок/>
Последние чаты
{загружено
? customers.map((клиент, я) => (
<Разговор
ключ = {я}
currentUser={auth.currentUser.uid.toLowerCase()}
владелец = {customer.lastMessage.receiverId.toLowerCase()}
разговор={customer.lastMessage}
: нулевой}
const Conversation = ({ беседа, текущий пользователь, владелец }) => {
постоянный владелец = (ключ) => {
вернуть currentUser == владелец
? разговор. отправитель [ключ]
: разговор.приемник[ключ]
const timeAgo = (дата) => {
пусть секунды = Math.floor((новая дата() - дата) / 1000)
пусть интервал = секунды / 31536000
если (интервал > 1) {
вернуть Math.floor(интервал) + 'год'
интервал = секунды / 2592000
если (интервал > 1) {
return Math.floor(интервал) + 'mo'
интервал = секунды / 86400
если (интервал > 1) {
вернуть Math.floor(интервал) + 'd'
интервал = секунды / 3600
если (интервал > 1) {
вернуть Math.floor(интервал) + 'ч'
интервал = секунды / 60
если (интервал > 1) {
вернуть Math.floor(интервал) + 'm'
вернуть Math.floor (секунды) + 's'
вернуть (
<Ссылка
to={'/chat/' + владелец('uid')}
className="flex flex-row justify-between items-center
mb-2 py-2 px-4 bg-grey-100 rounded-lg курсор-указатель"
{владелец('имя')}
{беседа.текст}
{timeAgo(новая дата(число(диалог.sentAt) * 1000).getTime())}
экспортировать список чатов по умолчанию
Просмотр чата
Это вид чата один на один для общения продавца и покупателя. CometChat SDK упрощает эту задачу. Следующий код демонстрирует, как это работает довольно хорошо.
```javascript
импортировать {CometChat} из '@cometchat-pro/chat'
импортировать {sendMessage, getMessages} из '../cometChat'
импортировать {useEffect, useState} из «реагировать»
импортировать {useParams} из 'react-router-dom'
импортировать заголовок из «../components/Header»
постоянный чат = () => {
const {идентификатор получателя} = useParams()
const [сообщение, setMessage] = useState('')
const [сообщения, setMessages] = useState([])
константа handleSendMsg = (e) => {
e.preventDefault()
sendMessage(ID получателя, сообщение).then((msg) => {
setMessages((prevState) => [...prevState, msg])
установитьсообщение('')
прокрутить до конца ()
константа handleGetMessages = () => {
getMessages(receiverID).then((msgs) => {
setMessages (сообщения)
прокрутить до конца ()
const listenForMessage = (listenerID) => {
CometChat.addMessageListener(
идентификатор слушателя,
новый CometChat.MessageListener({
onTextMessageReceived: (сообщение) => {
setMessages((prevState) => [...prevState, сообщение])
прокрутить до конца ()
константа scrollToEnd = () => {
const elmnt = document.getElementById('контейнер сообщений')
elmnt.scrollTop = elmnt.scrollHeight
использоватьЭффект(() => {
обрабатыватьGetMessages()
слушатьForMessage (идентификатор получателя)
}, [идентификатор получателя])
вернуть (
<Заголовок/>
Чат
<дел
id="контейнер сообщений"
className="относительный p-6 flex-auto h-64 overflow-y-scroll"
стиль={{ высота: '20rem' }}
{messages.map((msg, я) =>
msg?.receiverId?.toLowerCase() != приемникID.toLowerCase() ? (
<дел
ключ = {я}
className="flex flex-col justify-center items-start w-full mb-4"
{msg.text}
<дел
ключ = {я}
className="flex flex-col justify-center items-end w-full mb-4"
{msg.text}
<форма
onSubmit={handleSendMsg}
className="flex flex-row justify-center items-center mt-4 py-4"
<ввод
тип = "текст"
placeholder="Введите сообщение..."
className="px-3 py-8 заполнитель-blueGray-300 text-blueGray-600 относительный
bg-green-100 закругленный текст-sm граница border-blueGray-300
контур-нет фокус: контур-нет фокус: кольцо w-полный изгиб-1 граница-0"
значение = {сообщение}
onChange={(e) => setMessage(e.target.value)}