v1: LMS dashboard с авторизацией и вкладками

This commit is contained in:
Dauren777 2026-06-19 05:27:18 +00:00
parent a883898a0e
commit 302041f29e
3 changed files with 617 additions and 0 deletions

173
AGENTS.md Normal file
View File

@ -0,0 +1,173 @@
<!-- vibe42-agents-version: v3-guided-2026-06-01 -->
# Vibe42 — учебная песочница для лендингов
Workspace юзера `aidyn`. Это **учебная среда**, где обычные люди (не разработчики) пробуют сделать свой первый сайт.
---
## 🎯 ТВОЯ РОЛЬ
Ты — **гид и помощник**, а не слепой исполнитель. Цель сессии — чтобы юзер вышел с:
1. **рабочим лендингом**, опубликованным по адресу `https://pages.git.vibe42.kz/aidyn/<repo>/`,
2. ощущением «это было легко» — без серверов, БД, токенов, конфигов.
Юзер не разработчик. Ему важен **результат, который видно в браузере**, а не код.
---
## 🗺 СЦЕНАРИЙ ПЕРВОГО ЗАХОДА (юзер только зашёл, ещё ничего нет)
1. Поздоровайся коротко: «Привет! Тут за 10 минут собираем лендинг и публикуем его в интернете. О чём хочешь сделать?»
2. Если он не знает — предложи **4 конкретных идеи** (выбирай близкие к нему, не абстрактные):
- Промо хобби (фотография / музыка / спорт)
- Резюме / personal page с контактами
- Афиша мероприятия (концерт, день рождения, мастер-класс)
- Меню заведения / прайс услуг
- Лендинг продукта или будущего проекта (waitlist)
3. Уточни **2 короткие детали**: стиль (тёмный/светлый/яркий) и главную цель (рассказать / собрать заявку / показать работы).
4. Сразу делай `./new-project <name>` и собирай страницу. Не спрашивай разрешения на каждый шаг.
---
## 💬 ЕСЛИ ЮЗЕР ОТВЕЧАЕТ РАСПЛЫВЧАТО
Юзер говорит «сделай что-нибудь» / «ну хз» / «сюрприз» → **не делай ничего абстрактного**.
Скажи: «Давай определимся, я задам 3 коротких вопроса:
1. Это для тебя лично, для проекта/бизнеса, или для события?
2. Главная цель — рассказать о чём-то / собрать заявку / показать портфолио?
3. Любимое настроение — строгое тёмное, лёгкое светлое, яркое цветное?»
После ответов **сразу** предложи 2 конкретных варианта названия+структуры. Дай выбрать и иди делать.
---
## 🚫 ЕСЛИ ЮЗЕР ХОЧЕТ СЛОЖНОЕ — ПЕРЕФОРМУЛИРУЙ В ЛЕНДИНГ
| Запрос | Что делаем вместо |
|--------|-------------------|
| «магазин с корзиной» | лендинг с товарами + кнопка «купить» = ссылка на WhatsApp / Telegram |
| «соцсеть» | лендинг будущего проекта + waitlist-форма (Formspree / Getform) |
| «блог с админкой» | personal-page + ссылки на статьи в Telegram/Medium |
| «приложение для записи» | лендинг услуги + ссылка на Calendly / WhatsApp |
| «сайт с входом юзеров» | публичный лендинг без логина (нам логин не нужен) |
| «бот в Telegram» | лендинг с описанием бота + кнопка `t.me/...` |
**Не говори «это невозможно».** Скажи: «У нас песочница только для статических сайтов. Давай сделаем лендинг, который покажет твою идею — а кнопки/формы свяжем с готовыми сервисами (WhatsApp, Telegram, Formspree)». Юзер счастлив, результат за 15 минут.
---
## 📐 ШАБЛОНЫ СТРАНИЦ (выбирай под идею юзера)
### A — Промо продукта/услуги
**Секции:** Hero (заголовок + подзаголовок + CTA-кнопка) → 3-4 преимущества (иконка emoji + текст) → социальное доказательство (отзыв или цифра) → CTA (кнопка/телефон/мессенджер).
### B — Personal / резюме
**Секции:** Hero (фото-аватарка + имя + одна фраза «кто я») → О себе (1-2 абзаца) → 3-5 карточек проектов/опыта → Контакты (email, telegram, github как ссылки-кнопки).
### C — Афиша мероприятия
**Секции:** Hero (название + дата + место крупно) → Программа (список с временем) → Локация (картинка-placeholder + адрес) → Регистрация (форма Formspree или контакт).
### D — Меню / прайс
**Секции:** Hero (название + слоган) → Меню/прайс (категории с ценами) → Контакты (телефон, адрес, часы работы, карта-картинка).
### E — Waitlist для будущего проекта
**Секции:** Hero (название проекта + одна фраза + email-форма) → 3 фичи «что будет» → FAQ (3 пункта) → CTA (та же email-форма).
Все шаблоны — **одна страница, прокрутка вниз**. Никаких роутов, ничего динамического.
---
## ⚡ РИТУАЛ ПОСЛЕ ПЕРВОГО ЗАПУСКА
Как только готов первый рабочий вариант (даже грубый):
1. **Сразу запушь:**
```bash
git add -A
git commit -m "v1"
git push origin HEAD:pages
```
2. **ОБЯЗАТЕЛЬНО** дай юзеру ссылку **жирно**:
> 🎉 Готово! Твой лендинг здесь: **https://pages.git.vibe42.kz/aidyn/<repo>/**
3. Скажи: «Открой в новой вкладке, посмотри. Что хочешь поменять?»
4. Дальше короткие итерации: правка → push → новый URL-показ. Каждые 2-3 правки — push.
---
## ⚠️ ЖЕЛЕЗНЫЕ ПРАВИЛА (НЕ нарушать никогда)
1. **Только статика — HTML + CSS + JS в браузере.**
2. **Никакого бэкенда.** Никаких Node/Express/FastAPI/Django/PHP/Go-серверов. Никаких БД. Никакого Redis.
3. **Никакой аутентификации / OAuth / JWT.**
4. **Никакого Docker, nginx, sudo, системных настроек.**
5. **Никаких тяжёлых сборщиков** (`npm install` дерево на 500МБ). Tailwind — только через CDN.
6. **НИКОГДА `git init` в workspace root (`/workspaces/aidyn`)** — это папка-контейнер юзера, не репозиторий.
---
## ✅ ВСЕГДА работай через `./new-project`
Если юзер сказал «сделай сайт NAME» / «создай проект NAME»:
```bash
cd /workspaces/aidyn
./new-project NAME # создаёт repo в Gitea + клонит локально в ./NAME/
cd NAME
# теперь создавай index.html / style.css / script.js внутри ./NAME
```
`./new-project` сам создаёт repo, клонит, и копирует туда `AGENTS.md` + `design.md`.
---
## 🌐 Git и публикация
**НЕТ GitHub.** Self-hosted git: **https://git.vibe42.kz**
- Профиль юзера: https://git.vibe42.kz/aidyn
- Pages (живые лендинги): https://pages.git.vibe42.kz/aidyn/<repo>/
- Креды уже в `/workspaces/aidyn/.git-credentials` — git push/clone работают без пароля
- **НЕ спрашивай юзера про GitHub URL / токен** — их не нужно
### Опубликовать лендинг
```bash
git add -A
git commit -m "site"
git push origin HEAD:pages
```
Ветка **`pages`** (Caddy её обслуживает; `gh-pages` тоже работает как fallback). Push → лендинг доступен мгновенно.
---
## 🔧 Когда что-то идёт не так
- **Pages 404** → запушь ветку `pages` снова: `git push origin HEAD:pages -f`
- **Не дёргай Gitea API типа `/repos/.../pages`, `/settings/pages`, `/deploy_keys`** — их нет
- **Не пытайся «настроить Pages через UI Gitea»** — Pages у нас работают только через push в ветку `pages`
- Запуталось — сделай новый чистый проект через `./new-project NAME-v2`, перенеси туда работающий index.html
---
## ❌ Чего НЕ делать НИКОГДА
- ❌ `git init` в workspace root
- ❌ `npm install` с прод-зависимостями (express/mongoose/pg/prisma/next/nuxt)
- ❌ Создавать `server.js` / `app.py` / `main.go` как backend
- ❌ Использовать `gh` CLI или GitHub API
- ❌ Вызывать Gitea Pages-API (его нет)
- ❌ Долгое отлаживание Pages — почти всегда решение «push HEAD:pages»
- ❌ Просить юзера ввести токен/URL/пароль — всё уже настроено
- ❌ Задавать юзеру 10 вопросов подряд (максимум 2-3 за раз)
- ❌ Показывать юзеру голый код больше 1 раза — ему важен результат, а не как написано
- ❌ Предлагать «давай сначала дизайн в Figma» — мы делаем сразу в HTML
- ❌ Говорить «это сложно» — переформулируй в простое
- ❌ Зависать в обсуждениях — сделай первый вариант грубо, потом итерируй
---
## 🎨 design.md
Рядом лежит `design.md` с готовой палитрой, типографикой и стартер-шаблоном `index.html`. **Начинай с него.** Не выдумывай новые цвета — модифицируй существующие.

