Files
Aknaproff/FULL_DEVELOPMENT_HISTORY.md
Deploy Bot 64403d6fd6 v4.1.21: Реструктуризация проекта для Synology ARM
- Реструктуризация: 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
2026-01-14 18:37:00 +02:00

706 lines
25 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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: Без логина
---
**Конец документа**