Задание
Разработать приложение - сайт магазина по продаже игр виртуальной реальности (Тильда НЕ ИСПОЛЬЗОВАТЬ!!!)
Функционал программы
- добавление игр через админ панель
- удаление игр через админ панель
- редактирование игр
- оформление заказа и его оплата
Стэк технологий:
- Бэкенд - язык Python и Django Rest Framework
- Фронтенд - React + Vite
Фрагмент программного кода (бэкенд)
from django.db import models
# Create your models here.
class Game(models.Model):
objects = models.Manager()
title = models.CharField(max_length=100, verbose_name="Название игры")
description = models.TextField(verbose_name="Описание игры")
GENRE_CHOICES = [
('Все жанры', 'Все жанры'),
('Симуляторы', 'Симуляторы'),
('Шутеры', 'Шутеры'),
('RPG', 'RPG'),
('Хоррор', 'Хоррор'),
]
genre = models.CharField(max_length=50, choices=GENRE_CHOICES, verbose_name='Категория', null=True, blank=True, default='Все жанры')
price = models.DecimalField(verbose_name="Цена игры", max_digits=10,
decimal_places=2)
photo = models.ImageField(upload_to='uploads/images',
verbose_name="Фото игры",
null=True, blank=True)
def __str__(self):
return self.title
class Meta:
verbose_name = 'Игра'
verbose_name_plural = 'Игры'
class Order(models.Model):
name = models.CharField(max_length=100, verbose_name="Имя клиента")
phone = models.CharField(max_length=20, verbose_name="Телефон")
game = models.ForeignKey(Game, on_delete=models.CASCADE, verbose_name="Выбранная игра")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата заказа")
def __str__(self):
return f"Заказ от {self.name} - {self.game.title}"
class Meta:
verbose_name = "Заказ"
verbose_name_plural = "Заказы"
from django.db import models
class Payment(models.Model):
card_number = models.CharField(max_length=19, verbose_name="Номер карты")
card_holder = models.CharField(max_length=100, verbose_name="Имя владельца")
expiry = models.CharField(max_length=5, verbose_name="Срок действия (MM/YY)")
cvv = models.CharField(max_length=4, verbose_name="CVV/CVC")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
class Meta:
verbose_name = "Платёж"
verbose_name_plural = "Платежи"
def __str__(self):
return f"{self.card_holder} — ****{self.card_number[-4:]}"
Фрагмент программного кода (фронтенд)
import React, { useState, useEffect } from 'react';
import GameModal from './GameModal'; // Импортируем новый компонент
import axios from 'axios'; // Импортируем axios для запросов к API
const Games = ({ setSelectedGame }) => {
const [games, setGames] = useState([]);
const [selectedGenre, setSelectedGenre] = useState('all');
const [modalGame, setModalGame] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const genres = ['Все жанры', 'Симуляторы', 'Шутеры', 'RPG', 'Хоррор'];
useEffect(() => {
const fetchGames = async () => {
try {
const response = await axios.get('http://127.0.0.1:8000/api/games');
setGames(response.data);
setLoading(false);
} catch (error) {
setError('Ошибка при загрузке игр:', error);
setLoading(false);
}
};
fetchGames();
}, []);
const filteredGames = selectedGenre === 'all'
? games
: games.filter(game => game.genre === selectedGenre);
const handleGenreClick = (genre) => {
setSelectedGenre(genre === 'Все жанры' ? 'all' : genre);
};
const openModal = (game) => {
setModalGame(game);
setSelectedGame(game); // Обновляем состояние selectedGame в компоненте App
};
const closeModal = () => {
setModalGame(null);
};
const scrollToOrderForm = () => {
const orderForm = document.getElementById('order');
if (orderForm) {
orderForm.scrollIntoView({ behavior: 'smooth' });
}
};
return (
<>
{loading && <div>Загрузка...</div>}
{error && <div>{error}</div>}
<section className="games" id="games">
<div className="container">
<h2>Выберите игру</h2>
<div className="games-content" id="games-content">
<aside className="games-filter">
<ul id="genre-filter">
{genres.map((genre, index) => (
<li
key={index}
className={selectedGenre === (genre === 'Все жанры' ? 'all' : genre) ? 'active' : ''}
data-genre={genre}
onClick={() => handleGenreClick(genre)}
>
{genre}
</li>
))}
</ul>
</aside>
<div className="games-items" id="games-list">
{filteredGames.map(game => (
<article key={game.id} className="game" data-genre={game.genre}>
<img
src="/images/loading-150-1.gif" data-src={game.photo}
alt={game.title}
onClick={() => openModal(game)} // Добавляем обработчик клика на изображение
/>
<div className="game-details">
<h4>{game.title}</h4>
<p>{game.description}</p>
<div className="game-action">
<ul>
<li>
<div className="game-price">{game.price} руб.</div>
</li>
</ul>
<a
href="#order"
className="button white-button"
onClick={(e) => {
e.preventDefault();
openModal(game);
scrollToOrderForm(); // Прокручиваем страницу до формы заказа
}}
>
Заказать
</a>
</div>
</div>
</article>
))}
</div>
</div>
</div>
</section>
<GameModal game={modalGame} onClose={closeModal} /> {/* Используем новый компонент */}
</>
);
};
export default Games;
Скриншот архива с проектом


Структура проекта (версия 1)

Структура проекта (версия 2)

Пояснения по запуску программы
1. Уставливаем Node.js 2. Устанавливаем Visual Studio Code 3. Устанавливаем все нужные расширения. 4. Запускаем Бэкенд - python manage.py runserver и фронтенд - npm run dev . 5. Для версии 2 только запускаем фронтенд той же командой.
Телеграм
-