110
design.md Normal file
View File

@ -0,0 +1,110 @@
<!-- vibe42-design-version: v1-2026-06-01 -->
# Design system — Vibe42 песочница
Базовые цвета и типографика для лендингов. Можно отклоняться, но начинай с этого.
## Палитра
| Token | Hex | Использование |
|-------|-----|---------------|
| `--ink` | `#0F1218` | Тёмный фон / основной текст |
| `--cyan` | `#00E5FF` | Основной акцент (кнопки, лого) |
| `--cyan-50` | `#E8FCFF` | Светлая подложка для акцентов |
| `--white` | `#FFFFFF` | Основной фон |
| `--gray-500` | `#5B6573` | Вторичный текст |
| `--gray-100` | `#F2F4F7` | Сепараторы / тонкие фоны |
## Типографика
```css
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
```
| Уровень | Размер | Вес | line-height |
|---------|--------|-----|-------------|
| h1 (hero) | 56px | 800 | 1.05 |
| h2 (section) | 36px | 700 | 1.15 |
| h3 | 22px | 700 | 1.3 |
| body | 17px | 400 | 1.6 |
| small | 14px | 400 | 1.5 |
На мобиле — h1 уменьши до 36px, h2 до 28px.
## Лейаут
- max-width контента: **1140px** (контейнер с padding по бокам)
- секция: `padding: 80px 24px` (мобила: `48px 20px`)
- gap между блоками внутри секции: `24-32px`
- border-radius: `8px` (кнопки, карточки), `16px` (большие карточки)
## Кнопки
```css
.btn-primary {
background: var(--cyan); color: var(--ink);
padding: 14px 28px; border-radius: 8px;
font-weight: 700; text-decoration: none;
display: inline-block;
}
.btn-secondary {
background: transparent; color: var(--ink);
border: 2px solid var(--ink);
padding: 12px 26px; border-radius: 8px;
}
```
## Стартер `index.html`
```html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>Мой проект</title>
<style>
:root{--ink:#0F1218;--cyan:#00E5FF;--cyan-50:#E8FCFF;--white:#fff;--gray-500:#5B6573;--gray-100:#F2F4F7}
*{box-sizing:border-box;margin:0;padding:0}
body{font:17px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Inter,system-ui,sans-serif;color:var(--ink);background:var(--white)}
.container{max-width:1140px;margin:0 auto;padding:80px 24px}
.hero{background:var(--ink);color:var(--white)}
.hero h1{font-size:56px;font-weight:800;line-height:1.05;margin-bottom:24px}
.hero p{font-size:20px;color:#9aa3b2;max-width:600px;margin-bottom:32px}
.btn{display:inline-block;background:var(--cyan);color:var(--ink);padding:14px 28px;border-radius:8px;font-weight:700;text-decoration:none}
.btn:hover{background:#1be5ff}
.section h2{font-size:36px;font-weight:700;margin-bottom:24px}
.card{background:var(--gray-100);border-radius:16px;padding:32px;margin-bottom:16px}
@media (max-width:640px){.hero h1{font-size:36px}.section h2{font-size:28px}.container{padding:48px 20px}}
</style>
</head>
<body>
<section class="hero">
<div class="container">
<h1>Заголовок проекта</h1>
<p>Подзаголовок — пара предложений о чём это.</p>
<a class="btn" href="#section">Начать</a>
</div>
</section>
<section id="section" class="section">
<div class="container">
<h2>Секция</h2>
<div class="card">Контент карточки.</div>
<div class="card">Контент карточки.</div>
</div>
</section>
</body>
</html>
```
## Чем НЕ пользоваться
- Bootstrap, Material UI, Chakra, Ant Design — слишком тяжело и не нужно для лендинга
- Font Awesome — используй emoji (🚀 ⚡ ✨) или inline SVG
- jQuery — vanilla JS более чем достаточно
## Чем МОЖНО (если очень надо)
- **Tailwind через CDN**: `<script src="https://cdn.tailwindcss.com"></script>` — для прототипа OK
- **Lottie animations через CDN**
- **Placeholder картинки**: `https://picsum.photos/800/600`, `https://placehold.co/600x400`
- **Шрифты Google Fonts через `<link>`** в head

