@mashka818/exam-de-template 1.0.1 → 1.0.2

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.
Files changed (140) hide show
  1. package/exam-guides/commented-code/client/index.html +7 -0
  2. package/exam-guides/commented-code/client/package-lock.json +541 -6
  3. package/exam-guides/commented-code/client/package.json +1 -0
  4. package/exam-guides/commented-code/client/public/images/README.txt +4 -24
  5. package/exam-guides/commented-code/client/public/images/about-cleaning.png +0 -0
  6. package/exam-guides/commented-code/client/public/images/admin-banner.png +0 -0
  7. package/exam-guides/commented-code/client/public/images/empty-requests.png +0 -0
  8. package/exam-guides/commented-code/client/public/images/footer-photo-1.png +0 -0
  9. package/exam-guides/commented-code/client/public/images/footer-photo-2.png +0 -0
  10. package/exam-guides/commented-code/client/public/images/footer-photo-3.png +0 -0
  11. package/exam-guides/commented-code/client/public/images/home-hero.png +0 -0
  12. package/exam-guides/commented-code/client/public/images/login-banner.png +0 -0
  13. package/exam-guides/commented-code/client/public/images/logo.png +0 -0
  14. package/exam-guides/commented-code/client/public/images/new-request-banner.png +0 -0
  15. package/exam-guides/commented-code/client/public/images/register-banner.png +0 -0
  16. package/exam-guides/commented-code/client/public/images/requests-banner.png +0 -0
  17. package/exam-guides/commented-code/client/public/images/slide-1.png +0 -0
  18. package/exam-guides/commented-code/client/public/images/slide-2.png +0 -0
  19. package/exam-guides/commented-code/client/public/images/slide-3.png +0 -0
  20. package/exam-guides/commented-code/client/src/App.jsx +14 -0
  21. package/exam-guides/commented-code/client/src/api.js +16 -2
  22. package/exam-guides/commented-code/client/src/components/FormField.jsx +7 -0
  23. package/exam-guides/commented-code/client/src/components/Layout.jsx +8 -0
  24. package/exam-guides/commented-code/client/src/components/PageImage.jsx +7 -0
  25. package/exam-guides/commented-code/client/src/components/ProtectedRoute.jsx +7 -0
  26. package/exam-guides/commented-code/client/src/components/UserNav.jsx +7 -0
  27. package/exam-guides/commented-code/client/src/components/landing/HeroSlider.jsx +7 -0
  28. package/exam-guides/commented-code/client/src/components/landing/LandingLayout.jsx +7 -0
  29. package/exam-guides/commented-code/client/src/components/landing/SiteFooter.jsx +7 -0
  30. package/exam-guides/commented-code/client/src/config/images.js +26 -28
  31. package/exam-guides/commented-code/client/src/constants/services.js +7 -0
  32. package/exam-guides/commented-code/client/src/context/AuthContext.jsx +8 -0
  33. package/exam-guides/commented-code/client/src/index.css +8 -0
  34. package/exam-guides/commented-code/client/src/main.jsx +8 -0
  35. package/exam-guides/commented-code/client/src/pages/AdminPage.jsx +59 -24
  36. package/exam-guides/commented-code/client/src/pages/LandingPage.jsx +7 -0
  37. package/exam-guides/commented-code/client/src/pages/LoginPage.jsx +8 -0
  38. package/exam-guides/commented-code/client/src/pages/RegisterPage.jsx +8 -0
  39. package/exam-guides/commented-code/client/src/pages/RequestFormPage.jsx +7 -0
  40. package/exam-guides/commented-code/client/src/pages/RequestsPage.jsx +7 -0
  41. package/exam-guides/commented-code/client/src/utils/validation.js +8 -0
  42. package/exam-guides/commented-code/client/vite.config.js +7 -0
  43. package/exam-guides/commented-code/server/db/init.js +7 -0
  44. package/exam-guides/commented-code/server/db/pool.js +7 -0
  45. package/exam-guides/commented-code/server/db/schema.sql +5 -0
  46. package/exam-guides/commented-code/server/db/seed.sql +5 -0
  47. package/exam-guides/commented-code/server/index.js +7 -0
  48. package/exam-guides/commented-code/server/middleware/auth.js +7 -0
  49. package/exam-guides/commented-code/server/routes/admin.js +23 -3
  50. package/exam-guides/commented-code/server/routes/auth.js +7 -0
  51. package/exam-guides/commented-code/server/routes/requests.js +7 -0
  52. package/exam-guides/commented-code/server/routes/services.js +7 -0
  53. package/exam-guides/commented-code/server/utils/validation.js +7 -0
  54. package/exam-project/EXAM_COMMANDS.txt +47 -0
  55. package/exam-project/README.md +4 -7
  56. package/exam-project/THEME_BANQUETAM_NET.md +106 -0
  57. package/exam-project/client/index.html +7 -0
  58. package/exam-project/client/package-lock.json +541 -6
  59. package/exam-project/client/package.json +1 -0
  60. package/exam-project/client/public/images/README.txt +4 -24
  61. package/exam-project/client/public/images/about-cleaning.png +0 -0
  62. package/exam-project/client/public/images/admin-banner.png +0 -0
  63. package/exam-project/client/public/images/empty-requests.png +0 -0
  64. package/exam-project/client/public/images/footer-photo-1.png +0 -0
  65. package/exam-project/client/public/images/footer-photo-2.png +0 -0
  66. package/exam-project/client/public/images/footer-photo-3.png +0 -0
  67. package/exam-project/client/public/images/home-hero.png +0 -0
  68. package/exam-project/client/public/images/login-banner.png +0 -0
  69. package/exam-project/client/public/images/logo.png +0 -0
  70. package/exam-project/client/public/images/new-request-banner.png +0 -0
  71. package/exam-project/client/public/images/register-banner.png +0 -0
  72. package/exam-project/client/public/images/requests-banner.png +0 -0
  73. package/exam-project/client/public/images/slide-1.png +0 -0
  74. package/exam-project/client/public/images/slide-2.png +0 -0
  75. package/exam-project/client/public/images/slide-3.png +0 -0
  76. package/exam-project/client/src/App.jsx +35 -1
  77. package/exam-project/client/src/api.js +37 -2
  78. package/exam-project/client/src/components/FormField.jsx +22 -1
  79. package/exam-project/client/src/components/Layout.jsx +34 -4
  80. package/exam-project/client/src/components/PageImage.jsx +24 -1
  81. package/exam-project/client/src/components/ProtectedRoute.jsx +23 -1
  82. package/exam-project/client/src/components/UserNav.jsx +20 -0
  83. package/exam-project/client/src/components/landing/HeroSlider.jsx +23 -2
  84. package/exam-project/client/src/components/landing/LandingLayout.jsx +23 -1
  85. package/exam-project/client/src/components/landing/SiteFooter.jsx +20 -0
  86. package/exam-project/client/src/config/images.js +45 -26
  87. package/exam-project/client/src/constants/services.js +20 -1
  88. package/exam-project/client/src/context/AuthContext.jsx +26 -1
  89. package/exam-project/client/src/index.css +28 -1
  90. package/exam-project/client/src/main.jsx +20 -1
  91. package/exam-project/client/src/pages/AdminPage.jsx +65 -7
  92. package/exam-project/client/src/pages/LandingPage.jsx +23 -0
  93. package/exam-project/client/src/pages/LoginPage.jsx +28 -2
  94. package/exam-project/client/src/pages/RegisterPage.jsx +29 -1
  95. package/exam-project/client/src/pages/RequestFormPage.jsx +28 -3
  96. package/exam-project/client/src/pages/RequestsPage.jsx +26 -1
  97. package/exam-project/client/src/utils/validation.js +26 -1
  98. package/exam-project/client/vite.config.js +22 -1
  99. package/exam-project/server/db/init.js +27 -3
  100. package/exam-project/server/db/pool.js +20 -1
  101. package/exam-project/server/db/schema.sql +17 -0
  102. package/exam-project/server/db/seed.sql +8 -0
  103. package/exam-project/server/index.js +24 -1
  104. package/exam-project/server/middleware/auth.js +22 -1
  105. package/exam-project/server/routes/admin.js +44 -4
  106. package/exam-project/server/routes/auth.js +27 -1
  107. package/exam-project/server/routes/requests.js +27 -4
  108. package/exam-project/server/routes/services.js +21 -1
  109. package/exam-project/server/utils/validation.js +27 -2
  110. package/package.json +1 -1
  111. package/exam-guides/commented-code/client/public/images/about-cleaning.svg +0 -4
  112. package/exam-guides/commented-code/client/public/images/admin-banner.svg +0 -1
  113. package/exam-guides/commented-code/client/public/images/empty-requests.svg +0 -1
  114. package/exam-guides/commented-code/client/public/images/footer-photo-1.svg +0 -4
  115. package/exam-guides/commented-code/client/public/images/footer-photo-2.svg +0 -4
  116. package/exam-guides/commented-code/client/public/images/footer-photo-3.svg +0 -4
  117. package/exam-guides/commented-code/client/public/images/home-hero.svg +0 -4
  118. package/exam-guides/commented-code/client/public/images/login-banner.svg +0 -1
  119. package/exam-guides/commented-code/client/public/images/logo.svg +0 -4
  120. package/exam-guides/commented-code/client/public/images/new-request-banner.svg +0 -1
  121. package/exam-guides/commented-code/client/public/images/register-banner.svg +0 -1
  122. package/exam-guides/commented-code/client/public/images/requests-banner.svg +0 -1
  123. package/exam-guides/commented-code/client/public/images/slide-1.svg +0 -6
  124. package/exam-guides/commented-code/client/public/images/slide-2.svg +0 -5
  125. package/exam-guides/commented-code/client/public/images/slide-3.svg +0 -5
  126. package/exam-project/client/public/images/about-cleaning.svg +0 -4
  127. package/exam-project/client/public/images/admin-banner.svg +0 -1
  128. package/exam-project/client/public/images/empty-requests.svg +0 -1
  129. package/exam-project/client/public/images/footer-photo-1.svg +0 -4
  130. package/exam-project/client/public/images/footer-photo-2.svg +0 -4
  131. package/exam-project/client/public/images/footer-photo-3.svg +0 -4
  132. package/exam-project/client/public/images/home-hero.svg +0 -4
  133. package/exam-project/client/public/images/login-banner.svg +0 -1
  134. package/exam-project/client/public/images/logo.svg +0 -4
  135. package/exam-project/client/public/images/new-request-banner.svg +0 -1
  136. package/exam-project/client/public/images/register-banner.svg +0 -1
  137. package/exam-project/client/public/images/requests-banner.svg +0 -1
  138. package/exam-project/client/public/images/slide-1.svg +0 -6
  139. package/exam-project/client/public/images/slide-2.svg +0 -5
  140. package/exam-project/client/public/images/slide-3.svg +0 -5
