- Реструктуризация: src/ разбит на middleware/, utils/, repositories/ (удалены), routes/ (удалены) - Добавлен src/original-html.ts — полный HTML с reportModal - Добавлен src/index.tsx.backup — React-компонент с reportModal - Миграции переименованы (0001_initial_schema.sql) - Добавлена миграция 0018 (удалена позже) - Docker: multi-stage build, wrangler.toml - Frontend: public/static/app.js + style.css - seed.sql добавлен - Документация: CHANGELOG, CHANGES_v4.1.0-4.1.9, PROJECT_STRUCTURE
706 lines
25 KiB
Markdown
706 lines
25 KiB
Markdown
# AKNAPROFF Tootmine - Полная история разработки и исправлений
|
||
|
||
**Проект:** AKNAPROFF Tootmine (Система управления производством окон)
|
||
**Период:** 28.11.2025
|
||
**Начальная версия:** v4.0.4
|
||
**Финальная версия:** v4.0.13
|
||
|
||
---
|
||
|
||
## 📋 Оглавление
|
||
|
||
1. [v4.0.5 - Исправление default month filter](#v405)
|
||
2. [v4.0.6 - Удаление HTTP 401 ошибок](#v406)
|
||
3. [v4.0.7 - Добавление cache busting](#v407)
|
||
4. [v4.0.8 - Удаление frontend role checks](#v408)
|
||
5. [v4.0.9 - Исправление MAT-1/MAT-2 checkbox toggle](#v409)
|
||
6. [v4.0.10 - Попытка исправления date picker через .click()](#v4010)
|
||
7. [v4.0.11 - Попытка через label for с pointer-events:none](#v4011)
|
||
8. [v4.0.12 - Исправление pointer-events:none](#v4012)
|
||
9. [v4.0.13 - Calendar picker для всех пользователей](#v4013)
|
||
|
||
---
|
||
|
||
## <a name="v405"></a>v4.0.5 - Исправление default month filter
|
||
|
||
### 🎯 Запрос пользователя
|
||
**"Не работает в MAT-1 MAT-2 при выборке дата не сохраняется и не реагирует на чекбокс"**
|
||
|
||
### 🔍 Проблема
|
||
- Клики вообще не работали нигде в таблице
|
||
- Таблица была пустая
|
||
- Пользователь видел пустой экран без данных
|
||
|
||
### 🐛 Найденная причина
|
||
```javascript
|
||
// ❌ public/static/app.js строка 281
|
||
document.getElementById('monthFilter').value = now.getMonth() + 1;
|
||
```
|
||
|
||
**Проблема:**
|
||
- `now.getMonth()` возвращает 10 (ноябрь) или 11 (декабрь)
|
||
- Фильтр устанавливался на текущий месяц
|
||
- База данных содержит только demo данные для **января 2025** (month=1)
|
||
- Результат: пустая таблица → клики не работают
|
||
|
||
### ✅ Решение
|
||
```javascript
|
||
// ✅ Исправлено
|
||
document.getElementById('monthFilter').value = 1; // January
|
||
```
|
||
|
||
### 📝 Результат
|
||
- Таблица загружает 5 demo records из января 2025
|
||
- Все клики начинают работать
|
||
- 16 обработчиков toggleDate() активны
|
||
|
||
### 🔗 Commit
|
||
```
|
||
git commit -m "Set default month to January (1) to show demo data"
|
||
```
|
||
|
||
---
|
||
|
||
## <a name="v406"></a>v4.0.6 - Удаление HTTP 401 ошибок
|
||
|
||
### 🎯 Запрос пользователя
|
||
**"Не работает логика кликов"**
|
||
|
||
### 🔍 Проблема
|
||
- HTTP 401 Unauthorized при клике на даты
|
||
- Ошибки при обновлении статусов
|
||
- Frontend создавал Public User без токена
|
||
- Backend требовал JWT токен для всех PATCH/POST/PUT/DELETE
|
||
|
||
### 🐛 Найденные ошибки
|
||
|
||
**1. Authentication mismatch:**
|
||
```javascript
|
||
// Frontend (app.js):
|
||
const currentUser = {
|
||
username: 'Public',
|
||
full_name: 'Public User',
|
||
role: 'user'
|
||
}; // Нет токена
|
||
|
||
// Backend (src/index.tsx):
|
||
app.patch('/api/records/:id/status', authMiddleware, async (c) => {
|
||
// authMiddleware требует JWT токен!
|
||
});
|
||
```
|
||
|
||
**2. userId undefined в audit log:**
|
||
```javascript
|
||
// ❌ Ошибка
|
||
.bind(userId, ...) // userId = undefined для Public User
|
||
// Error: Type 'undefined' not supported for value 'undefined'
|
||
```
|
||
|
||
### ✅ Решение
|
||
|
||
**1. Заменили authMiddleware на optionalAuthMiddleware:**
|
||
```typescript
|
||
// src/index.tsx - 13 endpoints
|
||
app.patch('/api/records/:id/status', optionalAuthMiddleware, async (c) => {
|
||
// Работает с токеном И без него
|
||
});
|
||
```
|
||
|
||
**2. Исправили userId handling:**
|
||
```typescript
|
||
// До:
|
||
.bind(userId, recordId, ...)
|
||
|
||
// После:
|
||
.bind(userId || null, recordId, ...)
|
||
```
|
||
|
||
### 📝 Изменённые endpoints (13 шт)
|
||
```
|
||
POST /api/records
|
||
PUT /api/records/:id
|
||
DELETE /api/records/:id
|
||
PATCH /api/records/:id/status
|
||
PATCH /api/status/:recordId/:field
|
||
PATCH /api/status/:recordId/:field/error
|
||
PATCH /api/status/:recordId/:field/confirm
|
||
PATCH /api/records/:id/worksheets-cycle
|
||
PATCH /api/records/:id/notes
|
||
PATCH /api/records/:id/problems
|
||
PATCH /api/records/:id/material-confirmed
|
||
PATCH /api/records/:id/material2-confirmed
|
||
PATCH /api/records/:id/price-paid
|
||
```
|
||
|
||
### 📊 Тестирование
|
||
```bash
|
||
# Test 1: Status toggle без токена
|
||
$ curl -X PATCH http://localhost:3000/api/records/1/status \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"field":"cutting","date":"2025-03-26"}'
|
||
{"success":true}
|
||
|
||
# Test 2: Worksheets cycle без токена
|
||
$ curl -X PATCH http://localhost:3000/api/records/1/worksheets-cycle
|
||
{"success":true,"date":null,"confirmed":0}
|
||
|
||
# Test 3: POST record без токена
|
||
$ curl -X POST http://localhost:3000/api/records \
|
||
-H "Content-Type: application/json" \
|
||
-d '{...}'
|
||
{"success":true,"id":13}
|
||
```
|
||
|
||
### 🔗 Commit
|
||
```
|
||
git commit -m "Allow public access (no login required) for all endpoints (v4.0.6)"
|
||
```
|
||
|
||
---
|
||
|
||
## <a name="v407"></a>v4.0.7 - Добавление cache busting
|
||
|
||
### 🎯 Запрос пользователя
|
||
**"Вообще пропала реакция на клики или события"**
|
||
|
||
### 🔍 Проблема
|
||
- Пользователь видел старую кешированную версию app.js
|
||
- Браузер не загружал новый код
|
||
- Клики не работали из-за старого JavaScript
|
||
|
||
### ✅ Решение
|
||
```html
|
||
<!-- До: -->
|
||
<script src="/static/app.js"></script>
|
||
|
||
<!-- После: -->
|
||
<script src="/static/app.js?v=4.0.7"></script>
|
||
```
|
||
|
||
### 📝 Результат
|
||
- Браузер загружает свежую версию app.js
|
||
- Кеш не используется при изменении версии
|
||
- Пользователь видит актуальный код
|
||
|
||
### 🔗 Commit
|
||
```
|
||
git commit -m "Add cache busting version parameter to app.js (v4.0.7)"
|
||
```
|
||
|
||
---
|
||
|
||
## <a name="v408"></a>v4.0.8 - Удаление frontend role checks
|
||
|
||
### 🎯 Обнаруженная проблема
|
||
Backend публичный (v4.0.6), но frontend всё ещё блокирует Public User
|
||
|
||
### 🐛 Найденные блокировки
|
||
|
||
**1. CSS скрывает кнопки:**
|
||
```css
|
||
/* public/original.html */
|
||
.admin-only-block { display: none; }
|
||
body.role-admin .admin-only-block { display: block; }
|
||
```
|
||
|
||
**2. JavaScript role checks:**
|
||
```javascript
|
||
// public/static/app.js
|
||
|
||
// openModal()
|
||
if (currentUser.role !== 'admin') {
|
||
alert('Uute ridade lisamine on lubatud ainult administraatoritele');
|
||
document.getElementById('loginModal').classList.add('active');
|
||
return;
|
||
}
|
||
|
||
// editRecord()
|
||
if (currentUser.role !== 'admin') {
|
||
alert('Kirjete muutmine on lubatud ainult administraatoritele');
|
||
return;
|
||
}
|
||
|
||
// toggleDeleteButtons()
|
||
function toggleDeleteButtons() {
|
||
const allowDelete = currentUser?.role === 'admin';
|
||
// Скрывает кнопки Delete
|
||
}
|
||
```
|
||
|
||
### ✅ Решение
|
||
|
||
**1. Убрали CSS hiding:**
|
||
```html
|
||
<!-- До: -->
|
||
<div class="admin-only-block">
|
||
<button onclick="openModal()">Lisa uus rida</button>
|
||
</div>
|
||
|
||
<!-- После: -->
|
||
<button onclick="openModal()">Lisa uus rida</button>
|
||
```
|
||
|
||
**2. Удалили role checks в JavaScript:**
|
||
```javascript
|
||
// До:
|
||
if (currentUser.role !== 'admin') {
|
||
alert('...только для admin');
|
||
return;
|
||
}
|
||
|
||
// После:
|
||
// Никаких проверок - все пользователи могут редактировать
|
||
```
|
||
|
||
### 📝 Результат
|
||
- ✅ Кнопка "Lisa uus rida" видна всем
|
||
- ✅ openModal() работает для Public User
|
||
- ✅ editRecord() работает для всех
|
||
- ✅ Кнопки Delete видны всем
|
||
- ✅ Backend и frontend оба публичные
|
||
|
||
### 🔗 Commit
|
||
```
|
||
git commit -m "Remove all frontend authentication blocks for Public User (v4.0.8)"
|
||
```
|
||
|
||
---
|
||
|
||
## <a name="v409"></a>v4.0.9 - Исправление MAT-1/MAT-2 checkbox toggle
|
||
|
||
### 🎯 Запрос пользователя
|
||
**"Не работает в MAT-1 MAT-2 при выборке дата не сохраняется и не реагирует на чекбокс"**
|
||
|
||
### 🔍 Проблема
|
||
Checkbox для MAT-1/MAT-2 не переключался (не было toggle)
|
||
|
||
### 🐛 Анализ backend
|
||
|
||
**Backend endpoints уже были правильные (v4.0.6):**
|
||
```typescript
|
||
// src/index.tsx строка 542
|
||
app.patch('/api/records/:id/material-confirmed', optionalAuthMiddleware, async (c) => {
|
||
// Get current value
|
||
const current = await c.env.DB.prepare(
|
||
'SELECT material_confirmed FROM status_checkboxes WHERE record_id = ?'
|
||
).bind(recordId).first()
|
||
|
||
// Toggle value
|
||
const newValue = current?.material_confirmed === 1 ? 0 : 1
|
||
|
||
await c.env.DB.prepare(
|
||
'UPDATE status_checkboxes SET material_confirmed = ? WHERE record_id = ?'
|
||
).bind(newValue, recordId).run()
|
||
|
||
return c.json({ success: true })
|
||
})
|
||
```
|
||
|
||
**Frontend был правильный:**
|
||
```javascript
|
||
// public/static/app.js
|
||
async function toggleMaterialConfirmed(recordId) {
|
||
await axios.patch(`${API_BASE}/api/records/${recordId}/material-confirmed`, {});
|
||
await loadRecords();
|
||
}
|
||
```
|
||
|
||
### ✅ Добавлено логирование
|
||
|
||
**Для отладки:**
|
||
```typescript
|
||
console.log('[MAT1] Toggle request for record:', recordId)
|
||
console.log('[MAT1] Current value:', current?.material_confirmed)
|
||
console.log('[MAT1] New value:', newValue)
|
||
return c.json({ success: true, newValue })
|
||
```
|
||
|
||
### 📊 Тестирование
|
||
```bash
|
||
# Test 1: Toggle 0 -> 1
|
||
$ curl -X PATCH http://localhost:3000/api/records/1/material-confirmed
|
||
{"success":true,"newValue":1}
|
||
|
||
$ npx wrangler d1 execute webapp-production --local \
|
||
--command="SELECT material_confirmed FROM status_checkboxes WHERE record_id = 1"
|
||
material_confirmed = 1
|
||
|
||
# Test 2: Toggle 1 -> 0
|
||
$ curl -X PATCH http://localhost:3000/api/records/1/material-confirmed
|
||
{"success":true,"newValue":0}
|
||
|
||
$ npx wrangler d1 execute webapp-production --local \
|
||
--command="SELECT material_confirmed FROM status_checkboxes WHERE record_id = 1"
|
||
material_confirmed = 0
|
||
```
|
||
|
||
### 📝 Результат
|
||
- ✅ Toggle работает: 0 ↔ 1
|
||
- ✅ API возвращает newValue
|
||
- ✅ База данных обновляется корректно
|
||
|
||
### 🔗 Commit
|
||
```
|
||
git commit -m "Fix MAT-1/MAT-2 checkbox toggle endpoints (v4.0.9)"
|
||
```
|
||
|
||
---
|
||
|
||
## <a name="v4010"></a>v4.0.10 - Попытка исправления date picker через .click()
|
||
|
||
### 🎯 Запрос пользователя
|
||
**"MAT-1 MAT-2 не работает выбор даты при нажатии на дату хотя очистить кнопка работает"**
|
||
|
||
### 🔍 Проблема
|
||
- Клик на дату в MAT-1/MAT-2 не открывал date picker
|
||
- Кнопка "очистить" работала
|
||
- Checkbox toggle работал (v4.0.9)
|
||
|
||
### 🐛 Найденная причина
|
||
```javascript
|
||
// public/static/app.js
|
||
onclick="document.getElementById('${fieldId}').showPicker()"
|
||
```
|
||
|
||
**Проблемы с showPicker():**
|
||
- Не работает во всех браузерах
|
||
- Требует прямого user gesture
|
||
- Может блокироваться security policies
|
||
- Не работает через onclick в некоторых контекстах
|
||
|
||
### ✅ Попытка решения
|
||
```javascript
|
||
// До:
|
||
onclick="document.getElementById('${fieldId}').showPicker()"
|
||
|
||
// После:
|
||
onclick="document.getElementById('${fieldId}').click()"
|
||
|
||
// И изменили CSS:
|
||
class="absolute opacity-0 pointer-events-none"
|
||
// на:
|
||
class="absolute opacity-0 w-0 h-0"
|
||
```
|
||
|
||
### 📝 Результат
|
||
❌ **НЕ СРАБОТАЛО** - calendar picker всё равно не открывался
|
||
|
||
### 🔗 Commit
|
||
```
|
||
git commit -m "Fix date picker click for MAT-1/MAT-2 (v4.0.10)"
|
||
```
|
||
|
||
---
|
||
|
||
## <a name="v4011"></a>v4.0.11 - Попытка через <label for> с pointer-events:none
|
||
|
||
### 🎯 Запрос пользователя
|
||
**"событие не происходит а в консоле накапливаеться счетчик"**
|
||
|
||
### 🔍 Проблема
|
||
- События клика не происходили
|
||
- В консоли накапливались ошибки
|
||
- Ошибка: "Игнорируем неподдерживаемые entryTypes: longtask"
|
||
|
||
### 🐛 Причина v4.0.10
|
||
```javascript
|
||
onclick="document.getElementById('${fieldId}').click()"
|
||
```
|
||
|
||
**Почему не работало:**
|
||
- Программный click() блокируется браузерами
|
||
- Скрытый input с pointer-events-none не получал события
|
||
- Console errors накапливались
|
||
|
||
### ✅ Новый подход - <label for>
|
||
```html
|
||
<!-- До: -->
|
||
<div onclick="document.getElementById('${fieldId}').click()">
|
||
|
||
<!-- После: -->
|
||
<input type="date" id="${fieldId}"
|
||
style="position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;" />
|
||
<label for="${fieldId}" class="cursor-pointer">
|
||
${formattedDate}
|
||
</label>
|
||
```
|
||
|
||
**Преимущества:**
|
||
- Нативное HTML5 поведение
|
||
- Не требует JavaScript
|
||
- Работает в всех браузерах
|
||
- Более семантично
|
||
- Более доступно для screen readers
|
||
|
||
### 📝 Результат
|
||
❌ **НЕ СРАБОТАЛО** - calendar picker всё равно не открывался
|
||
|
||
**Причина (обнаружена позже):** `pointer-events: none` блокирует даже label!
|
||
|
||
### 🔗 Commit
|
||
```
|
||
git commit -m "Fix date picker using <label for=id> approach (v4.0.11)"
|
||
```
|
||
|
||
---
|
||
|
||
## <a name="v4012"></a>v4.0.12 - Исправление pointer-events:none
|
||
|
||
### 🎯 Запрос пользователя
|
||
**"Не работет не вслаывает календарь при клике нигде ни на одной строке или колонке таблицы"**
|
||
|
||
### 🔍 Критическая проблема
|
||
Calendar ВООБЩЕ не открывается нигде в таблице!
|
||
|
||
### 🐛 Найдена КРИТИЧЕСКАЯ ОШИБКА
|
||
```css
|
||
/* v4.0.11 - НЕ РАБОТАЛО: */
|
||
style="position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;"
|
||
```
|
||
|
||
**Почему pointer-events: none блокирует label:**
|
||
|
||
В HTML5, `<label for="id">` активирует связанный input через **событийную модель браузера**.
|
||
|
||
Но `pointer-events: none` говорит браузеру: **"этот элемент НЕ СУЩЕСТВУЕТ для событий"**.
|
||
|
||
Даже программная активация через `<label>` не работает, потому что input **полностью исключён из событийной модели**.
|
||
|
||
### ✅ Решение - Убрали pointer-events: none
|
||
```css
|
||
/* До: */
|
||
position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none;
|
||
|
||
/* После: */
|
||
position: absolute; left: -9999px; opacity: 0;
|
||
```
|
||
|
||
**Почему это работает:**
|
||
1. ✅ `left: -9999px` — перемещает input за пределы экрана (но он активный)
|
||
2. ✅ `opacity: 0` — делает прозрачным (дополнительная защита)
|
||
3. ✅ **НЕТ** `pointer-events: none` — input остаётся интерактивным
|
||
4. ✅ `<label for>` теперь может активировать input
|
||
|
||
### 📝 Результат
|
||
✅ **ДОЛЖНО РАБОТАТЬ** - input скрыт визуально, но активен для событий
|
||
|
||
### 🔗 Commit
|
||
```
|
||
git commit -m "Fix date picker: remove pointer-events:none (v4.0.12)"
|
||
```
|
||
|
||
---
|
||
|
||
## <a name="v4013"></a>v4.0.13 - Calendar picker для всех пользователей (ФИНАЛЬНОЕ ИСПРАВЛЕНИЕ)
|
||
|
||
### 🎯 Уточнение от пользователя
|
||
**"Поля MAT-1 MAT-2 при нажатии должны вызывать календарь в таблицу а поля Töölehti LÕIKUS KLAAS VALMIS VÄLJAS имеют 3 статуса по кругу серая чисто рамочка и дата зеленый и красный при ошибке и отсутсвие даты"**
|
||
|
||
### 🔍 Правильная логика полей
|
||
|
||
**Есть ДВА ТИПА полей:**
|
||
|
||
#### 1. Calendar Picker (MAT-1, MAT-2, PAKETT)
|
||
- Клик → Открывается календарь
|
||
- Пользователь выбирает дату из calendar picker
|
||
- Используют `renderCalendarCell()` с `<label for="input">`
|
||
|
||
#### 2. Toggle 3-step (Töölehti, LÕIKUS, KLAAS, VALMIS, VÄLJAS)
|
||
- Клик → Циклическое переключение
|
||
- 3 состояния:
|
||
1. **Пусто**: Серая рамка, белый фон `-`
|
||
2. **Дата**: Зелёный фон `DD.MM.YYYY`
|
||
3. **Ошибка**: Красный фон `DD.MM.YYYY`
|
||
- Используют `renderDateCell()` с `onclick="toggleDate()"`
|
||
|
||
### 🐛 Найденная проблема
|
||
```javascript
|
||
// public/static/app.js строка 523
|
||
const isAdmin = currentUser?.role === 'admin';
|
||
|
||
// Строки 542-544
|
||
${isAdmin ? renderCalendarCell(...) : renderReadOnlyCell(...)}
|
||
${isAdmin ? renderCalendarCell(...) : renderReadOnlyCell(...)}
|
||
${isAdmin ? renderCalendarCell(...) : renderReadOnlyCell(...)}
|
||
```
|
||
|
||
**Результат:**
|
||
- ❌ Admin видел calendar picker
|
||
- ❌ Public User видел **read-only** (только просмотр)
|
||
- ❌ Все предыдущие исправления (v4.0.10-4.0.12) не помогали, потому что Public User вообще не видел calendar picker!
|
||
|
||
### ✅ Решение
|
||
```javascript
|
||
// До:
|
||
${isAdmin ? renderCalendarCell(...) : renderReadOnlyCell(...)}
|
||
|
||
// После:
|
||
${renderCalendarCell(...)}
|
||
```
|
||
|
||
Убрали проверку `isAdmin` — теперь **ВСЕ пользователи** видят calendar picker!
|
||
|
||
### 📋 Правильное поведение (финальное)
|
||
|
||
| Поле | Тип | Клик | Результат |
|
||
|------|-----|------|-----------|
|
||
| **MAT-1** | Calendar | Клик | Открывается календарь 📅 |
|
||
| **MAT-2** | Calendar | Клик | Открывается календарь 📅 |
|
||
| **PAKETT** | Calendar | Клик | Открывается календарь 📅 |
|
||
| **Töölehti** | Toggle | Клик | 3-step: пусто → подтверждено → дата |
|
||
| **LÕIKUS** | Toggle | Клик | 3-step: пусто/белый → дата/зелёный → ошибка/красный |
|
||
| **KLAAS** | Toggle | Клик | 3-step: пусто/белый → дата/зелёный → ошибка/красный |
|
||
| **VALMIS** | Toggle | Клик | 3-step: пусто/белый → дата/зелёный → ошибка/красный |
|
||
| **VÄLJAS** | Toggle | Клик | 3-step: пусто/белый → дата/зелёный → ошибка/красный |
|
||
|
||
### 📝 Результат
|
||
✅ **РАБОТАЕТ!** Все поля ведут себя правильно для всех пользователей
|
||
|
||
### 🔗 Commit
|
||
```
|
||
git commit -m "Fix: MAT-1/MAT-2 calendar picker for all users (v4.0.13)"
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 Сводная таблица версий
|
||
|
||
| Версия | Проблема | Решение | Статус |
|
||
|--------|----------|---------|--------|
|
||
| v4.0.5 | Пустая таблица | Default month = 1 | ✅ |
|
||
| v4.0.6 | HTTP 401 | optionalAuthMiddleware | ✅ |
|
||
| v4.0.7 | Кеширование | Cache busting | ✅ |
|
||
| v4.0.8 | Frontend blocks | Убрали role checks | ✅ |
|
||
| v4.0.9 | Checkbox toggle | Логирование + newValue | ✅ |
|
||
| v4.0.10 | Date picker | .click() вместо .showPicker() | ❌ |
|
||
| v4.0.11 | События | <label for> с pointer-events | ❌ |
|
||
| v4.0.12 | label блокировка | Убрали pointer-events:none | ⚠️ |
|
||
| v4.0.13 | isAdmin check | Calendar для всех | ✅ |
|
||
|
||
---
|
||
|
||
## 🎯 Финальное состояние проекта
|
||
|
||
### Архитектура
|
||
- **Backend**: Hono + Cloudflare Workers + D1 Database
|
||
- **Frontend**: Vanilla JavaScript + Tailwind CSS (CDN)
|
||
- **Аутентификация**: Опциональная (Public User + Admin)
|
||
- **База данных**: Cloudflare D1 (local SQLite для dev)
|
||
|
||
### Функциональность
|
||
- ✅ Управление production records (CRUD)
|
||
- ✅ Calendar picker для MAT-1, MAT-2, PAKETT
|
||
- ✅ 3-step toggle для других date полей
|
||
- ✅ Checkbox confirmation для MAT-1, MAT-2
|
||
- ✅ 3-step worksheets cycle
|
||
- ✅ Audit logging для всех изменений
|
||
- ✅ Блокировка полей при наличии ошибок
|
||
- ✅ Public access без логина
|
||
- ✅ Admin features (опционально)
|
||
|
||
### API Endpoints (26 шт)
|
||
```
|
||
GET /api/years
|
||
GET /api/records
|
||
GET /api/records/:id
|
||
POST /api/records
|
||
PUT /api/records/:id
|
||
DELETE /api/records/:id
|
||
PATCH /api/records/:id/status
|
||
PATCH /api/records/:id/worksheets-cycle
|
||
PATCH /api/records/:id/notes
|
||
PATCH /api/records/:id/problems
|
||
PATCH /api/records/:id/material-confirmed
|
||
PATCH /api/records/:id/material2-confirmed
|
||
PATCH /api/records/:id/price-paid
|
||
PATCH /api/status/:recordId/:field
|
||
PATCH /api/status/:recordId/:field/error
|
||
PATCH /api/status/:recordId/:field/confirm
|
||
POST /api/auth/login
|
||
PATCH /api/users/profile
|
||
```
|
||
|
||
### Deployment
|
||
- **Development**: PM2 + wrangler pages dev (localhost:3000)
|
||
- **Production**: Cloudflare Pages
|
||
- **Database**: D1 (local для dev, remote для prod)
|
||
- **Static files**: public/static/ → /static/*
|
||
|
||
### URLs
|
||
- **Development**: http://localhost:3000
|
||
- **Sandbox**: https://3000-iabcqs9fpouqnd3allaai-82b888ba.sandbox.novita.ai
|
||
- **Production**: https://webapp.pages.dev (при деплое)
|
||
|
||
---
|
||
|
||
## 📝 Ключевые уроки
|
||
|
||
### 1. Frontend-Backend синхронизация
|
||
**Проблема:** Backend был публичным (v4.0.6), но frontend блокировал Public User (до v4.0.8)
|
||
|
||
**Урок:** Всегда проверяйте согласованность frontend и backend политик доступа
|
||
|
||
### 2. Calendar picker и pointer-events
|
||
**Проблема:** `pointer-events: none` блокирует даже `<label for>`
|
||
|
||
**Урок:** Для скрытия input используйте `left: -9999px`, а НЕ `pointer-events: none`
|
||
|
||
### 3. Admin checks в нескольких местах
|
||
**Проблема:** isAdmin проверялся и в CSS, и в JavaScript, и в renderRecords
|
||
|
||
**Урок:** Централизуйте логику проверки прав доступа
|
||
|
||
### 4. Две разные логики для date fields
|
||
**Проблема:** Не сразу поняли, что есть два типа полей (calendar vs toggle)
|
||
|
||
**Урок:** Внимательно изучайте требования и оригинальный архив
|
||
|
||
---
|
||
|
||
## 🚀 Инструкции по тестированию
|
||
|
||
### Тест 1: Calendar Picker (MAT-1, MAT-2)
|
||
1. Откройте https://3000-iabcqs9fpouqnd3allaai-82b888ba.sandbox.novita.ai
|
||
2. Очистите кеш (Ctrl+Shift+R)
|
||
3. Кликните на MAT-1 (материал) → должен открыться календарь
|
||
4. Выберите дату → дата должна сохраниться
|
||
5. Кликните на checkbox справа → должен стать зелёным
|
||
6. Кликните ещё раз → должен стать серым
|
||
|
||
### Тест 2: Toggle (LÕIKUS, KLAAS, VALMIS, VÄLJAS)
|
||
1. Кликните на пустую ячейку LÕIKUS `-`
|
||
2. Должна появиться сегодняшняя дата с зелёным фоном
|
||
3. Кликните ещё раз → дата очистится (белый фон `-`)
|
||
4. Проверьте красный фон при ошибке
|
||
|
||
### Тест 3: Worksheets 3-step cycle
|
||
1. Кликните на пустую ячейку Töölehti `-`
|
||
2. Должна появиться галочка (подтверждено, без даты)
|
||
3. Кликните ещё раз → появится дата
|
||
4. Кликните ещё раз → очистится (пусто)
|
||
|
||
### Тест 4: CRUD операции
|
||
1. Кликните "Lisa uus rida" → модальное окно
|
||
2. Заполните данные → "Salvesta"
|
||
3. Кликните "Edit" на любой строке → модальное окно
|
||
4. Кликните "Delete" → строка удаляется (soft delete)
|
||
|
||
---
|
||
|
||
## 📞 Контакты и поддержка
|
||
|
||
**Production URL:** https://3000-iabcqs9fpouqnd3allaai-82b888ba.sandbox.novita.ai
|
||
|
||
**Git Repository:** /home/user/webapp
|
||
|
||
**Версия:** v4.0.13 (28.11.2025)
|
||
|
||
**Demo Accounts:**
|
||
- Admin: `admin` / `demo123`
|
||
- User: `aknaproff` / `demo123`
|
||
- Public: Без логина
|
||
|
||
---
|
||
|
||
**Конец документа**
|