8 Commits
lab_1 ... lab_5

Author SHA1 Message Date
qkrlnt
616c03e397 отчет 2025-05-28 19:15:11 +04:00
d1b2cea66c minimal 2025-05-23 17:43:10 +02:00
311491d554 some changes 2025-05-23 17:33:42 +02:00
5fa65ad591 lab_5 2025-05-22 15:18:29 +02:00
qkrlnt
9c80373c59 lab_4 2025-05-20 16:04:25 +04:00
qkrlnt
49fe7787f3 lab_3_done 2025-05-20 15:13:16 +04:00
qkrlnt
65407801d3 lab_3 2025-05-20 14:58:51 +04:00
qkrlnt
0f37299eab lab_2_done 2025-05-20 14:03:18 +04:00
35 changed files with 6561 additions and 406 deletions

54
.gitignore vendored Normal file
View File

@@ -0,0 +1,54 @@
# ---> VisualStudioCode
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
!.vscode/*.code-snippets
# Local History for Visual Studio Code
.history/
# Built Visual Studio Code Extensions
*.vsix
# Зависимости Node.js
/node_modules/
# Результаты сборки Vite
/dist/
# Логи
npm-debug.log*
yarn-debug.log*
pnpm-debug.log*
*.log
# Файлы окружения
.env
.env.*.local
# Кеши
/.cache/
.vite/
# IDE и редакторы
.vscode/
.idea/
/*.sublime-workspace
/*.sublime-project
# Системные файлы
.DS_Store
Thumbs.db
# Отчеты об покрытии тестами
/coverage/
# Порты и артефакты
*.pid
*.seed
*.pid.lock
# Прочее
/.DS_Store

View File

@@ -1,32 +0,0 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<title>Интернет-магазин: ЛК</title>
<link rel="stylesheet" href="css/style.css"/>
</head>
<body>
<header>
<img src="images/logo.jpg" alt="'Название магазина'">
<a href="newSite.html"><h1><b>"Название магазина"</b></h1></a>
<navbar>
<p><a href="Basket.html">Корзина</a></p>
<p><a href="Order.html">Заказы</a></p>
<p><a href="Favorites.html">Избранное</a></p>
<p><a href="Account.html">Личный кабинет</a></p>
</navbar>
</header>
<div class="account">
<img src="images/бананы.jpg">
<h3>Имя Фамилия</h3>
<p>Описание</p>
</div>
<footer>
<p>Помощь:</p>
<p>8 (800)-555-35-35</p>
<p>vk.com</p>
<p>tg.me</p>
<p>ozon-zon-zon@mail.joke</p>
</footer>
</body>
</html>

View File

@@ -1,40 +0,0 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<title>Интернет-магазин: Корзина</title>
<link rel="stylesheet" href="css/style.css"/>
</head>
<body>
<header>
<img src="images/logo.jpg" alt="'Название магазина'">
<a href="newSite.html"><h1><b>"Название магазина"</b></h1></a>
<navbar>
<p><a href="Basket.html">Корзина</a></p>
<p><a href="Order.html">Заказы</a></p>
<p><a href="Favorites.html">Избранное</a></p>
<p><a href="Account.html">Личный кабинет</a></p>
</navbar>
</header>
<div class="basket">
<h1>Корзина</h1>
<ol>
<li><img src="images/glasses.jpg">Очки<p>349 руб.</p></li>
<li><img src="images/chery.jpg">Chery Tiggo 7 Pro Max 64gb 128mp<p>5 руб.</p></li>
<li><img src="images/vanadiy.jpg">Ванадий<p>2099 руб.</p></li>
</ol>
</div>
<div class="buy">
<a href="https://xn----7sbon6aucai8a.xn--p1ai/wa-data/public/shop/products/98/27/2798/images/6060/6060.970.jpg" target="_blank">
Оплатить
</a>
</div>
<footer>
<p>Помощь:</p>
<p>8 (800)-555-35-35</p>
<p>vk.com</p>
<p>tg.me</p>
<p>ozon-zon-zon@mail.joke</p>
</footer>
</body>
</html>

View File

@@ -1,34 +0,0 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<title>Интернет-магазин: Избранное</title>
<link rel="stylesheet" href="css/style.css"/>
</head>
<body>
<header>
<img src="images/logo.jpg" alt="'Название магазина'">
<a href="newSite.html"><h1><b>"Название магазина"</b></h1></a>
<navbar>
<p><a href="Basket.html">Корзина</a></p>
<p><a href="Order.html">Заказы</a></p>
<p><a href="Favorites.html">Избранное</a></p>
<p><a href="Account.html">Личный кабинет</a></p>
</navbar>
</header>
<div class="favourites">
<h2>Избранное</h2>
<ul>
<li><img src ="images/masha.jpg">Женщина<p>бесценна</p></li>
<li><img src ="images/screwdriver.jpg">Отвертка<p>219 руб</p></li>
</ul>
</div>
<footer>
<p>Помощь:</p>
<p>8 (800)-555-35-35</p>
<p>vk.com</p>
<p>tg.me</p>
<p>ozon-zon-zon@mail.joke</p>
</footer>
</body>
</html>

View File

@@ -1,45 +0,0 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8" />
<title>Интернет-магазин: Заказы</title>
<link rel="stylesheet" href="css/style.css"/>
</head>
<body>
<header>
<img src="images/logo.jpg" alt="'Название магазина'">
<a href="newSite.html"><h1><b>"Название магазина"</b></h1></a>
<navbar>
<p><a href="Basket.html">Корзина</a></p>
<p><a href="Order.html">Заказы</a></p>
<p><a href="Favorites.html">Избранное</a></p>
<p><a href="Account.html">Личный кабинет</a></p>
</navbar>
</header>
<div class="orders"><h1>Заказы</h1></div>
<div class="typeOfOrder">
<div class="inProcess">
<h2>В процессе</h2>
<ul>
<li><img src="images/spoon.jpg">Ложка</li>
<li><img src="images/fork.jpg">Вилка</li>
<li><img src="images/knife.jpg">Нож</li>
</ul>
</div>
<div class="done">
<h2>Завершённые</h2>
<ul>
<li><img src="images/iron.jpg">Утюг</li>
<li><img src="images/bananas.jpg">Бананы</li>
</ul>
</div>
</div>
<footer>
<p>Помощь:</p>
<p>8 (800)-555-35-35</p>
<p>vk.com</p>
<p>tg.me</p>
<p>ozon-zon-zon@mail.joke</p>
</footer>
</body>
</html>

View File

@@ -1,228 +0,0 @@
body {
background-color: #edf7f6;
}
* {
font-family: 'Gill Sans', 'Gill Sans MT', Calibri, 'Trebuchet MS', sans-serif;
}
header {
margin-bottom: 50px;
}
img {
width: 180px;
height: 150px;
margin-right: 10px;
}
h2 {
text-align: center;
}
footer {
margin-top: 50px;
text-align: center;
font-size: 14px;
}
a {
text-decoration: none;
color: black;
font-weight: bold;
}
header h1 a{
text-decoration: none;
color: black;
font-weight: bold;
text-align: left;
}
header img {
width: 200px;
height: auto;
}
navbar {
display: flex;
gap: 15px;
position: fixed;
top: 10px;
right: 35px;
}
.links a {
text-decoration: none;
color: black;
font-weight: bold;
}
.goods {
display: flex;
justify-content: space-around;
flex-wrap: wrap;
margin-bottom: 70px;
}
.goods p {
text-align: center;
width: 270px;
height: 285px;
border: 2px solid black;
border-radius: 10px;
}
.goods img {
width: 250px;
height: 250px;
object-fit: cover;
margin: 0 auto; /* Центрирование */
}
.typeOfOrder {
display: flex;
justify-content: space-around;
}
.typeOfOrder h2{
border: 2px solid black;
border-radius: 5px;
width: 180px;
margin-left: auto;
}
.inProcess h2{
background-color: yellow;
}
.done h2{
background-color: green;
}
.basket {
margin-left: 50px;
}
.basket h1 {
margin-left: 50px;
}
li {
position: relative;
border: 2px solid black;
border-radius: 10px;
width: 400px;
height: auto;
display: flex;
font-size: large;
margin-bottom: 15px;
padding: 10px;
}
.basket p {
position: absolute;
bottom: 5px;
right: 10px;
margin: 0;
font-weight: bold;
}
.orders h1 {
margin-left: 50px;
}
.favourites p {
position: absolute;
bottom: 5px;
right: 10px;
margin: 0;
font-weight: bold;
}
/*.buy {
position: fixed;
bottom: 20px;
right: 20px;
background-color: greenyellow;
text-align: center;
border: 2px solid black;
border-radius: 10px;
width: 200px; /* Фиксированный размер кнопки *
height: 70px;
display: flex;
justify-content: center;
}
.buy a {
font-size: xx-large;
text-decoration: none;
color: black;
font-weight: bold;
width: 100%; /* Растягиваем ссылку на всю кнопку *
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}*/
.buy {
position: fixed;
bottom: 20px;
right: 20px;
background-color: greenyellow;
text-align: center;
border: 2px solid black;
border-radius: 10px;
width: 200px;
height: 60px;
display: flex;
transition: background-color 0.3s ease-in-out;
}
.buy a {
text-decoration: none;
color: black;
font-size: xx-large;
font-weight: bold;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.buy:hover {
background-color: lightgreen;
}
.account {
text-align: center;
margin: 50px auto;
width: 300px;
padding: 20px;
border: 2px solid black;
border-radius: 15px;
background-color: #f9f9f9;
box-shadow: 3px 3px 10px rgba(0, 0, 0, 0.1);
}
.account img {
width: 150px;
height: 150px;
object-fit: cover;
border-radius: 20px; /* Закругленные углы */
border: 3px solid black;
}
.account h3 {
font-weight: bold;
margin-top: 10px;
font-size: x-large;
}
/*
.dialogue {
text-align: left;
margin-top: 10px;
font-size: 20px;
}*/

14
db.json Normal file
View File

@@ -0,0 +1,14 @@
{
"products": [
{
"id": "1a54",
"name": "новый товар",
"price": 102
},
{
"id": "f4f8",
"name": " товарчик",
"price": 111
}
]
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 280 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 49 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 128 KiB

View File

@@ -2,35 +2,14 @@
<html lang="ru">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
<title>Интернет-магазин</title>
<link rel="stylesheet" href="css/style.css"/>
</head>
<body>
<header>
<img src="images/logo.jpg" alt="'Название магазина'">
<a href="newSite.html"><h1><b>"Название магазина"</b></h1></a>
<navbar>
<p><a href="Basket.html">Корзина</a></p>
<p><a href="Order.html">Заказы</a></p>
<p><a href="Favorites.html">Избранное</a></p>
<p><a href="Account.html">Личный кабинет</a></p>
</navbar>
</header>
<h2>Рекомендуемые товары:</h2>
<div class="goods">
<p><img src="images/iron.jpg">Утюг</p>
<p><img src="images/child.jpg">Ребёнок</p>
<p><img src="images/screwdriver.jpg">Отвертка</p>
<p><img src="images/skateboard.jpg">Скейтборд</p>
<p><img src="images/bananas.jpg">Бананы</p>
</div>
<footer>
<p>Помощь:</p>
<p>8 (800)-555-35-35</p>
<p>vk.com</p>
<p>tg.me</p>
<p>ozon-zon-zon@mail.joke</p>
<p><img src="images/бананы.jpg"></p>
</footer>
<main class="container">
<h2 class="text-center my-3">Рекомендуемые товары:</h2>
<div id="root"></div>
</main>
<script type="module" src="/src/index.jsx"></script>
</body>
</html>

6281
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

36
package.json Normal file
View File

@@ -0,0 +1,36 @@
{
"name": "ip",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"dev": "npm-run-all --parallel start json-server",
"start": "vite",
"build": "vite build",
"server": "http-server -p 3000 ./dist/",
"prod": "npm-run-all build server",
"lint": "eslint …",
"json-server": "json-server --watch db.json --port 5000"
},
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"bootstrap": "5.3.3",
"bootstrap-icons": "1.11.3",
"react": "^19.1.0",
"react-dom": "^19.1.0"
},
"devDependencies": {
"@vitejs/plugin-react": "^4.4.1",
"eslint": "8.56.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-config-prettier": "10.0.2",
"eslint-plugin-html": "8.1.2",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-prettier": "5.2.3",
"http-server": "14.1.1",
"json-server": "^1.0.0-beta.3",
"npm-run-all": "4.1.5",
"vite": "6.2.0"
}
}