@@ -1,27 +1,56 @@
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: client/src/pages/AdminPage.jsx | URL: /admin | п.5
4
+ * Фильтр filterStatus → api.adminGetRequests({ status }). Кнопки статусов → PATCH.
5
+ * Сервер: server/routes/admin.js
6
+ * =============================================================================
7
+ */
1
8
 
2
- import { useEffect, useState } from 'react';
9
+ /**
10
+ * =============================================================================
11
+ * АДМИН-ПАНЕЛЬ (/admin) — п.5 задания ДЭ
12
+ * =============================================================================
13
+ * ФИЛЬТР ПО СТАТУСУ:
14
+ * filterStatus — значение select (all / new / in_progress / completed / cancelled)
15
+ * load() → api.adminGetRequests({ status }) → GET /api/admin/requests?status=...
16
+ * На сервере: server/routes/admin.js — WHERE r.status = $1
17
+ *
18
+ * ЗАМЕНИТЕ: кнопки статусов, admin-banner, логин админа в server/db/init.js
19
+ * БАНКЕТАМ.НЕТ: подписи опций фильтра и кнопок — «Банкет назначен» и т.д.
20
+ * =============================================================================
21
+ */
22
+ import { useCallback, useEffect, useState } from 'react';
3
23
  import Layout from '../components/Layout.jsx';
