This commit is contained in:
Nastya_Kozlova 2023-12-22 14:06:40 +04:00
parent 984b511b62
commit 2f2b44b180
46 changed files with 7813 additions and 142 deletions

View File

@ -0,0 +1,20 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": "airbnb-base",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"rules": {
"quotes": "off",
"indent": "off",
"no-console": "off",
"no-use-before-define": "off",
"no-alert": "off",
"no-restricted-globals": "off",
"quote-props": "off"
}
}

View File

@ -0,0 +1,140 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="node_modules/@fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet" />
<link rel="stylesheet" href="styles/style.css">
<title>Document</title>
</head>
<body>
<header class="fixed-top">
<nav class="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.html"><img src="images/icons8-ос-mac (4).svg" alt=""></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Переключатель навигации">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<div class="admin-header"><a class="nav-link" href="admin.html">Администратор</a></div>
<a class="nav-link" href="about us.html">О нас</a>
<a class="nav-link" href="catalog.html">Каталог</a>
<a class="nav-link" href="support.html">Поддержка</a>
<a class="nav-link" href="singin.html">Войти</a>
</div>
</div>
</div>
</nav>
</header>
<main class="mainAdmin">
<div class="container-fluid">
<div class="btn-group" role="group">
<button id="items-add" class="btn btn-primary">Добавить Товар</button>
</div>
<div>
<table id="items-table" class="table table-striped">
<thead>
<th scope="col"></th>
<th scope="col" class="w-25">Категория</th>
<th scope="col" class="w-25">Oпция</th>
<th scope="col" class="w-25">Товар</th>
<th scope="col" class="w-25">Цвет</th>
<th scope="col" class="w-25">Цена</th>
<th scope="col" ></th>
<th scope="col" ></th>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</main>
<div id="items-update" class="modal fade" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog">
<form id="items-form" class="needs-validation" novalidate>
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="items-update-title"></h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center">
<img id="image-preview" src="https://via.placeholder.com/200" class="rounded rounded-0"
alt="placeholder">
</div>
<input id="items-line-id" type="number" hidden>
<div class="mb-2">
<label for="item" class="form-label">Категория</label>
<select id="item" class="form-select" name="selected" required>
</select>
</div>
<!-- -->
<div id="optionHidden" class="mb-2" hidden>
<label for="productOption" class="form-label">Опция</label>
<select id="productOption" class="form-select" name="selected-option" required>
</select>
</div>
<!-- -->
<div class="mb-2">
<label class="form-label" for="product">Товар</label>
<input id="product" name="product" class="form-control" type="text"
required>
</div>
<div class="mb-2">
<label for="color" class="form-label">Цвет</label>
<select id="color" class="form-select" name="selected-color" required>
</select>
</div>
<div class="mb-2">
<label class="form-label" for="price">Цена</label>
<input id="price" name="price" class="form-control" type="number" value="0.00" min="1000.00"
step="0.50" required>
</div>
<div class="mb-2">
<label class="form-label" for="image">Изображение</label>
<input id="image" type="file" name="image" class="form-control" accept="image/*">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary">Сохранить</button>
</div>
</div>
</form>
</div>
</div>
<footer>
<nav class="navbar bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<div class="navbar-text">
Kozlovanastya260104@gmail.com
</div>
</div>
</nav>
</footer>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script type="module" src="js/option.js"></script>
<script type="module">
import validation from "./js/validation";
import { linesForm } from "./js/lines";
document.addEventListener('DOMContentLoaded', () => {
validation();
linesForm();
});
</script>
</body>
</html>

View File

@ -20,7 +20,7 @@
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<div class="admin-header"><a class="nav-link" href="admin.html">Администратор</a></div>
<div class="admin-header"><a class="nav-link" href="adminNew.html">Администратор</a></div>
<a class="nav-link" href="about us.html">О нас</a>
<a class="nav-link active" href="catalog.html">Каталог</a>
<a class="nav-link" href="support.html">Поддержка</a>
@ -37,135 +37,6 @@
<h2>Наши продукты</h2>
</div>
<div class="row gx-5">
<div class="col-md-6 col-lg-4 col-xxl-3">
<div class="card">
<img src="images/iphone_14_pro.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">iPhone 13 Pro</h5>
<p class="card-text">125 999 ₽</p>
<!-- <div class="button-center">
<a href="#" class="btn btn-primary">купить</a>
</div> -->
<div class="buttom-admin">
<button class="button-edit" alt="ред"></button>
<button class="button-delete" alt="ред"></button>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 col-xxl-3">
<div class="card">
<img src="images/iphone_14_pro.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">iPhone 13 Pro</h5>
<p class="card-text">125 999 ₽</p>
<!-- <div class="button-center">
<a href="#" class="btn btn-primary">купить</a>
</div> -->
<div class="buttom-admin">
<button class="button-edit" alt="ред"></button>
<button class="button-delete" alt="ред"></button>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 col-xxl-3">
<div class="card">
<img src="images/iphone_14_pro.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">iPhone 13 Pro</h5>
<p class="card-text">125 999 ₽</p>
<!-- <div class="button-center">
<a href="#" class="btn btn-primary">купить</a>
</div> -->
<div class="buttom-admin">
<button class="button-edit" alt="ред"></button>
<button class="button-delete" alt="ред"></button>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 col-xxl-3">
<div class="card">
<img src="images/iphone_14_pro.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">iPhone 13 Pro</h5>
<p class="card-text">125 999 ₽</p>
<!-- <div class="button-center">
<a href="#" class="btn btn-primary">купить</a>
</div> -->
<div class="buttom-admin">
<button class="button-edit" alt="ред"></button>
<button class="button-delete" alt="ред"></button>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 col-xxl-3">
<div class="card">
<img src="images/iphone_14_pro.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">iPhone 13 Pro</h5>
<p class="card-text">125 999 ₽</p>
<!-- <div class="button-center">
<a href="#" class="btn btn-primary">купить</a>
</div> -->
<div class="buttom-admin">
<button class="button-edit" alt="ред"></button>
<button class="button-delete" alt="ред"></button>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 col-xxl-3">
<div class="card">
<img src="images/iphone_14_pro.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">iPhone 13 Pro</h5>
<p class="card-text">125 999 ₽</p>
<!-- <div class="button-center">
<a href="#" class="btn btn-primary">купить</a>
</div> -->
<div class="buttom-admin">
<button class="button-edit" alt="ред"></button>
<button class="button-delete" alt="ред"></button>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 col-xxl-3">
<div class="card">
<img src="images/iphone_14_pro.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">iPhone 13 Pro</h5>
<p class="card-text">125 999 ₽</p>
<!-- <div class="button-center">
<a href="#" class="btn btn-primary">купить</a>
</div> -->
<div class="buttom-admin">
<button class="button-edit" alt="ред"></button>
<button class="button-delete" alt="ред"></button>
</div>
</div>
</div>
</div>
<div class="col-md-6 col-lg-4 col-xxl-3">
<div class="card">
<img src="images/iphone_14_pro.png" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">iPhone 13 Pro</h5>
<p class="card-text">125 999 ₽</p>
<!-- <div class="button-center">
<a href="#" class="btn btn-primary">купить</a>
</div> -->
<div class="buttom-admin">
<button class="button-edit" alt="ред"></button>
<button class="button-delete" alt="ред"></button>
</div>
</div>
</div>
</div>
</div>
@ -188,6 +59,7 @@
</footer>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script type="module" src="js/addCard.js"></script>
</body>
</html>

View File

