ИРИТ · Classroom

Основы веб-разработки JavaScript

Основы веб-разработки на JavaScript: язык, DOM, асинхронность, работа с сетью — еженедельные лабораторные в личном репозитории.

Преподаватель: Шадрин Д. Б.
Группы: Л-01
Пары: четверг: 16:00 лекция, 18:00 практика (Teams)

Мой репозиторий и задания

Ближайший срок: ДЗ 4. Витрина по модулям: стор, сетка, звезда — 05.11.2026 12:00

Задания и сроки

ЗаданиеСрок
ДЗ 4. Витрина по модулям: стор, сетка, звезда05.11.2026 12:00идёт
ДЗ 3. Витрина узлами: избранное и адрес29.10.2026 12:00срок прошёл
ДЗ 2. Витрина из функций22.10.2026 12:00срок прошёл
ДЗ 1. Ядро коллекции: от консоли к витрине15.10.2026 12:00срок прошёл
ДЗ 0. Инфраструктура: Git, редактор, консоль браузера, Node.js08.10.2026 12:00срок прошёл
Тексты заданий
ДЗ 4. Витрина по модулям: стор, сетка, звезда
срок 05.11.2026 12:00

ДЗ 4. Витрина по модулям: стор, сетка, звезда

Срок: 05.11.2026, 12:00 (Екатеринбург) · Сдача: Pull request lab4 → main, в описании Closes #<номер задачи>

Цель

Разложить витрину по модулям, хранить настройки в собственном сторе на замыкании и перерисовывать витрину по подписке. Каталог перевести на Grid, избранное отметить звездой в углу карточки, блок статистики научиться скрывать из адреса.

Перед началом

  • ДЗ 4 продолжает ДЗ 3: витрина собирается узлами, настройки и избранное берутся из адреса.
  • Теория — документ lab/lab4/Теория к ДЗ 4.docx (придёт запросом на слияние, как файлы задания) и презентация лекции 4.
  • ДЗ 3 уже слито в main: git checkout main && git pull → git checkout -b lab4.

ДЗ 3 ещё в работе: git checkout lab3 → git checkout -b lab4.

  • Страницу открывайте через сервер, не двойным кликом: в WebStorm — значок браузера в правом верхнем углу редактора, в VS Code — расширение Live Server. Из file:// модули не загрузятся.

Как выполнять

Код — в project-vanilla/js/, стили — в project-vanilla/styles/index.css, разметка — в project-vanilla/index.html.

  1. Модули. Разложите index.js на файлы:
  • data.js — только массив коллекции, export const collection = [...];
  • utils.js — чистые функции: поиск, фильтр, сортировка, дата, статистика, countByCategory;
  • ui.js — всё, что создаёт узлы: el, renderCard;
  • store.js — стор (шаг 2);
  • index.js — главный: импортирует остальное и собирает витрину.

В index.html — один тег <script type="module" src="js/index.js"></script>. Вспомогательные функции, которые не нужны снаружи, оставьте без export. Проверка: страница через сервер работает как в ДЗ 3, в консоли нет ошибок.

  1. Стор на замыкании. В store.js — функция createStore(initialState), которая возвращает объект с тремя функциями:
  • getState() — возвращает копию состояния ({ ...state });
  • setState(patch) — делает новое состояние { ...state, ...patch } и вызывает всех подписчиков;
  • subscribe(listener) — добавляет функцию в список подписчиков.