4
24
  import PageImage from '../components/PageImage.jsx';
5
25
  import { api } from '../api.js';
6
26
 
27
+ const STATUS_FILTER_OPTIONS = [
28
+ { value: 'all', label: 'Все заявки' },
29
+ { value: 'new', label: 'Новая' },
30
+ { value: 'in_progress', label: 'В работе' },
31
+ { value: 'completed', label: 'Выполнено' },
32
+ { value: 'cancelled', label: 'Отменено' },
33
+ ];
34
+
7
35
  export default function AdminPage() {
8
36
  const [requests, setRequests] = useState([]);
9
37
  const [loading, setLoading] = useState(true);
38
+ const [filterStatus, setFilterStatus] = useState('all');
10
39
  const [cancelReasons, setCancelReasons] = useState({});
11
40
  const [message, setMessage] = useState('');
12
41
 
13
- const load = () => {
42
+ const load = useCallback(() => {
14
43
  setLoading(true);
15
44
  api
16
- .adminGetRequests()
45
+ .adminGetRequests({ status: filterStatus })
17
46
  .then(setRequests)
18
47
  .catch((e) => setMessage(e.message))
19
48
  .finally(() => setLoading(false));
20
- };
49
+ }, [filterStatus]);
21
50
 
22
51
  useEffect(() => {
23
52
  load();
24
- }, []);
53
+ }, [load]);
25
54
 