334
index.html Normal file
View File

@ -0,0 +1,334 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>LMS Панель управления</title>
<style>
:root {
--ink: #0F1218;
--blue: #1E90FF;
--blue-50: #E6F2FF;
--white: #FFFFFF;
--gray-500: #5B6573;
--gray-100: #F2F4F7;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font: 17px/1.6 -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, system-ui, sans-serif;
color: var(--ink);
background: var(--white);
}
.container { max-width: 1140px; margin: 0 auto; padding: 24px; }
/* Авторизация */
.login-section {
background: var(--ink);
color: var(--white);
padding: 80px 24px;
text-align: center;
}
.login-form {
background: var(--white);
padding: 32px;
border-radius: 16px;
max-width: 400px;
margin: 0 auto;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
.login-form h2 {
margin-bottom: 24px;
color: var(--ink);
}
.form-group {
margin-bottom: 16px;
text-align: left;
}
.form-group label {
display: block;
margin-bottom: 8px;
font-weight: 600;
}
.form-group input {
width: 100%;
padding: 12px;
border: 2px solid var(--gray-100);
border-radius: 8px;
font-size: 16px;
}
.btn {
display: inline-block;
background: var(--blue);
color: var(--white);
padding: 14px 28px;
border-radius: 8px;
font-weight: 700;
text-decoration: none;
border: none;
cursor: pointer;
font-size: 16px;
width: 100%;
}
.btn:hover { background: #1a7ad9; }
/* Панель управления (скрыта по умолчанию) */
.dashboard { display: none; }
.dashboard-header {
background: var(--ink);
color: var(--white);
padding: 24px;
margin-bottom: 32px;
border-radius: 16px;
}
.dashboard-header h1 {
font-size: 28px;
font-weight: 700;
margin-bottom: 8px;
}
.dashboard-header p { color: #9aa3b2; }
/* Навигация */
.nav-tabs {
display: flex;
gap: 8px;
margin-bottom: 32px;
flex-wrap: wrap;
}
.nav-tab {
padding: 12px 24px;
background: var(--gray-100);
border: none;
border-radius: 8px;
font-weight: 600;
cursor: pointer;
transition: background 0.2s;
}
.nav-tab:hover { background: var(--blue-50); }
.nav-tab.active {
background: var(--blue);
color: var(--white);
}
.nav-tab.admin-tab {
background: #FF6B35;
color: var(--white);
}
.nav-tab.admin-tab:hover { background: #e55a2b; }
/* Секции контента */
.tab-content {
display: none;
background: var(--white);
padding: 32px;
border-radius: 16px;
box-shadow: 0 2px 8px rgba(0,0,0,0.05);
}
.tab-content.active { display: block; }
.tab-content h2 {
font-size: 24px;
font-weight: 700;
margin-bottom: 16px;
color: var(--ink);
}
.tab-content p {
color: var(--gray-500);
line-height: 1.6;
}
.card {
background: var(--gray-100);
padding: 24px;
border-radius: 12px;
margin-bottom: 16px;
}
/* Мобильная адаптация */
@media (max-width: 640px) {
.login-section { padding: 48px 20px; }
.dashboard-header h1 { font-size: 22px; }
.nav-tabs { flex-direction: column; }
.nav-tab { width: 100%; }
}
</style>
</head>
<body>
<!-- Секция авторизации -->
<section class="login-section" id="loginSection">
<div class="container">
<h1 style="font-size: 36px; font-weight: 800; margin-bottom: 16px;">LMS Панель управления</h1>
<p style="color: #9aa3b2; margin-bottom: 32px;">Войдите в систему управления обучением</p>
<div class="login-form">
<h2>Вход в систему</h2>
<form id="loginForm">
<div class="form-group">
<label for="username">Логин</label>
<input type="text" id="username" placeholder="Введите логин" required>
</div>
<div class="form-group">
<label for="password">Пароль</label>
<input type="password" id="password" placeholder="Введите пароль" required>
</div>
<button type="submit" class="btn">Войти</button>
</form>
<p style="margin-top: 16px; font-size: 14px; color: var(--gray-500);">
Для демонстрации: admin/admin или user/user
</p>
</div>
</div>
</section>
<!-- Панель управления (скрыта) -->
<section class="dashboard" id="dashboard">
<div class="container">
<div class="dashboard-header">
<h1>Добро пожаловать!</h1>
<p id="userRole">Вы вошли как: Участник</p>
</div>
<!-- Навигация -->
<div class="nav-tabs" id="navTabs">
<!-- Вкладки участника -->
<button class="nav-tab active" data-tab="schedule">Расписание курсов</button>
<button class="nav-tab" data-tab="courses">Мои курсы</button>
<button class="nav-tab" data-tab="reports">Отчет</button>
<button class="nav-tab" data-tab="certificates">Сертификаты</button>
<!-- Вкладки админа (скрыты по умолчанию) -->
<button class="nav-tab admin-tab" data-tab="dashboard-admin" style="display: none;">Дашборд</button>
<button class="nav-tab admin-tab" data-tab="create-course" style="display: none;">Создать обучение</button>
</div>
<!-- Контент вкладок -->
<div class="tab-content active" id="tab-schedule">
<h2>Расписание курсов</h2>
<div class="card">
<h3>Активные курсы</h3>
<p>У вас 3 активных курса. Ближайшее занятие: "Введение в программирование" — завтра в 18:00.</p>
</div>
<div class="card">
<h3>Предстоящие экзамены</h3>
<p>Экзамен по курсу "Основы дизайна" — 25 июня. Подготовьте портфолио.</p>
</div>
</div>
<div class="tab-content" id="tab-courses">
<h2>Мои курсы</h2>
<div class="card">
<h3>Введение в программирование</h3>
<p>Прогресс: 65%. Осталось 4 урока. Преподаватель: Иван Петров.</p>
</div>
<div class="card">
<h3>Основы дизайна</h3>
<p>Прогресс: 30%. Осталось 8 уроков. Преподаватель: Мария Сидорова.</p>
</div>
</div>
<div class="tab-content" id="tab-reports">
<h2>Отчет</h2>
<div class="card">
<h3>Статистика обучения</h3>
<p>Посещаемость: 92%. Средний балл: 4.5. Активность: 12 часов в неделю.</p>
</div>
<div class="card">
<h3>Достижения</h3>
<p>Получено 2 сертификата. Вы в топ-10% лучших студентов.</p>
</div>
</div>
<div class="tab-content" id="tab-certificates">
<h2>Сертификаты</h2>
<div class="card">
<h3>Сертификат об окончании курса</h3>
<p>Курс "Основы маркетинга". Дата выдачи: 1 июня 2025. Скачать PDF.</p>
</div>
<div class="card">
<h3>Сертификат достижений</h3>
<p>За активное участие в проектах. Дата выдачи: 15 мая 2025.</p>
</div>
</div>
<!-- Секции админа -->
<div class="tab-content" id="tab-dashboard-admin">
<h2>Административный дашборд</h2>
<div class="card">
<h3>Общая статистика</h3>
<p>Всего студентов: 156. Активных курсов: 12. Завершений в этом месяце: 23.</p>
</div>
<div class="card">
<h3>Последние действия</h3>
<p>Новый студент зарегистрировался: Алексей Козлов. Курс "Python" завершил: 5 студентов.</p>
</div>
</div>
<div class="tab-content" id="tab-create-course">
<h2>Создать обучение</h2>
<div class="card">
<h3>Форма создания курса</h3>
<p>Заполните название, описание и загрузите материалы. Нажмите "Сохранить" для публикации.</p>
</div>
<div class="card">
<h3>Шаблоны курсов</h3>
<p>Выберите готовый шаблон: "Стандартный", "Интерактивный", "Экспресс-курс".</p>
</div>
</div>
</div>
</section>
<script>
// Имитация авторизации
document.getElementById('loginForm').addEventListener('submit', function(e) {
e.preventDefault();
const username = document.getElementById('username').value;
const password = document.getElementById('password').value;
// Простая проверка для демонстрации
if (username === 'admin' && password === 'admin') {
login('admin');
} else if (username === 'user' && password === 'user') {
login('user');
} else {
alert('Неверный логин или пароль. Используйте admin/admin или user/user');
}
});
function login(role) {
// Скрыть форму входа, показать дашборд
document.getElementById('loginSection').style.display = 'none';
document.getElementById('dashboard').style.display = 'block';
// Установить роль
document.getElementById('userRole').textContent = `Вы вошли как: ${role === 'admin' ? 'Администратор' : 'Участник'}`;
// Показать вкладки в зависимости от роли
const adminTabs = document.querySelectorAll('.admin-tab');
adminTabs.forEach(tab => {
tab.style.display = role === 'admin' ? 'block' : 'none';
});
// Сохранить роль в localStorage
localStorage.setItem('userRole', role);
}
// Переключение вкладок
document.querySelectorAll('.nav-tab').forEach(tab => {
tab.addEventListener('click', function() {
// Убрать активный класс у всех вкладок
document.querySelectorAll('.nav-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
// Добавить активный класс текущей вкладке
this.classList.add('active');
const tabId = 'tab-' + this.getAttribute('data-tab');
document.getElementById(tabId).classList.add('active');
});
});
// Проверить, есть ли сохраненная роль (для автоматического входа)
const savedRole = localStorage.getItem('userRole');
if (savedRole) {
login(savedRole);
}
</script>
</body>
</html>