@mashka818/exam-de-template 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/exam-guides/COMMENTS_GUIDE.md +53 -0
- package/exam-guides/EXAM_COMMANDS.txt +47 -0
- package/exam-guides/GUIDE_PAGES.md +529 -0
- package/exam-guides/NPM_PACKAGE.md +206 -0
- package/exam-guides/README.md +40 -0
- package/exam-guides/RESPONSIVE.md +224 -0
- package/exam-guides/TECH_STACK.txt +142 -0
- package/exam-guides/THEME_BANQUETAM_NET.md +106 -0
- package/exam-guides/commented-code/README.txt +5 -0
- package/exam-guides/commented-code/client/index.html +14 -0
- package/exam-guides/commented-code/client/package-lock.json +2298 -0
- package/exam-guides/commented-code/client/package.json +21 -0
- package/exam-guides/commented-code/client/public/images/README.txt +26 -0
- package/exam-guides/commented-code/client/public/images/about-cleaning.svg +4 -0
- package/exam-guides/commented-code/client/public/images/admin-banner.svg +1 -0
- package/exam-guides/commented-code/client/public/images/empty-requests.svg +1 -0
- package/exam-guides/commented-code/client/public/images/footer-photo-1.svg +4 -0
- package/exam-guides/commented-code/client/public/images/footer-photo-2.svg +4 -0
- package/exam-guides/commented-code/client/public/images/footer-photo-3.svg +4 -0
- package/exam-guides/commented-code/client/public/images/home-hero.svg +4 -0
- package/exam-guides/commented-code/client/public/images/login-banner.svg +1 -0
- package/exam-guides/commented-code/client/public/images/logo.svg +4 -0
- package/exam-guides/commented-code/client/public/images/new-request-banner.svg +1 -0
- package/exam-guides/commented-code/client/public/images/register-banner.svg +1 -0
- package/exam-guides/commented-code/client/public/images/requests-banner.svg +1 -0
- package/exam-guides/commented-code/client/public/images/slide-1.svg +6 -0
- package/exam-guides/commented-code/client/public/images/slide-2.svg +5 -0
- package/exam-guides/commented-code/client/public/images/slide-3.svg +5 -0
- package/exam-guides/commented-code/client/src/App.jsx +72 -0
- package/exam-guides/commented-code/client/src/api.js +71 -0
- package/exam-guides/commented-code/client/src/components/FormField.jsx +25 -0
- package/exam-guides/commented-code/client/src/components/Layout.jsx +83 -0
- package/exam-guides/commented-code/client/src/components/PageImage.jsx +38 -0
- package/exam-guides/commented-code/client/src/components/ProtectedRoute.jsx +35 -0
- package/exam-guides/commented-code/client/src/components/UserNav.jsx +33 -0
- package/exam-guides/commented-code/client/src/components/landing/HeroSlider.jsx +103 -0
- package/exam-guides/commented-code/client/src/components/landing/LandingLayout.jsx +76 -0
- package/exam-guides/commented-code/client/src/components/landing/SiteFooter.jsx +74 -0
- package/exam-guides/commented-code/client/src/config/images.js +104 -0
- package/exam-guides/commented-code/client/src/constants/services.js +19 -0
- package/exam-guides/commented-code/client/src/context/AuthContext.jsx +72 -0
- package/exam-guides/commented-code/client/src/index.css +73 -0
- package/exam-guides/commented-code/client/src/main.jsx +28 -0
- package/exam-guides/commented-code/client/src/pages/AdminPage.jsx +151 -0
- package/exam-guides/commented-code/client/src/pages/LandingPage.jsx +131 -0
- package/exam-guides/commented-code/client/src/pages/LoginPage.jsx +81 -0
- package/exam-guides/commented-code/client/src/pages/RegisterPage.jsx +117 -0
- package/exam-guides/commented-code/client/src/pages/RequestFormPage.jsx +196 -0
- package/exam-guides/commented-code/client/src/pages/RequestsPage.jsx +112 -0
- package/exam-guides/commented-code/client/src/utils/validation.js +71 -0
- package/exam-guides/commented-code/client/vite.config.js +31 -0
- package/exam-guides/commented-code/server/db/init.js +67 -0
- package/exam-guides/commented-code/server/db/pool.js +23 -0
- package/exam-guides/commented-code/server/db/schema.sql +53 -0
- package/exam-guides/commented-code/server/db/seed.sql +15 -0
- package/exam-guides/commented-code/server/index.js +45 -0
- package/exam-guides/commented-code/server/middleware/auth.js +38 -0
- package/exam-guides/commented-code/server/package-lock.json +1084 -0
- package/exam-guides/commented-code/server/package.json +17 -0
- package/exam-guides/commented-code/server/routes/admin.js +96 -0
- package/exam-guides/commented-code/server/routes/auth.js +128 -0
- package/exam-guides/commented-code/server/routes/requests.js +115 -0
- package/exam-guides/commented-code/server/routes/services.js +31 -0
- package/exam-guides/commented-code/server/utils/validation.js +81 -0
- package/exam-guides/exam-starter/README.txt +22 -0
- package/exam-guides/exam-starter/package.json +13 -0
- package/exam-guides//320/243/320/224/320/220/320/233/320/230/320/242/320/254-/320/237/320/225/320/240/320/225/320/224-/320/241/320/224/320/220/320/247/320/225/320/231.txt +9 -0
- package/exam-project/README.md +16 -0
- package/exam-project/client/index.html +14 -0
- package/exam-project/client/package-lock.json +2298 -0
- package/exam-project/client/package.json +21 -0
- package/exam-project/client/public/images/README.txt +26 -0
- package/exam-project/client/public/images/about-cleaning.svg +4 -0
- package/exam-project/client/public/images/admin-banner.svg +1 -0
- package/exam-project/client/public/images/empty-requests.svg +1 -0
- package/exam-project/client/public/images/footer-photo-1.svg +4 -0
- package/exam-project/client/public/images/footer-photo-2.svg +4 -0
- package/exam-project/client/public/images/footer-photo-3.svg +4 -0
- package/exam-project/client/public/images/home-hero.svg +4 -0
- package/exam-project/client/public/images/login-banner.svg +1 -0
- package/exam-project/client/public/images/logo.svg +4 -0
- package/exam-project/client/public/images/new-request-banner.svg +1 -0
- package/exam-project/client/public/images/register-banner.svg +1 -0
- package/exam-project/client/public/images/requests-banner.svg +1 -0
- package/exam-project/client/public/images/slide-1.svg +6 -0
- package/exam-project/client/public/images/slide-2.svg +5 -0
- package/exam-project/client/public/images/slide-3.svg +5 -0
- package/exam-project/client/src/App.jsx +52 -0
- package/exam-project/client/src/api.js +50 -0
- package/exam-project/client/src/components/FormField.jsx +11 -0
- package/exam-project/client/src/components/Layout.jsx +61 -0
- package/exam-project/client/src/components/PageImage.jsx +22 -0
- package/exam-project/client/src/components/ProtectedRoute.jsx +20 -0
- package/exam-project/client/src/components/UserNav.jsx +20 -0
- package/exam-project/client/src/components/landing/HeroSlider.jsx +89 -0
- package/exam-project/client/src/components/landing/LandingLayout.jsx +61 -0
- package/exam-project/client/src/components/landing/SiteFooter.jsx +61 -0
- package/exam-project/client/src/config/images.js +83 -0
- package/exam-project/client/src/constants/services.js +7 -0
- package/exam-project/client/src/context/AuthContext.jsx +55 -0
- package/exam-project/client/src/index.css +54 -0
- package/exam-project/client/src/main.jsx +17 -0
- package/exam-project/client/src/pages/AdminPage.jsx +128 -0
- package/exam-project/client/src/pages/LandingPage.jsx +115 -0
- package/exam-project/client/src/pages/LoginPage.jsx +63 -0
- package/exam-project/client/src/pages/RegisterPage.jsx +97 -0
- package/exam-project/client/src/pages/RequestFormPage.jsx +178 -0
- package/exam-project/client/src/pages/RequestsPage.jsx +94 -0
- package/exam-project/client/src/utils/validation.js +54 -0
- package/exam-project/client/vite.config.js +17 -0
- package/exam-project/package.json +18 -0
- package/exam-project/scripts/init-project.js +86 -0
- package/exam-project/scripts/prepack.js +27 -0
- package/exam-project/scripts/unpack-template.js +105 -0
- package/exam-project/server/db/init.js +50 -0
- package/exam-project/server/db/pool.js +11 -0
- package/exam-project/server/db/schema.sql +41 -0
- package/exam-project/server/db/seed.sql +12 -0
- package/exam-project/server/index.js +29 -0
- package/exam-project/server/middleware/auth.js +24 -0
- package/exam-project/server/package-lock.json +1084 -0
- package/exam-project/server/package.json +17 -0
- package/exam-project/server/routes/admin.js +76 -0
- package/exam-project/server/routes/auth.js +109 -0
- package/exam-project/server/routes/requests.js +99 -0
- package/exam-project/server/routes/services.js +18 -0
- package/exam-project/server/utils/validation.js +63 -0
- package/package.json +25 -0
- package/scripts/init-project.js +86 -0
- package/scripts/prepack.js +27 -0
- package/scripts/unpack-template.js +105 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# Шпаргалка: что менять при другой теме
|
|
2
|
+
|
|
3
|
+
В **каждом** файле `client/src/**` и `server/**` (кроме `node_modules`) в начале есть блок `===` с пояснением: что менять, куда смотреть, метки `БАНКЕТАМ.НЕТ:`. Старые комментарии про клининг **не удаляйте**.
|
|
4
|
+
|
|
5
|
+
Поиск по проекту: `БАНКЕТАМ.НЕТ` | `ЗАМЕНИТЕ` | `GUIDE_PAGES`
|
|
6
|
+
|
|
7
|
+
## Тема «Банкетам.Нет» (ваш вариант 09.02.07)
|
|
8
|
+
|
|
9
|
+
| Файл | Для чего |
|
|
10
|
+
|------|----------|
|
|
11
|
+
| **`GUIDE_PAGES.md`** | **Главный гайд:** кнопки, картинки, секции, новая страница, формы, админ-фильтр |
|
|
12
|
+
| **`THEME_BANQUETAM_NET.md`** | Таблица замен «клининг → банкеты» |
|
|
13
|
+
| **`RESPONSIVE.md`** | Мобильный 390px и ПК |
|
|
14
|
+
|
|
15
|
+
В коде ищите **`БАНКЕТАМ.НЕТ:`** — старые комментарии про клининг **не удаляйте**, они для шаблона «Мой Не Сам».
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Картинки (Figma → проект)
|
|
20
|
+
|
|
21
|
+
1. Экспорт PNG в **`client/public/images/`**
|
|
22
|
+
2. Имена файлов и `alt` — в **`client/src/config/images.js`**
|
|
23
|
+
3. На страницах только `<PageImage imageKey="..." />`, пути вручную не пишем
|
|
24
|
+
|
|
25
|
+
## Бэкенд
|
|
26
|
+
|
|
27
|
+
| Файл | Что менять |
|
|
28
|
+
|------|------------|
|
|
29
|
+
| `server/db/schema.sql` | Таблицы, поля, статусы CHECK |
|
|
30
|
+
| `server/db/seed.sql` | Список услуг |
|
|
31
|
+
| `server/db/init.js` | adminka/password |
|
|
32
|
+
| `server/utils/validation.js` | Правила полей |
|
|
33
|
+
| `server/routes/*.js` | SQL-запросы, подписи статусов |
|
|
34
|
+
|
|
35
|
+
## Фронтенд
|
|
36
|
+
|
|
37
|
+
| Файл | Что менять |
|
|
38
|
+
|------|------------|
|
|
39
|
+
| `client/src/config/images.js` | Файлы картинок и alt |
|
|
40
|
+
| `client/src/components/Layout.jsx` | Название портала, цвета шапок |
|
|
41
|
+
| `client/src/pages/*.jsx` | Тексты, баннеры imageKey |
|
|
42
|
+
| `client/src/utils/validation.js` | Как на сервере |
|
|
43
|
+
| `client/index.html` | `<title>` |
|
|
44
|
+
|
|
45
|
+
## Сущности
|
|
46
|
+
|
|
47
|
+
- `requests` → заявка на уборку (другая тема: заказ, бронь)
|
|
48
|
+
- `service_types` → виды услуг из задания
|
|
49
|
+
- `users.role`: `user` | `admin`
|
|
50
|
+
|
|
51
|
+
## Адаптив
|
|
52
|
+
|
|
53
|
+
Мобильный макет 390×844 + версия под ПК — **`RESPONSIVE.md`**
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
================================================================================
|
|
2
|
+
ЭКЗАМЕН — одна команда, две папки
|
|
3
|
+
================================================================================
|
|
4
|
+
Нужны: Node.js, PostgreSQL, интернет, пакет на npm (см. NPM_PACKAGE.md)
|
|
5
|
+
|
|
6
|
+
Пакет: @mashka818/exam-de-template
|
|
7
|
+
|
|
8
|
+
--------------------------------------------------------------------------------
|
|
9
|
+
НА ЭКЗАМЕНЕ
|
|
10
|
+
--------------------------------------------------------------------------------
|
|
11
|
+
|
|
12
|
+
mkdir C:\DE\work
|
|
13
|
+
cd C:\DE\work
|
|
14
|
+
npx @mashka818/exam-de-template@1.0.0 init
|
|
15
|
+
|
|
16
|
+
Появятся:
|
|
17
|
+
exam-project\ — правите и запускаете ЗДЕСЬ
|
|
18
|
+
exam-guides\ — шпоры (удалить перед сдачей)
|
|
19
|
+
|
|
20
|
+
cd exam-project
|
|
21
|
+
copy server\.env.example server\.env
|
|
22
|
+
notepad server\.env
|
|
23
|
+
npm run db:init
|
|
24
|
+
npm run dev
|
|
25
|
+
|
|
26
|
+
Сайт: http://localhost:5173
|
|
27
|
+
|
|
28
|
+
--------------------------------------------------------------------------------
|
|
29
|
+
ПЕРЕД СДАЧЕЙ
|
|
30
|
+
--------------------------------------------------------------------------------
|
|
31
|
+
|
|
32
|
+
cd ..
|
|
33
|
+
rmdir /s /q exam-guides
|
|
34
|
+
|
|
35
|
+
(остаётся только exam-project — его и показываете)
|
|
36
|
+
|
|
37
|
+
--------------------------------------------------------------------------------
|
|
38
|
+
ДОМА (один раз)
|
|
39
|
+
--------------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
npm run sync:project
|
|
42
|
+
cd exam-project
|
|
43
|
+
npm publish --access public
|
|
44
|
+
|
|
45
|
+
Подробно: NPM_PACKAGE.md
|
|
46
|
+
|
|
47
|
+
================================================================================
|
|
@@ -0,0 +1,529 @@
|
|
|
1
|
+
# Полный гайд: как править шаблон на экзамене
|
|
2
|
+
|
|
3
|
+
Этот файл — **пошаговая инструкция**, как из шаблона «Мой Не Сам» сделать другую тему (например **«Банкетам.Нет»**).
|
|
4
|
+
|
|
5
|
+
- В коде ищите **`БАНКЕТАМ.НЕТ:`** — короткие подсказки у строк.
|
|
6
|
+
- Таблица замен темы: **`THEME_BANQUETAM_NET.md`**.
|
|
7
|
+
- Запуск проекта: **`README.md`**.
|
|
8
|
+
|
|
9
|
+
**Важно:** старые комментарии про клининг **не удаляйте** — они описывают текущий шаблон. Добавляйте свои правки рядом или по гайду ниже.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## 1. Карта проекта (что за что отвечает)
|
|
14
|
+
|
|
15
|
+
```
|
|
16
|
+
examTemplate/
|
|
17
|
+
├── client/
|
|
18
|
+
│ ├── public/images/ ← файлы картинок (PNG/SVG из Figma)
|
|
19
|
+
│ ├── src/
|
|
20
|
+
│ │ ├── config/images.js ← имена файлов и alt (НЕ пути вручную в JSX!)
|
|
21
|
+
│ │ ├── pages/ ← страницы (один файл ≈ один экран)
|
|
22
|
+
│ │ ├── components/ ← общие части (Layout, FormField…)
|
|
23
|
+
│ │ ├── components/landing/ ← лендинг: слайдер, футер
|
|
24
|
+
│ │ ├── App.jsx ← URL → какая страница открывается
|
|
25
|
+
│ │ ├── api.js ← все запросы к серверу
|
|
26
|
+
│ │ └── utils/validation.js ← проверка полей на форме
|
|
27
|
+
│ └── index.html ← <title> вкладки браузера
|
|
28
|
+
├── server/
|
|
29
|
+
│ ├── db/schema.sql ← таблицы
|
|
30
|
+
│ ├── db/seed.sql ← справочник (услуги / помещения)
|
|
31
|
+
│ ├── db/init.js ← админ при npm run db:init
|
|
32
|
+
│ ├── routes/ ← API
|
|
33
|
+
│ └── utils/validation.js ← проверка на сервере (как на клиенте!)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### URL → файл страницы
|
|
37
|
+
|
|
38
|
+
| Адрес | Файл | Задание ДЭ |
|
|
39
|
+
|-------|------|------------|
|
|
40
|
+
| `/` | `LandingPage.jsx` | лендинг (не в п.1–5, но для защиты) |
|
|
41
|
+
| `/register` | `RegisterPage.jsx` | п.1 регистрация |
|
|
42
|
+
| `/login` | `LoginPage.jsx` | п.2 вход |
|
|
43
|
+
| `/requests` | `RequestsPage.jsx` | п.3 личный кабинет / история |
|
|
44
|
+
| `/requests/form` | `RequestFormPage.jsx` | п.4 оформление заявки |
|
|
45
|
+
| `/admin` | `AdminPage.jsx` | п.5 админ |
|
|
46
|
+
|
|
47
|
+
Маршруты прописаны в **`client/src/App.jsx`**.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 2. Как заменить название и тексты (самое частое)
|
|
52
|
+
|
|
53
|
+
### 2.1. Название сайта в шапке
|
|
54
|
+
|
|
55
|
+
**Файл:** `client/src/components/Layout.jsx`
|
|
56
|
+
|
|
57
|
+
Найдите:
|
|
58
|
+
|
|
59
|
+
```javascript
|
|
60
|
+
const brandName = 'Мой Не Сам'; // ← заменить текст в кавычках
|
|
61
|
+
const brandTagline = 'Клининг без хлопот';
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**Лендинг** (отдельная шапка): `client/src/components/landing/LandingLayout.jsx` — там тоже `brandName`.
|
|
65
|
+
|
|
66
|
+
### 2.2. Заголовок вкладки браузера
|
|
67
|
+
|
|
68
|
+
**Файл:** `client/index.html`
|
|
69
|
+
|
|
70
|
+
```html
|
|
71
|
+
<title>Мой Не Сам — портал клининговых услуг</title>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 2.3. Тексты на конкретной странице
|
|
75
|
+
|
|
76
|
+
Откройте файл из `client/src/pages/`. Ищите:
|
|
77
|
+
|
|
78
|
+
- `<h1>`, `<h2>`, `<p>` — заголовки и абзацы;
|
|
79
|
+
- текст внутри `<Link>…</Link>` и `<button>…</button>`;
|
|
80
|
+
- `placeholder="..."` у `<input>`.
|
|
81
|
+
|
|
82
|
+
**Пример** (`RegisterPage.jsx`):
|
|
83
|
+
|
|
84
|
+
```jsx
|
|
85
|
+
<h2 className="text-xl font-bold text-teal-900 mb-1">Регистрация</h2>
|
|
86
|
+
<p className="text-sm text-slate-500 mb-6">Все поля обязательны</p>
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Меняете только русский текст между тегами. **Классы** (`className="..."`) трогайте только если меняете дизайн.
|
|
90
|
+
|
|
91
|
+
### 2.4. Подписи полей формы
|
|
92
|
+
|
|
93
|
+
Используется компонент **`FormField`**:
|
|
94
|
+
|
|
95
|
+
```jsx
|
|
96
|
+
<FormField label="ФИО" error={errors.fullName}>
|
|
97
|
+
<input ... />
|
|
98
|
+
</FormField>
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Меняете **`label="..."`**. Для банкетов: `label="Помещение"`, `label="Дата начала банкета"` и т.д.
|
|
102
|
+
|
|
103
|
+
---
|
|
104
|
+
|
|
105
|
+
## 3. Картинки: куда класть и как подключить
|
|
106
|
+
|
|
107
|
+
### 3.1. Куда положить файл
|
|
108
|
+
|
|
109
|
+
Папка: **`client/public/images/`**
|
|
110
|
+
|
|
111
|
+
Пример: `client/public/images/logo.png`
|
|
112
|
+
|
|
113
|
+
В браузере картинка откроется как: **`http://localhost:5173/images/logo.png`**
|
|
114
|
+
|
|
115
|
+
### 3.2. Зарегистрировать в конфиге (обязательно!)
|
|
116
|
+
|
|
117
|
+
**Файл:** `client/src/config/images.js`
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
export const IMAGES = {
|
|
121
|
+
logo: {
|
|
122
|
+
file: 'logo.png', // имя файла в public/images/
|
|
123
|
+
alt: 'Логотип Банкетам.Нет', // для доступности
|
|
124
|
+
},
|
|
125
|
+
// добавьте новый ключ:
|
|
126
|
+
myBanner: {
|
|
127
|
+
file: 'my-banner.png',
|
|
128
|
+
alt: 'Описание картинки',
|
|
129
|
+
},
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### 3.3. Показать на странице
|
|
134
|
+
|
|
135
|
+
**Не пишите** `src="/images/logo.png"` напрямую (так тоже можно, но в шаблоне принят единый способ):
|
|
136
|
+
|
|
137
|
+
```jsx
|
|
138
|
+
import PageImage from '../components/PageImage.jsx';
|
|
139
|
+
|
|
140
|
+
<PageImage imageKey="logo" className="h-9 w-auto" />
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
- `imageKey` — ключ из `IMAGES` (`logo`, `homeHero`, `registerBanner`…).
|
|
144
|
+
- `className` — размер и скругление (Tailwind).
|
|
145
|
+
|
|
146
|
+
### 3.4. Добавить новую картинку с нуля
|
|
147
|
+
|
|
148
|
+
1. Положите `new-photo.png` в `public/images/`.
|
|
149
|
+
2. В `images.js` добавьте блок с ключом, например `newPhoto: { file: 'new-photo.png', alt: '...' }`.
|
|
150
|
+
3. На странице: `<PageImage imageKey="newPhoto" className="w-full h-40 object-cover rounded-xl" />`.
|
|
151
|
+
|
|
152
|
+
### 3.5. Заменить картинку (Figma → PNG)
|
|
153
|
+
|
|
154
|
+
1. Экспорт из Figma с **тем же именем** (например `register-banner.png`).
|
|
155
|
+
2. Замените файл в `public/images/` (перезапишите).
|
|
156
|
+
3. В `images.js` поменяйте `file: 'register-banner.svg'` → `file: 'register-banner.png'`.
|
|
157
|
+
4. Обновите страницу в браузере (F5).
|
|
158
|
+
|
|
159
|
+
### 3.6. Слайдер (4 картинки)
|
|
160
|
+
|
|
161
|
+
**Файлы:** `HeroSlider.jsx`, `images.js`, `public/images/slide-1.png` … `slide-4.png`
|
|
162
|
+
|
|
163
|
+
В `HeroSlider.jsx`:
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
const SLIDE_KEYS = ['slide1', 'slide2', 'slide3', 'slide4']; // добавить slide4
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
В `images.js` — ключи `slide1`…`slide4`.
|
|
170
|
+
|
|
171
|
+
Интервал смены:
|
|
172
|
+
|
|
173
|
+
```javascript
|
|
174
|
+
}, 5000); // миллисекунды → для 3 сек: 3000
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
Тексты на слайдах — объект `slideText` в том же файле.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## 4. Кнопки и ссылки
|
|
182
|
+
|
|
183
|
+
### 4.1. Кнопка «отправить форму»
|
|
184
|
+
|
|
185
|
+
Уже есть на формах:
|
|
186
|
+
|
|
187
|
+
```jsx
|
|
188
|
+
<button type="submit" className="w-full rounded-xl bg-teal-600 text-white py-3 ...">
|
|
189
|
+
Зарегистрироваться
|
|
190
|
+
</button>
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
Меняете только текст между тегами. `type="submit"` не убирайте.
|
|
194
|
+
|
|
195
|
+
### 4.2. Ссылка на другую страницу
|
|
196
|
+
|
|
197
|
+
```jsx
|
|
198
|
+
import { Link } from 'react-router-dom';
|
|
199
|
+
|
|
200
|
+
<Link to="/register" className="text-teal-700 underline">
|
|
201
|
+
Еще не зарегистрированы? Регистрация
|
|
202
|
+
</Link>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
- `to="/register"` — куда ведёт (должен быть в `App.jsx`).
|
|
206
|
+
- Текст внутри `Link` — что видит пользователь.
|
|
207
|
+
|
|
208
|
+
### 4.3. Добавить новую кнопку-ссылку
|
|
209
|
+
|
|
210
|
+
Скопируйте блок `Link` выше, вставьте под существующим, поменяйте `to` и текст.
|
|
211
|
+
|
|
212
|
+
### 4.4. Удалить кнопку
|
|
213
|
+
|
|
214
|
+
Удалите целиком блок `<button>...</button>` или `<Link>...</Link>` **вместе с обёрткой**, если она только для этой кнопки.
|
|
215
|
+
|
|
216
|
+
**Пример — убрать «Отменить» в админке** (`AdminPage.jsx`):
|
|
217
|
+
|
|
218
|
+
Удалите блок:
|
|
219
|
+
|
|
220
|
+
```jsx
|
|
221
|
+
{r.status !== 'cancelled' && (
|
|
222
|
+
<button ...>Отменить</button>
|
|
223
|
+
)}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
И поле «Причина отмены», если отмены больше нет в задании.
|
|
227
|
+
|
|
228
|
+
### 4.5. Кнопка с действием (без перехода)
|
|
229
|
+
|
|
230
|
+
```jsx
|
|
231
|
+
<button
|
|
232
|
+
type="button"
|
|
233
|
+
onClick={() => updateStatus(r.id, 'completed')}
|
|
234
|
+
className="px-3 py-1.5 rounded-lg bg-emerald-600 text-white text-xs"
|
|
235
|
+
>
|
|
236
|
+
Выполнено
|
|
237
|
+
</button>
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
- `onClick` — что выполнить (функция в том же файле).
|
|
241
|
+
- Текст кнопки — подпись для пользователя.
|
|
242
|
+
|
|
243
|
+
Для «Банкетам.Нет» меняете текст на **«Банкет завершен»**, логику `updateStatus` не трогаете.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## 5. Как убрать / добавить секцию на странице
|
|
248
|
+
|
|
249
|
+
Страница — это набор блоков `<section>...</section>` или `<div>...</div>`.
|
|
250
|
+
|
|
251
|
+
### 5.1. Удалить секцию
|
|
252
|
+
|
|
253
|
+
Найдите комментарий `{/* --- О клининге --- */}` и удалите **весь** блок от `<section` до `</section>` (или от `<div` до `</div>`).
|
|
254
|
+
|
|
255
|
+
**Проверка:** в редакторе должен остаться валидный JSX — у каждого открывающего тега есть закрывающий.
|
|
256
|
+
|
|
257
|
+
### 5.2. Добавить секцию
|
|
258
|
+
|
|
259
|
+
Скопируйте похожую секцию с другой страницы (например с `LandingPage.jsx`), вставьте в нужное место, поменяйте тексты и `imageKey`.
|
|
260
|
+
|
|
261
|
+
Шаблон секции:
|
|
262
|
+
|
|
263
|
+
```jsx
|
|
264
|
+
<section className="mx-auto max-w-lg lg:max-w-5xl px-4 py-10">
|
|
265
|
+
<h2 className="text-xl font-bold text-slate-800 mb-4">Заголовок блока</h2>
|
|
266
|
+
<p className="text-sm text-slate-600">Текст блока.</p>
|
|
267
|
+
</section>
|
|
268
|
+
```
|
|
269
|
+
|
|
270
|
+
### 5.3. Перенести слайдер в личный кабинет
|
|
271
|
+
|
|
272
|
+
1. В `RequestsPage.jsx` вверху добавьте импорт:
|
|
273
|
+
```jsx
|
|
274
|
+
import HeroSlider from '../components/landing/HeroSlider.jsx';
|
|
275
|
+
```
|
|
276
|
+
2. Внутри `return`, после шапки страницы:
|
|
277
|
+
```jsx
|
|
278
|
+
<HeroSlider />
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
---
|
|
282
|
+
|
|
283
|
+
## 6. Как создать новую страницу с нуля
|
|
284
|
+
|
|
285
|
+
### Шаг 1. Файл страницы
|
|
286
|
+
|
|
287
|
+
Создайте `client/src/pages/MyPage.jsx`:
|
|
288
|
+
|
|
289
|
+
```jsx
|
|
290
|
+
/**
|
|
291
|
+
* Описание страницы. URL: /my-page
|
|
292
|
+
*/
|
|
293
|
+
import Layout from '../components/Layout.jsx';
|
|
294
|
+
|
|
295
|
+
export default function MyPage() {
|
|
296
|
+
return (
|
|
297
|
+
<Layout variant="default">
|
|
298
|
+
<h1 className="text-xl font-bold">Заголовок</h1>
|
|
299
|
+
<p className="text-slate-600">Содержимое.</p>
|
|
300
|
+
</Layout>
|
|
301
|
+
);
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
`variant` для шапки: `default` | `register` | `login` | `dashboard` | `admin` (см. `Layout.jsx`).
|
|
306
|
+
|
|
307
|
+
### Шаг 2. Маршрут
|
|
308
|
+
|
|
309
|
+
**Файл:** `App.jsx`
|
|
310
|
+
|
|
311
|
+
```jsx
|
|
312
|
+
import MyPage from './pages/MyPage.jsx';
|
|
313
|
+
|
|
314
|
+
// внутри <Routes>:
|
|
315
|
+
<Route path="/my-page" element={<MyPage />} />
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
Если страница только для вошедших:
|
|
319
|
+
|
|
320
|
+
```jsx
|
|
321
|
+
<Route
|
|
322
|
+
path="/my-page"
|
|
323
|
+
element={
|
|
324
|
+
<ProtectedRoute role="user">
|
|
325
|
+
<MyPage />
|
|
326
|
+
</ProtectedRoute>
|
|
327
|
+
}
|
|
328
|
+
/>
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Шаг 3. Ссылка на страницу
|
|
332
|
+
|
|
333
|
+
Где нужно — добавьте `<Link to="/my-page">Текст</Link>`.
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
## 7. Формы: поля, валидация, отправка
|
|
338
|
+
|
|
339
|
+
### 7.1. Добавить поле в форму
|
|
340
|
+
|
|
341
|
+
1. В `useState` формы добавьте ключ:
|
|
342
|
+
```javascript
|
|
343
|
+
const [form, setForm] = useState({
|
|
344
|
+
address: '',
|
|
345
|
+
newField: '', // новое
|
|
346
|
+
});
|
|
347
|
+
```
|
|
348
|
+
2. В JSX:
|
|
349
|
+
```jsx
|
|
350
|
+
<FormField label="Новое поле" error={errors.newField}>
|
|
351
|
+
<input
|
|
352
|
+
className={inputClass}
|
|
353
|
+
value={form.newField}
|
|
354
|
+
onChange={(e) => setForm((f) => ({ ...f, newField: e.target.value }))}
|
|
355
|
+
/>
|
|
356
|
+
</FormField>
|
|
357
|
+
```
|
|
358
|
+
3. В `validateRegistration` / `validateRequestForm` (`client/src/utils/validation.js`) — проверка.
|
|
359
|
+
4. То же в `server/utils/validation.js`.
|
|
360
|
+
5. В `server/routes/...` — сохранение в SQL (INSERT/UPDATE).
|
|
361
|
+
|
|
362
|
+
### 7.2. Убрать поле
|
|
363
|
+
|
|
364
|
+
Обратный порядок: удалите из JSX → из `useState` → из валидации → из API/SQL.
|
|
365
|
+
|
|
366
|
+
### 7.3. Выпадающий список (select)
|
|
367
|
+
|
|
368
|
+
Уже есть в `RequestFormPage.jsx`:
|
|
369
|
+
|
|
370
|
+
```jsx
|
|
371
|
+
<select value={form.serviceTypeId} onChange={set('serviceTypeId')}>
|
|
372
|
+
<option value="">— выберите —</option>
|
|
373
|
+
{services.map((s) => (
|
|
374
|
+
<option key={s.id} value={s.id}>{s.name}</option>
|
|
375
|
+
))}
|
|
376
|
+
</select>
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
Список приходит с API `/api/services` из таблицы `service_types` (`seed.sql`).
|
|
380
|
+
|
|
381
|
+
### 7.4. Дата ДД.ММ.ГГГГ (для «Банкетам.Нет»)
|
|
382
|
+
|
|
383
|
+
Сейчас: `<input type="datetime-local" />`.
|
|
384
|
+
|
|
385
|
+
Заменить на:
|
|
386
|
+
|
|
387
|
+
```jsx
|
|
388
|
+
<input
|
|
389
|
+
type="text"
|
|
390
|
+
placeholder="ДД.ММ.ГГГГ"
|
|
391
|
+
value={form.banquetDate}
|
|
392
|
+
onChange={set('banquetDate')}
|
|
393
|
+
/>
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
В `validation.js` добавить проверку регуляркой, например `/^\d{2}\.\d{2}\.\d{4}$/`.
|
|
397
|
+
|
|
398
|
+
На сервере при сохранении — преобразовать строку в дату для PostgreSQL.
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## 8. База данных и API
|
|
403
|
+
|
|
404
|
+
### 8.1. Сменить список услуг / помещений
|
|
405
|
+
|
|
406
|
+
**Файл:** `server/db/seed.sql` — замените `INSERT INTO service_types`.
|
|
407
|
+
|
|
408
|
+
После изменения: `npm run db:init` (или вручную очистить и вставить).
|
|
409
|
+
|
|
410
|
+
**Файл:** `client/src/constants/services.js` — массив для лендинга (должен совпадать по смыслу).
|
|
411
|
+
|
|
412
|
+
### 8.2. Сменить админа
|
|
413
|
+
|
|
414
|
+
**Файл:** `server/db/init.js` — `login` и `password` в `seedAdmin()`.
|
|
415
|
+
|
|
416
|
+
Затем: `npm run db:init`.
|
|
417
|
+
|
|
418
|
+
### 8.3. Сменить подписи статусов
|
|
419
|
+
|
|
420
|
+
| Где | Что |
|
|
421
|
+
|-----|-----|
|
|
422
|
+
| `server/routes/requests.js` | `STATUS_LABELS` |
|
|
423
|
+
| `server/routes/admin.js` | объект в `statusLabel` |
|
|
424
|
+
| `RequestsPage.jsx` | `statusColors` + отображение |
|
|
425
|
+
| `AdminPage.jsx` | тексты на кнопках |
|
|
426
|
+
|
|
427
|
+
Ключи в БД (`new`, `in_progress`, `completed`) можно **не менять** — меняйте только **русские подписи**.
|
|
428
|
+
|
|
429
|
+
### 8.4. Добавить фильтр в админке (пошагово)
|
|
430
|
+
|
|
431
|
+
**Сейчас фильтра нет** — только комментарий. Как сделать:
|
|
432
|
+
|
|
433
|
+
**Вариант А — только на фронте (быстро на экзамене):**
|
|
434
|
+
|
|
435
|
+
1. `AdminPage.jsx` — состояние:
|
|
436
|
+
```javascript
|
|
437
|
+
const [filterStatus, setFilterStatus] = useState('all');
|
|
438
|
+
```
|
|
439
|
+
2. Перед списком — `<select>`:
|
|
440
|
+
```jsx
|
|
441
|
+
<select value={filterStatus} onChange={(e) => setFilterStatus(e.target.value)}>
|
|
442
|
+
<option value="all">Все</option>
|
|
443
|
+
<option value="new">Новая</option>
|
|
444
|
+
<option value="in_progress">В работе</option>
|
|
445
|
+
<option value="completed">Выполнено</option>
|
|
446
|
+
</select>
|
|
447
|
+
```
|
|
448
|
+
3. Вместо `requests.map` используйте:
|
|
449
|
+
```javascript
|
|
450
|
+
const visible = filterStatus === 'all'
|
|
451
|
+
? requests
|
|
452
|
+
: requests.filter((r) => r.status === filterStatus);
|
|
453
|
+
```
|
|
454
|
+
и `visible.map(...)`.
|
|
455
|
+
|
|
456
|
+
**Вариант Б — на сервере:** в `admin.js` читать `req.query.status` и добавлять `WHERE r.status = $1` в SQL.
|
|
457
|
+
|
|
458
|
+
**Сортировка:** `useState sortBy` + `.sort()` по дате или ФИО.
|
|
459
|
+
|
|
460
|
+
**Пагинация:** `const PAGE = 5`, `page` state, `slice((page-1)*PAGE, page*PAGE)`.
|
|
461
|
+
|
|
462
|
+
---
|
|
463
|
+
|
|
464
|
+
## 9. Отзывы (личный кабинет, «Банкетам.Нет»)
|
|
465
|
+
|
|
466
|
+
Сейчас **не реализовано** — только комментарии.
|
|
467
|
+
|
|
468
|
+
План:
|
|
469
|
+
|
|
470
|
+
1. **schema.sql** — поле `review_text TEXT` в `requests` или таблица `reviews`.
|
|
471
|
+
2. **API** — `POST /api/requests/:id/review` (только если `status !== 'new'`).
|
|
472
|
+
3. **RequestsPage.jsx** — под карточкой заявки textarea + кнопка «Оставить отзыв», если админ уже менял статус.
|
|
473
|
+
|
|
474
|
+
---
|
|
475
|
+
|
|
476
|
+
## 10. Цвета и «отличительная черта» раздела
|
|
477
|
+
|
|
478
|
+
Каждая страница передаёт в Layout свой **`variant`**:
|
|
479
|
+
|
|
480
|
+
| Страница | variant | Цвет шапки |
|
|
481
|
+
|----------|---------|------------|
|
|
482
|
+
| Регистрация | `register` | бирюзовая |
|
|
483
|
+
| Вход | `login` | тёмная |
|
|
484
|
+
| Заявки | `dashboard` | градиент |
|
|
485
|
+
| Админ | `admin` | фиолетовая |
|
|
486
|
+
|
|
487
|
+
Менять цвета: `headerStyles` в `Layout.jsx`.
|
|
488
|
+
|
|
489
|
+
Кнопки на странице: классы Tailwind `bg-teal-600`, `bg-cyan-600` и т.д. — можно заменить на одну палитру под банкеты (золотой/бордовый).
|
|
490
|
+
|
|
491
|
+
---
|
|
492
|
+
|
|
493
|
+
## 11. Чек-лист перед сдачей
|
|
494
|
+
|
|
495
|
+
- [ ] `npm run db:init` прошёл без ошибок
|
|
496
|
+
- [ ] `npm run dev` — сайт открывается
|
|
497
|
+
- [ ] Регистрация → вход → заявка → видна в списке
|
|
498
|
+
- [ ] Админ видит заявки и меняет статус
|
|
499
|
+
- [ ] Картинки в `public/images/` отображаются
|
|
500
|
+
- [ ] На ширине 390px в DevTools ничего не обрезано
|
|
501
|
+
- [ ] Все тексты соответствуют **вашей** теме задания
|
|
502
|
+
- [ ] 3+ коммита в git (по заданию модуля 2/3)
|
|
503
|
+
|
|
504
|
+
---
|
|
505
|
+
|
|
506
|
+
## 12. Частые ошибки
|
|
507
|
+
|
|
508
|
+
| Проблема | Решение |
|
|
509
|
+
|----------|---------|
|
|
510
|
+
| Картинка не видна | Файл в `public/images/`? Ключ в `images.js`? `imageKey` верный? |
|
|
511
|
+
| 404 на API | Запущен ли `npm run dev` из корня? Сервер на :3001? |
|
|
512
|
+
| После входа белый экран | F12 → Console; проверьте токен / роль user vs admin |
|
|
513
|
+
| Валидация не совпадает | Правьте **и** client, **и** server `validation.js` |
|
|
514
|
+
| Список услуг пустой | `npm run db:init`, проверьте `seed.sql` |
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## 13. Порядок работы на экзамене (60–90 мин)
|
|
519
|
+
|
|
520
|
+
1. Прочитать задание, открыть **`THEME_BANQUETAM_NET.md`** или свой конспект.
|
|
521
|
+
2. `Layout.jsx` + `LandingLayout` — название.
|
|
522
|
+
3. `init.js` + `seed.sql` → `npm run db:init`.
|
|
523
|
+
4. `validation.js` (×2) — логин/пароль/дата.
|
|
524
|
+
5. Все `pages/*.jsx` — тексты и ссылки.
|
|
525
|
+
6. `STATUS_LABELS` + админ-кнопки.
|
|
526
|
+
7. Картинки Figma → `public/images/` + `images.js`.
|
|
527
|
+
8. По оставшемуся времени: фильтр админа, отзывы, 4-й слайд.
|
|
528
|
+
|
|
529
|
+
Удачи на демоэкзамене.
|