39
src/App.jsx Normal file
View File

@@ -0,0 +1,39 @@
import React, { useState } from 'react';
import useProducts from './hooks/useProducts';
import ProductList from './components/ProductList';
import ProductForm from './components/ProductForm';
export default function App() {
const { products, add, update, remove } = useProducts();
const [editing, setEditing] = useState(null);
const [showForm, setShowForm] = useState(false);
const [sortOrder, setSortOrder] = useState('asc');
const handleAdd = () => { setEditing(null); setShowForm(true); };
const handleEdit = prod => { setEditing(prod); setShowForm(true); };
const handleDelete = id => remove(id);
const handleSave = prod => {
editing ? update({ ...prod, id: editing.id }) : add(prod);
setShowForm(false);
};
const handleCancel = () => setShowForm(false);
const sortedProducts = [...products].sort((a, b) =>
sortOrder === 'asc'
? a.price - b.price
: b.price - a.price
);
const toggleSortOrder = () => setSortOrder(prev => prev === 'asc' ? 'desc' : 'asc');
return (
<div className="container my-4">
{/* <h1 className="mb-4">Каталог товаров</h1> */}
<button className="btn btn-success mb-3" onClick={handleAdd}>Добавить товар</button>
<button className="btn btn-outline-primary mb-3 ms-3" onClick={toggleSortOrder}> Сортировать по цене {sortOrder === 'asc' ? '▲' : '▼'}</button>
{showForm && <ProductForm initial={editing} onSave={handleSave} onCancel={handleCancel} mode={editing ? 'edit' : 'add'} />}
<ProductList products={sortedProducts} onEdit={handleEdit} onDelete={handleDelete} />
</div>
);
}

