This commit is contained in:
Egor Deev 2025-11-20 15:46:57 +03:00 committed by GitHub
parent e73e6cb672
commit b90fea83f5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
26 changed files with 5349 additions and 0 deletions

BIN
labs/lab-10/img/main.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

207
labs/lab-10/index.html Normal file
View file

@ -0,0 +1,207 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="https://deev.space/media/favicon.ico" type="image/x-icon">
<title>ЭкоЛанч - Доставка здоровых бизнес-ланчей в Москве</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="styles/styles.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top shadow-sm">
<div class="container">
<a class="navbar-brand" href="index.html">
<span class="brand-icon">🌿</span>
<span class="brand-eco">Эко</span><span class="brand-lunch">Ланч</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link active" href="index.html">Главная</a>
</li>
<li class="nav-item">
<a class="nav-link" href="templates/menu.html">Собрать ланч</a>
</li>
<li class="nav-item">
<a class="nav-link" href="templates/order.html">Оформить заказ</a>
</li>
<li class="nav-item">
<a class="nav-link" href="templates/orders.html">Мои заказы</a>
</li>
<li class="nav-item">
<a class="nav-link" href="templates/delivery.html">Доставка</a>
</li>
<li class="nav-item">
<a class="nav-link" href="templates/about.html">О нас</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#contacts">Контакты</a>
</li>
</ul>
</div>
</div>
</nav>
<section class="hero-section">
<div class="container">
<div class="row align-items-center min-vh-100">
<div class="col-lg-6 mb-5 mb-lg-0">
<h1 class="display-4 fw-bold mb-4">Здоровое питание для вашего офиса</h1>
<p class="lead mb-4">ЭкоЛанч доставляет свежие и полезные бизнес-ланчи прямо в ваш офис. Сбалансированное меню, быстрая доставка и забота о вашем здоровье.</p>
<div class="d-flex flex-column flex-sm-row gap-3">
<a href="templates/menu.html" class="btn btn-primary btn-lg">Собрать ланч</a>
<a href="#advantages" class="btn btn-outline-primary btn-lg">Узнать больше</a>
</div>
</div>
<div class="col-lg-6">
<img src="img/main.jpg" alt="Здоровая еда" class="img-fluid rounded-4 shadow-lg">
</div>
</div>
</div>
</section>
<section id="advantages" class="advantages-section py-5">
<div class="container">
<div class="row mb-5">
<div class="col-lg-8 mx-auto text-center">
<h2 class="display-5 fw-bold mb-3">Почему выбирают нас</h2>
<p class="lead text-muted">Мы предлагаем лучшие условия для здорового питания вашей команды</p>
</div>
</div>
<div class="row g-4">
<div class="col-md-6 col-lg-4">
<div class="advantage-card text-center p-4 h-100">
<div class="advantage-icon mb-3"></div>
<h4 class="fw-bold mb-3">Быстрая доставка</h4>
<p class="text-muted">Доставляем в течение 60 минут по всей Москве в пределах МКАД</p>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="advantage-card text-center p-4 h-100">
<div class="advantage-icon mb-3">🥗</div>
<h4 class="fw-bold mb-3">Свежие продукты</h4>
<p class="text-muted">Готовим каждое утро из качественных продуктов от проверенных поставщиков</p>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="advantage-card text-center p-4 h-100">
<div class="advantage-icon mb-3">💚</div>
<h4 class="fw-bold mb-3">Сбалансированное меню</h4>
<p class="text-muted">Меню разработано профессиональными диетологами</p>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="advantage-card text-center p-4 h-100">
<div class="advantage-icon mb-3">♻️</div>
<h4 class="fw-bold mb-3">Эко-упаковка</h4>
<p class="text-muted">Используем биоразлагаемую упаковку, заботимся о планете</p>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="advantage-card text-center p-4 h-100">
<div class="advantage-icon mb-3">💰</div>
<h4 class="fw-bold mb-3">Гибкие цены</h4>
<p class="text-muted">Система скидок для корпоративных клиентов и постоянных заказчиков</p>
</div>
</div>
<div class="col-md-6 col-lg-4">
<div class="advantage-card text-center p-4 h-100">
<div class="advantage-icon mb-3">👨‍🍳</div>
<h4 class="fw-bold mb-3">Опытные повара</h4>
<p class="text-muted">Профессиональные повара с многолетним опытом</p>
</div>
</div>
</div>
</div>
</section>
<section class="cta-section py-5">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-8 text-center">
<h2 class="display-5 fw-bold mb-4">Готовы попробовать здоровое питание?</h2>
<p class="lead mb-4">Соберите свой идеальный ланч прямо сейчас. Выберите блюда из нашего меню и оформите заказ всего за несколько минут.</p>
<a href="templates/menu.html" class="btn btn-light btn-lg px-5">Собрать ланч сейчас</a>
</div>
</div>
</div>
</section>
<section id="contacts" class="contacts-section py-5">
<div class="container">
<div class="row mb-5">
<div class="col-lg-8 mx-auto text-center">
<h2 class="display-5 fw-bold mb-3">Свяжитесь с нами</h2>
<p class="lead text-muted">Есть вопросы? Напишите нам, и мы обязательно ответим</p>
</div>
</div>
<div class="row g-5">
<div class="col-lg-6">
<h4 class="fw-bold mb-4">Контактная информация</h4>
<div class="contact-info">
<div class="contact-item mb-3">
<strong>Телефон:</strong>
<a href="tel:+79993737737">+7 (999) 373-77-37</a>
</div>
<div class="contact-item mb-3">
<strong>Email:</strong>
<a href="mailto:egor@deev.space">egor@deev.space</a>
</div>
<div class="contact-item mb-3">
<strong>Адрес:</strong>
<span>г. Москва, ул. Михалковская, д. 7, к. 1</span>
</div>
<div class="contact-item mb-3">
<strong>Режим работы:</strong>
<span>Пн-Пт с 7:00 до 23:00</span>
</div>
</div>
</div>
<div class="col-lg-6">
<h4 class="fw-bold mb-4">Форма обратной связи</h4>
<form id="contact-form">
<div class="mb-3">
<label for="contact-name" class="form-label">Имя</label>
<input type="text" class="form-control" id="contact-name" required>
</div>
<div class="mb-3">
<label for="contact-email" class="form-label">Email</label>
<input type="email" class="form-control" id="contact-email" required>
</div>
<div class="mb-3">
<label for="contact-phone" class="form-label">Телефон</label>
<input type="tel" class="form-control" id="contact-phone">
</div>
<div class="mb-3">
<label for="contact-message" class="form-label">Сообщение</label>
<textarea class="form-control" id="contact-message" rows="4" required></textarea>
</div>
<button type="submit" class="btn btn-primary w-100">Отправить сообщение</button>
</form>
</div>
</div>
</div>
</section>
<footer id="contacts" class="footer py-4">
<div class="container">
<div class="footer-content">
<div class="footer-info">
<div class="footer-row footer-title">
© 2024 ЭкоЛанч. Все права защищены.
</div>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

314
labs/lab-10/js/menu.js Normal file
View file

@ -0,0 +1,314 @@
let dishes = [];
const selectedDishes = {
soup: null,
'main-course': null,
salad: null,
drink: null,
dessert: null
};
let activeFilters = {
soup: null,
'main-course': null,
salad: null,
drink: null,
dessert: null
};
function loadDishes() {
const apiUrl = 'https://edu.std-900.ist.mospolytech.ru/labs/api/dishes';
console.log('Загрузка блюд из API...');
fetch(apiUrl)
.then(function(response) {
console.log('Ответ получен:', response.status);
if (!response.ok) {
throw new Error('Ошибка загрузки данных: ' + response.status);
}
return response.json();
})
.then(function(data) {
console.log('Данные загружены:', data.length, 'блюд');
dishes = data;
sortDishes();
loadOrderFromLocalStorage();
displayDishes();
updateOrderPanel();
})
.catch(function(error) {
console.error('Ошибка при загрузке блюд:', error);
alert('Не удалось загрузить меню. Проверьте подключение к интернету.');
});
}
function sortDishes() {
dishes.sort(function(a, b) {
return a.name.localeCompare(b.name, 'ru');
});
}
function displayDishes() {
const soupSection = document.getElementById('soup-section');
const mainCourseSection = document.getElementById('main-course-section');
const saladSection = document.getElementById('salad-section');
const drinkSection = document.getElementById('drink-section');
const dessertSection = document.getElementById('dessert-section');
if (!soupSection || !mainCourseSection || !saladSection ||
!drinkSection || !dessertSection) {
console.error('Не найдены секции для отображения блюд');
return;
}
soupSection.innerHTML = '';
mainCourseSection.innerHTML = '';
saladSection.innerHTML = '';
drinkSection.innerHTML = '';
dessertSection.innerHTML = '';
console.log('Отображение блюд. Всего:', dishes.length);
dishes.forEach(function(dish) {
const dishCard = createDishCard(dish);
if (dish.category === 'soup') {
if (!activeFilters.soup || dish.kind === activeFilters.soup) {
soupSection.insertAdjacentHTML('beforeend', dishCard);
}
} else if (dish.category === 'main-course') {
if (!activeFilters['main-course'] ||
dish.kind === activeFilters['main-course']) {
mainCourseSection.insertAdjacentHTML('beforeend', dishCard);
}
} else if (dish.category === 'salad') {
if (!activeFilters.salad || dish.kind === activeFilters.salad) {
saladSection.insertAdjacentHTML('beforeend', dishCard);
}
} else if (dish.category === 'drink') {
if (!activeFilters.drink || dish.kind === activeFilters.drink) {
drinkSection.insertAdjacentHTML('beforeend', dishCard);
}
} else if (dish.category === 'dessert') {
if (!activeFilters.dessert ||
dish.kind === activeFilters.dessert) {
dessertSection.insertAdjacentHTML('beforeend', dishCard);
}
}
});
addDishClickHandlers();
restoreSelection();
}
function createDishCard(dish) {
return `
<div class="dish-card" data-dish="${dish.keyword}">
<img src="${dish.image}" alt="${dish.name}">
<p class="dish-price">${dish.price} руб.</p>
<p class="dish-name">${dish.name}</p>
<p class="dish-weight">${dish.count}</p>
<button>Добавить</button>
</div>
`;
}
function addDishClickHandlers() {
const dishCards = document.querySelectorAll('.dish-card');
dishCards.forEach(function(card) {
card.addEventListener('click', function() {
const keyword = this.dataset.dish;
const dish = dishes.find(function(d) {
return d.keyword === keyword;
});
if (dish) {
selectDish(dish);
}
});
});
}
function selectDish(dish) {
const previousDish = selectedDishes[dish.category];
if (previousDish) {
const previousCard = document.querySelector(
`.dish-card[data-dish="${previousDish.keyword}"]`
);
if (previousCard) {
previousCard.classList.remove('selected');
}
}
selectedDishes[dish.category] = dish;
const currentCard = document.querySelector(
`.dish-card[data-dish="${dish.keyword}"]`
);
if (currentCard) {
currentCard.classList.add('selected');
}
saveOrderToLocalStorage();
updateOrderPanel();
}
function saveOrderToLocalStorage() {
const order = {};
Object.keys(selectedDishes).forEach(function(category) {
if (selectedDishes[category]) {
order[category] = selectedDishes[category].id;
}
});
localStorage.setItem('selectedDishes', JSON.stringify(order));
console.log('Заказ сохранен в localStorage:', order);
}
function loadOrderFromLocalStorage() {
const savedOrder = localStorage.getItem('selectedDishes');
if (!savedOrder) {
console.log('Сохраненный заказ не найден');
return;
}
try {
const order = JSON.parse(savedOrder);
console.log('Загружен заказ из localStorage:', order);
Object.keys(order).forEach(function(category) {
const dishId = order[category];
const dish = dishes.find(function(d) {
return d.id === dishId;
});
if (dish) {
selectedDishes[category] = dish;
}
});
} catch (error) {
console.error('Ошибка при загрузке заказа:', error);
}
}
function updateOrderPanel() {
const orderPanel = document.getElementById('order-panel');
const orderPanelPrice = document.getElementById('order-panel-price');
const orderPanelLink = document.getElementById('order-panel-link');
if (!orderPanel || !orderPanelPrice || !orderPanelLink) {
console.error('Не найдены элементы панели заказа');
return;
}
const hasSelection = selectedDishes.soup ||
selectedDishes['main-course'] ||
selectedDishes.salad ||
selectedDishes.drink ||
selectedDishes.dessert;
if (!hasSelection) {
orderPanel.classList.add('hidden');
return;
}
orderPanel.classList.remove('hidden');
let totalPrice = 0;
Object.keys(selectedDishes).forEach(function(category) {
if (selectedDishes[category]) {
totalPrice += selectedDishes[category].price;
}
});
orderPanelPrice.textContent = totalPrice + ' руб.';
if (isValidCombo()) {
orderPanelLink.classList.remove('disabled');
} else {
orderPanelLink.classList.add('disabled');
}
}
function setupFilters() {
const filterButtons = document.querySelectorAll('.filter-btn');
filterButtons.forEach(function(button) {
button.addEventListener('click', function() {
const kind = this.dataset.kind;
const section = this.closest('section');
const category = section.querySelector('.dishes-grid').id
.replace('-section', '');
if (this.classList.contains('active')) {
this.classList.remove('active');
activeFilters[category] = null;
} else {
const sectionButtons = section
.querySelectorAll('.filter-btn');
sectionButtons.forEach(function(btn) {
btn.classList.remove('active');
});
this.classList.add('active');
activeFilters[category] = kind;
}
displayDishes();
});
});
}
function isValidCombo() {
const hasSoup = selectedDishes.soup !== null;
const hasMainCourse = selectedDishes['main-course'] !== null;
const hasSalad = selectedDishes.salad !== null;
const hasDrink = selectedDishes.drink !== null;
if (hasSoup && hasMainCourse && hasSalad && hasDrink) {
return true;
}
if (hasSoup && hasMainCourse && hasDrink) {
return true;
}
if (hasSoup && hasSalad && hasDrink) {
return true;
}
if (hasMainCourse && hasSalad && hasDrink) {
return true;
}
if (hasMainCourse && hasDrink) {
return true;
}
return false;
}
function restoreSelection() {
Object.keys(selectedDishes).forEach(function(category) {
const dish = selectedDishes[category];
if (dish) {
const card = document.querySelector(
`.dish-card[data-dish="${dish.keyword}"]`
);
if (card) {
card.classList.add('selected');
}
}
});
}
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM загружен, начинаем инициализацию');
loadDishes();
setupFilters();
});

