257 lines
11 KiB
Markdown
257 lines
11 KiB
Markdown
# 🎨 Frontend
|
||
|
||
## Общая информация
|
||
|
||
| Параметр | Значение |
|
||
|----------|----------|
|
||
| **Фреймворк** | Нет (Vanilla JavaScript) |
|
||
| **Модульная система** | ES6 Modules (`import`/`export`) |
|
||
| **Стили** | CSS (модульный подход) |
|
||
| **Шрифт** | [Inter](https://fonts.google.com/specimen/Inter) (Google Fonts) |
|
||
| **Веб-сервер** | Apache httpd:alpine |
|
||
|
||
---
|
||
|
||
## Структура файлов
|
||
|
||
```
|
||
frontend/
|
||
├── index.html # 🔐 Страница авторизации (общая)
|
||
├── script.js # Логика авторизации
|
||
├── style.css # Стили страницы авторизации
|
||
├── theme-toggle.js # Переключение светлой/тёмной темы
|
||
├── Dockerfile # httpd:alpine
|
||
│
|
||
├── admin/ # 👨💼 Интерфейс администратора
|
||
│ ├── index.html # SPA-оболочка с sidebar
|
||
│ ├── css/
|
||
│ │ ├── main.css # CSS-переменные, цвета, типографика
|
||
│ │ ├── layout.css # Раскладка (sidebar, topbar, content)
|
||
│ │ ├── components.css # Кнопки, таблицы, карточки, формы
|
||
│ │ ├── modals.css # Модальные окна
|
||
│ │ ├── department.css # Стили кафедры
|
||
│ │ └── departments-data.css # Стили создания кафедры/специальности
|
||
│ ├── js/
|
||
│ │ ├── main.js # Инициализация, маршрутизация, навигация
|
||
│ │ ├── api.js # HTTP-обёртка (fetch + Authorization)
|
||
│ │ ├── utils.js # Утилиты
|
||
│ │ ├── otel.js # OpenTelemetry (клиентская телеметрия, только прод)
|
||
│ │ └── views/ # Модули представлений
|
||
│ │ ├── users.js # Управление пользователями
|
||
│ │ ├── groups.js # Управление группами
|
||
│ │ ├── classrooms.js # Управление аудиториями
|
||
│ │ ├── subjects.js # Управление дисциплинами
|
||
│ │ ├── equipments.js # Управление оборудованием
|
||
│ │ ├── edu-forms.js # Формы обучения
|
||
│ │ ├── schedule.js # Расписание занятий
|
||
│ │ ├── database.js # Управление тенантами
|
||
│ │ ├── department.js # Кафедры
|
||
│ │ └── departments-data.js # Создание кафедры/специальности
|
||
│ ├── views/ # HTML-шаблоны представлений
|
||
│ │ ├── users.html
|
||
│ │ ├── groups.html
|
||
│ │ ├── classrooms.html
|
||
│ │ ├── subjects.html
|
||
│ │ ├── equipments.html
|
||
│ │ ├── edu-forms.html
|
||
│ │ ├── schedule.html
|
||
│ │ ├── database.html
|
||
│ │ ├── department.html
|
||
│ │ └── departments-data.html
|
||
│ │
|
||
│ └── settings/ # ⚙️ Страница настроек (отдельный SPA)
|
||
│ ├── index.html # Оболочка с собственной sidebar
|
||
│ ├── css/
|
||
│ │ ├── main.css # CSS-переменные, базовые стили
|
||
│ │ └── layout.css # Sidebar, topbar, content
|
||
│ ├── js/
|
||
│ │ └── main.js # Навигация по вкладкам настроек
|
||
│ └── views/
|
||
│ └── general.html # Общие настройки (заглушка)
|
||
│
|
||
├── teacher/ # 👩🏫 Интерфейс преподавателя
|
||
│ └── index.html # Просмотр расписания
|
||
│
|
||
└── student/ # 🎓 Интерфейс студента
|
||
└── index.html # Просмотр расписания (read-only)
|
||
```
|
||
|
||
---
|
||
|
||
## Система маршрутизации (Admin SPA)
|
||
|
||
Админ-панель работает как **Single Page Application** без фреймворка.
|
||
|
||
Навигация реализована через `data-tab` атрибуты на элементах sidebar:
|
||
|
||
```html
|
||
<a href="#" class="nav-item" data-tab="users">Пользователи</a>
|
||
<a href="#" class="nav-item" data-tab="groups">Группы</a>
|
||
<a href="#" class="nav-item" data-tab="schedule">Расписание занятий</a>
|
||
```
|
||
|
||
При клике на пункт меню `main.js`:
|
||
1. Загружает HTML-шаблон из `views/{tab}.html` через `fetch()`
|
||
2. Вставляет его в `#app-content`
|
||
3. Подключает соответствующий JS-модуль из `js/views/{tab}.js`
|
||
4. Обновляет заголовок страницы (`#page-title`)
|
||
|
||
### Разделы админ-панели
|
||
|
||
| Tab | Описание | API |
|
||
|-----|----------|-----|
|
||
| `users` | CRUD пользователей | `/api/users` |
|
||
| `groups` | CRUD групп | `/api/groups` |
|
||
| `edu-forms` | Формы обучения | `/api/education-forms` |
|
||
| `equipments` | Оборудование | `/api/equipments` |
|
||
| `classrooms` | Аудитории | `/api/classrooms` |
|
||
| `subjects` | Дисциплины | `/api/subjects` |
|
||
| `schedule` | Расписание | `/api/users/lessons` |
|
||
| `database` | Тенанты | `/api/database` |
|
||
| `department` | Кафедры | `/api/departments` |
|
||
| `departments-data` | Создание кафедры/специальности | `/api/departments` |
|
||
|
||
### Страница настроек (`/admin/settings/`)
|
||
|
||
Настройки — это **отдельный SPA** со своей боковой панелью и вкладками, не связанными с основной админ-панелью.
|
||
|
||
- Доступ: через dropdown «Настройки» в footer боковой панели админки
|
||
- Кнопка «Назад в панель» для возврата в `/admin/`
|
||
- Текущие вкладки:
|
||
- **Общие настройки** — заглушка (в разработке)
|
||
|
||
---
|
||
|
||
## API-клиент (`api.js`)
|
||
|
||
Все HTTP-запросы проходят через обёртку `apiFetch()`:
|
||
|
||
```javascript
|
||
export async function apiFetch(endpoint, method = 'GET', body = null) {
|
||
const response = await fetch(endpoint, {
|
||
method,
|
||
headers: {
|
||
'Authorization': `Bearer ${token}`,
|
||
'Content-Type': 'application/json'
|
||
},
|
||
body: body ? JSON.stringify(body) : null
|
||
});
|
||
|
||
if (!response.ok) {
|
||
throw new Error(data?.message || `Ошибка HTTP: ${response.status}`);
|
||
}
|
||
|
||
return await response.json();
|
||
}
|
||
|
||
// Shortcut-методы
|
||
export const api = {
|
||
get: (url) => apiFetch(url, 'GET'),
|
||
post: (url, body) => apiFetch(url, 'POST', body),
|
||
put: (url, body) => apiFetch(url, 'PUT', body),
|
||
delete: (url, body) => apiFetch(url, 'DELETE', body)
|
||
};
|
||
```
|
||
|
||
Токен берётся из `localStorage.getItem('token')`.
|
||
|
||
---
|
||
|
||
## Аутентификация (Frontend)
|
||
|
||
### Страница входа (`/index.html`)
|
||
|
||
1. Пользователь вводит логин/пароль
|
||
2. `script.js` отправляет `POST /api/auth/login`
|
||
3. При успехе сохраняет в `localStorage`:
|
||
- `token` — UUID-токен
|
||
- `role` — роль пользователя
|
||
4. Перенаправляет на соответствующий интерфейс:
|
||
- `ADMIN` → `/admin/`
|
||
- `TEACHER` → `/teacher/`
|
||
- `STUDENT` → `/student/`
|
||
|
||
### Проверка авторизации
|
||
|
||
На каждой странице проверяется наличие токена и роли:
|
||
|
||
```javascript
|
||
export function isAuthenticatedAsAdmin() {
|
||
const role = localStorage.getItem('role');
|
||
return token && role === 'ADMIN';
|
||
}
|
||
```
|
||
|
||
### Выход
|
||
|
||
Кнопка «Выйти» находится в dropdown-меню «Настройки» в footer боковой панели. Очищает `localStorage` и перенаправляет на `/`.
|
||
|
||
---
|
||
|
||
## CSS-архитектура
|
||
|
||
### Модульный подход
|
||
|
||
Стили разделены на модульные файлы (порядок подключения важен):
|
||
|
||
1. **`main.css`** — CSS-переменные (цвета, шрифты, отступы), глобальные стили, тёмная тема
|
||
2. **`layout.css`** — Sidebar, topbar, content area, dropdown настроек, responsive
|
||
3. **`components.css`** — Кнопки, таблицы, карточки, badge, формы, theme-toggle
|
||
4. **`modals.css`** — Модальные окна
|
||
5. **`department.css`** — Стили страницы кафедр
|
||
6. **`departments-data.css`** — Стили создания кафедры/специальности
|
||
|
||
### Темизация
|
||
|
||
CSS-переменные позволяют поддерживать светлую/тёмную тему:
|
||
|
||
```css
|
||
:root {
|
||
--bg-primary: #ffffff;
|
||
--text-primary: #1a1a2e;
|
||
--accent: #6366f1;
|
||
}
|
||
|
||
[data-theme="dark"] {
|
||
--bg-primary: #0f0f23;
|
||
--text-primary: #e2e8f0;
|
||
--accent: #818cf8;
|
||
}
|
||
```
|
||
|
||
Переключение — через `theme-toggle.js`.
|
||
|
||
---
|
||
|
||
## Боковая панель (Sidebar)
|
||
|
||
- **Скрытие/раскрытие** — кнопка-крестик в правом верхнем углу sidebar
|
||
- **Десктоп** (`>768px`): sidebar складывается влево, контент расширяется; состояние сохраняется в `localStorage` (`sidebar-collapsed`)
|
||
- **Мобильные** (`≤768px`): sidebar скрывается за кнопкой-гамбургер, выезжает как overlay с затемнением
|
||
- **Dropdown «Настройки»** в footer sidebar — содержит ссылку на страницу настроек и кнопку выхода
|
||
|
||
---
|
||
|
||
## OpenTelemetry (`otel.js`)
|
||
|
||
Клиентская телеметрия (document-load, fetch, XHR) отправляется через `BatchSpanProcessor` на `/otel/v1/traces`.
|
||
|
||
- **На production** — загружается автоматически через динамический `import()`
|
||
- **На localhost** — пропускается, чтобы избежать таймаутов CDN `esm.sh`
|
||
|
||
```javascript
|
||
if (!['localhost', '127.0.0.1'].includes(window.location.hostname)) {
|
||
import('./otel.js').catch(e => console.warn('OTel init skipped:', e.message));
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## Адаптивность
|
||
|
||
Интерфейс адаптирован под мобильные устройства:
|
||
- Sidebar скрывается на экранах < 768px, выезжает как overlay
|
||
- Появляется кнопка-гамбургер (`#menu-toggle`)
|
||
- Кнопка-крестик закрывает sidebar на всех устройствах
|
||
- Таблицы получают горизонтальный скролл
|