@ -1,19 +1,121 @@
{
"posts": [
"items": [
{
"id": 1,
"title": "json-server",
"author": "typicode"
"name": "Телефон",
"optionsId": 1
},
{
"id": 2,
"name": "Ноутбук",
"optionsId": 2
},
{
"id": 3,
"name": "наушники",
"optionsId": 3
},
{
"id": 4,
"name": "часы",
"optionsId": 4
}
],
"comments": [
"options": [
{
"id": 1,
"body": "some comment",
"postId": 1
"name": "белый красный зеленый"
},
{
"id": 2,
"name": "64gb 128gb 512gb"
},
{
"id": 3,
"name": "92dB 107dB 110dB"
},
{
"id": 4,
"name": "41mm 49mm"
}
],
"profile": {
"name": "typicode"
"colors": [
{
"id": 1,
"name": "белый"
},
{
"id": 2,
"name": "черный"
},
{
"id": 3,
"name": "фиолетовый"
}
],
"lines": [
{
"itemsId": "2",
"option": "128gb",
"product": "Apple Watch Series",
"colorsId": "3",
"price": "10000.00",
"image": "",
"id": 36
},
{
"itemsId": "1",
"option": "белый",
"product": "вапвав",
"colorsId": "1",
"price": "50000.00",
"image": "",
"id": 38
},
{
"itemsId": "4",
"option": "49mm",
"product": "часики",
"colorsId": "2",
"price": "400000.00",
"image": "",
"id": 39
},
{
"itemsId": "1",
"option": "белый",
"product": "1111",
"colorsId": "2",
"price": "100000000000.00",
"image": "",
"id": 40
},
{
"itemsId": "1",
"option": "белый",
"product": "всывсв",
"colorsId": "1",
"price": "100000000.00",
"image": "",
"id": 41
},
{
"itemsId": "1",
"option": "белый",
"product": "Apple iPhone 15 SIM 128 ГБ, черный",
"colorsId": "1",
"price": "10000.00",
"image": "",
"id": 42
},
{
"itemsId": "2",
"option": "512gb",
"product": "Apple iPhone 13 128 ГБ розовый",
"colorsId": "2",
"price": "10000000.00",
"image": "",
"id": 43
}
]
}

View File

@ -0,0 +1,45 @@
import {
getAllLines
} from "./lines-rest-api";
async function createCard(product, color, price, image) {
const cardContainer = document.querySelector('.row.gx-5');
if (image == "") {
image = "images/iphone_14_pro.png"
}
const cardItem =
`
<div class="col-md-6 col-lg-4 col-xxl-3">
<div class="card">
<img src="${image}" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">${product}, ${color}</h5>
<p class="card-text">${price} </p>
</div>
</div>
</div>
`;
cardContainer.insertAdjacentHTML('beforeend', cardItem);
}
async function createCards() {
const data = await getAllLines();
console.log(data)
data.forEach(item => {
createCard(item.product, item.colors.name, item.price, item.image);
});
}
document.addEventListener('DOMContentLoaded', () => {
createCards();
});

View File

@ -0,0 +1,73 @@
// Модуль для работы с модальным окном
// импорт компонента Modal из bootstrap
import { Modal } from "bootstrap";
import { cntrls, imagePlaceholder } from "./lines-ui";
// поиск модального окна на странице
const modal = document.getElementById("items-update");
// если он найден, то создается экземпляр компонента Modal
// для программного управления модальным окном
const myModal = modal ? new Modal(modal, {}) : null;
// поиск тега с заголовком модального кона для его смены
const modalTitle = document.getElementById("items-update-title");
const element = document.getElementById('optionHidden');
// обнуление значений модального окна, т. к.
// используется одно окно для всех операций
function resetValues() {
element.setAttribute('hidden',true);
cntrls.lineId.value = "";
cntrls.itemsType.value = "";
//
cntrls.option.value = "";
//
cntrls.product.value = "";
cntrls.colorsType.value = "";
cntrls.price.value = parseFloat(0).toFixed(2);
cntrls.image.value = "";
cntrls.imagePreview.src = imagePlaceholder;
}
// функция для показа модального окна
// перед показом происходит заполнение формы для редактирования
// если объект item не пуст
export function showUpdateModal(item) {
modalTitle.innerHTML = item === null ? "Добавить" : "Изменить";
console.info(item);
if (item) {
cntrls.lineId.value = item.id;
cntrls.itemsType.value = item.itemsId;
//
cntrls.option.value = item.option;
//
cntrls.product.value = item.product;
cntrls.colorsType.value = item.colorsId;
cntrls.price.value = item.price;
// заполнение превью
// Если пользователь выбрал изображение, то оно загружается
// в тэг image с id image - preview
// иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
cntrls.imagePreview.src = item.image ? item.image : imagePlaceholder;
} else {
resetValues();
}
myModal.show();
}
// функция для скрытия модального окна
export function hideUpdateModal() {
resetValues();
// удаление класса was-validated для скрытия результатов валидации
cntrls.form.classList.remove("was-validated");
myModal.hide();
}

View File

@ -0,0 +1,124 @@
// модуль для работы с REST API сервера
// адрес сервера
const serverUrl = "http://localhost:8081";
// функция возвращает объект нужной структуры для отправки на сервер
function createLineObject(item, option, product, color, price, image) {
return {
itemsId: item,
option,
product,
colorsId: color,
price: parseFloat(price).toFixed(2),
image,
};
}
// обращение к серверу для получения всех типов товара (get)
export async function getAllItemTypes() {
const response = await fetch(`${serverUrl}/items`);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
export async function getAllColorTypes() {
const response = await fetch(`${serverUrl}/colors`);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
export async function getAllOptionTypes() {
const response = await fetch(`${serverUrl}/options`);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
// export async function getAllOptionsTypes(type) {
// const response = await fetch(`${serverUrl}/items/${type}?_expand=options`);
// if (!response.ok) {
// throw response.statusText;
// }
// return response.json();
// }
export async function getAllLines() {
const response = await fetch(`${serverUrl}/lines?_expand=items&_expand=colors`);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
// обращение к серверу для получения записи по первичному ключу (id) (get)
// id передается в качестве части пути URL get-запроса
export async function getLine(id) {
const response = await fetch(`${serverUrl}/lines/${id}?_expand=items`);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
// обращение к серверу для создания записи (post)
// объект отправляется в теле запроса (body)
export async function createLine(item, option, product, color, price, image) {
const itemObject = createLineObject(item, option, product, color, price, image);
const options = {
method: "POST",
body: JSON.stringify(itemObject),
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
};
const response = await fetch(`${serverUrl}/lines`, options);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
// обращение к серверу для обновления записи по id (put)
// объект отправляется в теле запроса (body)
// id передается в качестве части пути URL get-запроса
export async function updateLine(id, item, option, product, color, price, image) {
const itemObject = createLineObject(item, option, product, color, price, image);
const options = {
method: "PUT",
body: JSON.stringify(itemObject),
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
};
const response = await fetch(`${serverUrl}/lines/${id}`, options);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
// обращение к серверу для удаления записи по id (delete)
// id передается в качестве части пути URL get-запроса
export async function deleteLine(id) {
const options = {
method: "DELETE",
};
const response = await fetch(`${serverUrl}/lines/${id}`, options);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}

View File

@ -0,0 +1,107 @@
// модуль для работы с элементами управления
// объект для удобного получения элементов
// при обращении к атрибуту объекта вызывается
// нужная функция для поиска элемента
export const cntrls = {
button: document.getElementById("items-add"),
table: document.querySelector("#items-table tbody"),
form: document.getElementById("items-form"),
lineId: document.getElementById("items-line-id"),
itemsType: document.getElementById("item"),
option: document.getElementById("productOption"),
product: document.getElementById("product"),
colorsType: document.getElementById("color"),
price: document.getElementById("price"),
image: document.getElementById("image"),
imagePreview: document.getElementById("image-preview"),
};
// Дефолтное превью
export const imagePlaceholder = "https://via.placeholder.com/200";
// функция создает тег option для select
// <option value="" selected>name</option>
export function createItemsOption(name, value = "", isSelected = false) {
const option = document.createElement("option");
option.value = value || "";
option.selected = isSelected;
option.text = name;
return option;
}
export function createItemsOption2(name, value = "", isSelected = false) {
const option = document.createElement("option");
option.value = value || "";
option.selected = isSelected;
option.text = name;
return option;
}
// функция создает ссылку (a) для таблицы
// содержимое тега a заполняется необходимой иконкой (icon)
// при нажатии вызывается callback
// ссылка "оборачивается" тегом td
// <td><a href="#" onclick="callback()"><i class="fa-solid icon"></i></a></td>
function createTableAnchor(icon, callback) {
const i = document.createElement("i");
i.classList.add("fa-solid", icon);
const a = document.createElement("a");
a.href = "#";
a.appendChild(i);
a.onclick = (event) => {
// чтобы в URL не добавлялась решетка
event.preventDefault();
event.stopPropagation();
callback();
};
const td = document.createElement("td");
td.appendChild(a);
return td;
}
// функция создает колонку таблицы с текстом value
// <td>value</td>
function createTableColumn(value) {
const td = document.createElement("td");
td.textContent = value;
return td;
}
// функция создает строку таблицы
// <tr>
// <th scope="row">index + 1</th>
// <td>item.items.name</td>
// <td>parseFloat(item.price).toFixed(2))</td>
// <td>item.count</td>
// <td>parseFloat(item.sum).toFixed(2))</td>
// <td><a href="#" onclick="editCallback()"><i class="fa-solid fa-pencil"></i></a></td>
// <td><a href="#" onclick="editPageCallback()"><i class="fa-solid fa-pen-to-square"></i></a></td>
// <td><a href="#" onclick="deleteCallback()"><i class="fa-solid fa-trash"></i></a></td>
// </tr>
export function createTableRow(item, index, editCallback, deleteCallback) {
const rowNumber = document.createElement("th");
rowNumber.scope = "row";
rowNumber.textContent = index + 1;
const row = document.createElement("tr");
row.id = `line-${item.id}`;
row.appendChild(rowNumber);
row.appendChild(createTableColumn(item.items.name));
//
row.appendChild(createTableColumn(item.option));
//
row.appendChild(createTableColumn(item.product));
row.appendChild(createTableColumn(item.colors.name));
row.appendChild(createTableColumn(parseFloat(item.price).toFixed(2)));
// редактировать в модальном окне
row.appendChild(createTableAnchor("fa-pencil", editCallback));
// удаление
row.appendChild(createTableAnchor("fa-trash", deleteCallback))
return row;
}

304
laba2/ip laba2/js/lines.js Normal file
View File

@ -0,0 +1,304 @@
// модуль с логикой
import { hideUpdateModal, showUpdateModal } from "./lines-modal";
import {
createLine, deleteLine, getAllItemTypes, getAllColorTypes, getAllOptionTypes, getAllLines, updateLine,
} from "./lines-rest-api";
import {
cntrls, createItemsOption, createTableRow, imagePlaceholder,
} from "./lines-ui";
async function drawItemsSelect() {
// вызов метода REST API для получения списка типов товаров
const data = await getAllItemTypes();
// очистка содержимого select
// удаляется все, что находится между тегами <select></select>
// но не атрибуты
cntrls.itemsType.innerHTML = "";
// пустое значение
cntrls.itemsType.appendChild(createItemsOption("Выберите значение", "", true));
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item) => {} аналогично function(item) {}
data.forEach((item) => {
cntrls.itemsType.appendChild(createItemsOption(item.name, item.id));
});
}
async function drawColorsSelect() {
// вызов метода REST API для получения списка типов товаров
const data = await getAllColorTypes();
// очистка содержимого select
// удаляется все, что находится между тегами <select></select>
// но не атрибуты
cntrls.colorsType.innerHTML = "";
// пустое значение
cntrls.colorsType.appendChild(createItemsOption("Выберите значение", "", true));
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item) => {} аналогично function(item) {}
data.forEach((item) => {
cntrls.colorsType.appendChild(createItemsOption(item.name, item.id));
});
}
//
async function drawOptionsSelect() {
// вызов метода REST API для получения списка типов товаров
const data = await getAllOptionTypes();
// очистка содержимого select
// удаляется все, что находится между тегами <select></select>
// но не атрибуты
cntrls.option.innerHTML = "";
// пустое значение
cntrls.option.appendChild(createItemsOption("Выберите значение", "", true));
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item) => {} аналогично function(item) {}
// data.forEach((item) => {
// cntrls.option.appendChild(createItemsOption(item.name, 1));
// });
console.log(data)
const array1 = data[0].name.split(" ");
const array2 = data[1].name.split(" ");
const array3 = data[2].name.split(" ");
const array4 = data[3].name.split(" ");
cntrls.itemsType.addEventListener('change', function() {
const selectedValue = this.value; // Получаем выбранное значение
cntrls.option.innerHTML = "";
cntrls.option.appendChild(createItemsOption("Выберите значение", "", true));
// if (selectedValue == 1) {
// array1.forEach((item) => {
// cntrls.option.appendChild(createItemsOption(item, 1));
// });
// }
if (selectedValue == 1) {
for (let i = 0; i < array1.length; i++) {
cntrls.option.appendChild(createItemsOption(array1[i], i+1));
}
}
if (selectedValue == 2) {
for (let i = 0; i < array2.length; i++) {
cntrls.option.appendChild(createItemsOption(array2[i], i+1));
}
}
if (selectedValue == 3) {
for (let i = 0; i < array3.length; i++) {
cntrls.option.appendChild(createItemsOption(array3[i], i+1));
}
}
if (selectedValue == 4) {
for (let i = 0; i < array4.length; i++) {
cntrls.option.appendChild(createItemsOption(array4[i], i+1));
}
}
// const selectElement = document.querySelector('option[value="3"]');
// console.log(selectElement.innerHTML)
});
}
async function drawLinesTable() {
console.info("Try to load data");
if (!cntrls.table) {
return;
}
// вызов метода REST API для получения всех записей
const data = await getAllLines();
// очистка содержимого table
// удаляется все, что находится между тегами <table></table>
// но не атрибуты
cntrls.table.innerHTML = "";
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item, index) => {} аналогично function(item, index) {}
data.forEach((item, index) => {
cntrls.table.appendChild(
createTableRow(
item,
index,
// функции передаются в качестве параметра
// это очень удобно, так как аргументы функций доступны только
// в данном месте кода и не передаются в сервисные модули
() => showUpdateModal(item),
() => removeLine(item.id),
),
);
});
}
async function addLine(item, option, product, color, price, image) {
console.info("Try to add item");
// вызов метода REST API для добавления записи
const data = await createLine(item, option, product, color, price, image);
console.info("Added");
console.info(data);
// загрузка и заполнение table
drawLinesTable();
}
async function editLine(id, item, option, product, color, price, image) {
console.info("Try to update item");
// вызов метода REST API для обновления записи
const data = await updateLine(id, item, option, product, color, price, image);
console.info("Updated");
console.info(data);
// загрузка и заполнение table
drawLinesTable();
}
async function removeLine(id) {
if (!confirm("Do you really want to remove this item?")) {
console.info("Canceled");
return;
}
console.info("Try to remove item");
// вызов метода REST API для удаления записи
const data = await deleteLine(id);
console.info(data);
// загрузка и заполнение table
drawLinesTable();
}
// функция для получения содержимого файла в виде base64 строки
// https://ru.wikipedia.org/wiki/Base64
async function readFile(file) {
const reader = new FileReader();
// создание Promise-объекта для использования функции
// с помощью await (асинхронно) без коллбэков (callback)
// https://learn.javascript.ru/promise
return new Promise((resolve, reject) => {
// 2. "Возвращаем" содержимое когда файл прочитан
// через вызов resolve
// Если не использовать Promise, то всю работу по взаимодействию
// с REST API пришлось бы делать в обработчике (callback) функции
// onloadend
reader.onloadend = () => {
const fileContent = reader.result;
// Здесь могла бы быть работа с REST API
// Чтение заканчивает выполняться здесь
resolve(fileContent);
};
// 3. Возвращаем ошибку
reader.onerror = () => {
// Или здесь в случае ошибки
reject(new Error("oops, something went wrong with the file reader."));
};
// Шаг 1. Сначала читаем файл
// Чтение начинает выполняться здесь
reader.readAsDataURL(file);
});
}
// функция для обновления блока с превью выбранного изображения
async function updateImagePreview() {
// получение выбранного файла
// возможен выбор нескольких файлов, поэтому необходимо получить только первый
const file = cntrls.image.files[0];
// чтение содержимого файла в виде base64 строки
const fileContent = await readFile(file);
console.info("base64 ", fileContent);
// обновление атрибута src для тега img с id image-preview
cntrls.imagePreview.src = fileContent;
}
// Функция для обработки создания и редактирования элементов таблицы через модальное окно
// Если хотите делать через страницу, то удалите эту функцию
export function linesForm() {
console.info("linesForm");
// загрузка и заполнение select со списком товаров
drawItemsSelect()
drawColorsSelect()
drawOptionsSelect()
// загрузка и заполнение table
drawLinesTable()
// Вызов функции обновления превью изображения при возникновении
// события oncahnge в тэге input с id image
cntrls.image.addEventListener("change", () => updateImagePreview());
// обработчик события нажатия на кнопку для показа модального окна
cntrls.button.addEventListener("click", () => showUpdateModal(null));
// обработчик события отправки формы
// возникает при нажатии на кнопку (button) с типом submit
// кнопка должна находится внутри тега form
cntrls.form.addEventListener("submit", async (event) => {
console.info("Form onSubmit");
// отключение стандартного поведения формы при отправке
// при отправке страница обновляется и JS перестает работать
event.preventDefault();
event.stopPropagation();
// если форма не прошла валидацию, то ничего делать не нужно
if (!cntrls.form.checkValidity()) {
return;
}
let imageBase64 = "";
// Получение выбранного пользователем изображения в виде base64 строки
// Если пользователь ничего не выбрал, то не нужно сохранять в БД
// дефолтное изображение
if (cntrls.imagePreview.src !== imagePlaceholder) {
// Загрузка содержимого атрибута src тэга img с id image-preview
// Здесь выполняется HTTP запрос с типом GET
const result = await fetch(cntrls.imagePreview.src);
// Получение из HTTP-ответа бинарного содержимого
const blob = await result.blob();
// Получение base64 строки для файла
// Здесь выполняется Promise из функции readFile
// Promise позволяет писать линейный код для работы с асинхронными методами
// без использования обработчиков (callback) с помощью await
imageBase64 = await readFile(blob);
}
// получение id строки для редактирования
// это значение содержится в скрытом input
const currentId = cntrls.lineId.value;
// если значение id не задано,
// то необходимо выполнить добавление записи
// иначе обновление записи
const selectedValue = parseInt(cntrls.option.value);
const data3 = await getAllOptionTypes();
const idInt = parseInt(cntrls.itemsType.value)-1
const array12 = data3[idInt].name.split(" ");
const index = array12[selectedValue-1]
console.log(index)
if (!currentId) {
await addLine(
cntrls.itemsType.value,
index,
cntrls.product.value,
cntrls.colorsType.value,
cntrls.price.value,
imageBase64,
);
} else {
await editLine(
currentId,
cntrls.itemsType.value,
index,
cntrls.product.value,
cntrls.colorsType.value,
cntrls.price.value,
imageBase64,
);
}
// после выполнения добавления/обновления модальное окно скрывается
hideUpdateModal();
});
}

View File

@ -0,0 +1,20 @@
import { hideUpdateModal, showUpdateModal } from "./lines-modal";
import {
createLine, deleteLine, getAllItemTypes, getAllColorTypes, getAllLines, updateLine,
} from "./lines-rest-api";
import {
cntrls, createItemsOption, createTableRow, imagePlaceholder,
} from "./lines-ui";
console.log("hi")
document.getElementById('item').addEventListener('change', function() {
const element = document.getElementById('optionHidden');
if (cntrls.itemsType.value == false) {
element.setAttribute('hidden',true);
} else {
element.removeAttribute('hidden');
}
});

View File

@ -0,0 +1,25 @@
// модуль используется для валидации форма на странице
function validation() {
// поиск всех форма с классом .needs-validation
const forms = document.querySelectorAll("form.needs-validation");
for (let i = 0; i < forms.length; i += 1) {
const form = forms[i];
// для каждой формы добавляется обработчик события отправки
form.addEventListener("submit", (event) => {
// если форма не прошла валидацию
// то выключить стандартное действие
if (!form.checkValidity()) {
event.preventDefault();
// предотвращает распространение preventDefault
// на другие объекты
event.stopPropagation();
}
// добавляет к форме класс was-validated
form.classList.add("was-validated");
});
}
}
export default validation;

View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Node",
"target": "ES2020",
"jsx": "preserve",
"strictNullChecks": true,
"strictFunctionTypes": true
},
"exclude": [
"node_modules",
"**/node_modules/*"
]
}

View File

@ -81,14 +81,17 @@
.card {
align-items: center;
height: 256px;
}
.card>img {
width: 75px;
width: auto;
height: 130px;
margin-top: 10px;
}
.card-title {
font-size: 19px;
font-size: 16px;
font-weight: 700;
max-width: 160px;
text-align: center;
@ -97,7 +100,7 @@
.card-text {
max-width: 160px;
text-align: center;
font-size: 19px;
font-size: 18px;
}
@ -328,6 +331,34 @@ form {
.down-text>p {
max-width: 630px;
font-size: 20px;
}
/* страница админа новая */
#image-preview {
width: 200px;
}
.mainAdmin {
display: flex;
height: 100vh;
margin-top: 100px;
}
.needs-validation {
margin-top: 0px;
}
.btn {
margin-bottom: 15px;
}
.button-delete-admin {
width: 22px;
height: 22px;
border: none;
background-color: #ffffff;
background-image: url('icons8-удалить-64.png');
background-size: cover;
}
@media (max-width: 1200px) {

View File

@ -0,0 +1,21 @@
import { resolve } from "path";
// eslint-disable-next-line import/no-extraneous-dependencies
import { defineConfig } from "vite";
export default defineConfig({
build: {
sourcemap: true,
emptyOutDir: true,
rollupOptions: {
input: {
main: resolve(__dirname, "index.html"),
page2: resolve(__dirname, "about us.html"),
page3: resolve(__dirname, "adminNew.html"),
page4: resolve(__dirname, "catalog.html"),
page5: resolve(__dirname, "singin.html"),
page6: resolve(__dirname, "singup.html"),
page7: resolve(__dirname, "support.html"),
},
},
},
});

Binary file not shown.

View File

@ -0,0 +1,20 @@
{
"env": {
"browser": true,
"es2021": true
},
"extends": "airbnb-base",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"rules": {
"quotes": "off",
"indent": "off",
"no-console": "off",
"no-use-before-define": "off",
"no-alert": "off",
"no-restricted-globals": "off",
"quote-props": "off"
}
}

42
laba3/ip laba3/.gitignore vendored Normal file
View File

@ -0,0 +1,42 @@
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Compiled output
/dist
/tmp
/out-tsc
/bazel-out
# Node
/node_modules
npm-debug.log
yarn-error.log
# IDEs and editors
.idea/
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# Visual Studio Code
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
.history/*
# Miscellaneous
/.angular/cache
.sass-cache/
/connect.lock
/coverage
/libpeerconnection.log
testem.log
/typings
# System files
.DS_Store
Thumbs.db

View File

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="styles/style.css">
<title>Document</title>
</head>
<body>
<header class="fixed-top">
<nav class="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.html"><img src="images/icons8-ос-mac (4).svg" alt=""></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false"
aria-label="Переключатель навигации">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link active" href="about us.html">О нас</a>
<a class="nav-link" href="catalog.html">Каталог</a>
<a class="nav-link" href="support.html">Поддержка</a>
<a class="nav-link" href="singin.html">Войти</a>
</div>
</div>
</div>
</nav>
</header>
<section class="about-us-section">
<div class="container-fluid">
<div class="group-text">
<div class="up-text">
<h1>О нас</h1>
</div>
<div class="down-text">
<h3>Магазины в офлайне и в онлайне</h3>
<p>Apple помогает оптимизировать работу крупнейших розничных компаний — от кассовых операций до управления на
высшем уровне. Устройства Apple и мощную платформу приложений просто использовать и внедрять. А для
мобильных сотрудников они предоставляют эффективные инструменты, которые помогают наладить рабочий процесс в
магазине и предложить каждому посетителю качественное обслуживание, чтобы выгодно отличаться от конкурентов.
Передовые функции безопасности, экосистема надёжных корпоративных партнёров, технологии машинного обучения и
дополненная реальность на устройствах — будущее розничной торговли строится на платформе Apple.</p>
</div>
</div>
</div>
</section>
<footer>
<nav class="navbar bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<div class="navbar-text">
Kozlovanastya260104@gmail.com
</div>
</div>
</nav>
</footer>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

74
laba3/ip laba3/admin.html Normal file
View File

@ -0,0 +1,74 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="styles/style.css">
<title>Document</title>
</head>
<body>
<header class="fixed-top">
<nav class="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.html"><img src="images/icons8-ос-mac (4).svg" alt=""></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Переключатель навигации">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<div class="admin-header"><a class="nav-link" href="admin.html">Администратор</a></div>
<a class="nav-link" href="about us.html">О нас</a>
<a class="nav-link" href="catalog.html">Каталог</a>
<a class="nav-link" href="support.html">Поддержка</a>
<a class="nav-link" href="singin.html">Войти</a>
</div>
</div>
</div>
</nav>
</header>
<section class="sing-section">
<div class="container-fluid">
<div class="border-sin">
<form class="form-admin">
<p class="title-admin">Добавление товара</p>
<div class="mb-3">
<label class="margin-admin">Название</label>
<input class="form-control">
</div>
<div class="mb-3">
<label class="margin-admin">Цена</label>
<input class="form-control">
</div>
<!-- <div class="add-picture">
<button type="submit" class="btn btn-primary">Добавить фото</button>
</div> -->
<div class="mb-3">
<label for="formFile" class="form-label">Фото</label>
<input class="form-control" type="file" id="formFile">
</div>
<div class="add-tovar">
<button type="submit" class="btn btn-primary">Добавить товар</button>
</div>
</form>
</div>
</div>
</section>
<footer>
<nav class="navbar bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<div class="navbar-text">
Kozlovanastya260104@gmail.com
</div>
</div>
</nav>
</footer>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,140 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="node_modules/@fortawesome/fontawesome-free/css/all.min.css" rel="stylesheet" />
<link rel="stylesheet" href="styles/style.css">
<title>Document</title>
</head>
<body>
<header class="fixed-top">
<nav class="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.html"><img src="images/icons8-ос-mac (4).svg" alt=""></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Переключатель навигации">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<div class="admin-header"><a class="nav-link" href="admin.html">Администратор</a></div>
<a class="nav-link" href="about us.html">О нас</a>
<a class="nav-link" href="catalog.html">Каталог</a>
<a class="nav-link" href="support.html">Поддержка</a>
<a class="nav-link" href="singin.html">Войти</a>
</div>
</div>
</div>
</nav>
</header>
<main class="mainAdmin">
<div class="container-fluid">
<div class="btn-group" role="group">
<button id="items-add" class="btn btn-primary">Добавить Товар</button>
</div>
<div>
<table id="items-table" class="table table-striped">
<thead>
<th scope="col"></th>
<th scope="col" class="w-25">Категория</th>
<th scope="col" class="w-25">Oпция</th>
<th scope="col" class="w-25">Товар</th>
<th scope="col" class="w-25">Цвет</th>
<th scope="col" class="w-25">Цена</th>
<th scope="col" ></th>
<th scope="col" ></th>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</main>
<div id="items-update" class="modal fade" tabindex="-1" data-bs-backdrop="static" data-bs-keyboard="false">
<div class="modal-dialog">
<form id="items-form" class="needs-validation" novalidate>
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="items-update-title"></h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<div class="text-center">
<img id="image-preview" src="https://via.placeholder.com/200" class="rounded rounded-0"
alt="placeholder">
</div>
<input id="items-line-id" type="number" hidden>
<div class="mb-2">
<label for="item" class="form-label">Категория</label>
<select id="item" class="form-select" name="selected" required>
</select>
</div>
<!-- -->
<div id="optionHidden" class="mb-2" hidden>
<label for="productOption" class="form-label">Опция</label>
<select id="productOption" class="form-select" name="selected-option" required>
</select>
</div>
<!-- -->
<div class="mb-2">
<label class="form-label" for="product">Товар</label>
<input id="product" name="product" class="form-control" type="text"
required>
</div>
<div class="mb-2">
<label for="color" class="form-label">Цвет</label>
<select id="color" class="form-select" name="selected-color" required>
</select>
</div>
<div class="mb-2">
<label class="form-label" for="price">Цена</label>
<input id="price" name="price" class="form-control" type="number" value="0.00" min="1000.00"
step="0.50" required>
</div>
<div class="mb-2">
<label class="form-label" for="image">Изображение</label>
<input id="image" type="file" name="image" class="form-control" accept="image/*">
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Закрыть</button>
<button type="submit" class="btn btn-primary">Сохранить</button>
</div>
</div>
</form>
</div>
</div>
<footer>
<nav class="navbar bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<div class="navbar-text">
Kozlovanastya260104@gmail.com
</div>
</div>
</nav>
</footer>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script type="module" src="js/option.js"></script>
<script type="module">
import validation from "./js/validation";
import { linesForm } from "./js/lines";
document.addEventListener('DOMContentLoaded', () => {
validation();
linesForm();
});
</script>
</body>
</html>

View File

@ -0,0 +1,65 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="styles/style.css">
<title>Document</title>
</head>
<body>
<header class="fixed-top">
<nav class="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.html"> <img src="images/icons8-ос-mac (4).svg" alt=""></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup"
aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Переключатель навигации">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<div class="admin-header"><a class="nav-link" href="adminNew.html">Администратор</a></div>
<a class="nav-link" href="about us.html">О нас</a>
<a class="nav-link active" href="catalog.html">Каталог</a>
<a class="nav-link" href="support.html">Поддержка</a>
<a class="nav-link" href="singin.html">Войти</a>
</div>
</div>
</div>
</nav>
</header>
<section class="catalog-section">
<div class="container-fluid">
<div class="title-one">
<h2>Наши продукты</h2>
</div>
<div class="row gx-5">
</div>
<div class="what-buy">
<h3>Где купить?</h3>
<p>Вы можете приобрести товар у реселлера или оператора
сотовой связи. Выбирайте любой удобный вариант.</p>
</div>
</div>
</section>
<footer>
<nav class="navbar bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<div class="navbar-text">
Kozlovanastya260104@gmail.com
</div>
</div>
</nav>
</footer>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
<script type="module" src="js/addCard.js"></script>
</body>
</html>

121
laba3/ip laba3/data.json Normal file
View File

@ -0,0 +1,121 @@
{
"items": [
{
"id": 1,
"name": "Телефон",
"optionsId": 1
},
{
"id": 2,
"name": "Ноутбук",
"optionsId": 2
},
{
"id": 3,
"name": "наушники",
"optionsId": 3
},
{
"id": 4,
"name": "часы",
"optionsId": 4
}
],
"options": [
{
"id": 1,
"name": "белый красный зеленый"
},
{
"id": 2,
"name": "64gb 128gb 512gb"
},
{
"id": 3,
"name": "92dB 107dB 110dB"
},
{
"id": 4,
"name": "41mm 49mm"
}
],
"colors": [
{
"id": 1,
"name": "белый"
},
{
"id": 2,
"name": "черный"
},
{
"id": 3,
"name": "фиолетовый"
}
],
"lines": [
{
"itemsId": "2",
"option": "128gb",
"product": "Apple Watch Series",
"colorsId": "3",
"price": "10000.00",
"image": "",
"id": 36
},
{
"itemsId": "1",
"option": "белый",
"product": "вапвав",
"colorsId": "1",
"price": "50000.00",
"image": "",
"id": 38
},
{
"itemsId": "4",
"option": "49mm",
"product": "часики",
"colorsId": "2",
"price": "400000.00",
"image": "",
"id": 39
},
{
"itemsId": "1",
"option": "белый",
"product": "1111",
"colorsId": "2",
"price": "100000000000.00",
"image": "",
"id": 40
},
{
"itemsId": "1",
"option": "белый",
"product": "всывсв",
"colorsId": "1",
"price": "100000000.00",
"image": "",
"id": 41
},
{
"itemsId": "1",
"option": "белый",
"product": "Apple iPhone 15 SIM 128 ГБ, черный",
"colorsId": "1",
"price": "10000.00",
"image": "",
"id": 42
},
{
"itemsId": "2",
"option": "512gb",
"product": "Apple iPhone 13 128 ГБ розовый",
"colorsId": "2",
"price": "10000000.00",
"image": "",
"id": 43
}
]
}

View File

@ -0,0 +1 @@
<svg fill="#a8a9ba" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 50" width="50px" height="50px"><path d="M 44.527344 34.75 C 43.449219 37.144531 42.929688 38.214844 41.542969 40.328125 C 39.601563 43.28125 36.863281 46.96875 33.480469 46.992188 C 30.46875 47.019531 29.691406 45.027344 25.601563 45.0625 C 21.515625 45.082031 20.664063 47.03125 17.648438 47 C 14.261719 46.96875 11.671875 43.648438 9.730469 40.699219 C 4.300781 32.429688 3.726563 22.734375 7.082031 17.578125 C 9.457031 13.921875 13.210938 11.773438 16.738281 11.773438 C 20.332031 11.773438 22.589844 13.746094 25.558594 13.746094 C 28.441406 13.746094 30.195313 11.769531 34.351563 11.769531 C 37.492188 11.769531 40.8125 13.480469 43.1875 16.433594 C 35.421875 20.691406 36.683594 31.78125 44.527344 34.75 Z M 31.195313 8.46875 C 32.707031 6.527344 33.855469 3.789063 33.4375 1 C 30.972656 1.167969 28.089844 2.742188 26.40625 4.78125 C 24.878906 6.640625 23.613281 9.398438 24.105469 12.066406 C 26.796875 12.152344 29.582031 10.546875 31.195313 8.46875 Z"/></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 329 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 979 KiB

49
laba3/ip laba3/index.html Normal file
View File

@ -0,0 +1,49 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="styles/style.css">
<title>Document</title>
</head>
<body>
<header class="fixed-top">
<nav class="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.html"><img src="images/icons8-ос-mac (4).svg" alt=""></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Переключатель навигации">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link" href="about us.html">О нас</a>
<a class="nav-link" href="catalog.html">Каталог</a>
<a class="nav-link" href="support.html">Поддержка</a>
<a class="nav-link" href="singin.html">Войти</a>
</div>
</div>
</div>
</nav>
</header>
<section class="index-section">
<div class="index-page">
<div class="title">
<p>APPLE</p>
</div>
</div>
</section>
<footer>
<nav class="navbar bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<div class="navbar-text">
Kozlovanastya260104@gmail.com
</div>
</div>
</nav>
</footer>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,45 @@
import {
getAllLines
} from "./lines-rest-api";
async function createCard(product, color, price, image) {
const cardContainer = document.querySelector('.row.gx-5');
if (image == "") {
image = "images/iphone_14_pro.png"
}
const cardItem =
`
<div class="col-md-6 col-lg-4 col-xxl-3">
<div class="card">
<img src="${image}" class="card-img-top" alt="...">
<div class="card-body">
<h5 class="card-title">${product}, ${color}</h5>
<p class="card-text">${price} </p>
</div>
</div>
</div>
`;
cardContainer.insertAdjacentHTML('beforeend', cardItem);
}
async function createCards() {
const data = await getAllLines();
console.log(data)
data.forEach(item => {
createCard(item.product, item.colors.name, item.price, item.image);
});
}
document.addEventListener('DOMContentLoaded', () => {
createCards();
});

View File

@ -0,0 +1,73 @@
// Модуль для работы с модальным окном
// импорт компонента Modal из bootstrap
import { Modal } from "bootstrap";
import { cntrls, imagePlaceholder } from "./lines-ui";
// поиск модального окна на странице
const modal = document.getElementById("items-update");
// если он найден, то создается экземпляр компонента Modal
// для программного управления модальным окном
const myModal = modal ? new Modal(modal, {}) : null;
// поиск тега с заголовком модального кона для его смены
const modalTitle = document.getElementById("items-update-title");
const element = document.getElementById('optionHidden');
// обнуление значений модального окна, т. к.
// используется одно окно для всех операций
function resetValues() {
element.setAttribute('hidden',true);
cntrls.lineId.value = "";
cntrls.itemsType.value = "";
//
cntrls.option.value = "";
//
cntrls.product.value = "";
cntrls.colorsType.value = "";
cntrls.price.value = parseFloat(0).toFixed(2);
cntrls.image.value = "";
cntrls.imagePreview.src = imagePlaceholder;
}
// функция для показа модального окна
// перед показом происходит заполнение формы для редактирования
// если объект item не пуст
export function showUpdateModal(item) {
modalTitle.innerHTML = item === null ? "Добавить" : "Изменить";
console.info(item);
if (item) {
cntrls.lineId.value = item.id;
cntrls.itemsType.value = item.itemsId;
//
cntrls.option.value = item.option;
//
cntrls.product.value = item.product;
cntrls.colorsType.value = item.colorsId;
cntrls.price.value = item.price;
// заполнение превью
// Если пользователь выбрал изображение, то оно загружается
// в тэг image с id image - preview
// иначе устанавливается заглушка, адрес которой указан в imagePlaceholder
cntrls.imagePreview.src = item.image ? item.image : imagePlaceholder;
} else {
resetValues();
}
myModal.show();
}
// функция для скрытия модального окна
export function hideUpdateModal() {
resetValues();
// удаление класса was-validated для скрытия результатов валидации
cntrls.form.classList.remove("was-validated");
myModal.hide();
}

View File

@ -0,0 +1,124 @@
// модуль для работы с REST API сервера
// адрес сервера
const serverUrl = "http://localhost:8081";
// функция возвращает объект нужной структуры для отправки на сервер
function createLineObject(item, option, product, color, price, image) {
return {
itemsId: item,
option,
product,
colorsId: color,
price: parseFloat(price).toFixed(2),
image,
};
}
// обращение к серверу для получения всех типов товара (get)
export async function getAllItemTypes() {
const response = await fetch(`${serverUrl}/items`);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
export async function getAllColorTypes() {
const response = await fetch(`${serverUrl}/colors`);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
export async function getAllOptionTypes() {
const response = await fetch(`${serverUrl}/options`);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
// export async function getAllOptionsTypes(type) {
// const response = await fetch(`${serverUrl}/items/${type}?_expand=options`);
// if (!response.ok) {
// throw response.statusText;
// }
// return response.json();
// }
export async function getAllLines() {
const response = await fetch(`${serverUrl}/lines?_expand=items&_expand=colors`);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
// обращение к серверу для получения записи по первичному ключу (id) (get)
// id передается в качестве части пути URL get-запроса
export async function getLine(id) {
const response = await fetch(`${serverUrl}/lines/${id}?_expand=items`);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
// обращение к серверу для создания записи (post)
// объект отправляется в теле запроса (body)
export async function createLine(item, option, product, color, price, image) {
const itemObject = createLineObject(item, option, product, color, price, image);
const options = {
method: "POST",
body: JSON.stringify(itemObject),
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
};
const response = await fetch(`${serverUrl}/lines`, options);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
// обращение к серверу для обновления записи по id (put)
// объект отправляется в теле запроса (body)
// id передается в качестве части пути URL get-запроса
export async function updateLine(id, item, option, product, color, price, image) {
const itemObject = createLineObject(item, option, product, color, price, image);
const options = {
method: "PUT",
body: JSON.stringify(itemObject),
headers: {
"Accept": "application/json",
"Content-Type": "application/json",
},
};
const response = await fetch(`${serverUrl}/lines/${id}`, options);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}
// обращение к серверу для удаления записи по id (delete)
// id передается в качестве части пути URL get-запроса
export async function deleteLine(id) {
const options = {
method: "DELETE",
};
const response = await fetch(`${serverUrl}/lines/${id}`, options);
if (!response.ok) {
throw response.statusText;
}
return response.json();
}

View File

@ -0,0 +1,107 @@
// модуль для работы с элементами управления
// объект для удобного получения элементов
// при обращении к атрибуту объекта вызывается
// нужная функция для поиска элемента
export const cntrls = {
button: document.getElementById("items-add"),
table: document.querySelector("#items-table tbody"),
form: document.getElementById("items-form"),
lineId: document.getElementById("items-line-id"),
itemsType: document.getElementById("item"),
option: document.getElementById("productOption"),
product: document.getElementById("product"),
colorsType: document.getElementById("color"),
price: document.getElementById("price"),
image: document.getElementById("image"),
imagePreview: document.getElementById("image-preview"),
};
// Дефолтное превью
export const imagePlaceholder = "https://via.placeholder.com/200";
// функция создает тег option для select
// <option value="" selected>name</option>
export function createItemsOption(name, value = "", isSelected = false) {
const option = document.createElement("option");
option.value = value || "";
option.selected = isSelected;
option.text = name;
return option;
}
export function createItemsOption2(name, value = "", isSelected = false) {
const option = document.createElement("option");
option.value = value || "";
option.selected = isSelected;
option.text = name;
return option;
}
// функция создает ссылку (a) для таблицы
// содержимое тега a заполняется необходимой иконкой (icon)
// при нажатии вызывается callback
// ссылка "оборачивается" тегом td
// <td><a href="#" onclick="callback()"><i class="fa-solid icon"></i></a></td>
function createTableAnchor(icon, callback) {
const i = document.createElement("i");
i.classList.add("fa-solid", icon);
const a = document.createElement("a");
a.href = "#";
a.appendChild(i);
a.onclick = (event) => {
// чтобы в URL не добавлялась решетка
event.preventDefault();
event.stopPropagation();
callback();
};
const td = document.createElement("td");
td.appendChild(a);
return td;
}
// функция создает колонку таблицы с текстом value
// <td>value</td>
function createTableColumn(value) {
const td = document.createElement("td");
td.textContent = value;
return td;
}
// функция создает строку таблицы
// <tr>
// <th scope="row">index + 1</th>
// <td>item.items.name</td>
// <td>parseFloat(item.price).toFixed(2))</td>
// <td>item.count</td>
// <td>parseFloat(item.sum).toFixed(2))</td>
// <td><a href="#" onclick="editCallback()"><i class="fa-solid fa-pencil"></i></a></td>
// <td><a href="#" onclick="editPageCallback()"><i class="fa-solid fa-pen-to-square"></i></a></td>
// <td><a href="#" onclick="deleteCallback()"><i class="fa-solid fa-trash"></i></a></td>
// </tr>
export function createTableRow(item, index, editCallback, deleteCallback) {
const rowNumber = document.createElement("th");
rowNumber.scope = "row";
rowNumber.textContent = index + 1;
const row = document.createElement("tr");
row.id = `line-${item.id}`;
row.appendChild(rowNumber);
row.appendChild(createTableColumn(item.items.name));
//
row.appendChild(createTableColumn(item.option));
//
row.appendChild(createTableColumn(item.product));
row.appendChild(createTableColumn(item.colors.name));
row.appendChild(createTableColumn(parseFloat(item.price).toFixed(2)));
// редактировать в модальном окне
row.appendChild(createTableAnchor("fa-pencil", editCallback));
// удаление
row.appendChild(createTableAnchor("fa-trash", deleteCallback))
return row;
}

304
laba3/ip laba3/js/lines.js Normal file
View File

@ -0,0 +1,304 @@
// модуль с логикой
import { hideUpdateModal, showUpdateModal } from "./lines-modal";
import {
createLine, deleteLine, getAllItemTypes, getAllColorTypes, getAllOptionTypes, getAllLines, updateLine,
} from "./lines-rest-api";
import {
cntrls, createItemsOption, createTableRow, imagePlaceholder,
} from "./lines-ui";
async function drawItemsSelect() {
// вызов метода REST API для получения списка типов товаров
const data = await getAllItemTypes();
// очистка содержимого select
// удаляется все, что находится между тегами <select></select>
// но не атрибуты
cntrls.itemsType.innerHTML = "";
// пустое значение
cntrls.itemsType.appendChild(createItemsOption("Выберите значение", "", true));
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item) => {} аналогично function(item) {}
data.forEach((item) => {
cntrls.itemsType.appendChild(createItemsOption(item.name, item.id));
});
}
async function drawColorsSelect() {
// вызов метода REST API для получения списка типов товаров
const data = await getAllColorTypes();
// очистка содержимого select
// удаляется все, что находится между тегами <select></select>
// но не атрибуты
cntrls.colorsType.innerHTML = "";
// пустое значение
cntrls.colorsType.appendChild(createItemsOption("Выберите значение", "", true));
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item) => {} аналогично function(item) {}
data.forEach((item) => {
cntrls.colorsType.appendChild(createItemsOption(item.name, item.id));
});
}
//
async function drawOptionsSelect() {
// вызов метода REST API для получения списка типов товаров
const data = await getAllOptionTypes();
// очистка содержимого select
// удаляется все, что находится между тегами <select></select>
// но не атрибуты
cntrls.option.innerHTML = "";
// пустое значение
cntrls.option.appendChild(createItemsOption("Выберите значение", "", true));
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item) => {} аналогично function(item) {}
// data.forEach((item) => {
// cntrls.option.appendChild(createItemsOption(item.name, 1));
// });
console.log(data)
const array1 = data[0].name.split(" ");
const array2 = data[1].name.split(" ");
const array3 = data[2].name.split(" ");
const array4 = data[3].name.split(" ");
cntrls.itemsType.addEventListener('change', function() {
const selectedValue = this.value; // Получаем выбранное значение
cntrls.option.innerHTML = "";
cntrls.option.appendChild(createItemsOption("Выберите значение", "", true));
// if (selectedValue == 1) {
// array1.forEach((item) => {
// cntrls.option.appendChild(createItemsOption(item, 1));
// });
// }
if (selectedValue == 1) {
for (let i = 0; i < array1.length; i++) {
cntrls.option.appendChild(createItemsOption(array1[i], i+1));
}
}
if (selectedValue == 2) {
for (let i = 0; i < array2.length; i++) {
cntrls.option.appendChild(createItemsOption(array2[i], i+1));
}
}
if (selectedValue == 3) {
for (let i = 0; i < array3.length; i++) {
cntrls.option.appendChild(createItemsOption(array3[i], i+1));
}
}
if (selectedValue == 4) {
for (let i = 0; i < array4.length; i++) {
cntrls.option.appendChild(createItemsOption(array4[i], i+1));
}
}
// const selectElement = document.querySelector('option[value="3"]');
// console.log(selectElement.innerHTML)
});
}
async function drawLinesTable() {
console.info("Try to load data");
if (!cntrls.table) {
return;
}
// вызов метода REST API для получения всех записей
const data = await getAllLines();
// очистка содержимого table
// удаляется все, что находится между тегами <table></table>
// но не атрибуты
cntrls.table.innerHTML = "";
// цикл по результату ответа от сервера
// используется лямбда-выражение
// (item, index) => {} аналогично function(item, index) {}
data.forEach((item, index) => {
cntrls.table.appendChild(
createTableRow(
item,
index,
// функции передаются в качестве параметра
// это очень удобно, так как аргументы функций доступны только
// в данном месте кода и не передаются в сервисные модули
() => showUpdateModal(item),
() => removeLine(item.id),
),
);
});
}
async function addLine(item, option, product, color, price, image) {
console.info("Try to add item");
// вызов метода REST API для добавления записи
const data = await createLine(item, option, product, color, price, image);
console.info("Added");
console.info(data);
// загрузка и заполнение table
drawLinesTable();
}
async function editLine(id, item, option, product, color, price, image) {
console.info("Try to update item");
// вызов метода REST API для обновления записи
const data = await updateLine(id, item, option, product, color, price, image);
console.info("Updated");
console.info(data);
// загрузка и заполнение table
drawLinesTable();
}
async function removeLine(id) {
if (!confirm("Do you really want to remove this item?")) {
console.info("Canceled");
return;
}
console.info("Try to remove item");
// вызов метода REST API для удаления записи
const data = await deleteLine(id);
console.info(data);
// загрузка и заполнение table
drawLinesTable();
}
// функция для получения содержимого файла в виде base64 строки
// https://ru.wikipedia.org/wiki/Base64
async function readFile(file) {
const reader = new FileReader();
// создание Promise-объекта для использования функции
// с помощью await (асинхронно) без коллбэков (callback)
// https://learn.javascript.ru/promise
return new Promise((resolve, reject) => {
// 2. "Возвращаем" содержимое когда файл прочитан
// через вызов resolve
// Если не использовать Promise, то всю работу по взаимодействию
// с REST API пришлось бы делать в обработчике (callback) функции
// onloadend
reader.onloadend = () => {
const fileContent = reader.result;
// Здесь могла бы быть работа с REST API
// Чтение заканчивает выполняться здесь
resolve(fileContent);
};
// 3. Возвращаем ошибку
reader.onerror = () => {
// Или здесь в случае ошибки
reject(new Error("oops, something went wrong with the file reader."));
};
// Шаг 1. Сначала читаем файл
// Чтение начинает выполняться здесь
reader.readAsDataURL(file);
});
}
// функция для обновления блока с превью выбранного изображения
async function updateImagePreview() {
// получение выбранного файла
// возможен выбор нескольких файлов, поэтому необходимо получить только первый
const file = cntrls.image.files[0];
// чтение содержимого файла в виде base64 строки
const fileContent = await readFile(file);
console.info("base64 ", fileContent);
// обновление атрибута src для тега img с id image-preview
cntrls.imagePreview.src = fileContent;
}
// Функция для обработки создания и редактирования элементов таблицы через модальное окно
// Если хотите делать через страницу, то удалите эту функцию
export function linesForm() {
console.info("linesForm");
// загрузка и заполнение select со списком товаров
drawItemsSelect()
drawColorsSelect()
drawOptionsSelect()
// загрузка и заполнение table
drawLinesTable()
// Вызов функции обновления превью изображения при возникновении
// события oncahnge в тэге input с id image
cntrls.image.addEventListener("change", () => updateImagePreview());
// обработчик события нажатия на кнопку для показа модального окна
cntrls.button.addEventListener("click", () => showUpdateModal(null));
// обработчик события отправки формы
// возникает при нажатии на кнопку (button) с типом submit
// кнопка должна находится внутри тега form
cntrls.form.addEventListener("submit", async (event) => {
console.info("Form onSubmit");
// отключение стандартного поведения формы при отправке
// при отправке страница обновляется и JS перестает работать
event.preventDefault();
event.stopPropagation();
// если форма не прошла валидацию, то ничего делать не нужно
if (!cntrls.form.checkValidity()) {
return;
}
let imageBase64 = "";
// Получение выбранного пользователем изображения в виде base64 строки
// Если пользователь ничего не выбрал, то не нужно сохранять в БД
// дефолтное изображение
if (cntrls.imagePreview.src !== imagePlaceholder) {
// Загрузка содержимого атрибута src тэга img с id image-preview
// Здесь выполняется HTTP запрос с типом GET
const result = await fetch(cntrls.imagePreview.src);
// Получение из HTTP-ответа бинарного содержимого
const blob = await result.blob();
// Получение base64 строки для файла
// Здесь выполняется Promise из функции readFile
// Promise позволяет писать линейный код для работы с асинхронными методами
// без использования обработчиков (callback) с помощью await
imageBase64 = await readFile(blob);
}
// получение id строки для редактирования
// это значение содержится в скрытом input
const currentId = cntrls.lineId.value;
// если значение id не задано,
// то необходимо выполнить добавление записи
// иначе обновление записи
const selectedValue = parseInt(cntrls.option.value);
const data3 = await getAllOptionTypes();
const idInt = parseInt(cntrls.itemsType.value)-1
const array12 = data3[idInt].name.split(" ");
const index = array12[selectedValue-1]
console.log(index)
if (!currentId) {
await addLine(
cntrls.itemsType.value,
index,
cntrls.product.value,
cntrls.colorsType.value,
cntrls.price.value,
imageBase64,
);
} else {
await editLine(
currentId,
cntrls.itemsType.value,
index,
cntrls.product.value,
cntrls.colorsType.value,
cntrls.price.value,
imageBase64,
);
}
// после выполнения добавления/обновления модальное окно скрывается
hideUpdateModal();
});
}

View File

@ -0,0 +1,20 @@
import { hideUpdateModal, showUpdateModal } from "./lines-modal";
import {
createLine, deleteLine, getAllItemTypes, getAllColorTypes, getAllLines, updateLine,
} from "./lines-rest-api";
import {
cntrls, createItemsOption, createTableRow, imagePlaceholder,
} from "./lines-ui";
console.log("hi")
document.getElementById('item').addEventListener('change', function() {
const element = document.getElementById('optionHidden');
if (cntrls.itemsType.value == false) {
element.setAttribute('hidden',true);
} else {
element.removeAttribute('hidden');
}
});

View File

@ -0,0 +1,25 @@
// модуль используется для валидации форма на странице
function validation() {
// поиск всех форма с классом .needs-validation
const forms = document.querySelectorAll("form.needs-validation");
for (let i = 0; i < forms.length; i += 1) {
const form = forms[i];
// для каждой формы добавляется обработчик события отправки
form.addEventListener("submit", (event) => {
// если форма не прошла валидацию
// то выключить стандартное действие
if (!form.checkValidity()) {
event.preventDefault();
// предотвращает распространение preventDefault
// на другие объекты
event.stopPropagation();
}
// добавляет к форме класс was-validated
form.classList.add("was-validated");
});
}
}
export default validation;

View File

@ -0,0 +1,14 @@
{
"compilerOptions": {
"module": "ESNext",
"moduleResolution": "Node",
"target": "ES2020",
"jsx": "preserve",
"strictNullChecks": true,
"strictFunctionTypes": true
},
"exclude": [
"node_modules",
"**/node_modules/*"
]
}

4627
laba3/ip laba3/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,27 @@
{
"name": "int-prog",
"version": "1.0.0",
"type": "module",
"scripts": {
"vite": "vite",
"serve": "http-server -p 3000 ./dist/",
"build": "vite build",
"rest": "json-server --watch data.json -p 8081",
"dev": "npm-run-all --parallel rest vite",
"prod": "npm-run-all build --parallel serve rest",
"start": "vite"
},
"dependencies": {
"@fortawesome/fontawesome-free": "6.4.2",
"bootstrap": "^5.3.2"
},
"devDependencies": {
"eslint": "8.50.0",
"eslint-config-airbnb-base": "15.0.0",
"eslint-plugin-import": "2.28.1",
"http-server": "14.1.1",
"json-server": "0.17.4",
"npm-run-all": "4.1.5",
"vite": "4.4.9"
}
}

View File

@ -0,0 +1,63 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="styles/style.css">
<title>Document</title>
</head>
<body>
<header class="fixed-top">
<nav class="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.html"><img src="images/icons8-ос-mac (4).svg" alt=""></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Переключатель навигации">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link" href="about us.html">О нас</a>
<a class="nav-link" href="catalog.html">Каталог</a>
<a class="nav-link" href="support.html">Поддержка</a>
<a class="nav-link active" href="singin.html">Войти</a>
</div>
</div>
</div>
</nav>
</header>
<section class="sing-section">
<div class="container-fluid">
<div class="border-sin">
<form>
<p class="title-sign">Вход</p>
<div class="mb-3">
<label class="margin-admin">E-mail</label>
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
</div>
<div class="mb-3">
<label class="margin-admin">Пароль</label>
<input type="password" class="form-control" id="exampleInputPassword1">
</div>
<div class="krug">
<button type="submit" class="btn btn-primary">Войти</button>
</div>
<p class="string">У вас еще нет аккаунта? <a class="nav-link" href="singup.html">Зарегистрироваться</a></p>
</form>
</div>
</div>
</section>
<footer>
<nav class="navbar bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<div class="navbar-text">
Kozlovanastya260104@gmail.com
</div>
</div>
</nav>
</footer>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,70 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="styles/style.css">
<title>Document</title>
</head>
<body>
<header class="fixed-top">
<nav class="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.html"><img src="images/icons8-ос-mac (4).svg" alt=""></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Переключатель навигации">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link" href="about us.html">О нас</a>
<a class="nav-link" href="catalog.html">Каталог</a>
<a class="nav-link" href="support.html">Поддержка</a>
<a class="nav-link active" href="singin.html">Войти</a>
</div>
</div>
</div>
</nav>
</header>
<section class="sing-section">
<div class="container-fluid">
<div class="border-sin">
<form>
<p class="title-sign">Регистрация</p>
<div class="mb-3">
<label class="margin-admin">Логин</label>
<input class="form-control">
</div>
<div class="mb-3">
<label class="margin-admin">E-mail</label>
<input type="email" class="form-control" id="exampleInputEmail1" aria-describedby="emailHelp">
</div>
<div class="mb-3">
<label class="margin-admin">Пароль</label>
<input type="password" class="form-control" id="exampleInputPassword1">
</div>
<div class="krug2">
<button type="submit" class="btn btn-primary">Зарегистрироваться</button>
</div>
</form>
</div>
</div>
</section>
<footer>
<nav class="navbar bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<div class="navbar-text">
Kozlovanastya260104@gmail.com
</div>
</div>
</nav>
</footer>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 867 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 368 B

View File

@ -0,0 +1,615 @@
/* header */
.navbar-brand>img {
width: 25px;
}
.navbar-brand {
display: flex;
align-items: center;
}
.container-fluid {
margin-left: 140px;
margin-right: 140px;
}
.navbar {
display: flex;
justify-content: space-between;
}
.navbar-collapse {
flex-grow: 0;
}
/* фон */
.index-section {
margin-top: 55.6px;
}
/* главная */
.index-page {
background-image: url("fon7.jpg");
background-size: no-repeat;
background-position: center;
background-size: cover;
height: 100vh;
}
.title {
height: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.title>p {
font-weight: 700;
font-size: 50px;
}
.navbar-text {
width: 100%;
display: flex;
Justify-content: flex-end;
padding-right: 8px;
}
/* каталог */
.catalog-section {
display: flex;
margin-top: 55.6px;
}
.title-one>h2 {
font-size: 35px;
margin-top: 30px;
text-align: center;
/* font-family: Verdana, Geneva, Tahoma, sans-serif; */
}
.main-margin-for-card {
margin-top: 15px;
margin-bottom: 50px;
display: flex;
}
.col-md-6 {
margin-top: 30px;
}
.col-lg-4 {
margin-top: 30px;
}
.col-xxl-3 {
margin-top: 30px;
}
.card {
align-items: center;
height: 256px;
}
.card>img {
width: auto;
height: 130px;
margin-top: 10px;
}
.card-title {
font-size: 16px;
font-weight: 700;
max-width: 160px;
text-align: center;
}
.card-text {
max-width: 160px;
text-align: center;
font-size: 18px;
}
.what-buy {
padding-top: 40px;
padding-bottom: 40px;
width: 400px;
}
.what-buy>h3 {
font-size: 28px;
}
.what-buy>p {
font-size: 18px;
}
.buttom-admin {
display: flex;
justify-content: center;
}
.button-edit {
width: 25px;
height: 25px;
border: none;
background-color: #ffffff;
background-image: url('icons8-редактировать-64.png');
background-size: cover;
margin-right: 5px;
}
.button-delete {
width: 25px;
height: 25px;
border: none;
background-color: #ffffff;
background-image: url('icons8-удалить-64.png');
background-size: cover;
}
/* войти */
.title-sign {
text-align: center;
font-size: 23px;
font-weight: 500;
padding-bottom: 15px;
}
.sing-section {
display: flex;
height: 100vh;
}
.border-sin {
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
form {
width: 500px;
margin-top: -100px;
}
.krug {
margin-top: 30px;
display: flex;
justify-content: center;
}
.krug>.btn-primary {
border-radius: 20px;
width: 120px;
}
.string {
text-align: center;
margin-top: 16px;
}
.string>.nav-link {
color: #0d6efd;
font-weight: 700;
display: inline;
}
.krug2 {
margin-top: 30px;
display: flex;
justify-content: center;
}
.krug2>.btn-primary {
border-radius: 20px;
width: 200px;
}
/* страница админа */
.admin-header>a {
color: rgb(133, 203, 253);
}
.margin-admin {
margin-bottom: 8px;
}
.title-admin {
text-align: center;
font-size: 18px;
font-weight: 500;
padding-bottom: 15px;
}
.form-admin {
width: 300px;
}
/* .add-picture {
margin-top: 30px;
}
.add-picture>.btn-primary {
background: #000000;
border-color:#000000;
width: 100%;
}
.add-picture>.btn-primary:hover {
background: #333333;
border-color: #606060;
}
.add-picture>.btn-primary:active {
background: #333333;
border-color: #606060;
} */
.add-tovar {
margin-top: 30px;
display: flex;
justify-content: center;
}
.add-tovar >.btn-primary {
border-radius: 5px;
width: 160px;
width: 100%;
}
/* поддержка */
.support-section {
height: 100vh;
}
.top-picture {
background-image: url("support.jpg");
background-size: no-repeat;
background-position: center;
background-size: cover;
height: 450px;
display: flex;
}
.main {
display: flex;
}
.title-support {
height: 100%;
width: 100%;
display: flex;
justify-content: center;
align-items: center;
}
.title-support>p {
font-weight: 500;
font-size: 50px;
color:#ffffff;
margin-top: -100px;
}
.conteiner-support {
margin-top: 40px;
max-width: 450px;
}
.call-as-about {
font-size: 25px;
}
.content {
margin-top: 20px;
font-size: 18px;
}
/* о нас */
.about-us-section {
/* height: 100vh; */
margin-top: 55.6px;
background-color: #000000;
display: flex;
}
.group-text {
color: #ffffff;
}
.up-text {
margin-top: 90px;
}
.up-text>h1 {
font-size: 70px;
}
.down-text {
display: flex;
margin-top: 75px;
justify-content: space-between;
margin-bottom: 200px;
}
.down-text>h3 {
margin-right: 100px;
font-size: 35px;
min-width: 360px;
max-width: 360px;
}
.down-text>p {
max-width: 630px;
font-size: 20px;
}
/* страница админа новая */
#image-preview {
width: 200px;
}
.mainAdmin {
display: flex;
height: 100vh;
margin-top: 100px;
}
.needs-validation {
margin-top: 0px;
}
.btn {
margin-bottom: 15px;
}
.button-delete-admin {
width: 22px;
height: 22px;
border: none;
background-color: #ffffff;
background-image: url('icons8-удалить-64.png');
background-size: cover;
}
@media (max-width: 1200px) {
.down-text>h3 {
margin-right: 70px;
font-size: 30px;
min-width: 310px;
}
}
@media (max-width: 1100px) {
.down-text>h3 {
margin-right: 50px;
font-size: 25px;
min-width: 280px;
}
.up-text>h1 {
font-size: 60px;
}
}
@media (max-width: 991px) {
.down-text {
display: block;
margin-top: 20px;
}
.down-text>p {
margin-top: 15px;
}
}
@media (max-width: 700px) {
.container-fluid {
margin-left: 20px;
margin-right: 20px;
}
.title>p {
font-weight: 600;
font-size: 40px;
}
.navbar-text {
font-size: 14px;
}
.what-buy {
padding-top: 40px;
padding-bottom: 40px;
width: 300px;
}
.what-buy>h3 {
font-size: 30px;
}
.what-buy>p {
font-size: 18px;
}
form {
width: 400px;
}
.up-text {
margin-top: 50px;
}
.up-text>h1 {
font-size: 50px;
}
.down-text>h3 {
font-size: 30px;
}
.title-support>p {
font-size: 33px;
margin-left: 10px;
margin-right: 10px;
}
}
@media (max-width: 570px) {
.title-support>p {
font-size: 30px;
}
.conteiner-support {
max-width: 350px;
}
.call-as-about {
font-size: 20px;
}
.content {
font-size: 15px;
}
}
@media (max-width: 500px) {
.navbar-text {
display: flex;
justify-content: center;
}
.what-buy {
padding-top: 35px;
padding-bottom: 35px;
width: 250px;
}
.what-buy>h3 {
font-size: 20px;
}
.what-buy>p {
font-size: 15px;
}
form {
width: auto;
}
.krug2>.btn-primary {
width: 130px;
font-size: 11px;
}
}
@media (max-width: 420px) {
.up-text {
margin-top: 30px;
}
.up-text>h1 {
font-size: 40px;
}
.down-text>h3 {
font-size: 20px;
min-width: 50px;
}
.down-text>p {
font-size: 14px;
}
}
@media (max-width: 400px) {
.krug>.btn-primary {
width: 100px;
font-size: 14px;
}
.string {
font-size: 14px;
}
.form-admin {
width: 200px;
}
.title-sign {
font-size: 18px;
}
.title-support>p {
font-size: 21px;
text-align: center;
margin-left: 10px;
margin-right: 10px;
}
.conteiner-support {
max-width: 260px;
}
.call-as-about {
font-size: 18px;
}
.content {
font-size: 14px;
}
.what-buy>p {
font-size: 13px;
}
}
@media (max-width: 325px) {
.title>p {
font-weight: 500;
font-size: 30px;
}
.container-fluid {
padding: 0;
}
.navbar-text {
font-size: 10px;
display: flex;
justify-content: center;
}
.what-buy {
padding-top: 35px;
padding-bottom: 35px;
width: 200px;
}
.what-buy>h3 {
font-size: 16px;
}
.what-buy>p {
font-size: 13px;
}
.col-xxl-3 {
display: flex;
justify-content: center;
}
.card {
width: 230px;
}
.card-title {
font-size: 16px;
}
.card-text {
font-size: 16px;
}
.card>img {
width: 8rem;
}
}
@media (max-width: 280px) {
.card {
width: 140px;
}
.card-title {
font-size: 16px;
}
.card-text {
font-size: 14px;
}
.card>img {
width: 6rem;
}
.what-buy {
width: 140px;
}
.form-admin {
width: 150px;
}
.up-text {
margin-top: 20px;
}
.up-text>h1 {
font-size: 30px;
}
.down-text>h3 {
font-size: 16px;
}
.down-text>p {
font-size: 13px;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 618 KiB

View File

@ -0,0 +1,61 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="node_modules/bootstrap/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="styles/style.css">
<title>Document</title>
</head>
<body>
<header class="fixed-top">
<nav class="navbar navbar-expand-lg bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<a class="navbar-brand" href="index.html"><img src="images/icons8-ос-mac (4).svg" alt=""></a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNavAltMarkup" aria-controls="navbarNavAltMarkup" aria-expanded="false" aria-label="Переключатель навигации">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNavAltMarkup">
<div class="navbar-nav">
<a class="nav-link" href="about us.html">О нас</a>
<a class="nav-link" href="catalog.html">Каталог</a>
<a class="nav-link active" href="support.html">Поддержка</a>
<a class="nav-link" href="singin.html">Войти</a>
</div>
</div>
</div>
</nav>
</header>
<section class="support-section ">
<div class="top-picture">
<div class="title-support">
<p>Служба поддержки Apple</p>
</div>
</div>
<div class="main">
<div class="container-fluid">
<div class="conteiner-support">
<h3 class="call-as-about">Расскажите нам о проблеме</h3>
<p class="content">
Расскажите подробнее, и мы предложим лучшее решение. Напишите нам на электронную почту:
applesupport@mail.com</p>
</div>
</div>
</div>
</section>
<footer>
<nav class="navbar bg-body-tertiary" data-bs-theme="dark">
<div class="container-fluid">
<div class="navbar-text">
Kozlovanastya260104@gmail.com
</div>
</div>
</nav>
</footer>
<script src="node_modules/bootstrap/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View File

@ -0,0 +1,21 @@
import { resolve } from "path";
// eslint-disable-next-line import/no-extraneous-dependencies
import { defineConfig } from "vite";
export default defineConfig({
build: {
sourcemap: true,
emptyOutDir: true,
rollupOptions: {
input: {
main: resolve(__dirname, "index.html"),
page2: resolve(__dirname, "about us.html"),
page3: resolve(__dirname, "adminNew.html"),
page4: resolve(__dirname, "catalog.html"),
page5: resolve(__dirname, "singin.html"),
page6: resolve(__dirname, "singup.html"),
page7: resolve(__dirname, "support.html"),
},
},
},
});

BIN
laba3/отчет 3.docx Normal file

Binary file not shown.