448
labs/lab-10/js/order.js Normal file
View file

@ -0,0 +1,448 @@
const API_URL = 'https://edu.std-900.ist.mospolytech.ru/labs/api';
const API_KEY = '358a63a5-52ae-4ab0-800b-90f75ce5a5c2';
let dishes = [];
let orderDishes = {
soup: null,
'main-course': null,
salad: null,
drink: null,
dessert: null
};
function loadDishes() {
console.log('Загрузка блюд для страницы заказа...');
fetch(API_URL + '/dishes')
.then(function(response) {
console.log('Ответ получен:', response.status);
if (!response.ok) {
throw new Error('Ошибка загрузки данных: ' + response.status);
}
return response.json();
})
.then(function(data) {
console.log('Данные загружены:', data.length, 'блюд');
dishes = data;
loadOrderFromLocalStorage();
displayOrderDishes();
updateOrderSummary();
})
.catch(function(error) {
console.error('Ошибка при загрузке блюд:', error);
alert('Не удалось загрузить данные о блюдах. Проверьте подключение.');
});
}
function loadOrderFromLocalStorage() {
const savedOrder = localStorage.getItem('selectedDishes');
if (!savedOrder) {
console.log('Сохраненный заказ не найден');
return;
}
try {
const order = JSON.parse(savedOrder);
console.log('Загружен заказ из localStorage:', order);
Object.keys(order).forEach(function(category) {
const dishId = order[category];
const dish = dishes.find(function(d) {
return d.id === dishId;
});
if (dish) {
orderDishes[category] = dish;
console.log('Найдено блюдо:', dish.name, 'для категории:', category);
} else {
console.warn('Блюдо с ID', dishId, 'не найдено для категории:', category);
}
});
} catch (error) {
console.error('Ошибка при загрузке заказа:', error);
}
}
function displayOrderDishes() {
const orderDishesContainer = document.getElementById('order-dishes');
const emptyOrderMessage = document.getElementById('empty-order');
if (!orderDishesContainer || !emptyOrderMessage) {
console.error('Не найдены контейнеры для отображения заказа');
return;
}
const hasAnyDish = Object.values(orderDishes).some(function(dish) {
return dish !== null;
});
console.log('Есть блюда для отображения:', hasAnyDish);
if (!hasAnyDish) {
orderDishesContainer.innerHTML = '';
emptyOrderMessage.classList.remove('hidden');
return;
}
emptyOrderMessage.classList.add('hidden');
orderDishesContainer.innerHTML = '';
Object.keys(orderDishes).forEach(function(category) {
const dish = orderDishes[category];
if (dish) {
const dishCard = createOrderDishCard(dish);
orderDishesContainer.insertAdjacentHTML('beforeend', dishCard);
}
});
addRemoveHandlers();
}
function createOrderDishCard(dish) {
return `
<div class="dish-card" data-dish-id="${dish.id}">
<img src="${dish.image}" alt="${dish.name}">
<p class="dish-price">${dish.price} руб.</p>
<p class="dish-name">${dish.name}</p>
<p class="dish-weight">${dish.count}</p>
<button class="remove-dish-btn">Удалить</button>
</div>
`;
}
function addRemoveHandlers() {
const removeButtons = document.querySelectorAll('.remove-dish-btn');
removeButtons.forEach(function(button) {
button.addEventListener('click', function(event) {
event.stopPropagation();
const card = this.closest('.dish-card');
const dishId = parseInt(card.dataset.dishId);
removeDishFromOrder(dishId);
});
});
}
function removeDishFromOrder(dishId) {
console.log('Удаление блюда с ID:', dishId);
Object.keys(orderDishes).forEach(function(category) {
if (orderDishes[category] && orderDishes[category].id === dishId) {
orderDishes[category] = null;
}
});
saveOrderToLocalStorage();
displayOrderDishes();
updateOrderSummary();
}
function saveOrderToLocalStorage() {
const order = {};
Object.keys(orderDishes).forEach(function(category) {
if (orderDishes[category]) {
order[category] = orderDishes[category].id;
}
});
localStorage.setItem('selectedDishes', JSON.stringify(order));
console.log('Заказ сохранен в localStorage:', order);
}
function updateOrderSummary() {
const orderSummary = document.getElementById('order-summary');
if (!orderSummary) {
console.error('Не найден контейнер для сводки заказа');
return;
}
const hasSelection = Object.values(orderDishes).some(function(dish) {
return dish !== null;
});
if (!hasSelection) {
orderSummary.innerHTML =
'<p class="empty-order">Ничего не выбрано</p>';
return;
}
let summaryHTML = '';
let totalPrice = 0;
if (orderDishes.soup) {
summaryHTML += `
<div class="order-category">
<h3>Суп</h3>
<div class="order-item">
<span>${orderDishes.soup.name}</span>
<span>${orderDishes.soup.price} руб.</span>
</div>
</div>
`;
totalPrice += orderDishes.soup.price;
} else {
summaryHTML += `
<div class="order-category">
<h3>Суп</h3>
<p class="not-selected">Блюдо не выбрано</p>
</div>
`;
}
if (orderDishes['main-course']) {
summaryHTML += `
<div class="order-category">
<h3>Главное блюдо</h3>
<div class="order-item">
<span>${orderDishes['main-course'].name}</span>
<span>${orderDishes['main-course'].price} руб.</span>
</div>
</div>
`;
totalPrice += orderDishes['main-course'].price;
} else {
summaryHTML += `
<div class="order-category">
<h3>Главное блюдо</h3>
<p class="not-selected">Блюдо не выбрано</p>
</div>
`;
}
if (orderDishes.salad) {
summaryHTML += `
<div class="order-category">
<h3>Салат</h3>
<div class="order-item">
<span>${orderDishes.salad.name}</span>
<span>${orderDishes.salad.price} руб.</span>
</div>
</div>
`;
totalPrice += orderDishes.salad.price;
} else {
summaryHTML += `
<div class="order-category">
<h3>Салат</h3>
<p class="not-selected">Блюдо не выбрано</p>
</div>
`;
}
if (orderDishes.drink) {
summaryHTML += `
<div class="order-category">
<h3>Напиток</h3>
<div class="order-item">
<span>${orderDishes.drink.name}</span>
<span>${orderDishes.drink.price} руб.</span>
</div>
</div>
`;
totalPrice += orderDishes.drink.price;
} else {
summaryHTML += `
<div class="order-category">
<h3>Напиток</h3>
<p class="not-selected">Напиток не выбран</p>
</div>
`;
}
if (orderDishes.dessert) {
summaryHTML += `
<div class="order-category">
<h3>Десерт</h3>
<div class="order-item">
<span>${orderDishes.dessert.name}</span>
<span>${orderDishes.dessert.price} руб.</span>
</div>
</div>
`;
totalPrice += orderDishes.dessert.price;
} else {
summaryHTML += `
<div class="order-category">
<h3>Десерт</h3>
<p class="not-selected">Десерт не выбран</p>
</div>
`;
}
summaryHTML += `
<div class="order-total">
<h3>Стоимость заказа</h3>
<p class="total-price">${totalPrice} руб.</p>
</div>
`;
orderSummary.innerHTML = summaryHTML;
}
function isValidCombo() {
const hasSoup = orderDishes.soup !== null;
const hasMainCourse = orderDishes['main-course'] !== null;
const hasSalad = orderDishes.salad !== null;
const hasDrink = orderDishes.drink !== null;
if (hasSoup && hasMainCourse && hasSalad && hasDrink) {
return true;
}
if (hasSoup && hasMainCourse && hasDrink) {
return true;
}
if (hasSoup && hasSalad && hasDrink) {
return true;
}
if (hasMainCourse && hasSalad && hasDrink) {
return true;
}
if (hasMainCourse && hasDrink) {
return true;
}
return false;
}
function getValidationMessage() {
const hasSoup = orderDishes.soup !== null;
const hasMainCourse = orderDishes['main-course'] !== null;
const hasSalad = orderDishes.salad !== null;
const hasDrink = orderDishes.drink !== null;
if (!hasSoup && !hasMainCourse && !hasSalad && !hasDrink) {
return 'Ничего не выбрано. Выберите блюда для заказа';
}
if (!hasDrink) {
return 'Выберите напиток';
}
if (hasSoup && !hasMainCourse && !hasSalad) {
return 'Выберите главное блюдо/салат/стартер';
}
if (hasSalad && !hasSoup && !hasMainCourse) {
return 'Выберите суп или главное блюдо';
}
if (!hasMainCourse && !hasSoup && (hasDrink || orderDishes.dessert)) {
return 'Выберите главное блюдо';
}
return '';
}
function setupFormSubmission() {
const form = document.getElementById('order-form');
if (!form) {
console.error('Форма заказа не найдена');
return;
}
form.addEventListener('submit', function(event) {
event.preventDefault();
console.log('Отправка формы заказа');
if (!isValidCombo()) {
const message = getValidationMessage();
alert(message);
return;
}
const formData = new FormData(form);
const orderData = {
full_name: formData.get('full_name'),
email: formData.get('email'),
subscribe: formData.get('subscribe') === '1' ? 1 : 0,
phone: formData.get('phone'),
delivery_address: formData.get('delivery_address'),
delivery_type: formData.get('delivery_type'),
delivery_time: formData.get('delivery_time') || '',
comment: formData.get('comment') || ''
};
if (orderDishes.soup) {
orderData.soup_id = orderDishes.soup.id;
}
if (orderDishes['main-course']) {
orderData.main_course_id = orderDishes['main-course'].id;
}
if (orderDishes.salad) {
orderData.salad_id = orderDishes.salad.id;
}
if (orderDishes.drink) {
orderData.drink_id = orderDishes.drink.id;
}
if (orderDishes.dessert) {
orderData.dessert_id = orderDishes.dessert.id;
}
console.log('Данные заказа:', orderData);
sendOrder(orderData);
});
}
function sendOrder(orderData) {
const url = API_URL + '/orders?api_key=' + API_KEY;
console.log('Отправка заказа на сервер:', url);
fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(orderData)
})
.then(function(response) {
console.log('Ответ от сервера:', response.status);
if (!response.ok) {
return response.json().then(function(errorData) {
throw new Error(errorData.error ||
'Ошибка при оформлении заказа');
});
}
return response.json();
})
.then(function(data) {
console.log('Заказ успешно создан:', data);
alert('Заказ успешно оформлен! Номер заказа: ' + data.id);
localStorage.removeItem('selectedDishes');
window.location.href = '../templates/orders.html';
})
.catch(function(error) {
console.error('Ошибка при отправке заказа:', error);
alert('Не удалось оформить заказ: ' + error.message);
});
}
function setupResetButton() {
const resetButton = document.getElementById('reset-button');
if (!resetButton) {
console.error('Кнопка сброса не найдена');
return;
}
resetButton.addEventListener('click', function() {
const form = document.getElementById('order-form');
form.reset();
});
}
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM загружен для страницы заказа');
loadDishes();
setupFormSubmission();
setupResetButton();
});

584
labs/lab-10/js/orders.js Normal file
View file