26
55
  const updateStatus = async (id, status) => {
27
56
  setMessage('');
@@ -32,6 +61,7 @@ export default function AdminPage() {
32
61
  }
33
62
  try {
34
63
  await api.adminUpdateStatus(id, { status, cancelReason });
64
+ setMessage('Статус обновлён');
35
65
  load();
36
66
  } catch (e) {
37
67
  setMessage(e.data?.message || e.message);
@@ -43,10 +73,39 @@ export default function AdminPage() {
43
73
  <div className="animate-fade-up">
44
74
  <PageImage imageKey="adminBanner" className="w-full h-20 object-cover rounded-xl mb-4" />
45
75
 
46
-
47
76
  <h2 className="text-xl font-bold text-white mb-4">Панель администратора</h2>
77
+
78
+ {/* Фильтр: меняйте label в STATUS_FILTER_OPTIONS; value = status в БД */}
79
+ <div className="mb-4 flex flex-wrap items-center gap-2">
80
+ <label htmlFor="admin-filter-status" className="text-white/90 text-sm">
81
+ Статус:
82
+ </label>
83
+ <select
84
+ id="admin-filter-status"
85
+ value={filterStatus}
86
+ onChange={(e) => setFilterStatus(e.target.value)}
87
+ className="rounded-lg border border-violet-200 px-3 py-1.5 text-sm text-slate-800 bg-white"
88
+ >
89
+ {STATUS_FILTER_OPTIONS.map((opt) => (
90
+ <option key={opt.value} value={opt.value}>
91
+ {opt.label}
92
+ </option>
93
+ ))}
94
+ </select>
95
+ <button
96
+ type="button"
97
+ onClick={load}
98
+ className="text-xs px-2 py-1.5 rounded-lg bg-white/20 text-white hover:bg-white/30"
99
+ >
100
+ Обновить
101
+ </button>
102
+ </div>
103
+
48
104
  {message && <p className="text-amber-200 text-sm mb-3">{message}</p>}
49
105
  {loading && <p className="text-white/70">Загрузка…</p>}
106
+ {!loading && requests.length === 0 && (
107
+ <p className="text-white/70 text-sm">Нет заявок с выбранным статусом</p>
108
+ )}
50
109
 
51
110
  <div className="space-y-4">
52
111
  {requests.map((r) => (
@@ -75,7 +134,6 @@ export default function AdminPage() {
75
134
  {new Date(r.scheduledAt).toLocaleString('ru-RU')} · {r.paymentLabel}
76
135
  </p>
77
136
 
78
-
79
137
  <div className="mt-3 flex flex-wrap gap-2">
80
138
  {r.status === 'new' && (
81
139
  <button
@@ -1,4 +1,27 @@
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: client/src/pages/LandingPage.jsx | URL: /
4
+ * Лендинг: слайдер, тексты perks, блок услуг. Не путать с Layout.jsx
5
+ * =============================================================================
6
+ */
1
7
 
8
+ /**
9
+ * =============================================================================
10
+ * ЛЕНДИНГ (/) — главная для гостей
11
+ * =============================================================================
12
+ * Слайдер, о клининге, услуги, футер с фото-заглушками.
13
+ * Картинки: config/images.js → public/images/
14
+ * БАНКЕТАМ.НЕТ: тексты про банкеты; perks и «Наши услуги» = 4 помещения;
15
+ * LandingLayout brandName → Банкетам.Нет; слайдер 4×3 сек (HeroSlider).
16
+ *
17
+ * СТРУКТУРА: LandingLayout → HeroSlider → секции → SiteFooter
18
+ * perks[] — карточки преимуществ (массив строк, менять под тему)
19
+ * DEFAULT_SERVICE_NAMES — подписи на лендинге (дубль seed.sql; синхронизировать)
20
+ * cabinetPath — куда ведёт CTA для вошедшего пользователя
21
+ *
22
+ * GUIDE_PAGES.md §5 | config/images.js (aboutCleaning, homeHero…)
23
+ * =============================================================================
24
+ */
2
25
  import { Link } from 'react-router-dom';
3
26
  import { useAuth } from '../context/AuthContext.jsx';
4
27
  import LandingLayout from '../components/landing/LandingLayout.jsx';
@@ -1,4 +1,30 @@
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: client/src/pages/LoginPage.jsx | URL: /login | п.2
4
+ * login + password → AuthContext.login → admin: /admin, user: /requests
5
+ * Админ: server/db/init.js (adminka или Admin26)
6
+ * =============================================================================
7
+ */
1
8
 
9
+ /**
10
+ * =============================================================================
11
+ * АВТОРИЗАЦИЯ (/login) — п.2 задания ДЭ
12
+ * =============================================================================
13
+ * ЗАМЕНИТЕ:
14
+ * сообщения об ошибке — уже есть «Неверный логин или пароль»
15
+ * adminka/password — заданы в server/db/init.js (не на этой странице)
16
+ * картинку — login-banner в public/images/
17
+ * Отличительная черта: тёмная шапка (variant="login")
18
+ * БАНКЕТАМ.НЕТ (п.2): ссылка «Еще не зарегистрированы? Регистрация» (сейчас «Создать аккаунт»).
19
+ * БАНКЕТАМ.НЕТ: подсказка админа Admin26 / Demo20 внизу формы.
20
+ *
21
+ * ПОТОК: login({ login, password }) → JWT в localStorage
22
+ * user.role === 'admin' → /admin, иначе → /requests
23
+ * Админ создаётся в server/db/init.js (не на этой странице)
24
+ *
25
+ * GUIDE_PAGES.md §2.2 | server/db/init.js (adminka / Admin26)
26
+ * =============================================================================
27
+ */
2
28
  import { useState } from 'react';
3
29
  import { Link, useNavigate } from 'react-router-dom';
4
30
  import Layout from '../components/Layout.jsx';
@@ -49,11 +75,11 @@ export default function LoginPage() {
49
75
  Войти
50
76
  </button>
51
77
  </form>
52
-
78
+ {/* БАНКЕТАМ.НЕТ: текст ссылки → «Еще не зарегистрированы? Регистрация» */}
53
79
  <p className="text-center text-sm mt-4">
54
80
  <Link to="/register" className="text-teal-700 underline">Создать аккаунт</Link>
55
81
  </p>
56
-
82
+ {/* БАНКЕТАМ.НЕТ: Admin26 / Demo20 */}
57
83
  <p className="text-xs text-slate-500 mt-6 text-center">
58
84
  Админ: логин <code>adminka</code>, пароль <code>password</code>
59
85
  </p>
@@ -1,4 +1,32 @@
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: client/src/pages/RegisterPage.jsx | URL: /register | п.1
4
+ * Поля: login, password, fullName, phone, email → POST /api/auth/register
5
+ * Менять: label, banner imageKey registerBanner, validation.js
6
+ * =============================================================================
7
+ */
1
8
 
9
+ /**
10
+ * =============================================================================
11
+ * РЕГИСТРАЦИЯ (/register) — п.1 задания ДЭ
12
+ * =============================================================================
13
+ * ЗАМЕНИТЕ:
14
+ * label полей — если в теме другие данные пользователя (не ФИО, а «Название фирмы»)
15
+ * валидацию — utils/validation.js (сервер + клиент)
16
+ * API-тело — те же поля уходят в POST /api/auth/register
17
+ * картинку — register-banner в public/images/
18
+ * Отличительная черта: бирюзовая шапка (variant="register")
19
+ * БАНКЕТАМ.НЕТ (п.1): логин латиница+цифры ≥6, пароль ≥8; ссылка «Уже есть аккаунт? Вход».
20
+ * БАНКЕТАМ.НЕТ: brand в Layout → «Банкетам.Нет».
21
+ *
22
+ * ПОТОК: form state → validateRegistration → register() из AuthContext
23
+ * → POST /api/auth/register → navigate('/requests')
24
+ * Ошибки: errors.xxx под полями; serverError — общая строка сверху
25
+ * inputClass — стиль полей; при ошибке добавьте className input-error
26
+ *
27
+ * GUIDE_PAGES.md §2.1 | server/routes/auth.js
28
+ * =============================================================================
29
+ */
2
30
  import { useState } from 'react';
3
31
  import { Link, useNavigate } from 'react-router-dom';
4
32
  import Layout from '../components/Layout.jsx';
@@ -87,7 +115,7 @@ export default function RegisterPage() {
87
115
  </button>
88
116
  </form>
89
117
 
90
-
118
+ {/* БАНКЕТАМ.НЕТ: на странице регистрации — ссылка «Уже зарегистрированы? Вход» (сейчас «Уже есть аккаунт?») */}
91
119
  <p className="text-center text-sm mt-4 text-slate-600">
92
120
  Уже есть аккаунт? <Link to="/login" className="text-teal-700 underline">Войти</Link>
93
121
  </p>
@@ -1,4 +1,29 @@
1
-
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: client/src/pages/RequestFormPage.jsx | URL: /requests/form | п.4
4
+ * Форма заявки: api.getServices() + api.createRequest(). Поля form state → validation → POST
5
+ * =============================================================================
6
+ */
7
+
8
+ /**
9
+ * =============================================================================
10
+ * п.4 ЗАДАНИЯ — «Страница формирования заявки» (/requests/form)
11
+ * =============================================================================
12
+ * Поля: адрес, контактные данные (телефон), дата/время, вид услуги, оплата.
13
+ * «Иная услуга» — чекбокс + текст (модуль ПУ).
14
+ * После отправки — редирект на п.3 (/requests).
15
+ * БАНКЕТАМ.НЕТ (п.4): select помещение; дата type="text" placeholder ДД.ММ.ГГГГ (не datetime-local).
16
+ * БАНКЕТАМ.НЕТ: убрать блок «Иная услуга» (isCustomService).
17
+ * БАНКЕТАМ.НЕТ: заголовок «Оформление бронирования», label «Помещение».
18
+ *
19
+ * services: api.getServices() → select option (id, name) из service_types
20
+ * submit: validateRequestForm → api.createRequest(form) → navigate('/requests')
21
+ * paymentType: 'cash' | 'card' — значения как в schema CHECK
22
+ * isCustomService — модуль ПУ; для Банкетам.Нет удалить блок и поля в API
23
+ *
24
+ * GUIDE_PAGES.md §2.4 | server/routes/requests.js POST /
25
+ * =============================================================================
26
+ */
2
27
  import { useEffect, useState } from 'react';
3
28
  import { Link, useNavigate } from 'react-router-dom';
4
29
  import Layout from '../components/Layout.jsx';
@@ -79,7 +104,7 @@ export default function RequestFormPage() {
79
104
  className="bg-white rounded-2xl shadow-lg p-6 animate-fade-up border border-cyan-100"
80
105
  noValidate
81
106
  >
82
-
107
+ {/* БАНКЕТАМ.НЕТ: «Оформление бронирования» */}
83
108
  <h1 className="text-xl font-bold text-cyan-900 mb-1">Формирование заявки</h1>
84
109
  <p className="text-sm text-slate-500 mb-6">Все поля обязательны</p>
85
110
 
@@ -130,7 +155,7 @@ export default function RequestFormPage() {
130
155
  </FormField>
131
156
  )}
132
157
 
133
-
158
+ {/* БАНКЕТАМ.НЕТ: удалить весь блок «Иная услуга» — в задании только select помещений */}
134
159
  <label className="flex items-center gap-2 mb-4 cursor-pointer">
135
160
  <input
136
161
  type="checkbox"
@@ -1,4 +1,29 @@
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: client/src/pages/RequestsPage.jsx | URL: /requests | п.3
4
+ * История: api.getMyRequests(). Кнопка на /requests/form. statusLabel с сервера.
5
+ * =============================================================================
6
+ */
1
7
 
8
+ /**
9
+ * =============================================================================
10
+ * п.3 ЗАДАНИЯ — «Страница создания заявки» (/requests)
11
+ * =============================================================================
12
+ * История заявок + кнопка перехода на /requests/form (п.4)
13
+ * БАНКЕТАМ.НЕТ (п.3): «Личный кабинет» — история бронирований + слайдер (HeroSlider).
14
+ * БАНКЕТАМ.НЕТ: блок отзыва под заявкой, если status !== 'new' (после админа).
15
+ * БАНКЕТАМ.НЕТ: statusColors — подписи «Банкет назначен» / «Банкет завершен».
16
+ *
17
+ * ДАННЫЕ: useEffect → api.getMyRequests() → GET /api/requests/mine
18
+ * statusLabel приходит с сервера (requests.js STATUS_LABELS)
19
+ * Кнопка «Новая заявка» → Link to="/requests/form" (п.4)
20
+ *
21
+ * БАНКЕТАМ.НЕТ (отзывы): под карточкой textarea + кнопка, если status !== 'new'
22
+ * → api.postReview + route на сервере + таблица reviews в schema.sql
23
+ *
24
+ * GUIDE_PAGES.md §2.3 | THEME_BANQUETAM_NET.md
25
+ * =============================================================================
26
+ */
2
27
  import { useEffect, useState } from 'react';
3
28
  import { Link } from 'react-router-dom';
4
29
  import Layout from '../components/Layout.jsx';
@@ -31,7 +56,7 @@ export default function RequestsPage() {
31
56
  <p className="text-white/70 text-xs mb-1">Пункт 3 задания</p>
32
57
  <PageImage imageKey="requestsBanner" className="w-full h-20 object-cover rounded-xl mb-4" />
33
58
 
34
-
59
+ {/* БАНКЕТАМ.НЕТ: заголовок «Личный кабинет» */}
35
60
  <h1 className="text-xl font-bold text-white drop-shadow mb-1">Создание заявки</h1>
36
61
  <p className="text-white/80 text-sm mb-6">
37
62
  История ваших заявок и переход к оформлению новой
@@ -1,4 +1,29 @@
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: client/src/utils/validation.js
4
+ * Валидация форм на клиенте. ДУБЛИРОВАТЬ в server/utils/validation.js!
5
+ * register → RegisterPage | request → RequestFormPage
6
+ * =============================================================================
7
+ */
1
8
 
9
+ /**
10
+ * =============================================================================
11
+ * ВАЛИДАЦИЯ НА КЛИЕНТЕ (мгновенные ошибки на форме)
12
+ * =============================================================================
13
+ * Должна совпадать с server/utils/validation.js
14
+ * ЗАМЕНИТЕ регулярки и сообщения, если в задании другие правила.
15
+ * БАНКЕТАМ.НЕТ: см. server/utils/validation.js (логин, пароль 8, дата ДД.ММ.ГГГГ).
16
+ *
17
+ * ВАЖНО: любое изменение здесь — продублировать в server/utils/validation.js
18
+ * Иначе форма пройдёт на клиенте, но сервер вернёт 400.
19
+ *
20
+ * validateRegistration — RegisterPage submit
21
+ * validateRequestForm — RequestFormPage submit
22
+ * formatPhoneInput — onChange телефона (маска +7(999)-999-99-99)
23
+ *
24
+ * GUIDE_PAGES.md §7.1 | THEME_BANQUETAM_NET.md
25
+ * =============================================================================
26
+ */
2
27
 
3
28
  /** Телефон строго в формате задания — менять вместе с подсказкой на форме */
4
29
  export const PHONE_REGEX = /^\+7\(\d{3}\)-\d{3}-\d{2}-\d{2}$/;
@@ -10,7 +35,7 @@ export function validateRegistration(form) {
10
35
  const errors = {};
11
36
  if (!form.login?.trim()) errors.login = 'Логин обязателен';
12
37
  if (!form.password) errors.password = 'Пароль обязателен';
13
- else if (form.password.length < 6) errors.password = 'Минимум 6 символов';
38
+ else if (form.password.length < 6) errors.password = 'Минимум 6 символов'; // БАНКЕТАМ.НЕТ: 8
14
39
  if (!form.fullName?.trim()) errors.fullName = 'ФИО обязательно';
15
40
  else if (!FIO_REGEX.test(form.fullName.trim())) errors.fullName = 'Только кириллица и пробелы';
16
41
  if (!form.phone?.trim()) errors.phone = 'Телефон обязателен';
@@ -1,8 +1,29 @@
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: client/vite.config.js
4
+ * Порт 5173. proxy /api → :3001 (если сменили PORT в server/.env — поменять target)
5
+ * =============================================================================
6
+ */
7
+
1
8
  import { defineConfig } from 'vite';
2
9
  import react from '@vitejs/plugin-react';
3
10
  import tailwindcss from '@tailwindcss/vite';
4
11
 
5
-
12
+ /**
13
+ * =============================================================================
14
+ * vite.config.js — СБОРКА И DEV-СЕРВЕР ФРОНТА
15
+ * =============================================================================
16
+ * port: 5173 — адрес сайта http://localhost:5173
17
+ * proxy /api → http://localhost:3001 — поэтому в api.js пишем fetch('/api/...')
18
+ *
19
+ * Картинки: всё из client/public/ отдаётся как /имя-файла (папка images → /images/...)
20
+ *
21
+ * Если сменили PORT в server/.env (например 3002) — поменяйте target ниже.
22
+ * production build: npm run build → папка client/dist
23
+ *
24
+ * GUIDE_PAGES.md §3.1
25
+ * =============================================================================
26
+ */
6
27
  export default defineConfig({
7
28
  plugins: [react(), tailwindcss()],
8
29
  server: {
@@ -1,4 +1,28 @@
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: server/db/init.js
4
+ * npm run db:init — schema+seed+админ. Логин/пароль админа ЗДЕСЬ (seedAdmin).
5
+ * =============================================================================
6
+ */
1
7
 
8
+ /**
9
+ * =============================================================================
10
+ * ИНИЦИАЛИЗАЦИЯ БД: npm run db:init
11
+ * =============================================================================
12
+ * Читает schema.sql + seed.sql, создаёт админа adminka/password
13
+ * ЗАМЕНИТЕ: login/password в seedAdmin(), ФИО и email админа
14
+ *
15
+ * КОМАНДА: из корня проекта npm run db:init
16
+ * 1) Читает schema.sql (таблицы)
17
+ * 2) Читает seed.sql (услуги)
18
+ * 3) seedAdmin() — UPSERT админа (ON CONFLICT login)
19
+ *
20
+ * Перед запуском: CREATE DATABASE в psql + DATABASE_URL в server/.env
21
+ * БАНКЕТАМ.НЕТ: login Admin26, password Demo20 в seedAdmin()
22
+ *
23
+ * README.md | GUIDE_PAGES.md §8.5
24
+ * =============================================================================
25
+ */
2
26
  import fs from 'fs';
3
27
  import path from 'path';
4
28
  import { fileURLToPath } from 'url';
@@ -17,7 +41,7 @@ async function runSqlFile(filename) {
17
41
 
18
42
  async function seedAdmin() {
19
43
  // --- Учётка админа по заданию ДЭ (п.5) — менять здесь ---
20
-
44
+ // БАНКЕТАМ.НЕТ: const login = 'Admin26'; const password = 'Demo20';
21
45
  const login = 'adminka';
22
46
  const password = 'password';
23
47
  const hash = await bcrypt.hash(password, 10);
@@ -28,9 +52,9 @@ async function seedAdmin() {
28
52
  ON CONFLICT (login) DO UPDATE SET
29
53
  password_hash = EXCLUDED.password_hash,
30
54
  role = 'admin'`,
31
- [login, hash, 'Администратор системы', '+7(000)-000-00-00', 'admin@moynesam.local']
55
+ [login, hash, 'Администратор системы', '+7(000)-000-00-00', 'admin@moynesam.local'] // БАНКЕТАМ.НЕТ: email admin@banketam.net
32
56
  );
33
- console.log('Админ: логин adminka, пароль password');
57
+ console.log('Админ: логин adminka, пароль password'); // БАНКЕТАМ.НЕТ: Admin26 / Demo20
34
58
  }
35
59
 
36
60
  async function main() {
@@ -1,4 +1,23 @@
1
-
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: server/db/pool.js
4
+ * Подключение БД. Только server/.env → DATABASE_URL
5
+ * =============================================================================
6
+ */
7
+
8
+ /**
9
+ * =============================================================================
10
+ * pool.js — ПОДКЛЮЧЕНИЕ К PostgreSQL
11
+ * =============================================================================
12
+ * Менять ТОЛЬКО server/.env → DATABASE_URL
13
+ * postgresql://ЛОГИН:ПАРОЛЬ@localhost:5432/ИМЯ_БАЗЫ
14
+ *
15
+ * БАНКЕТАМ.НЕТ: CREATE DATABASE banketam_net; и /banketam_net в URL
16
+ * Импорт pool везде: import { pool } from '../db/pool.js';
17
+ *
18
+ * GUIDE_PAGES.md §8 | README.md (разбор строки подключения)
19
+ * =============================================================================
20
+ */
2
21
  import pg from 'pg';
3
22
  import dotenv from 'dotenv';
4
23
 
@@ -1,6 +1,20 @@
1
+ -- =============================================================================
2
+ -- ФАЙЛ: server/db/schema.sql
3
+ -- Таблицы users, service_types, requests. Статусы CHECK. npm run db:init
4
+ -- =============================================================================
5
+
6
+ -- =============================================================================
1
7
  -- СХЕМА БД — при смене темы переименуйте БД и таблицы по смыслу предметной области
2
8
  -- Тема сейчас: клининг «Мой Не Сам» → заявки на уборку (requests)
3
9
  -- Другая тема: заявки → orders / bookings / applications
10
+ -- =============================================================================
11
+ -- БАНКЕТАМ.НЕТ: users — без изменений структуры (ФИО, телефон, email, login, password).
12
+ -- БАНКЕТАМ.НЕТ: service_types → помещения (зал, ресторан, веранды).
13
+ -- БАНКЕТАМ.НЕТ: requests → бронирования; address можно оставить или переименовать в comment_only.
14
+ -- БАНКЕТАМ.НЕТ: scheduled_at → дата начала банкета.
15
+ -- БАНКЕТАМ.НЕТ: статусы CHECK → 'new','in_progress','completed' (без cancelled при необходимости).
16
+ -- БАНКЕТАМ.НЕТ: Модуль 3 — ДОБАВИТЬ таблицу reviews (request_id, user_id, text, rating, created_at).
17
+ -- =============================================================================
4
18
 
5
19
  CREATE TABLE IF NOT EXISTS users (
6
20
  id SERIAL PRIMARY KEY,
@@ -14,6 +28,7 @@ CREATE TABLE IF NOT EXISTS users (
14
28
  );
15
29
 
16
30
  -- Справочник видов услуг — при другой теме замените названия и список в seed
31
+ -- БАНКЕТАМ.НЕТ: по смыслу «помещения», таблицу можно не переименовывать (меньше правок в SQL)
17
32
  CREATE TABLE IF NOT EXISTS service_types (
18
33
  id SERIAL PRIMARY KEY,
19
34
  name VARCHAR(255) NOT NULL UNIQUE
@@ -21,6 +36,7 @@ CREATE TABLE IF NOT EXISTS service_types (
21
36
 
22
37
  -- Заявки заказчика — ядро предметной области
23
38
  -- Поля: address (адрес/комментарий), scheduled_at (дата услуги), payment_type cash|card
39
+ -- service_type_id ИЛИ custom_service — «иная услуга»; БАНКЕТАМ.НЕТ: только service_type_id
24
40
  -- status: new → админ меняет на in_progress / completed (подписи в requests.js STATUS_LABELS)
25
41
  CREATE TABLE IF NOT EXISTS requests (
26
42
  id SERIAL PRIMARY KEY,
@@ -35,6 +51,7 @@ CREATE TABLE IF NOT EXISTS requests (
35
51
  CHECK (status IN ('new', 'in_progress', 'completed', 'cancelled')),
36
52
  cancel_reason TEXT, -- БАНКЕТАМ.НЕТ: поле можно не использовать (в задании нет «отмены»)
37
53
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
54
+ -- БАНКЕТАМ.НЕТ: ALTER TABLE requests ADD COLUMN review_text TEXT; — отзыв после смены статуса админом
38
55
  );
39
56
 
40
57
  CREATE INDEX IF NOT EXISTS idx_requests_user_id ON requests(user_id);
@@ -1,8 +1,16 @@
1
+ -- =============================================================================
2
+ -- ФАЙЛ: server/db/seed.sql
3
+ -- Список услуг в service_types. Менять VALUES под тему.
4
+ -- =============================================================================
5
+
6
+ -- =============================================================================
1
7
  -- SEED — начальные записи в service_types (справочник для формы п.4)
8
+ -- =============================================================================
2
9
  -- Выполняется из init.js при npm run db:init
3
10
  -- ON CONFLICT DO NOTHING — повторный init не дублирует строки
4
11
  --
5
12
  -- Синхронизируйте с client/src/constants/services.js (лендинг)
13
+ -- БАНКЕТАМ.НЕТ: замените VALUES на четыре помещения:
6
14
  -- ('Банкетный зал'), ('Ресторан'), ('Летняя веранда'), ('Закрытая веранда')
7
15
  INSERT INTO service_types (name) VALUES
8
16
  ('Общий клининг'),
@@ -1,4 +1,27 @@
1
-
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: server/index.js
4
+ * Старт API :3001. Роуты /api/auth, /services, /requests, /admin
5
+ * =============================================================================
6
+ */
7
+
8
+ /**
9
+ * =============================================================================
10
+ * EXPRESS — точка входа сервера
11
+ * =============================================================================
12
+ * PORT, DATABASE_URL, JWT_SECRET — в server/.env
13
+ * Префикс /api обычно не меняют. Роуты подключаются из папки routes/
14
+ * БАНКЕТАМ.НЕТ: DATABASE_URL в .env — имя БД например banketam_net
15
+ *
16
+ * ЗАПУСК: npm run dev (корень) или node index.js из server/
17
+ * Порядок: dotenv → cors → express.json() → роуты → listen(PORT)
18
+ *
19
+ * Новый роут: import + app.use('/api/xxx', xxxRoutes)
20
+ * Health-check: GET /api/health → { ok: true } — проверка что API жив
21
+ *
22
+ * GUIDE_PAGES.md §8 | README.md
23
+ * =============================================================================
24
+ */
2
25
  import express from 'express';
3
26
  import cors from 'cors';
4
27
  import dotenv from 'dotenv';
@@ -1,4 +1,25 @@
1
-
1
+ /**
2
+ * =============================================================================
3
+ * ФАЙЛ: server/middleware/auth.js
4
+ * JWT. authRequired — user routes. adminRequired — /api/admin
5
+ * =============================================================================
6
+ */
7
+
8
+ /**
9
+ * =============================================================================
10
+ * JWT middleware
11
+ * =============================================================================
12
+ * JWT_SECRET — server/.env
13
+ * role в токене: user | admin — из таблицы users
14
+ *
15
+ * authRequired — вешать на роуты /requests, /admin (заголовок Authorization: Bearer ...)
16
+ * adminRequired — только для /api/admin/* (роль admin после входа Admin26)
17
+ *
18
+ * На фронте: ProtectedRoute role="user" | "admin" (client/src/components/ProtectedRoute.jsx)
19
+ *
20
+ * JWT_SECRET в server/.env — обязательно задать перед экзаменом
21
+ * =============================================================================
22
+ */
2
23
  import jwt from 'jsonwebtoken';
3
24
 
4
25
  export function authRequired(req, res, next) {