Само состояние — переменная внутри createStore, снаружи до неё не добраться. В состоянии — настройки витрины (query, category, sortKey, sortDir), список id избранного и признак показа статистики. В index.js: функция render(state) собирает витрину целиком, store.subscribe(render), а настройки из адреса передаются одним store.setState({...}) — это и есть первая отрисовка. Проверка: витрина рисуется только из render, вызванного подпиской; store.getState().query = "x" не меняет состояние стора.

  1. Деструктуризация и замыкание. renderCard разбирает элемент прямо в параметрах: function renderCard({ id, title, … }, favorites) — имена полей своей темы. В utils.js — функция byCategory(category), которая возвращает функцию-проверку для filter; filterByCategory использует её. Проверка: в renderCard нет обращений вида item.поле.
  2. Сетка. Каталог и блок «Избранное» — на Grid: display: grid; grid-template-columns: repeat(auto-fill, minmax(…, 1fr));, ширину у li уберите. Проверка: сузили окно — колонок стало меньше, карточки в ряду одной ширины.
  3. Звезда в углу. У избранной карточки — значок (например ★) в правом верхнем углу: у .card — position: relative, у значка — position: absolute с top и right. Проверка: звезда стоит в углу каждой избранной карточки, текст карточки её не обходит.
  4. Скрыть статистику. Параметр адреса stats=hide скрывает блок статистики: в CSS — класс .is-hidden { display: none; }, в render — classList добавляет или убирает его. Проверка: index.html?stats=hide — статистики нет, каталог на месте.
  5. Отчёт. В lab/lab4/report.md — код createStore и render блоками с подсветкой, скриншоты из таблицы ниже (под каждым — адрес, с которым он снят) и ответы:
  • почему страница с модулями не работает, если открыть её двойным кликом;
  • почему getState возвращает копию, а не сам объект состояния;
  • что помнит функция, которую вернула byCategory("…");
  • чем display: none отличается от visibility: hidden и что будет со звездой, если убрать position: relative у карточки.

Проверка: на каждый вопрос есть ответ.

Что сдать