@ -0,0 +1,584 @@
const API_URL = 'https://edu.std-900.ist.mospolytech.ru/labs/api';
const API_KEY = '358a63a5-52ae-4ab0-800b-90f75ce5a5c2';
let orders = [];
let dishes = [];
let currentOrderId = null;
function loadOrders() {
console.log('Загрузка заказов...');
const url = API_URL + '/orders?api_key=' + API_KEY;
fetch(url)
.then(function(response) {
console.log('Ответ получен:', response.status);
if (!response.ok) {
throw new Error('Ошибка загрузки заказов: ' + response.status);
}
return response.json();
})
.then(function(data) {
console.log('Заказы загружены:', data.length);
orders = data;
sortOrders();
return loadDishes();
})
.then(function() {
displayOrders();
})
.catch(function(error) {
console.error('Ошибка при загрузке заказов:', error);
showNotification('Не удалось загрузить заказы: ' + error.message,
'error');
});
}
function loadDishes() {
console.log('Загрузка блюд...');
return fetch(API_URL + '/dishes')
.then(function(response) {
if (!response.ok) {
throw new Error('Ошибка загрузки блюд');
}
return response.json();
})
.then(function(data) {
console.log('Блюда загружены:', data.length);
dishes = data;
});
}
function sortOrders() {
orders.sort(function(a, b) {
const dateA = new Date(a.created_at);
const dateB = new Date(b.created_at);
return dateB - dateA;
});
}
function displayOrders() {
const ordersList = document.getElementById('orders-list');
const emptyOrders = document.getElementById('empty-orders');
if (!ordersList || !emptyOrders) {
console.error('Не найдены контейнеры для отображения заказов');
return;
}
if (orders.length === 0) {
ordersList.innerHTML = '';
emptyOrders.classList.remove('hidden');
return;
}
emptyOrders.classList.add('hidden');
ordersList.innerHTML = '';
orders.forEach(function(order, index) {
const orderCard = createOrderCard(order, index + 1);
ordersList.insertAdjacentHTML('beforeend', orderCard);
});
addOrderActionHandlers();
}
function createOrderCard(order, number) {
const date = formatDate(order.created_at);
const composition = getOrderComposition(order);
const price = calculateOrderPrice(order);
const deliveryTime = getDeliveryTime(order);
return `
<div class="order-card" data-order-id="${order.id}">
<div class="order-card-header">
<span class="order-number">Заказ ${number}</span>
<span class="order-date">${date}</span>
</div>
<div class="order-card-body">
<div class="order-info">
<p><strong>Состав:</strong> ${composition}</p>
<p><strong>Стоимость:</strong> ${price} руб.</p>
<p><strong>Доставка:</strong> ${deliveryTime}</p>
</div>
</div>
<div class="order-card-footer">
<button class="order-btn order-btn-view" data-action="view">Подробнее</button>
<button class="order-btn order-btn-edit" data-action="edit">Редактировать</button>
<button class="order-btn order-btn-delete" data-action="delete">Удалить</button>
</div>
</div>
`;
}
function formatDate(dateString) {
const date = new Date(dateString);
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0');
const year = date.getFullYear();
return day + '.' + month + '.' + year;
}
function formatDateTime(dateString) {
const date = new Date(dateString);
const day = String(date.getDate()).padStart(2, '0');
const month = String(date.getMonth() + 1).padStart(2, '0');
const year = date.getFullYear();
const hours = String(date.getHours()).padStart(2, '0');
const minutes = String(date.getMinutes()).padStart(2, '0');
return day + '.' + month + '.' + year + ' ' + hours + ':' + minutes;
}
function getOrderComposition(order) {
const dishNames = [];
if (order.soup_id) {
const dish = dishes.find(function(d) {
return d.id === order.soup_id;
});
if (dish) {
dishNames.push(dish.name);
}
}
if (order.main_course_id) {
const dish = dishes.find(function(d) {
return d.id === order.main_course_id;
});
if (dish) {
dishNames.push(dish.name);
}
}
if (order.salad_id) {
const dish = dishes.find(function(d) {
return d.id === order.salad_id;
});
if (dish) {
dishNames.push(dish.name);
}
}
if (order.drink_id) {
const dish = dishes.find(function(d) {
return d.id === order.drink_id;
});
if (dish) {
dishNames.push(dish.name);
}
}
if (order.dessert_id) {
const dish = dishes.find(function(d) {
return d.id === order.dessert_id;
});
if (dish) {
dishNames.push(dish.name);
}
}
return dishNames.join(', ');
}
function calculateOrderPrice(order) {
let total = 0;
if (order.soup_id) {
const dish = dishes.find(function(d) {
return d.id === order.soup_id;
});
if (dish) {
total += dish.price;
}
}
if (order.main_course_id) {
const dish = dishes.find(function(d) {
return d.id === order.main_course_id;
});
if (dish) {
total += dish.price;
}
}
if (order.salad_id) {
const dish = dishes.find(function(d) {
return d.id === order.salad_id;
});
if (dish) {
total += dish.price;
}
}
if (order.drink_id) {
const dish = dishes.find(function(d) {
return d.id === order.drink_id;
});
if (dish) {
total += dish.price;
}
}
if (order.dessert_id) {
const dish = dishes.find(function(d) {
return d.id === order.dessert_id;
});
if (dish) {
total += dish.price;
}
}
return total;
}
function getDeliveryTime(order) {
if (order.delivery_type === 'by_time') {
return order.delivery_time;
}
return 'Как можно скорее (с 7:00 до 23:00)';
}
function addOrderActionHandlers() {
const buttons = document.querySelectorAll('.order-btn');
buttons.forEach(function(button) {
button.addEventListener('click', function() {
const action = this.dataset.action;
const card = this.closest('.order-card');
const orderId = parseInt(card.dataset.orderId);
const order = orders.find(function(o) {
return o.id === orderId;
});
if (!order) {
console.error('Заказ не найден');
return;
}
if (action === 'view') {
showViewModal(order);
} else if (action === 'edit') {
showEditModal(order);
} else if (action === 'delete') {
showDeleteModal(order);
}
});
});
}
function showViewModal(order) {
const modal = document.getElementById('view-modal');
if (!modal) {
return;
}
const dateTime = formatDateTime(order.created_at);
const deliveryTime = getDeliveryTime(order);
document.getElementById('view-date').textContent = dateTime;
document.getElementById('view-name').textContent = order.full_name;
document.getElementById('view-address').textContent =
order.delivery_address;
document.getElementById('view-time').textContent = deliveryTime;
document.getElementById('view-phone').textContent = order.phone;
document.getElementById('view-email').textContent = order.email;
const commentSection = document.getElementById('view-comment-section');
const commentElement = document.getElementById('view-comment');
if (order.comment && order.comment.trim() !== '') {
commentSection.style.display = 'block';
commentElement.textContent = order.comment;
} else {
commentSection.style.display = 'none';
}
const itemsContainer = document.getElementById('view-items');
itemsContainer.innerHTML = '';
const orderItems = getOrderItems(order);
orderItems.forEach(function(item) {
const itemHTML = `
<div class="modal-order-item">
<span>${item.label}</span>
<span>${item.name} (${item.price}Р)</span>
</div>
`;
itemsContainer.insertAdjacentHTML('beforeend', itemHTML);
});
const total = calculateOrderPrice(order);
document.getElementById('view-total').textContent = total + 'Р';
modal.classList.remove('hidden');
}
function showEditModal(order) {
const modal = document.getElementById('edit-modal');
if (!modal) {
return;
}
currentOrderId = order.id;
const dateTime = formatDateTime(order.created_at);
document.getElementById('edit-created-date').textContent = dateTime;
document.getElementById('edit-name').value = order.full_name;
document.getElementById('edit-email').value = order.email;
document.getElementById('edit-phone').value = order.phone;
document.getElementById('edit-address').value = order.delivery_address;
document.getElementById('edit-time').value = order.delivery_time || '';
document.getElementById('edit-comment').value = order.comment || '';
const itemsContainer = document.getElementById('edit-items');
itemsContainer.innerHTML = '';
const orderItems = getOrderItems(order);
orderItems.forEach(function(item) {
const itemHTML = `
<div class="modal-order-item">
<span>${item.label}</span>
<span>${item.name} (${item.price}Р)</span>
</div>
`;
itemsContainer.insertAdjacentHTML('beforeend', itemHTML);
});
const total = calculateOrderPrice(order);
document.getElementById('edit-total').textContent = total + 'Р';
modal.classList.remove('hidden');
}
function showDeleteModal(order) {
const modal = document.getElementById('delete-modal');
if (!modal) {
return;
}
currentOrderId = order.id;
modal.classList.remove('hidden');
}
function getOrderItems(order) {
const items = [];
if (order.main_course_id) {
const dish = dishes.find(function(d) {
return d.id === order.main_course_id;
});
if (dish) {
items.push({
label: 'Основное блюдо',
name: dish.name,
price: dish.price
});
}
}
if (order.drink_id) {
const dish = dishes.find(function(d) {
return d.id === order.drink_id;
});
if (dish) {
items.push({
label: 'Напиток',
name: dish.name,
price: dish.price
});
}
}
if (order.dessert_id) {
const dish = dishes.find(function(d) {
return d.id === order.dessert_id;
});
if (dish) {
items.push({
label: 'Десерт',
name: dish.name,
price: dish.price
});
}
}
return items;
}
function closeModal(modalId) {
const modal = document.getElementById(modalId);
if (modal) {
modal.classList.add('hidden');
}
currentOrderId = null;
}
function setupModalHandlers() {
const closeButtons = document.querySelectorAll('.modal-close');
closeButtons.forEach(function(button) {
button.addEventListener('click', function() {
const modal = this.closest('.modal');
if (modal) {
modal.classList.add('hidden');
}
currentOrderId = null;
});
});
const viewOkButton = document.getElementById('view-modal-ok');
if (viewOkButton) {
viewOkButton.addEventListener('click', function() {
closeModal('view-modal');
});
}
const editCancelButton = document.getElementById('edit-modal-cancel');
if (editCancelButton) {
editCancelButton.addEventListener('click', function() {
closeModal('edit-modal');
});
}
const editSaveButton = document.getElementById('edit-modal-save');
if (editSaveButton) {
editSaveButton.addEventListener('click', function() {
saveOrderEdit();
});
}
const deleteCancelButton = document.getElementById('delete-modal-cancel');
if (deleteCancelButton) {
deleteCancelButton.addEventListener('click', function() {
closeModal('delete-modal');
});
}
const deleteConfirmButton =
document.getElementById('delete-modal-confirm');
if (deleteConfirmButton) {
deleteConfirmButton.addEventListener('click', function() {
deleteOrder();
});
}
const modals = document.querySelectorAll('.modal');
modals.forEach(function(modal) {
modal.addEventListener('click', function(event) {
if (event.target === modal) {
modal.classList.add('hidden');
currentOrderId = null;
}
});
});
}
function saveOrderEdit() {
const form = document.getElementById('edit-form');
if (!form.checkValidity()) {
form.reportValidity();
return;
}
const formData = new FormData(form);
const updateData = {
full_name: formData.get('full_name'),
email: formData.get('email'),
phone: formData.get('phone'),
delivery_address: formData.get('delivery_address'),
delivery_type: formData.get('delivery_time') ? 'by_time' : 'now',
delivery_time: formData.get('delivery_time') || '',
comment: formData.get('comment') || ''
};
const url = API_URL + '/orders/' + currentOrderId + '?api_key=' + API_KEY;
console.log('Отправка изменений заказа:', updateData);
fetch(url, {
method: 'PUT',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(updateData)
})
.then(function(response) {
console.log('Ответ от сервера:', response.status);
if (!response.ok) {
return response.json().then(function(errorData) {
throw new Error(errorData.error ||
'Ошибка при редактировании заказа');
});
}
return response.json();
})
.then(function(data) {
console.log('Заказ успешно изменен:', data);
closeModal('edit-modal');
showNotification('Заказ успешно изменён', 'success');
loadOrders();
})
.catch(function(error) {
console.error('Ошибка при редактировании заказа:', error);
showNotification('Не удалось изменить заказ: ' + error.message,
'error');
});
}
function deleteOrder() {
const url = API_URL + '/orders/' + currentOrderId + '?api_key=' + API_KEY;
console.log('Удаление заказа:', currentOrderId);
fetch(url, {
method: 'DELETE'
})
.then(function(response) {
console.log('Ответ от сервера:', response.status);
if (!response.ok) {
return response.json().then(function(errorData) {
throw new Error(errorData.error ||
'Ошибка при удалении заказа');
});
}
return response.json();
})
.then(function(data) {
console.log('Заказ успешно удален:', data);
closeModal('delete-modal');
showNotification('Заказ успешно удалён', 'success');
loadOrders();
})
.catch(function(error) {
console.error('Ошибка при удалении заказа:', error);
showNotification('Не удалось удалить заказ: ' + error.message,
'error');
});
}
function showNotification(message, type) {
const notification = document.getElementById('notification');
if (!notification) {
return;
}
notification.textContent = message;
notification.className = 'notification notification-' + type;
notification.classList.remove('hidden');
setTimeout(function() {
notification.classList.add('hidden');
}, 3000);
}
document.addEventListener('DOMContentLoaded', function() {
console.log('DOM загружен для страницы заказов');
setupModalHandlers();
loadOrders();
});