View File

@@ -0,0 +1,16 @@
import React from 'react';
export default function ProductCard({ product, onEdit, onDelete }) {
return (
<div className="col">
<div className="card h-100">
<div className="card-body">
<h5 className="card-title">{product.name}</h5>
<p className="card-text">Цена: {product.price} </p>
<button className="btn btn-sm btn-outline-primary me-2" onClick={() => onEdit(product)}>Изменить</button>
<button className="btn btn-sm btn-outline-danger" onClick={() => onDelete(product.id)}>Удалить</button>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,35 @@
import React, { useState, useEffect } from 'react';
export default function ProductForm({ initial, onSave, onCancel, mode }) {
const [form, setForm] = useState({ name: '', price: '' });
useEffect(() => {
if (initial) setForm({ name: initial.name, price: initial.price });
}, [initial]);
const handleChange = e => setForm({ ...form, [e.target.name]: e.target.value });
const handleSubmit = e => {
e.preventDefault();
onSave({ ...initial, name: form.name, price: Number(form.price) });
setForm({ name: '', price: '' });
};
return (
<form onSubmit={handleSubmit} className="card p-3 mb-4">
<h4 className="mb-3">
{mode === 'edit' ? 'Редактирование товара' : 'Добавление товара'}
</h4>
<div className="mb-2">
<label className="form-label">Название</label>
<input name="name" value={form.name} onChange={handleChange} required className="form-control" />
</div>
<div className="mb-2">
<label className="form-label">Цена</label>
<input name="price" value={form.price} onChange={handleChange} type="number" required className="form-control" />
</div>
<button type="submit" className="btn btn-primary mb-2" style={{ width: 'auto' }}>Сохранить</button>
<button type="button" className="btn btn-secondary" style={{ width: 'auto' }} onClick={onCancel}>Отмена</button>
</form>
);
}

View File

@@ -0,0 +1,12 @@
import React from 'react';
import ProductCard from './ProductCard';
export default function ProductList({ products, onEdit, onDelete }) {
return (
<div className="row row-cols-1 row-cols-md-3 g-4">
{products.map(prod => (
<ProductCard key={prod.id} product={prod} onEdit={onEdit} onDelete={onDelete} />
))}
</div>
);
}

33
src/hooks/useProducts.jsx Normal file
View File

@@ -0,0 +1,33 @@
import { useState, useEffect } from 'react';
export default function useProducts() {
const [products, setProducts] = useState([]);
useEffect(() => {
fetch('http://localhost:5000/products')
.then(res => res.json())
.then(setProducts);
}, []);
const add = async prod => {
const res = await fetch('http://localhost:5000/products', {
method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(prod)
});
const newProd = await res.json();
setProducts([...products, newProd]);
};
const update = async prod => {
await fetch(`http://localhost:5000/products/${prod.id}`, {
method: 'PUT', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(prod)
});
setProducts(products.map(p => p.id === prod.id ? prod : p));
};
const remove = async id => {
await fetch(`http://localhost:5000/products/${id}`, { method: 'DELETE' });
setProducts(products.filter(p => p.id !== id));
};
return { products, add, update, remove };
}

7
src/index.jsx Normal file
View File

@@ -0,0 +1,7 @@
import React from 'react'
import { createRoot } from 'react-dom/client'
import App from './App'
import 'bootstrap/dist/css/bootstrap.min.css' // опционально
const container = document.getElementById('root')
createRoot(container).render(<App />)

28
vite.config.js Normal file
View File

@@ -0,0 +1,28 @@
import { resolve } from 'path';
import { defineConfig } from "vite";
import react from '@vitejs/plugin-react';
export default defineConfig({
root: '.',
plugins: [react()],
build: {
outDir: 'dist',
rollupOptions: {
input: {
main: resolve(__dirname, "newSite.html"),
basketPage: resolve(__dirname, "Basket.html"),
favouritesPage: resolve(__dirname, "Favorites.html"),
orderPage: resolve(__dirname, "Order.html"),
accountPage: resolve(__dirname, "Account.html"),
},
},
},
server: {
open: '/newSite.html',
},
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
},
},
});

BIN
Отчет2.docx Normal file

Binary file not shown.

BIN
Отчет3.docx Normal file

Binary file not shown.

BIN
Отчет4.docx Normal file

Binary file not shown.

BIN
Отчет5.docx Normal file

Binary file not shown.