ЧтоГде в репозитории
Модули витриныproject-vanilla/js/data.js, utils.js, ui.js, store.js, index.js
Стили: сетка, звезда, .is-hiddenproject-vanilla/styles/index.css
Подключение модуляproject-vanilla/index.html
Код, ответы на вопросыlab/lab4/report.md
Витрина через сервер (видна адресная строка http://localhost…) · с stats=hide и другим адресом · узкое окно (меньше колонок)lab/lab4/screenshots/01-server.png, 02-address.png, 03-narrow.png

Критерии приёмки

#КритерийБаллы
1Код разложен на data.js, utils.js, ui.js, store.js, index.js; type="module"; страница работает через сервер20
2createStore: состояние в замыкании, getState отдаёт копию, setState создаёт новое состояние и вызывает подписчиков; витрина рисуется по подписке25
3Деструктуризация в параметрах renderCard; byCategory возвращает функцию-проверку10
4Каталог и «Избранное» на Grid с auto-fill и minmax15
5Звезда избранного через position: relative + absolute10
6stats=hide скрывает статистику классом is-hidden5
7В отчёте код, три скриншота с адресами и ответы на четыре вопроса15
Итого100

Не принимается: страница через сервер не открывается или в консоли ошибки; на витрине видно null, undefined или NaN; нет lab/lab4/report.md; работа сделана нейросетью.

Как сдать

  1. git add . && git commit -m "ДЗ 4: витрина по модулям" → git push -u origin lab4
  2. На GitHub: Pull request lab4 → main, в описании Closes #<номер задачи>.
  3. Сдано в срок, если запрос открыт до срока — время фиксирует GitHub.
ДЗ 3. Витрина узлами: избранное и адрес
срок 29.10.2026 12:00

Срок: 29.10.2026, 12:00 (Екатеринбург) · Сдача: Pull request lab3 → main, в описании Closes #<номер задачи>

Цель

Переписать витрину со строк HTML на узлы DOM, добавить избранное и разбивку по категориям, а поиск, фильтр и сортировку брать из адреса страницы — так, чтобы витрину можно было переключать ссылкой.

Перед началом

  • ДЗ 3 продолжает ДЗ 2: нужны функции renderCard, getStats, searchItems, filterByCategory, sortItems и объект settings.
  • Теория — документ lab/lab3/Теория к ДЗ 3.docx (придёт запросом на слияние, как файлы задания) и презентация лекции 3.
  • ДЗ 2 уже слито в main: git checkout main && git pull → git checkout -b lab3.

ДЗ 2 ещё в работе: git checkout lab2 → git checkout -b lab3 — ветка ДЗ 3 от ветки ДЗ 2.

Как выполнять

Код — в project-vanilla/js/index.js, стили — в project-vanilla/styles/index.css и variables.css, разметка — в project-vanilla/index.html.

  1. Карточка узлами. renderCard(item) возвращает не строку, а элемент <li> с карточкой внутри. Каждый элемент — document.createElement, текст — textContent, классы — classList.add, вложение — append. Карточка выглядит так же, как в ДЗ 2. Проверка: в renderCard нет ни innerHTML, ни обратных кавычек с разметкой.
  2. Каталог без innerHTML. Каталог собирается так: catalog.replaceChildren(...visible.map(renderCard)). Если видимых элементов нет — в каталоге абзац «Ничего не найдено». Проверка: поиск по несуществующему слову показывает «Ничего не найдено».
  3. Настройки из адреса. settings собирается из адреса страницы, для каждого параметра — значение по умолчанию:

```js

const params = new URLSearchParams(location.search);

const settings = {

query: params.get("query") ?? "",

category: params.get("category") ?? "all",

// sortKey, sortDir — так же

};

```

Заголовок вкладки показывает, сколько видно: document.title = "Коллекция — 4 из 12". Проверка: index.html?category=<категория>&sortDir=desc меняет витрину и заголовок вкладки без правки кода.

  1. Избранное. Избранное — Set из идентификаторов. Берётся из параметра fav (index.html?fav=52801,52806), а без параметра — 3 id по умолчанию в коде. Из адреса приходят строки: если идентификатор в вашей теме — число, сравнивайте favorites.has(String(item.id)).
  • у карточки избранного элемента — класс card--favorite со своим стилем (рамка, фон или метка — на выбор);
  • над каталогом — блок «Избранное» (<h2> и <ul id="favorites"> в index.html), карточки в нём — тем же renderCard;
  • в статистике — строка «В избранном» с favorites.size.

Проверка: поменяли fav в адресе — поменялись блок «Избранное», выделенные карточки и число в статистике.

  1. Разбивка по категориям. countByCategory(items) возвращает Map «категория → сколько элементов» по видимым элементам. В блоке статистики — подзаголовок «По категориям» и список «категория — число», собранный узлами (перебор for (const [category, count] of map)). Проверка: с category=all числа в сумме равны «Всего».
  2. Коробка. В начале index.css — , ::before, ::after { box-sizing: border-box; } (если уже есть — оставьте). Отступы в variables.css переведите из px в rem (16px = 1rem). В DevTools откройте карточку: Elements → Computed — там нарисована её коробка. Проверка:* витрина выглядит как раньше, на скриншоте DevTools видны padding и border карточки.
  3. Отчёт. В lab/lab3/report.md — код renderCard и countByCategory блоками с подсветкой, скриншоты из таблицы ниже (под каждым — адрес, с которым он снят) и ответы:
  • чем textContent лучше innerHTML, когда вставляем данные;
  • почему для избранного взят Set, а не массив;
  • откуда location.search берёт значения и почему все они строки;
  • чем border-box отличается от content-box — на примере вашей карточки.

Проверка: на каждый вопрос есть ответ.

Что сдать

ЧтоГде в репозитории
Код витриныproject-vanilla/js/index.js
Стили и переменныеproject-vanilla/styles/index.css, project-vanilla/styles/variables.css
Блок «Избранное»project-vanilla/index.html
Код функций, ответы на вопросыlab/lab3/report.md
Витрина без параметров · с другим адресом · «Ничего не найдено» · коробка карточки в DevToolslab/lab3/screenshots/01-default.png, 02-address.png, 03-empty.png, 04-devtools.png

Критерии приёмки

#КритерийБаллы
1renderCard строит карточку узлами: createElement, textContent, classList, append; без innerHTML20
2Каталог через replaceChildren; «Ничего не найдено», когда пусто5
3settings из URLSearchParams со значениями по умолчанию; document.title с числом видимых20
4Избранное — Set из адреса или по умолчанию; класс card--favorite, блок «Избранное», число в статистике20
5countByCategory возвращает Map; список категорий в статистике собран узлами15
6border-box, отступы в rem, скриншот коробки в DevTools10
7В отчёте код функций, четыре скриншота с адресами и ответы на четыре вопроса10
Итого100

Не принимается: страница не открывается или в консоли ошибки; на витрине видно null, undefined или NaN; нет lab/lab3/report.md; работа сделана нейросетью.

Как сдать

  1. git add . && git commit -m "ДЗ 3: витрина узлами" → git push -u origin lab3
  2. На GitHub: Pull request lab3 → main, в описании Closes #<номер задачи>.
  3. Сдано в срок, если запрос открыт до срока — время фиксирует GitHub.
ДЗ 2. Витрина из функций
срок 22.10.2026 12:00

Срок: 22.10.2026, 12:00 (Екатеринбург) · Сдача: Pull request lab2 → main, в описании Closes #<номер задачи>

Цель

Доработать витрину ДЗ 1: у элементов появляются идентификатор, категория и дата, код из одного большого цикла раскладывается по функциям, витрина умеет искать, фильтровать и сортировать по настройкам в коде.

Перед началом

  • Витрина ДЗ 1 открывается в браузере. Если ДЗ 1 не доделано — сначала доделайте его, срок у него тоже 15.10.
  • Теория — «Теория к ДЗ 2» (6 страниц) и презентация лекции 2.
  • git checkout main && git pull → git checkout -b lab2

Как выполнять

Код — в project-vanilla/js/index.js, стиль метки — в project-vanilla/styles/index.css.

  1. Данные. Каждому элементу добавьте идентификатор, категорию и дату — имена по своей теме, таблица ниже. Категорий 3–4, с повторами. Дата — ISO-строкой: "2026-01-15T00:00:00.000Z". У одного элемента даты нет совсем, у другого она битая: "вчера". Проверка: страница открывается без ошибок в консоли.
ТемаИдентификаторКатегорияДата
БлюдаidMeal (строка)strCategorydateModified
КоктейлиidDrink (строка)strCategorydateModified
Сериалыid (число)genrespremiered
Искусствоid (число)artwork_type_titledate_start
Книгиkey (строка)subjectfirst_publish_year
Товарыid (число)categorycreatedAt
СпортidTeam (строка)strCountryintFormedYear
АльбомыcollectionId (число)primaryGenreNamereleaseDate
  1. Карточка и статистика — функции. Вынесите из цикла ДЗ 1:
  • renderCard(item) — принимает элемент, возвращает строку HTML карточки; каталог собирается так: collection.map(renderCard).join("");
  • getStats(items) — принимает массив, возвращает объект со статистикой; сумму считайте через reduce с начальным значением 0.

Проверка: витрина и статистика выглядят так же, как в ДЗ 1.

  1. Дата и категория на карточке. Функция formatDate(value) возвращает «ДД.ММ.ГГГГ» (toLocaleDateString("ru-RU")) или "дата неизвестна", если даты нет или она битая (Number.isNaN(date.getTime())). На карточке — дата и категория меткой своего цвета (отдельный класс в index.css). Проверка: у элементов без даты и с битой датой написано «дата неизвестна».
  2. Поиск, фильтр, сортировка. Три функции, каждая возвращает новый массив и не меняет исходный:
  • searchItems(items, query) — элементы, в названии которых есть query, без учёта регистра;
  • filterByCategory(items, category) — элементы этой категории, "all" — все;
  • sortItems(items, key, dir) — копия, отсортированная по свойству key с компаратором, dir — "asc" или "desc".

Вверху файла — настройки витрины, в каталог и статистику идут только видимые элементы:

```js

const settings = { query: "", category: "all", sortKey: "<свойство>", sortDir: "asc" };

const visible = sortItems(filterByCategory(searchItems(collection, settings.query), settings.category), settings.sortKey, settings.sortDir);

```

Проверка: поменяли значение в settings, обновили страницу — карточки и статистика изменились.

  1. Отчёт. В lab/lab2/report.md — код функций блоками с подсветкой, два скриншота витрины (до и после смены settings) и ответы:
  • почему new Date("вчера") не выдаёт ошибку;
  • зачем у reduce второй аргумент 0;
  • почему sortItems сортирует копию, а не переданный массив.

Проверка: на каждый вопрос есть ответ.

Что сдать

ЧтоГде в репозитории
Данные и функцииproject-vanilla/js/index.js
Стиль метки категорииproject-vanilla/styles/index.css
Код функций, ответы на вопросыlab/lab2/report.md
Витрина с датами и категориями · та же витрина с другими settingslab/lab2/screenshots/01-cards.png, 02-settings.png

Критерии приёмки

#КритерийБаллы
1У элементов есть идентификатор, категория (3–4 с повторами) и дата ISO; одна дата пропущена, одна битая15
2renderCard и каталог через map + join(""); getStats с суммой через reduce30
3formatDate даёт «ДД.ММ.ГГГГ» или «дата неизвестна»; дата и категория-метка на карточке20
4searchItems, filterByCategory, sortItems возвращают новый массив, сортировка с компаратором; витрина и статистика строятся по settings25
5В отчёте код функций, два скриншота и ответы на три вопроса10
Итого100

Не принимается: страница не открывается или в консоли ошибки; нет lab/lab2/report.md; работа сделана нейросетью.

Как сдать

  1. git add . && git commit -m "ДЗ 2: витрина из функций" → git push -u origin lab2
  2. На GitHub: Pull request lab2 → main, в описании Closes #<номер задачи>.
  3. Сдано в срок, если запрос открыт до срока — время фиксирует GitHub.
ДЗ 1. Ядро коллекции: от консоли к витрине
срок 15.10.2026 12:00

Срок: 15.10.2026, 12:00 (Екатеринбург) · Сдача: Pull request lab1 → main, в описании Closes #<номер задачи>

Цель

Начать проект «Коллекция»: подготовить данные своей темы, разобраться с типами JavaScript на «грязных» данных в консоли и вывести коллекцию на страницу карточками со статистикой, свёрстанную по макету.

Перед началом

  • Сдано ДЗ 0: репозиторий, Git, Node.js работают.
  • Методичка с теорией, шагами и макетом: «Практика 1» (её часть 0 пропустите — это ДЗ 0).
  • git checkout main && git pull → git checkout -b lab1

Как выполнять

  1. Данные. В exp/data-lab.js — массив из 10–15 объектов своей темы: название, два числовых поля, логический признак (имена полей — таблица «Контракт полей» в методичке). Проверка: node exp/data-lab.js запускается без ошибок.
  2. Ловушки. У 1–2 элементов число строкой ("8"), у одного элемента null, у другого это поле отсутствует. Проверка: typeof по каждому полю одного объекта выводит и string, и number, и boolean.
  3. Сравнения. Для строкового числа, отсутствующего поля и null выведите результаты == и ===. Проверка: таблица из трёх строк в отчёте заполнена.
  4. Цикл. Один проход for...of: всего элементов, два средних, сколько с признаком true, метки каждого элемента, итоговая сводка. Среднее по первому числу — двумя способами: с Number() и без. Проверка: сводка печатается числами, без NaN и склеенных строк.
  5. Каркас страницы. project-vanilla/index.html: header с nav, main с aside#stats и ul#catalog, стили в head, скрипт в конце body. Проведите опыт со специфичностью трёх селекторов из методички. Проверка: страница открывается в браузере без ошибок в консоли.
  6. Витрина. Перенесите массив и цикл в project-vanilla/js/index.js: карточки (article с h3, меток ul, чисел dl) и статистика собираются в строку и вставляются через innerHTML. Проверка: в браузере видны все элементы коллекции и блок статистики.
  7. Стили. styles/variables.css из методички + styles/index.css по макету на Flexbox, цвета и отступы только через var(--…). Проверка: витрина совпадает с макетом; смена --color-accent перекрашивает акценты разом.
  8. Вставка из данных. Добавьте в название одного элемента тег с onerror из методички. Проверка: на карточке появилась надпись из кода — код из данных выполнился.
  9. Отчёт. В lab/lab1/report.md — ответы на все вопросы раздела «Что сдать в report.md» методички, код блоками с подсветкой. Проверка: на каждый вопрос есть ответ.

Что сдать

ЧтоГде в репозитории
Данные с ловушками, цикл, сводкаexp/data-lab.js
Страница, скрипт, стилиproject-vanilla/index.html, project-vanilla/js/index.js, project-vanilla/styles/
Ответы на вопросы, таблица сравнений, оба среднихlab/lab1/report.md
Вывод typeof · сводка в консолиlab/lab1/screenshots/01-types.png, 02-console.png
Сработавшая вставка · страница до стилей · готовая витринаlab/lab1/screenshots/05-injection.png, 06-page.png, 08-styled.png

Критерии приёмки

#КритерийБаллы
110–15 объектов своей темы, все три ловушки заложены10
2typeof по полям и таблица == / === с объяснениями10
3Цикл: статистика без NaN и склеек (Number(), проверка != null), метки, сводка; оба средних с объяснением20
4Семантический каркас страницы; опыт со специфичностью с подсчётом15
5Карточки и статистика выводятся через innerHTML из массива15
6Стили по макету на Flexbox, в index.css нет «сырых» цветов15
7Вставка из данных воспроизведена и объяснена5
8В отчёте ответы на все вопросы методички10
Итого100

Не принимается: node exp/data-lab.js падает с ошибкой; страница не открывается; нет lab/lab1/report.md; работа сделана нейросетью.

Как сдать

  1. git add . && git commit -m "ДЗ 1: ядро коллекции" → git push -u origin lab1
  2. На GitHub: Pull request lab1 → main, в описании Closes #<номер задачи>.
  3. Сдано в срок, если запрос открыт до срока — время фиксирует GitHub.
ДЗ 0. Инфраструктура: Git, редактор, консоль браузера, Node.js
срок 08.10.2026 12:00

Срок: 08.10.2026, 12:00 (Екатеринбург) · Сдача: Pull request lab0 → main, в описании Closes #<номер задачи>

Цель

Собрать рабочее место на весь семестр: репозиторий курса, Git, редактор, Node.js и консоль браузера — и впервые сдать работу так, как будут сдаваться все следующие.

Перед началом

  • Аккаунт на GitHub.
  • Получите репозиторий: https://ai-info.ru/?c=js → войдите через GitHub → выберите себя → примите приглашение.
  • Установите Git (https://git-scm.com), редактор (VS Code или WebStorm) и Node.js LTS (https://nodejs.org).
  • git clone <ваш репозиторий> → git checkout -b lab0

Как выполнять

  1. Представьтесь Git: git config --global user.name "Имя Фамилия" и git config --global user.email "почта от GitHub". Проверка: git config --global --list показывает имя и почту.
  2. README: допишите в начало README.md строку со своими данными — ФИО, группа, тема проекта (коктейли · сериалы · искусство · книги · товары · международный спорт · музыкальные альбомы). Проверка: git status показывает изменённый README.md.
  3. Редактор: откройте папку репозитория целиком, работайте во встроенном терминале. Создайте .gitignore со строками node_modules/, .env, .idea/, .vscode/. Проверка: git status в терминале редактора показывает ветку lab0.
  4. Node.js: создайте exp/hello.js с console.log("Node работает!") и запустите node exp/hello.js. Затем допишите alert("Привет"), запустите ещё раз, посмотрите на ошибку и закомментируйте эту строку. Проверка: node -v, npm -v выводят версии, node exp/hello.js печатает строку без ошибок.
  5. Консоль браузера: F12 → Console, выполните "2" + 3, "7" - 2, 0.1 + 0.2, 7 / 2, typeof null, document.title. Проверка: у каждой строки есть результат.
  6. Отчёт: в lab/lab0/report.md ответьте своими словами:
  • чем commit отличается от push; что будет, если сделать commit и забыть push;
  • почему Git не показывает на GitHub пустые папки;
  • что такое Node.js и почему alert(...) в нём падает;
  • почему "2" + 3 и "7" - 2 дают результаты разных типов; почему 0.1 + 0.2 не равно 0.3; откуда document.title взял значение.

Проверка: на каждый вопрос есть ответ, рядом — скриншоты из таблицы ниже (вывод терминала можно вставить блоком кода).

Что сдать

ЧтоГде в репозитории
ФИО, группа, темаREADME.md
Служебные папки вне Git.gitignore
Скрипт Node.jsexp/hello.js
Ответы на вопросыlab/lab0/report.md
git --version и git log --onelinelab/lab0/screenshots/01-git.png или блок кода в отчёте
node -v, npm -v, node exp/hello.jslab/lab0/screenshots/02-node.png или блок кода в отчёте
Консоль браузера с результатами шага 5lab/lab0/screenshots/03-console.png

Критерии приёмки

#КритерийБаллы
1Репозиторий получен, работа в ветке lab0, коммиты подписаны вашим именем15
2README.md с ФИО, группой и темой из списка10
3.gitignore с node_modules/, .env, .idea/, .vscode/10
4node exp/hello.js запускается без ошибок; вывод версий Node и npm в отчёте20
5Скриншот консоли браузера со всеми выражениями шага 515
6В отчёте ответы на все вопросы шага 630
Итого100

Не принимается: нет lab/lab0/report.md; Pull request не из ветки lab0 или не в main; ответы сгенерированы нейросетью.

Как сдать

  1. git add . && git commit -m "ДЗ 0: инфраструктура" → git push -u origin lab0
  2. На GitHub: Pull request lab0 → main, в описании Closes #<номер задачи>.
  3. Сдано в срок, если запрос открыт до срока — время фиксирует GitHub.