View file

@ -0,0 +1,236 @@
<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="robots" content="noindex">
<!-- Bootstrap CSS -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"
integrity="sha384-Vkoo8x4CGsO3+Hhxv8T/Q5PaXtkKtu6ug5TOeNV6gBiFeWPGFN9MuhOf23Q9Ifjh" crossorigin="anonymous">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="static/css/styles.css">
<link href="https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@1,400;1,700&display=swap" rel="stylesheet">
<title>Основы веб-технологий</title>
</head>
<body>
<nav class="navbar navbar-expand-lg fixed-top">
<div class="container">
<a class="navbar-brand" href="#">
<img class="img-fluid" src="static/img/logo_187_50.png">
</a>
<button class="navbar-toggler d-lg-none" type="button" data-toggle="collapse" data-target="#collapsibleNavId" aria-controls="collapsibleNavId"
aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon">
<i class="fa fa-bars"></i>
</span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="collapsibleNavId">
<ul class="navbar-nav mt-2 mt-lg-0">
<li class="nav-item active">
<a class="nav-link" href="#">О курсе <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Программа</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Технологии</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Контакты</a>
</li>
</ul>
</div>
</div>
</nav>
<div class="container-fluid banner">
<div class="container mt-5">
<div class="row">
<div class="col-lg-6 py-5 my-auto">
<div class="row">
<div class="col-lg-10 offset-lg-1">
<h1 class="pb-3">Основы веб-технологий</h1>
<p class="pb-3">
Курс знакомит слушателей с основами технологий HTML, CSS и JavaScript.
</p>
<div class="btn btn-lg btn-outline-secondary mb-3">Подробнее</div>
<div class="btn btn-lg btn-secondary mb-3">Написать нам</div>
</div>
</div>
</div>
<div class="col-lg-6 p-5 d-flex justify-content-center align-items-center">
<img class="img-fluid" src="static/img/web-dev.svg">
</div>
</div>
</div>
</div>
<section class="call-to-action py-5">
<div class="container text-center w-50">
<h2 class="pb-3">Заложите прочный фундамент будущей карьеры!</h2>
<div class="heading-line mx-auto"></div>
<p class="py-3">
Только знакомитесь с веб-технологиями? Интернет на сегодняшний день развивается феноменально быстро. Поэтому выбрав
веб-технологии в качестве своей специализации, можете быть уверены — достойное будущее Вам гарантировано.
</p>
<button class="btn btn-lg btn-primary">Записаться на курс</button>
</div>
</section>
<section class="program">
<div class="text-center py-5">
<h2 class="pb-3">Программа курса</h2>
<div class="heading-line mx-auto"></div>
</div>
<div class="container">
<div class="row">
<div class="col-md-4 text-center">
<i class="fa fa-code"></i>
<h4 class="py-3">Development tools</h4>
<p class="pb-3">Visual Studio Code, Emmet, Postman.</p>
</div>
<div class="col-md-4 text-center">
<i class="fa fa-html5"></i>
<h4 class="py-3">HTML</h4>
<p class="pb-3">Основы языка разметки HTML5.</p>
</div>
<div class="col-md-4 text-center">
<i class="fa fa-css3"></i>
<h4 class="py-3">CSS</h4>
<p class="pb-3">Основы каскадных таблиц стилей.</p>
</div>
<div class="col-md-4 text-center">
<i class="fa fa-code"></i>
<h4 class="py-3">HTTP</h4>
<p class="pb-3">Структура протокола HTTP.</p>
</div>
<div class="col-md-4 text-center">
<i class="fa fa-code"></i>
<h4 class="py-3">JavaScript</h4>
<p class="pb-3">Язык программирования JavaScript, формат JSON.</p>
</div>
<div class="col-md-4 text-center">
<i class="fa fa-code"></i>
<h4 class="py-3">Twitter Bootstrap</h4>
<p class="pb-3">Фреймворк для вёрстки Twitter Bootstrap.</p>
</div>
</div>
</div>
</section>
<section class="techologies container-fluid bg-light">
<div class="text-center py-5">
<h2 class="pb-3">Технологии</h2>
<div class="heading-line mx-auto"></div>
</div>
<div class="row">
<div class="col-md-3 p-0">
<img class="img-fluid" src="static/img/html.jpg">
</div>
<div class="col-md-3 p-0 d-flex align-items-center justify-content-center dark">
<div class="text-center">
<h4 class="py-3">HTML &amp; CSS</h4>
</div>
</div>
<div class="col-md-3 p-0">
<img class="img-fluid" src="static/img/js.jpg">
</div>
<div class="col-md-3 p-0 d-flex align-items-center justify-content-center dark">
<h4 class="py-3">JavaScript</h4>
</div>
<div class="col-md-3 p-0 d-flex align-items-center justify-content-center dark">
<h4 class="py-3">Twitter Bootstrap</h4>
</div>
<div class="col-md-3 p-0">
<img class="img-fluid" src="static/img/bootstrap.jpg">
</div>
<div class="col-md-3 p-0 d-flex align-items-center justify-content-center dark">
<h4 class="py-3">HTTP</h4>
</div>
<div class="col-md-3 p-0">
<img class="img-fluid" src="static/img/http.jpg">
</div>
</div>
</section>
<section class="contacts">
<div class="container">
<div class="text-center py-5">
<h2 class="pb-3">Контакты</h2>
<div class="heading-line mx-auto"></div>
</div>
<div class="row">
<div class="col-lg-6 mb-5">
<iframe
src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2243.6754304915576!2d37.70853691593169!3d55.781508730561015!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x46b54ad610abc8a5%3A0xc42703145fe53a8d!2sMoskovskiy%20Politekhnicheskiy%20Universitet!5e0!3m2!1sen!2sru!4v1589880984408!5m2!1sen!2sru"
width="100%" height="450" frameborder="0" style="border:0;" allowfullscreen="" aria-hidden="false"
tabindex="0"></iframe>
</div>
<div class="col-lg-6 mb-5">
<form>
<div class="form-group">
<label for="email">Электронная почта</label>
<input class="form-control" type="email" id="email" placeholder="Введите адрес электронной почты">
<small id="emailHelp" class="form-text text-muted">Мы не передём введённые данные третьим лицам.</small>
</div>
<div class="form-group">
<label for="name">Имя</label>
<input class="form-control" type="text" id="name" placeholder="Введите имя">
</div>
<div class="form-group">
<label for="message">Сообщение</label>
<textarea class="form-control" name="message" id="message" cols="30" rows="10" placeholder="Введите сообщение"></textarea>
</div>
<button type="submit" class="btn btn-lg btn-outline-secondary">Отправить</button>
</form>
</div>
</div>
</div>
</section>
<footer class="footer">
<div class="text-center py-5">
<a href="#">
<img src="static/img/logo_187_50_white.png">
</a>
</div>
<div class="container mb-3">
<div class="row">
<div class="col-lg-8 offset-lg-2 text-center">
<p>
Московский политехнический университет является крупнейшей образовательной организацией, готовящей квалифицированных
специалистов для производства.
</p>
</div>
</div>
</div>
<div class="copyright text-center py-3 border-top text-muted">
<p>Московский политех &copy; 2020</p>
<p>Vector disign by <a href="https://www.vecteezy.com/">Vecteezy</a></p>
</div>
</footer>
<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.4.1.slim.min.js"
integrity="sha384-J6qa4849blE2+poT4WnyKhv5vZF5SrPo0iEjwBvKU7imGFAV0wwj1yYfoRSJoZ+n"
crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"
integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo"
crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.4.1/js/bootstrap.min.js"
integrity="sha384-wfSDF2E50Y2D1uUdj0O3uMBJnjuUD4Ih7YwaYd1iqfktj0Uod8GCExl3Og8ifwB6"
crossorigin="anonymous"></script>
</body>
</html>

View file

