Add offline RefreshPrices, fix sync bugs, implement auto-restart

- Implement RefreshPrices for local-first mode
  - Update prices from local_components.current_price cache
  - Graceful degradation when component not found
  - Add PriceUpdatedAt timestamp to LocalConfiguration model
  - Support both authenticated and no-auth price refresh

- Fix sync duplicate entry bug
  - pushConfigurationUpdate now ensures server_id exists before update
  - Fetch from LocalConfiguration.ServerID or search on server if missing
  - Update local config with server_id after finding

- Add application auto-restart after settings save
  - Implement restartProcess() using syscall.Exec
  - Setup handler signals restart via channel
  - Setup page polls /health endpoint and redirects when ready
  - Add "Back" button on setup page when settings exist

- Fix setup handler password handling
  - Use PasswordEncrypted field consistently
  - Support empty password by using saved value

- Improve sync status handling
  - Add fallback for is_offline check in SyncStatusPartial
  - Enhance background sync logging with prefixes

- Update CLAUDE.md documentation
  - Mark Phase 2.5 tasks as complete
  - Add UI Improvements section with future tasks
  - Update SQLite tables documentation

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Mikhail Chusavitin
2026-02-02 11:03:41 +03:00
parent ec3c16f3fc
commit 9bd2acd4f7
11 changed files with 330 additions and 48 deletions

View File

@@ -29,12 +29,48 @@
- ✅ Add routes for new sync endpoints (`/api/sync/push`, `/pending/count`, `/pending`)
- ✅ ConfigurationGetter interface for handler compatibility
- ✅ Background sync worker: auto-sync every 5min (push + pull) - `internal/services/sync/worker.go`
- ✅ UI: sync status indicator (pending badge + sync button + offline/online dot) - `web/templates/partials/sync_status.html`
- ✅ RefreshPrices for local mode:
- `RefreshPrices()` / `RefreshPricesNoAuth()` в `local_configuration.go`
- Берёт цены из `local_components.current_price`
- Graceful degradation при отсутствии компонента
- Добавлено поле `price_updated_at` в `LocalConfiguration` (models.go:72)
- Обновлены converters для PriceUpdatedAt
- UI кнопка "Пересчитать цену" работает offline/online
- ✅ Fixed sync bugs:
- Duplicate entry error при update конфигураций (`sync/service.go:334-365`)
- pushConfigurationUpdate теперь проверяет наличие server_id перед update
- Если нет ID → получает из LocalConfiguration.ServerID или ищет на сервере
- Fixed setup.go: `settings.Password``settings.PasswordEncrypted`
**TODO:**
- ❌ UI: sync status partial (pending badge + sync button + offline indicator)
- ❌ RefreshPrices for local mode (via local_components)
- ❌ Conflict resolution (Phase 4, last-write-wins default)
### UI Improvements 🔶 IN PROGRESS
**1. Sync icon + pricelist badge в header (tasks 4+2):**
-`sync_status.html`: заменить текст Online/Offline на SVG иконку
- ❌ Кнопка sync → иконка (circular arrows) вместо текста
- ❌ Dropdown при клике: Push changes, Full sync, статус последней синхронизации
-`configs.html`: рядом с кнопкой "Создать" показать badge с версией активного прайслиста
- ❌ Загружать через `/api/pricelists/latest` при DOMContentLoaded
**2. Прайслисты → вкладка в "Администратор цен" (task 1):**
-`base.html`: убрать отдельную ссылку "Прайслисты" из навигации
-`admin_pricing.html`: добавить 4-ю вкладку "Прайслисты"
- ❌ Перенести логику из `pricelists.html` (table, create modal, CRUD) в эту вкладку
- ❌ Route `/pricelists` → редирект на `/admin/pricing?tab=pricelists` или удалить
**3. Страница настроек: расширить + синхронизация (task 3):**
-`setup.html`: переделать на `{{template "base" .}}` структуру
- ❌ Увеличить до `max-w-4xl`, разделить на 2 секции
- ❌ Секция A: Подключение к БД (текущая форма)
- ❌ Секция B: Синхронизация данных:
- Статус Online/Offline
- Кнопки: "Синхронизировать всё", "Обновить компоненты", "Обновить прайслисты"
- Журнал синхронизации (последние N операций)
- ❌ Возможно: новый API endpoint для sync log
### Phase 3: Projects and Specifications
- qt_projects, qt_specifications tables (MariaDB)
- Replace qt_configurations → Project/Specification hierarchy
@@ -65,12 +101,12 @@ Go 1.22+ | Gin | GORM | MariaDB 11 | SQLite (glebarez/sqlite) | htmx + Tailwind
- `qt_specifications` - project_id, pricelist_id, variant, rev, qty, items JSON (Phase 3)
### SQLite (data/quoteforge.db)
- `connection_settings` - encrypted DB credentials
- `connection_settings` - encrypted DB credentials (PasswordEncrypted field)
- `local_pricelists/items` - cached from server
- `local_components` - lot cache for offline search
- `local_configurations` - with sync_status (pending/synced/conflict)
- `local_components` - lot cache for offline search (with current_price)
- `local_configurations` - UUID, items, price_updated_at, sync_status (pending/synced/conflict), server_id
- `local_projects/specifications` - Phase 3
- `pending_changes` - sync queue (entity_type, uuid, op, payload, created_at)
- `pending_changes` - sync queue (entity_type, uuid, op, payload, created_at, attempts, last_error)
## Business Logic
@@ -91,6 +127,7 @@ Go 1.22+ | Gin | GORM | MariaDB 11 | SQLite (glebarez/sqlite) | htmx + Tailwind
| Pricelists | CRUD /api/pricelists, GET /latest, POST /compare |
| Projects | CRUD /api/projects/:uuid (Phase 3) |
| Specs | CRUD /api/specs/:uuid, POST /upgrade, GET /diff (Phase 3) |
| Configs | POST /:uuid/refresh-prices (обновить цены из local_components) |
| Sync | GET /status, POST /components, /pricelists, /push, /pull, /resolve-conflict |
| Export | GET /api/specs/:uuid/export, /api/projects/:uuid/export |