@ -0,0 +1,95 @@
body {
font-family: "Montserrat", sans-serif;
}
h1, h2, h3 {
font-weight: 900;
}
h4, h5, h6 {
font-weight: 700;
}
.navbar {
background-color: white;
border-bottom: 1px solid #011627;
}
.nav-link {
color: #011627;
}
.banner {
background-color: #eaecf0;
}
.btn-outline-secondary {
background-color: transparent !important;
border-color: #011627;
color: #011627;
border-radius: 0;
}
.btn-outline-secondary:hover {
color: #011627 !important;
}
.btn-secondary {
border-radius: 0;
background-color: #011627;
}
.btn-secondary:hover {
color: #83F3ED;
background-color: #011627;
}
.btn-primary {
border-radius: 0;
background-color: #83F3ED;
color: #011627;
border-color: #83F3ED !important;
}
.btn-primary:hover {
background-color: #011627;
color: #83F3ED;
}
.call-to-action {
background-color: #011627;
color: white;
}
.heading-line {
height: 3px;
width: 200px;
background-color: #83F3ED;
}
.program i {
font-size: 2rem;
color: #83F3ED;
}
.techologies .img-fluid {
height: 300px;
object-fit: cover;
}
.dark {
background-color: #011627;
color: #83F3ED;
height: 300px;
}
.footer {
background-color: #011627;
color: white;
}
@media (max-width: 900px) {
.call-to-action .container {
width: 100% !important;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 509 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 138 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -0,0 +1,258 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="335.986px" height="235.19px" viewBox="0 0 335.986 235.19" enable-background="new 0 0 335.986 235.19"
xml:space="preserve">
<g>
<g>
<rect fill="#EAECF0" width="335.986" height="235.19"/>
</g>
</g>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M263.858,182.53V59.717
c0-1.817-1.592-3.302-3.539-3.302H78.123c-1.946,0-3.539,1.484-3.539,3.302V182.53H263.858z"/>
<g>
<g>
<circle fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" cx="80.825" cy="61.891" r="1.975"/>
<circle fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" cx="94.095" cy="61.891" r="1.977"/>
</g>
<circle fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" cx="87.46" cy="61.891" r="1.978"/>
</g>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M242.043,66.6H103.695
c-0.778,0-1.411-0.636-1.411-1.412v-1.694c0-0.776,0.633-1.412,1.411-1.412h138.348c0.776,0,1.413,0.636,1.413,1.412v1.694
C243.456,65.964,242.819,66.6,242.043,66.6z"/>
<rect x="78.729" y="72.01" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="180.983" height="106.975"/>
<rect x="175.95" y="96.681" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="76.813" height="31.102"/>
<rect x="132.328" y="96.677" fill="#84DBFF" width="34.374" height="31.102"/>
<g>
<rect x="132.328" y="137.48" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="34.374" height="31.104"/>
</g>
<g>
<rect x="86.601" y="96.677" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="37.378" height="1.427"/>
<rect x="86.601" y="102.178" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="37.378" height="1.427"/>
<rect x="86.601" y="107.678" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="37.378" height="1.427"/>
<rect x="86.601" y="113.178" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="37.378" height="1.428"/>
<rect x="86.601" y="124.181" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="37.378" height="1.427"/>
<rect x="86.601" y="129.682" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="37.378" height="1.427"/>
<rect x="86.601" y="135.182" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="37.378" height="1.428"/>
<rect x="86.601" y="146.184" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="37.378" height="1.427"/>
</g>
<g>
<rect x="175.95" y="135.337" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="76.397" height="1.428"/>
<rect x="175.95" y="140.839" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="76.397" height="1.427"/>
<rect x="175.95" y="151.84" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="76.397" height="1.427"/>
<rect x="175.95" y="157.34" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="76.397" height="1.428"/>
<rect x="175.95" y="168.342" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="76.397" height="1.427"/>
</g>
<rect x="213.496" y="78.265" fill="#EDEEF1" width="37.579" height="9.376"/>
<rect x="85.678" y="80.131" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="120.849" height="5.645"/>
<rect x="242.611" y="78.265" fill="#84DBFF" width="9.846" height="9.376"/>
<path fill="#FFFFFF" d="M249.938,83.256c0.745-0.748,0.745-1.958,0-2.705c-0.749-0.748-1.959-0.748-2.709,0
c-0.644,0.645-0.731,1.636-0.261,2.377l-1.834,1.836c-0.162,0.16-0.162,0.428,0,0.59l0,0c0.162,0.163,0.428,0.163,0.593,0
l1.83-1.834C248.3,83.989,249.29,83.902,249.938,83.256z M247.687,82.8c-0.494-0.495-0.494-1.298,0-1.792
c0.494-0.495,1.295-0.495,1.792,0c0.496,0.494,0.496,1.297,0,1.792C248.981,83.296,248.181,83.296,247.687,82.8z"/>
<rect x="185.989" y="204.798" fill="#34525C" width="15.758" height="2.061"/>
<rect x="158.999" y="209.427" fill="#34525C" width="50.626" height="2.061"/>
<g>
<rect x="135.566" y="194.098" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="9.713" height="9.711"/>
</g>
<g>
<rect x="276.949" y="123.143" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="9.713" height="9.711"/>
</g>
<g>
<rect x="190.935" y="33.9" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="9.713" height="9.711"/>
</g>
<path fill="#84DBFF" d="M155.145,151.843l-9.795-5.655c-0.199-0.116-0.428-0.176-0.655-0.176c-0.725,0-1.314,0.59-1.314,1.313v11.31
c0,0.726,0.59,1.313,1.314,1.313c0.228,0,0.456-0.06,0.655-0.177l9.795-5.654c0.409-0.238,0.655-0.664,0.656-1.137
C155.801,152.506,155.554,152.08,155.145,151.843z"/>
<g>
<rect x="25.071" y="108.357" fill="#34525C" width="1.291" height="3.869"/>
<rect x="27.019" y="113.299" fill="#34525C" width="3.869" height="1.29"/>
<rect x="25.071" y="115.66" fill="#34525C" width="1.291" height="3.869"/>
<rect x="20.546" y="113.299" fill="#34525C" width="3.87" height="1.29"/>
</g>
<g>
<rect x="213.712" y="191.465" fill="#34525C" width="1.29" height="3.869"/>
<rect x="215.658" y="196.406" fill="#34525C" width="3.869" height="1.29"/>
<rect x="213.712" y="198.768" fill="#34525C" width="1.29" height="3.869"/>
<rect x="209.186" y="196.406" fill="#34525C" width="3.87" height="1.29"/>
</g>
<rect x="29.301" y="81.933" fill="#34525C" width="28.443" height="2.061"/>
<rect x="29.301" y="91.288" fill="#34525C" width="20.718" height="2.061"/>
<rect x="20.078" y="86.562" fill="#34525C" width="44.994" height="2.061"/>
<rect x="22.48" y="205.786" fill="#34525C" width="28.444" height="2.061"/>
<rect x="22.48" y="215.142" fill="#34525C" width="20.719" height="2.061"/>
<rect x="13.257" y="210.415" fill="#34525C" width="44.995" height="2.061"/>
<g>
<rect x="311.243" y="122.834" fill="#34525C" width="1.29" height="3.869"/>
<rect x="313.189" y="127.774" fill="#34525C" width="3.869" height="1.29"/>
<rect x="311.243" y="130.137" fill="#34525C" width="1.29" height="3.869"/>
<rect x="306.717" y="127.774" fill="#34525C" width="3.87" height="1.29"/>
</g>
<g>
<rect x="177.72" y="18.91" fill="#34525C" width="1.29" height="3.869"/>
<rect x="179.666" y="23.851" fill="#34525C" width="3.869" height="1.29"/>
<rect x="177.72" y="26.213" fill="#34525C" width="1.29" height="3.869"/>
<rect x="173.193" y="23.851" fill="#34525C" width="3.869" height="1.29"/>
</g>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M67.533,42.965
c0,13.767-11.189,24.956-24.955,24.956c-13.769,0-24.96-11.189-24.96-24.956c0-13.768,11.191-24.956,24.96-24.956
c4.496,0,8.743,1.219,12.413,3.317c-4.894,8.656-12.413,21.639-12.413,21.639l24.52-4.545
C67.403,39.902,67.533,41.437,67.533,42.965z"/>
<path fill="#84DBFF" d="M74.043,33.808l-24.521,4.545c0,0,7.521-13.025,12.414-21.635C68.183,20.259,72.73,26.465,74.043,33.808z"/>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M318.2,220.982v-69.191
c0-1.817-1.591-3.302-3.538-3.302H231.65c-1.946,0-3.539,1.484-3.539,3.302v69.191H318.2z"/>
<g>
<g>
<circle fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" cx="234.352" cy="153.965" r="1.975"/>
<circle fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" cx="247.622" cy="153.965" r="1.977"/>
</g>
<circle fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" cx="240.987" cy="153.965" r="1.978"/>
</g>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M306.386,158.674h-49.163
c-0.777,0-1.412-0.636-1.412-1.412v-1.694c0-0.776,0.635-1.412,1.412-1.412h49.163c0.777,0,1.414,0.636,1.414,1.412v1.694
C307.8,158.038,307.163,158.674,306.386,158.674z"/>
<rect x="232.257" y="164.084" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="81.799" height="53.353"/>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M125.845,195.015v-69.191
c0-1.817-1.592-3.302-3.539-3.302H39.293c-1.945,0-3.538,1.484-3.538,3.302v69.191H125.845z"/>
<g>
<g>
<circle fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" cx="41.996" cy="127.997" r="1.974"/>
<circle fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" cx="55.266" cy="127.997" r="1.977"/>
</g>
<circle fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" cx="48.631" cy="127.997" r="1.978"/>
</g>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M114.03,132.706H64.867
c-0.778,0-1.412-0.636-1.412-1.412V129.6c0-0.776,0.634-1.412,1.412-1.412h49.163c0.776,0,1.413,0.636,1.413,1.412v1.694
C115.443,132.07,114.807,132.706,114.03,132.706z"/>
<rect x="39.9" y="138.116" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="81.799" height="53.353"/>
<g>
<path fill="#84DBFF" d="M297.452,207.153h-8.418c-0.206,0-0.358-0.153-0.358-0.358v-30.511c0-0.205,0.152-0.359,0.358-0.359h8.418
c0.204,0,0.357,0.154,0.357,0.359v30.511C297.81,206.982,297.656,207.153,297.452,207.153z"/>
<path fill="#84DBFF" d="M284.067,207.153h-8.419c-0.205,0-0.358-0.153-0.358-0.358v-21.548c0-0.204,0.153-0.357,0.358-0.357h8.419
c0.205,0,0.357,0.153,0.357,0.357v21.548C284.407,206.982,284.253,207.153,284.067,207.153z"/>
<path fill="#84DBFF" d="M270.664,207.153h-8.42c-0.204,0-0.356-0.153-0.356-0.358v-32.458c0-0.205,0.152-0.358,0.356-0.358h8.42
c0.204,0,0.357,0.153,0.357,0.358v32.458C271.021,206.982,270.868,207.153,270.664,207.153z"/>
<path fill="#84DBFF" d="M257.276,207.153h-8.416c-0.205,0-0.359-0.153-0.359-0.358v-15.999c0-0.205,0.154-0.357,0.359-0.357h8.416
c0.205,0,0.357,0.152,0.357,0.357v15.999C257.634,206.982,257.465,207.153,257.276,207.153z"/>
</g>
<rect x="242.559" y="207.526" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="61.194" height="3.968"/>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M60.032,155.895c0,0.168-0.014,0.336-0.028,0.503
c-1.301,0.504-2.839,0.798-4.477,0.798c-1.635,0-3.174-0.294-4.475-0.798c-0.014-0.167-0.029-0.335-0.029-0.503
c0-2.616,2.016-4.728,4.504-4.728C58.018,151.167,60.032,153.278,60.032,155.895z"/>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M57.375,147.88c0,1.272-0.826,2.825-1.848,2.825
c-1.02,0-1.846-1.553-1.846-2.825c0-1.273,0.826-1.776,1.846-1.776C56.549,146.104,57.375,146.606,57.375,147.88z"/>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M60.032,169.672c0,0.168-0.014,0.335-0.028,0.503
c-1.301,0.504-2.839,0.798-4.477,0.798c-1.635,0-3.174-0.294-4.475-0.798c-0.014-0.168-0.029-0.335-0.029-0.503
c0-2.616,2.016-4.728,4.504-4.728C58.018,164.944,60.032,167.056,60.032,169.672z"/>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M57.375,161.657c0,1.272-0.826,2.825-1.848,2.825
c-1.02,0-1.846-1.553-1.846-2.825c0-1.273,0.826-1.776,1.846-1.776C56.549,159.881,57.375,160.384,57.375,161.657z"/>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M60.032,183.449c0,0.167-0.014,0.335-0.028,0.503
c-1.301,0.504-2.839,0.798-4.477,0.798c-1.635,0-3.174-0.294-4.475-0.798c-0.014-0.168-0.029-0.336-0.029-0.503
c0-2.616,2.016-4.729,4.504-4.729C58.018,178.721,60.032,180.833,60.032,183.449z"/>
<path fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" d="M57.375,175.435c0,1.272-0.826,2.825-1.848,2.825
c-1.02,0-1.846-1.553-1.846-2.825c0-1.273,0.826-1.776,1.846-1.776C56.549,173.658,57.375,174.161,57.375,175.435z"/>
<rect x="63.473" y="147.152" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="29.947" height="8.979"/>
<rect x="64.97" y="148.649" fill="#84DBFF" width="21.456" height="5.972"/>
<rect x="63.473" y="160.93" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="29.947" height="8.979"/>
<rect x="64.97" y="162.426" fill="#84DBFF" width="13.204" height="5.973"/>
<rect x="63.473" y="174.707" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="29.947" height="8.979"/>
<rect x="64.97" y="176.203" fill="#84DBFF" width="17.009" height="5.973"/>
<g>
<g>
<polygon fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" points="280.581,23.927 290.509,24.71
295.438,18.087 302.198,20.458 301.539,29.101 307.919,35.934 317.438,34.079 319.842,41.836 311.042,47.13 311.872,56.425
318.749,62.126 315.956,68.486 309.43,66.071 300.237,72.755 301.65,82.764 294.271,85.321 289.666,77.497 280.159,77.789
275.234,85.448 267.93,82.239 269.295,73.022 262.492,65.118 254.202,67.728 251.253,60.186 258.759,56.323 259.251,46.045
252.081,39.997 255.04,33.255 264.145,35.921 270.925,28.614 268.91,19.471 276.021,17.007 "/>
<path fill="#84DBFF" d="M272.043,45.773c-3.309,7.352-0.029,16.019,7.33,19.332c7.367,3.316,16.029,0.026,19.34-7.322
c3.316-7.366,0.036-16.031-7.33-19.349C284.022,35.121,275.36,38.409,272.043,45.773z"/>
</g>
</g>
<g>
<g>
<polygon fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" points="223.943,19.476 228.381,17.479
228.935,13.483 232.389,12.913 234.137,16.771 238.474,18.197 242.115,15.169 244.967,17.927 242.441,22.26 244.979,26.047
249.265,26.873 249.561,30.255 246.198,30.752 243.83,35.774 246.784,39.729 244.226,42.557 240.416,40.288 236.412,42.646
236.102,47.081 232.218,47.423 230.639,43.154 225.87,41.366 222.932,44.431 219.898,41.894 222.206,38.477 220.003,33.959
215.512,33.051 215.195,29.469 219.721,28.474 220.909,23.75 217.899,20.307 220.367,17.582 "/>
<path fill="#84DBFF" d="M225.419,30.839c0.31,3.925,3.749,6.867,7.679,6.559c3.934-0.311,6.872-3.754,6.564-7.679
c-0.31-3.934-3.75-6.875-7.684-6.565C228.049,23.462,225.109,26.904,225.419,30.839z"/>
</g>
</g>
<g>
<g>
<g>
<g>
<g>
<g>
<rect x="92.973" y="16.838" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="65.744" height="8.657"/>
</g>
</g>
</g>
</g>
</g>
</g>
<g>
<g>
<g>
<g>
<g>
<rect x="108.618" y="18.787" fill="#84DBFF" width="47.907" height="4.762"/>
</g>
</g>
</g>
</g>
</g>
<g>
<g>
<g>
<g>
<g>
<g>
<rect x="92.973" y="32.815" fill="#FFFFFF" stroke="#34525C" stroke-width="2" stroke-miterlimit="10" width="65.744" height="8.657"/>
</g>
</g>
</g>
</g>
</g>
</g>
<g>
<g>
<g>
<g>
<g>
<rect x="125.844" y="34.764" fill="#84DBFF" width="30.681" height="4.762"/>
</g>
</g>
</g>
</g>
</g>
<path fill="#84DBFF" d="M83.343,215.663c0,2.938,2.678,5.319,5.98,5.319h29.559c2.317,0,4.197-1.675,4.197-3.736
c0-1.945-1.668-3.523-3.799-3.699c0.01-0.148,0.023-0.283,0.023-0.433c0-4.347-3.961-7.862-8.846-7.862
c-0.445,0-0.883,0.038-1.312,0.098c-2-2.311-5.127-3.795-8.647-3.795c-2.845,0-5.426,0.967-7.366,2.55
c-1.947,1.582-3.239,3.78-3.501,6.253C86.328,210.357,83.343,212.726,83.343,215.663z"/>
<path fill="#84DBFF" d="M319.875,108.323c0,2.938-2.678,5.319-5.98,5.319h-29.559c-2.317,0-4.197-1.675-4.197-3.736
c0-1.945,1.668-3.523,3.799-3.699c-0.01-0.148-0.023-0.283-0.023-0.433c0-4.347,3.961-7.862,8.846-7.862
c0.444,0,0.883,0.038,1.312,0.098c2-2.311,5.127-3.795,8.647-3.795c2.845,0,5.425,0.967,7.365,2.55
c1.947,1.582,3.24,3.78,3.502,6.253C316.89,103.018,319.875,105.386,319.875,108.323z"/>
</svg>

After

Width:  |  Height:  |  Size: 16 KiB

View file

@ -0,0 +1,15 @@
Bootstrap
1. Что такое Bootstrap? Для каких целей он используется?
2. Какие основные компоненты входят в состав фреймворка Bootstrap?
3. В чем заключается принцип адаптивного дизайна в Bootstrap? Как реализуется мобильная версия сайта?
4. Опишите структуру HTML-документа, использующего Bootstrap. Какие обязательные элементы должны присутствовать?
5. Что такое сетка Bootstrap? Сколько колонок она содержит и какие классы используются для их создания?
6. Какие возможности предоставляет Bootstrap для стилизации текста? Приведите примеры классов.
7. Расскажите о компонентах навигации в Bootstrap. Как создать простую панель навигации?
8. Как реализовать модальные окна в Bootstrap? Какие параметры необходимо указать для настройки модального окна?
9. Как добавить формы в проект с использованием Bootstrap? Какие классы применяются для оформления полей ввода?
10. Какие компоненты Bootstrap вы использовали в своей работе? Почему именно они были выбраны?
11. Каким образом можно изменить стандартные стили компонентов Bootstrap? Можно ли использовать свои собственные CSS-правила поверх Bootstrap?
12. Как реализуются всплывающие подсказки (tooltips) и выпадающие списки (dropdowns) в Bootstrap?
13. Как работает система медиа-запросов в Bootstrap? Приведите пример использования различных размеров экрана.

View file

@ -0,0 +1,27 @@
# Лабораторная работа № 10. Вёрстка адаптивного лендинга для компании доставки еды
---
При помощи фреймворка Bootstrap 5 создайте адаптивный лендинг (landing page) для компании, доставляющей бизнес-ланчи.
## Порядок выполнения
Добавьте к вашему проекту страницу лендинга. На этой странице должны присутствовать следующие элементы:
- Блок с названием компании, её кратким описанием и картинкой для привлечения внимания (hero section).
- Блок с перечислением основных особенностей/преимуществ компании.
- Блок с призывом к действию (CTA, call to action) и ссылкой для перехода на страницу "Собрать ланч".
- Блок с формой для обратной связи и контактами.
Дизайн страницы формируется на усмотрение студента с учётом обязательных блоков, перечисленных выше. Идеи для оформления лендинга можно посмотреть [здесь](https://www.landingfolio.com/).
- Вёрстка лендинга должна быть выполнена с использованием фреймворка Bootstrap 5.
- Допускается определение кастомных стилей в случае, если требуемого результата нельзя добиться средствами Bootstrap.
- Обязательно использование системы сеток Bootstrap (Bootstrap Grid System).
- Страница должна корректно отображаться на мобильных устройствах (смартфонах, планшетах).
- Вёрстка должна быть выполнена студентом самостоятельно (использование готовых шаблонов лендингов не допускается).
### Материалы для изучения
[Документация Bootstrap](https://getbootstrap.com/)
[Bootstrap Grid System](https://www.tutorialrepublic.com/twitter-bootstrap-tutorial/bootstrap-grid-system.php)
[Отзывчивый дизайн [MDN]](https://developer.mozilla.org/ru/docs/Learn/CSS/CSS_layout/Responsive_Design)
[Что такое лендинг и чем он отличается от сайта?](https://www.insales.ru/page/landing)

298
labs/lab-10/styles/menu.css Normal file
View file

@ -0,0 +1,298 @@
.combo-section {
margin-bottom: 50px;
}
.combo-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 20px;
margin-bottom: 20px;
}
.combo-card {
display: flex;
flex-direction: row;
justify-content: center;
align-items: center;
gap: 20px;
background-color: white;
padding: 20px;
border-radius: 15px;
}
.combo-dessert {
flex-direction: column;
gap: 10px;
}
.combo-item {
display: flex;
flex-direction: column;
align-items: center;
}
.combo-icon {
font-size: 50px;
display: block;
margin: 0 0 8px 0;
transition: transform 0.3s;
}
.combo-item:hover .combo-icon {
transform: translateY(-5px) scale(1.1);
}
.combo-item p {
margin: 0;
font-size: 14px;
text-align: center;
}
.combo-note-inline {
font-size: 12px;
text-align: center;
color: #666;
margin: 0;
font-style: italic;
}
.combo-note {
font-size: 14px;
color: #666;
font-style: italic;
text-align: center;
margin: 10px 0 0 0;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
flex-wrap: wrap;
gap: 15px;
}
.section-header h2 {
margin: 0;
}
.filter-buttons {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.filter-btn {
background-color: white;
border: 2px solid #2d5016;
color: #2d5016;
padding: 10px 20px;
border-radius: 10px;
cursor: pointer;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
font-weight: 600;
transition: all 0.3s;
}
.filter-btn:hover {
background-color: #f1eee9;
}
.filter-btn.active {
background-color: #2d5016;
color: white;
}
.dishes-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 10px;
}
.dish-card {
display: flex;
flex-direction: column;
padding: 30px 40px;
border-radius: 35px;
cursor: pointer;
filter: drop-shadow(17px 19px 24px rgba(0, 0, 0, 0.13));
background-color: white;
}
.dish-card.selected {
border: 3px solid #2d5016;
background-color: #f0f7ec;
}
.dish-card.selected button {
background-color: #2d5016;
color: white;
}
.dish-card:hover {
border: 2px solid tomato;
}
.dish-card:hover button {
background-color: tomato;
color: white;
}
.dish-card img {
width: 100%;
height: auto;
aspect-ratio: 12 / 9;
object-fit: cover;
border-radius: 35px;
margin: 0 0 15px 0;
}
.dish-price {
font-size: 20px;
font-weight: 600;
margin: 0 0 10px 0;
}
.dish-name {
font-size: 18px;
font-weight: 600;
margin: 0 0 10px 0;
}
.dish-weight {
color: #888;
margin: 0 0 10px 0;
margin-top: auto;
}
.dish-card button {
background-color: #f1eee9;
border: none;
padding: 10px 30px;
border-radius: 10px;
cursor: pointer;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
margin-top: 10px;
}
.remove-dish-btn {
background-color: tomato;
color: white;
}
.remove-dish-btn:hover {
background-color: #ff4500;
}
.order-panel {
position: sticky;
bottom: 20px;
background-color: white;
padding: 20px 40px;
margin: 0 auto;
max-width: 1200px;
border-radius: 15px;
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.2);
z-index: 100;
}
.order-panel.hidden {
display: none;
}
.order-panel-content {
display: flex;
justify-content: space-between;
align-items: center;
gap: 20px;
}
.order-panel-info {
display: flex;
align-items: center;
gap: 15px;
}
.order-panel-label {
font-size: 18px;
font-weight: 600;
margin: 0;
}
.order-panel-price {
font-size: 24px;
font-weight: 700;
color: #2d5016;
margin: 0;
}
.order-panel-button {
background-color: #2d5016;
color: white;
text-decoration: none;
padding: 15px 40px;
border-radius: 10px;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
font-weight: 600;
transition: all 0.3s;
}
.order-panel-button:hover {
background-color: #3d6020;
}
.order-panel-button.disabled {
background-color: #ccc;
color: #888;
cursor: not-allowed;
pointer-events: none;
}
@media (max-width: 800px) {
.combo-grid {
grid-template-columns: 1fr 1fr;
}
.dishes-grid {
grid-template-columns: 1fr 1fr;
}
nav {
justify-content: space-between;
}
.about-company img {
width: 500px;
}
.order-panel-content {
flex-direction: column;
}
}
@media (max-width: 600px) {
.combo-grid {
grid-template-columns: 1fr;
}
.dishes-grid {
grid-template-columns: 1fr;
}
.order-panel {
padding: 15px 20px;
}
.order-panel-content {
flex-direction: column;
align-items: stretch;
}
.order-panel-button {
text-align: center;
}
}

View file

@ -0,0 +1,266 @@
.empty-order-message {
text-align: center;
padding: 40px 20px;
}
.empty-order-message.hidden {
display: none;
}
.empty-order-message p {
font-size: 18px;
color: #666;
}
.empty-order-message a {
color: #2d5016;
text-decoration: underline;
}
.order-form {
margin: 0 auto;
padding: 40px 60px;
max-width: 1200px;
background-color: white;
}
.order-form h2 {
text-align: center;
margin-bottom: 30px;
}
#order-summary {
background-color: #f9f9f9;
padding: 30px;
border-radius: 10px;
margin-bottom: 20px;
}
.empty-order {
text-align: center;
color: #888;
font-size: 18px;
}
.order-category {
margin-bottom: 25px;
}
.order-category h3 {
font-size: 20px;
color: #2d5016;
margin: 0 0 10px 0;
}
.order-item {
display: flex;
justify-content: space-between;
padding: 10px 0;
font-size: 16px;
}
.order-item span:first-child {
font-weight: 600;
word-wrap: break-word;
overflow-wrap: break-word;
max-width: 70%;
}
.order-item span:last-child {
color: #2d5016;
font-weight: 600;
white-space: nowrap;
}
.not-selected {
color: #888;
font-style: italic;
margin: 5px 0;
}
.order-total {
margin-top: 30px;
padding-top: 20px;
border-top: 2px solid #2d5016;
}
.order-total h3 {
font-size: 22px;
color: #2d5016;
margin: 0 0 10px 0;
}
.total-price {
font-size: 24px;
font-weight: 700;
color: #2d5016;
margin: 0;
}
.form-container {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 40px;
}
.order-section {
display: flex;
flex-direction: column;
}
.order-section h3 {
font-size: 22px;
color: #2d5016;
margin: 0 0 20px 0;
}
.customer-section {
display: flex;
flex-direction: column;
}
.customer-section h3 {
font-size: 22px;
color: #2d5016;
margin: 0 0 20px 0;
}
.order-form label {
font-size: 16px;
color: #333;
margin: 0 0 8px 0;
font-weight: 600;
}
.order-form select,
.order-form input[type="text"],
.order-form input[type="email"],
.order-form input[type="tel"],
.order-form input[type="time"],
.order-form textarea {
width: 100%;
padding: 12px 15px;
margin: 0 0 20px 0;
border: 1px solid #ddd;
border-radius: 8px;
font-family: 'Montserrat', sans-serif;
font-size: 15px;
}
.order-form select {
cursor: pointer;
background-color: white;
}
.order-form textarea {
height: 100px;
resize: vertical;
}
.checkbox-group {
display: flex;
align-items: center;
margin: 0 0 20px 0;
}
.checkbox-group input[type="checkbox"] {
width: 18px;
height: 18px;
margin: 0 10px 0 0;
cursor: pointer;
}
.checkbox-group label {
margin: 0;
font-weight: 400;
cursor: pointer;
}
.radio-group {
margin: 0 0 20px 0;
}
.radio-group > label {
display: block;
margin-bottom: 10px;
}
.radio-group > div {
display: flex;
align-items: center;
margin: 0 0 8px 0;
}
.radio-group input[type="radio"] {
width: 18px;
height: 18px;
margin: 0 10px 0 0;
cursor: pointer;
}
.radio-group input[type="radio"] + label {
margin: 0;
font-weight: 400;
cursor: pointer;
}
.form-buttons {
display: flex;
gap: 15px;
margin-top: 10px;
}
.form-buttons button {
flex: 1;
padding: 15px 30px;
border: none;
border-radius: 10px;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: background-color 0.3s;
}
.form-buttons button[type="button"] {
background-color: #f1eee9;
color: #333;
}
.form-buttons button[type="button"]:hover {
background-color: #e0ddd8;
}
.form-buttons button[type="submit"] {
background-color: #2d5016;
color: white;
}
.form-buttons button[type="submit"]:hover {
background-color: #3d6020;
}
.form-hint {
display: block;
font-size: 13px;
color: #666;
margin: -15px 0 20px 0;
line-height: 1.4;
}
@media (max-width: 800px) {
.form-container {
grid-template-columns: 1fr;
gap: 20px;
}
}
@media (max-width: 600px) {
.form-container {
grid-template-columns: 1fr;
}
.form-buttons {
flex-direction: column;
}
}

View file

@ -0,0 +1,414 @@
.orders-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.empty-orders-message {
text-align: center;
padding: 60px 20px;
}
.empty-orders-message.hidden {
display: none;
}
.empty-orders-message p {
font-size: 18px;
color: #666;
line-height: 1.6;
}
.empty-orders-message a {
color: #2d5016;
text-decoration: underline;
font-weight: 600;
}
.order-card {
background-color: white;
border-radius: 15px;
padding: 25px 30px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
transition: box-shadow 0.3s;
}
.order-card:hover {
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
}
.order-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
padding-bottom: 15px;
border-bottom: 2px solid #f0f0f0;
}
.order-number {
font-size: 20px;
font-weight: 700;
color: #2d5016;
}
.order-date {
font-size: 16px;
color: #666;
}
.order-card-body {
margin-bottom: 20px;
}
.order-info p {
margin: 10px 0;
font-size: 16px;
line-height: 1.6;
}
.order-info strong {
color: #2d5016;
}
.order-card-footer {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.order-btn {
padding: 10px 20px;
border: none;
border-radius: 8px;
font-family: 'Montserrat', sans-serif;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
}
.order-btn-view {
background-color: #2d5016;
color: white;
}
.order-btn-view:hover {
background-color: #3d6020;
}
.order-btn-edit {
background-color: #f1eee9;
color: #333;
}
.order-btn-edit:hover {
background-color: #e0ddd8;
}
.order-btn-delete {
background-color: tomato;
color: white;
}
.order-btn-delete:hover {
background-color: #ff4500;
}
.modal {
display: flex;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
justify-content: center;
align-items: center;
}
.modal.hidden {
display: none;
}
.modal-content {
background-color: white;
border-radius: 15px;
max-width: 600px;
width: 90%;
max-height: 90vh;
overflow-y: auto;
box-shadow: 0 5px 30px rgba(0, 0, 0, 0.3);
}
.modal-content-small {
max-width: 400px;
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20px 30px;
border-bottom: 1px solid #e0e0e0;
}
.modal-header h3 {
margin: 0;
font-size: 20px;
font-weight: 700;
color: #000;
}
.modal-close {
font-size: 28px;
font-weight: 400;
color: #999;
cursor: pointer;
line-height: 1;
transition: color 0.3s;
}
.modal-close:hover {
color: #333;
}
.modal-body {
padding: 20px 30px;
}
.modal-section {
margin-bottom: 25px;
}
.modal-section:last-child {
margin-bottom: 0;
}
.modal-section-title {
font-size: 16px;
font-weight: 700;
color: #000;
margin: 0 0 15px 0;
}
.modal-label {
font-size: 14px;
color: #666;
display: block;
margin-bottom: 5px;
}
.modal-value {
font-size: 16px;
color: #000;
display: block;
}
.modal-info-grid {
display: grid;
grid-template-columns: 1fr;
gap: 12px;
}
.modal-info-item {
display: flex;
flex-direction: column;
gap: 5px;
}
.modal-order-items {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 15px;
}
.modal-order-item {
display: flex;
justify-content: space-between;
font-size: 16px;
color: #000;
}
.modal-order-item span:first-child {
color: #666;
}
.modal-total {
display: flex;
justify-content: space-between;
font-size: 16px;
font-weight: 700;
color: #000;
padding-top: 15px;
border-top: 1px solid #e0e0e0;
}
.modal-delete-text {
font-size: 16px;
color: #000;
text-align: center;
margin: 0;
}
.modal-body form label {
display: block;
font-size: 14px;
color: #000;
margin: 0 0 8px 0;
font-weight: 400;
}
.modal-body form input[type="text"],
.modal-body form input[type="email"],
.modal-body form input[type="tel"],
.modal-body form input[type="time"],
.modal-body form textarea {
width: 100%;
padding: 10px 12px;
margin: 0 0 15px 0;
border: 1px solid #ddd;
border-radius: 5px;
font-family: 'Montserrat', sans-serif;
font-size: 15px;
}
.modal-body form textarea {
resize: vertical;
}
.modal-footer {
display: flex;
justify-content: flex-end;
gap: 10px;
padding: 20px 30px;
border-top: 1px solid #e0e0e0;
}
.modal-btn {
padding: 10px 30px;
border: none;
border-radius: 5px;
font-family: 'Montserrat', sans-serif;
font-size: 14px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s;
min-width: 100px;
}
.modal-btn-ok {
background-color: #e8e8e8;
color: #000;
}
.modal-btn-ok:hover {
background-color: #d0d0d0;
}
.modal-btn-cancel {
background-color: #e8e8e8;
color: #000;
}
.modal-btn-cancel:hover {
background-color: #d0d0d0;
}
.modal-btn-save {
background-color: #4caf50;
color: white;
}
.modal-btn-save:hover {
background-color: #45a049;
}
.modal-btn-delete {
background-color: #c62828;
color: white;
}
.modal-btn-delete:hover {
background-color: #b71c1c;
}
.notification {
position: fixed;
top: 20px;
right: 20px;
padding: 15px 25px;
border-radius: 10px;
font-family: 'Montserrat', sans-serif;
font-size: 16px;
font-weight: 600;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
z-index: 2000;
animation: slideIn 0.3s ease-out;
}
.notification.hidden {
display: none;
}
.notification-success {
background-color: #4caf50;
color: white;
}
.notification-error {
background-color: #c62828;
color: white;
}
@keyframes slideIn {
from {
transform: translateX(400px);
opacity: 0;
}
to {
transform: translateX(0);
opacity: 1;
}
}
@media (max-width: 800px) {
.modal-content {
width: 95%;
}
.order-card-footer {
flex-direction: column;
}
.order-btn {
width: 100%;
}
}
@media (max-width: 600px) {
.modal-header {
padding: 15px 20px;
}
.modal-body {
padding: 15px 20px;
}
.modal-footer {
padding: 15px 20px;
flex-direction: column;
}
.modal-btn {
width: 100%;
}
.notification {
right: 10px;
left: 10px;
text-align: center;
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,89 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="https://deev.space/media/favicon.ico" type="image/x-icon">
<title>О нас - ЭкоЛанч</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../styles/styles.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top shadow-sm">
<div class="container">
<a class="navbar-brand" href="../index.html">
<span class="brand-icon">🌿</span>
<span class="brand-eco">Эко</span><span class="brand-lunch">Ланч</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="../index.html">Главная</a>
</li>
<li class="nav-item">
<a class="nav-link" href="menu.html">Собрать ланч</a>
</li>
<li class="nav-item">
<a class="nav-link" href="order.html">Оформить заказ</a>
</li>
<li class="nav-item">
<a class="nav-link" href="orders.html">Мои заказы</a>
</li>
<li class="nav-item">
<a class="nav-link" href="delivery.html">Доставка</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="about.html">О нас</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#contacts">Контакты</a>
</li>
</ul>
</div>
</div>
</nav>
<main style="padding-top: 80px;">
<div class="container py-5">
<section class="bg-light p-5 rounded">
<h2 class="text-center mb-4">О нашей компании</h2>
<div class="row">
<div class="col-lg-8 mx-auto">
<p>ЭкоЛанч - это команда профессионалов, которые заботятся о вашем здоровье и комфорте. Мы готовим вкусные и полезные блюда из свежих продуктов и доставляем их прямо в ваш офис.</p>
<p>Наша миссия - сделать здоровое питание доступным и удобным для каждого занятого человека в Москве.</p>
</div>
</div>
</section>
</div>
</main>
<footer id="contacts" class="footer py-4">
<div class="container">
<div class="footer-content">
<div class="footer-title">Контактная информация</div>
<div class="footer-info">
<div class="footer-row">
<span>Телефон: <a href="tel:+79993737737">+7 (999) 373-77-37</a></span>
<span>Email: <a href="mailto:egor@deev.space">egor@deev.space</a></span>
</div>
<div class="footer-row">
<span>Адрес: г. Москва, ул. Михалковская, д. 7</span>
<span>Режим работы: Пн-Пт с 7:00 до 23:00</span>
</div>
<div class="footer-row footer-copyright">
© 2024 ЭкоЛанч. Все права защищены.
</div>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View file

@ -0,0 +1,89 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="https://deev.space/media/favicon.ico" type="image/x-icon">
<title>Доставка - ЭкоЛанч</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../styles/styles.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top shadow-sm">
<div class="container">
<a class="navbar-brand" href="../index.html">
<span class="brand-icon">🌿</span>
<span class="brand-eco">Эко</span><span class="brand-lunch">Ланч</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="../index.html">Главная</a>
</li>
<li class="nav-item">
<a class="nav-link" href="menu.html">Собрать ланч</a>
</li>
<li class="nav-item">
<a class="nav-link" href="order.html">Оформить заказ</a>
</li>
<li class="nav-item">
<a class="nav-link" href="orders.html">Мои заказы</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="delivery.html">Доставка</a>
</li>
<li class="nav-item">
<a class="nav-link" href="about.html">О нас</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#contacts">Контакты</a>
</li>
</ul>
</div>
</div>
</nav>
<main style="padding-top: 80px;">
<div class="container py-5">
<section class="bg-light p-5 rounded">
<h2 class="text-center mb-4">Условия доставки</h2>
<div class="row">
<div class="col-lg-8 mx-auto">
<p>Мы осуществляем доставку здоровых бизнес-ланчей по всей Москве. Доставка производится с понедельника по пятницу с 7:00 до 23:00.</p>
<p>Доставка по всей Москве в пределах МКАД осуществляется бесплатно!</p>
</div>
</div>
</section>
</div>
</main>
<footer id="contacts" class="footer py-4">
<div class="container">
<div class="footer-content">
<div class="footer-title">Контактная информация</div>
<div class="footer-info">
<div class="footer-row">
<span>Телефон: <a href="tel:+79993737737">+7 (999) 373-77-37</a></span>
<span>Email: <a href="mailto:egor@deev.space">egor@deev.space</a></span>
</div>
<div class="footer-row">
<span>Адрес: г. Москва, ул. Михалковская, д. 7</span>
<span>Режим работы: Пн-Пт с 7:00 до 23:00</span>
</div>
<div class="footer-row footer-copyright">
© 2024 ЭкоЛанч. Все права защищены.
</div>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

View file

@ -0,0 +1,240 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="https://deev.space/media/favicon.ico" type="image/x-icon">
<title>Собрать ланч - ЭкоЛанч</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../styles/styles.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top shadow-sm">
<div class="container">
<a class="navbar-brand" href="../index.html">
<span class="brand-icon">🌿</span>
<span class="brand-eco">Эко</span><span class="brand-lunch">Ланч</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="../index.html">Главная</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="menu.html">Собрать ланч</a>
</li>
<li class="nav-item">
<a class="nav-link" href="order.html">Оформить заказ</a>
</li>
<li class="nav-item">
<a class="nav-link" href="orders.html">Мои заказы</a>
</li>
<li class="nav-item">
<a class="nav-link" href="delivery.html">Доставка</a>
</li>
<li class="nav-item">
<a class="nav-link" href="about.html">О нас</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#contacts">Контакты</a>
</li>
</ul>
</div>
</div>
</nav>
<main style="padding-top: 80px;">
<div class="container py-5">
<section class="combo-section">
<h2 class="text-center mb-4">Доступные для заказа варианты ланча</h2>
<div class="combo-grid">
<div class="combo-card">
<div class="combo-item">
<span class="combo-icon">🍲</span>
<p>Суп</p>
</div>
<div class="combo-item">
<span class="combo-icon">🍽️</span>
<p>Главное блюдо</p>
</div>
<div class="combo-item">
<span class="combo-icon">🥗</span>
<p>Салат</p>
</div>
<div class="combo-item">
<span class="combo-icon">🥤</span>
<p>Напиток</p>
</div>
</div>
<div class="combo-card">
<div class="combo-item">
<span class="combo-icon">🍲</span>
<p>Суп</p>
</div>
<div class="combo-item">
<span class="combo-icon">🍽️</span>
<p>Главное блюдо</p>
</div>
<div class="combo-item">
<span class="combo-icon">🥤</span>
<p>Напиток</p>
</div>
</div>
<div class="combo-card">
<div class="combo-item">
<span class="combo-icon">🍲</span>
<p>Суп</p>
</div>
<div class="combo-item">
<span class="combo-icon">🥗</span>
<p>Салат</p>
</div>
<div class="combo-item">
<span class="combo-icon">🥤</span>
<p>Напиток</p>
</div>
</div>
<div class="combo-card">
<div class="combo-item">
<span class="combo-icon">🍽️</span>
<p>Главное блюдо</p>
</div>
<div class="combo-item">
<span class="combo-icon">🥗</span>
<p>Салат</p>
</div>
<div class="combo-item">
<span class="combo-icon">🥤</span>
<p>Напиток</p>
</div>
</div>
<div class="combo-card">
<div class="combo-item">
<span class="combo-icon">🍽️</span>
<p>Главное блюдо</p>
</div>
<div class="combo-item">
<span class="combo-icon">🥤</span>
<p>Напиток</p>
</div>
</div>
<div class="combo-card combo-dessert">
<div class="combo-item">
<span class="combo-icon">🍰</span>
<p>Десерт</p>
</div>
<p class="combo-note-inline">(Можно добавить к любому заказу)</p>
</div>
</div>
<p class="combo-note">Десерты можно добавить к любому варианту ланча</p>
</section>
<section class="mb-5">
<div class="section-header">
<h2>Супы</h2>
<div class="filter-buttons">
<button class="filter-btn" data-kind="fish">Рыбный</button>
<button class="filter-btn" data-kind="meat">Мясной</button>
<button class="filter-btn" data-kind="veg">Вегетарианский</button>
</div>
</div>
<div class="dishes-grid" id="soup-section"></div>
</section>
<section class="mb-5">
<div class="section-header">
<h2>Главные блюда</h2>
<div class="filter-buttons">
<button class="filter-btn" data-kind="fish">Рыбное</button>
<button class="filter-btn" data-kind="meat">Мясное</button>
<button class="filter-btn" data-kind="veg">Вегетарианское</button>
</div>
</div>
<div class="dishes-grid" id="main-course-section"></div>
</section>
<section class="mb-5">
<div class="section-header">
<h2>Салаты и стартеры</h2>
<div class="filter-buttons">
<button class="filter-btn" data-kind="fish">Рыбный</button>
<button class="filter-btn" data-kind="meat">Мясной</button>
<button class="filter-btn" data-kind="veg">Вегетарианский</button>
</div>
</div>
<div class="dishes-grid" id="salad-section"></div>
</section>
<section class="mb-5">
<div class="section-header">
<h2>Напитки</h2>
<div class="filter-buttons">
<button class="filter-btn" data-kind="cold">Холодный</button>
<button class="filter-btn" data-kind="hot">Горячий</button>
</div>
</div>
<div class="dishes-grid" id="drink-section"></div>
</section>
<section class="mb-5">
<div class="section-header">
<h2>Десерты</h2>
<div class="filter-buttons">
<button class="filter-btn" data-kind="small">Маленькая порция</button>
<button class="filter-btn" data-kind="medium">Средняя порция</button>
<button class="filter-btn" data-kind="large">Большая порция</button>
</div>
</div>
<div class="dishes-grid" id="dessert-section"></div>
</section>
</div>
</main>
<div id="order-panel" class="order-panel hidden">
<div class="order-panel-content">
<div class="order-panel-info">
<p class="order-panel-label">Ваш заказ:</p>
<p class="order-panel-price" id="order-panel-price">0 руб.</p>
</div>
<a href="order.html" id="order-panel-link" class="order-panel-button disabled">
Перейти к оформлению
</a>
</div>
</div>
<footer id="contacts" class="footer py-4">
<div class="container">
<div class="footer-content">
<div class="footer-title">Контактная информация</div>
<div class="footer-info">
<div class="footer-row">
<span>Телефон: <a href="tel:+79993737737">+7 (999) 373-77-37</a></span>
<span>Email: <a href="mailto:egor@deev.space">egor@deev.space</a></span>
</div>
<div class="footer-row">
<span>Адрес: г. Москва, ул. Михалковская, д. 7</span>
<span>Режим работы: Пн-Пт с 7:00 до 23:00</span>
</div>
<div class="footer-row footer-copyright">
© 2024 ЭкоЛанч. Все права защищены.
</div>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="../js/menu.js"></script>
</body>
</html>

View file

@ -0,0 +1,148 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="https://deev.space/media/favicon.ico" type="image/x-icon">
<title>Оформить заказ - ЭкоЛанч</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../styles/styles.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top shadow-sm">
<div class="container">
<a class="navbar-brand" href="../index.html">
<span class="brand-icon">🌿</span>
<span class="brand-eco">Эко</span><span class="brand-lunch">Ланч</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="../index.html">Главная</a>
</li>
<li class="nav-item">
<a class="nav-link" href="menu.html">Собрать ланч</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="order.html">Оформить заказ</a>
</li>
<li class="nav-item">
<a class="nav-link" href="orders.html">Мои заказы</a>
</li>
<li class="nav-item">
<a class="nav-link" href="delivery.html">Доставка</a>
</li>
<li class="nav-item">
<a class="nav-link" href="about.html">О нас</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#contacts">Контакты</a>
</li>
</ul>
</div>
</div>
</nav>
<main style="padding-top: 80px;">
<div class="container py-5">
<section class="mb-5">
<h2 class="text-center mb-4">Состав заказа</h2>
<div id="order-dishes" class="dishes-grid"></div>
<div id="empty-order" class="empty-order-message hidden">
<p>Ничего не выбрано. Чтобы добавить блюда в заказ, перейдите на страницу <a href="menu.html">Собрать ланч</a>.</p>
</div>
</section>
<section class="order-form">
<h2>Оформление заказа</h2>
<form id="order-form" method="POST">
<div class="form-container">
<div class="order-section">
<h3>Ваш заказ</h3>
<div id="order-summary">
<p class="empty-order">Ничего не выбрано</p>
</div>
</div>
<div class="customer-section">
<h3>Данные для доставки</h3>
<label for="name">Имя</label>
<input type="text" name="full_name" id="name" placeholder="Введите ваше имя" required>
<label for="email">Email</label>
<input type="email" name="email" id="email" placeholder="Введите ваш email" required>
<div class="checkbox-group">
<input type="checkbox" name="subscribe" id="subscribe" value="1" checked>
<label for="subscribe">Подписаться на рассылку</label>
</div>
<label for="phone">Телефон</label>
<input type="tel" name="phone" id="phone" placeholder="Введите ваш телефон" required>
<label for="address">Адрес доставки</label>
<input type="text" name="delivery_address" id="address" placeholder="Введите адрес доставки" required>
<small class="form-hint">Доставка осуществляется только по Москве</small>
<div class="radio-group">
<label>Время доставки</label>
<div>
<input type="radio" name="delivery_type" id="asap" value="now" required checked>
<label for="asap">Как можно скорее</label>
</div>
<div>
<input type="radio" name="delivery_type" id="specific-time" value="by_time" required>
<label for="specific-time">К определенному времени</label>
</div>
</div>
<label for="delivery-time">Указать время</label>
<input type="time" name="delivery_time" id="delivery-time" min="07:00" max="23:00" step="300">
<small class="form-hint">Доступное время доставки с 7:00 до 23:00</small>
<label for="comment">Комментарий к заказу</label>
<textarea name="comment" id="comment" placeholder="Введите комментарий к заказу"></textarea>
<div class="form-buttons">
<button type="button" id="reset-button">Сбросить</button>
<button type="submit">Отправить заказ</button>
</div>
</div>
</div>
</form>
</section>
</div>
</main>
<footer id="contacts" class="footer py-4">
<div class="container">
<div class="footer-content">
<div class="footer-title">Контактная информация</div>
<div class="footer-info">
<div class="footer-row">
<span>Телефон: <a href="tel:+79993737737">+7 (999) 373-77-37</a></span>
<span>Email: <a href="mailto:egor@deev.space">egor@deev.space</a></span>
</div>
<div class="footer-row">
<span>Адрес: г. Москва, ул. Михалковская, д. 7</span>
<span>Режим работы: Пн-Пт с 7:00 до 23:00</span>
</div>
<div class="footer-row footer-copyright">
© 2024 ЭкоЛанч. Все права защищены.
</div>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="../js/order.js"></script>
</body>
</html>

View file

@ -0,0 +1,218 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="icon" href="https://deev.space/media/favicon.ico" type="image/x-icon">
<title>Мои заказы - ЭкоЛанч</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Montserrat:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="../styles/styles.css">
</head>
<body>
<nav class="navbar navbar-expand-lg navbar-light bg-white fixed-top shadow-sm">
<div class="container">
<a class="navbar-brand" href="../index.html">
<span class="brand-icon">🌿</span>
<span class="brand-eco">Эко</span><span class="brand-lunch">Ланч</span>
</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse justify-content-end" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link" href="../index.html">Главная</a>
</li>
<li class="nav-item">
<a class="nav-link" href="menu.html">Собрать ланч</a>
</li>
<li class="nav-item">
<a class="nav-link" href="order.html">Оформить заказ</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="orders.html">Мои заказы</a>
</li>
<li class="nav-item">
<a class="nav-link" href="delivery.html">Доставка</a>
</li>
<li class="nav-item">
<a class="nav-link" href="about.html">О нас</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#contacts">Контакты</a>
</li>
</ul>
</div>
</div>
</nav>
<main style="padding-top: 80px;">
<div class="container py-5">
<section>
<h2 class="text-center mb-4">Мои заказы</h2>
<div id="orders-list" class="orders-list"></div>
<div id="empty-orders" class="empty-orders-message hidden">
<p>У вас пока нет оформленных заказов. Перейдите на страницу <a href="menu.html">Собрать ланч</a>, чтобы создать первый заказ.</p>
</div>
</section>
</div>
</main>
<div id="view-modal" class="modal hidden">
<div class="modal-content">
<div class="modal-header">
<h3>Просмотр заказа</h3>
<span class="modal-close">&times;</span>
</div>
<div class="modal-body" id="view-modal-body">
<div class="modal-section">
<p class="modal-label">Дата оформления</p>
<p class="modal-value" id="view-date"></p>
</div>
<div class="modal-section">
<h4 class="modal-section-title">Доставка</h4>
<div class="modal-info-grid">
<div class="modal-info-item">
<span class="modal-label">Имя получателя</span>
<span class="modal-value" id="view-name"></span>
</div>
<div class="modal-info-item">
<span class="modal-label">Адрес доставки</span>
<span class="modal-value" id="view-address"></span>
</div>
<div class="modal-info-item">
<span class="modal-label">Время доставки</span>
<span class="modal-value" id="view-time"></span>
</div>
<div class="modal-info-item">
<span class="modal-label">Телефон</span>
<span class="modal-value" id="view-phone"></span>
</div>
<div class="modal-info-item">
<span class="modal-label">Email</span>
<span class="modal-value" id="view-email"></span>
</div>
</div>
</div>
<div class="modal-section" id="view-comment-section">
<h4 class="modal-section-title">Комментарий</h4>
<p class="modal-value" id="view-comment"></p>
</div>
<div class="modal-section">
<h4 class="modal-section-title">Состав заказа</h4>
<div class="modal-order-items" id="view-items"></div>
<div class="modal-total">
<span>Стоимость:</span>
<span id="view-total"></span>
</div>
</div>
</div>
<div class="modal-footer">
<button class="modal-btn modal-btn-ok" id="view-modal-ok">OK</button>
</div>
</div>
</div>
<div id="edit-modal" class="modal hidden">
<div class="modal-content">
<div class="modal-header">
<h3>Редактирование заказа</h3>
<span class="modal-close">&times;</span>
</div>
<div class="modal-body">
<div class="modal-section">
<p class="modal-label">Дата оформления</p>
<p class="modal-value" id="edit-created-date"></p>
</div>
<form id="edit-form">
<div class="modal-section">
<h4 class="modal-section-title">Доставка</h4>
<label for="edit-name">Имя получателя</label>
<input type="text" id="edit-name" name="full_name" required>
<label for="edit-address">Адрес доставки</label>
<input type="text" id="edit-address" name="delivery_address" required>
<label for="edit-time">Время доставки</label>
<input type="time" id="edit-time" name="delivery_time" min="07:00" max="23:00" step="300">
<label for="edit-phone">Телефон</label>
<input type="tel" id="edit-phone" name="phone" required>
<label for="edit-email">Email</label>
<input type="email" id="edit-email" name="email" required>
</div>
<div class="modal-section">
<h4 class="modal-section-title">Комментарий</h4>
<textarea id="edit-comment" name="comment" rows="3"></textarea>
</div>
<div class="modal-section">
<h4 class="modal-section-title">Состав заказа</h4>
<div class="modal-order-items" id="edit-items"></div>
<div class="modal-total">
<span>Стоимость:</span>
<span id="edit-total"></span>
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button class="modal-btn modal-btn-cancel" id="edit-modal-cancel">Отмена</button>
<button class="modal-btn modal-btn-save" id="edit-modal-save">Сохранить</button>
</div>
</div>
</div>
<div id="delete-modal" class="modal hidden">
<div class="modal-content modal-content-small">
<div class="modal-header">
<h3>Удаление заказа</h3>
<span class="modal-close">&times;</span>
</div>
<div class="modal-body">
<p class="modal-delete-text">Вы уверены, что хотите удалить заказ?</p>
</div>
<div class="modal-footer">
<button class="modal-btn modal-btn-cancel" id="delete-modal-cancel">Отмена</button>
<button class="modal-btn modal-btn-delete" id="delete-modal-confirm">Да</button>
</div>
</div>
</div>
<div id="notification" class="notification hidden"></div>
<footer id="contacts" class="footer py-4">
<div class="container">
<div class="footer-content">
<div class="footer-title">Контактная информация</div>
<div class="footer-info">
<div class="footer-row">
<span>Телефон: <a href="tel:+79993737737">+7 (999) 373-77-37</a></span>
<span>Email: <a href="mailto:egor@deev.space">egor@deev.space</a></span>
</div>
<div class="footer-row">
<span>Адрес: г. Москва, ул. Михалковская, д. 7</span>
<span>Режим работы: Пн-Пт с 7:00 до 23:00</span>
</div>
<div class="footer-row footer-copyright">
© 2024 ЭкоЛанч. Все права защищены.
</div>
</div>
</div>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script src="../js/orders.js"></script>
</body>
</html>