## Overview
Removed the CurrentPrice and SyncedAt fields from local_components, transitioning to a
pricelist-based pricing model where all prices are sourced from local_pricelist_items
based on the configuration's selected pricelist.
## Changes
### Data Model Updates
- **LocalComponent**: Now stores only metadata (LotName, LotDescription, Category, Model)
- Removed: CurrentPrice, SyncedAt (both redundant)
- Pricing is now exclusively sourced from local_pricelist_items
- **LocalConfiguration**: Added pricelist selection fields
- Added: WarehousePricelistID, CompetitorPricelistID
- These complement the existing PricelistID (Estimate)
### Migrations
- Added migration "drop_component_unused_fields" to remove CurrentPrice and SyncedAt columns
- Added migration "add_warehouse_competitor_pricelists" to add new pricelist fields
### Component Sync
- Removed current_price from MariaDB query
- Removed CurrentPrice assignment in component creation
- SyncComponentPrices now exclusively updates based on pricelist_items via quote calculation
### Quote Calculation
- Added PricelistID field to QuoteRequest
- Updated local-first path to use pricelist_items instead of component.CurrentPrice
- Falls back to latest estimate pricelist if PricelistID not specified
- Maintains offline-first behavior: local queries work without MariaDB
### Configuration Refresh
- Removed fallback on component.CurrentPrice
- Prices are only refreshed from local_pricelist_items
- If price not found in pricelist, original price is preserved
### API Changes
- Removed CurrentPrice from ComponentView
- Components API no longer returns pricing information
- Pricing is accessed via QuoteService or PricelistService
### Code Cleanup
- Removed UpdateComponentPricesFromPricelist() method
- Removed EnsureComponentPricesFromPricelists() method
- Updated UnifiedRepository to remove offline pricing logic
- Updated converters to remove CurrentPrice mapping
## Architecture Impact
- Components = metadata store only
- Prices = managed by pricelist system
- Quote calculation = owns all pricing logic
- Local-first behavior preserved: SQLite queries work offline, no MariaDB dependency
## Testing
- Build successful
- All code compiles without errors
- Ready for migration testing with existing databases
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Sync was blocked because the migration registry table creation required
CREATE TABLE permissions that the database user might not have.
Changes:
- Check if migration registry tables exist before attempting to create them
- Skip creation if table exists and user lacks CREATE permissions
- Use information_schema to reliably check table existence
- Apply same fix to user sync status table creation
- Gracefully handle ALTER TABLE failures for backward compatibility
This allows sync to proceed even if the client is a read-limited database user,
as long as the required tables have already been created by an administrator.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
- Remove unused stockMappingsCache variable (dead code after selectStockMappingRow removal)
- Move data-description from SVG to button element for reliable access
- Add disabled guard on bulk add/ignore buttons to prevent duplicate requests
- Return explicit error in UpsertIgnoreRule when rule already exists
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fixed application freezing in offline mode by preventing unnecessary
reconnection attempts:
**Changes:**
1. **DSN timeouts** (localdb.go)
- Added timeout=3s, readTimeout=3s, writeTimeout=3s to MySQL DSN
- Reduces connection timeout from 75s to 3s when MariaDB unreachable
2. **Fast /api/db-status** (main.go)
- Check connection status before attempting GetDB()
- Avoid reconnection attempts on every status request
- Returns cached offline status instantly
3. **Optimized sync service** (sync/service.go)
- GetStatus() checks connection status before GetDB()
- NeedSync() skips server check if already offline
- Prevents repeated 3s timeouts on every sync info request
4. **Local pricelist fallback** (pricelist.go)
- GetLatest() returns local pricelists when server offline
- UI can now display pricelist version in offline mode
5. **Better UI error messages** (configs.html)
- 404 shows "Не загружен" instead of "Ошибка загрузки"
- Network errors show "Не доступен" in gray
- Distinguishes between missing data and real errors
**Performance:**
- Before: 75s timeout on every offline request
- After: <5ms response time in offline mode
- Cached error state prevents repeated connection attempts
**User Impact:**
- UI no longer freezes when loading pages offline
- Instant page loads and API responses
- Pricelist version displays correctly in offline mode
- Clear visual feedback for offline state
Fixes Phase 2.5 offline mode performance issues.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updated sync-related code to use ConnectionManager instead of direct
database references:
- SyncService now creates repositories on-demand when connection available
- SyncHandler uses ConnectionManager for lazy DB access
- Added ComponentFilter and ListComponents to localdb for offline queries
- All sync operations check connection status before attempting MariaDB access
This completes the transition to offline-first architecture where all
database access goes through ConnectionManager.
Part of Phase 2.5: Full Offline Mode
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Introduced ConnectionManager to support offline-first architecture:
- New internal/db/connection.go with thread-safe connection management
- Lazy connection establishment (5s timeout, 10s cooldown)
- Automatic ping caching (30s interval) to avoid excessive checks
- Updated middleware/offline.go to use ConnectionManager.IsOnline()
- Updated sync/worker.go to use ConnectionManager instead of direct DB
This enables the application to start without MariaDB and gracefully
handle offline/online transitions.
Part of Phase 2.5: Full Offline Mode
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fixed two critical issues preventing offline-first operation:
1. **Instant startup** - Removed blocking GetDB() call during server
initialization. Server now starts in <10ms instead of 1+ minute.
- Changed setupRouter() to use lazy DB connection via ConnectionManager
- mariaDB connection is now nil on startup, established only when needed
- Fixes timeout issues when MariaDB is unreachable
2. **Offline mode nil pointer panics** - Added graceful degradation
when database is offline:
- ComponentService.GetCategories() returns DefaultCategories if repo is nil
- ComponentService.List/GetByLotName checks for nil repo
- PricelistService methods return empty/error responses in offline mode
- All methods properly handle nil repositories
**Before**: Server startup took 1min+ and crashed with nil pointer panic
when trying to load /configurator page offline.
**After**: Server starts instantly and serves pages in offline mode using
DefaultCategories and SQLite data.
Related to Phase 2.5: Full Offline Mode (local-first architecture)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- 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>
- Add price_updated_at field to qt_configurations table to track when prices were last updated
- Add RefreshPrices() method in configuration service to update all component prices with current values from database
- Add POST /api/configs/:uuid/refresh-prices API endpoint for price updates
- Add "Refresh Prices" button in configurator UI next to Save button
- Display last price update timestamp in human-readable format (e.g., "5 min ago", "2 hours ago")
- Create migration 004_add_price_updated_at.sql for database schema update
- Update CLAUDE.md documentation with new API endpoint and schema changes
- Add MIGRATION_PRICE_REFRESH.md with detailed migration instructions
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Основные изменения:
1. CSV экспорт и веб-интерфейс:
- Компоненты теперь сортируются по иерархии категорий (display_order)
- Категории отображаются в правильном порядке: BB, CPU, MEM, GPU и т.д.
- Компоненты без категории отображаются в конце
2. Раздел PCI в конфигураторе:
- Разделен на секции: GPU/DPU, NIC/HCA, HBA
- Улучшена навигация и выбор компонентов
3. Сохранение "своей цены":
- Добавлено поле custom_price в модель Configuration
- Создана миграция 002_add_custom_price.sql
- "Своя цена" сохраняется при сохранении конфигурации
- При загрузке конфигурации восстанавливается сохраненная цена
4. Автосохранение:
- Конфигурация автоматически сохраняется через 1 секунду после изменений
- Debounce предотвращает избыточные запросы
- Автосохранение работает для всех изменений (компоненты, количество, цена)
5. Дополнительно:
- Добавлен cmd/importer для импорта метаданных из таблицы lot
- Создан скрипт apply_migration.sh для применения миграций
- Оптимизирована работа с категориями в ExportService
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Добавлено отображение последней полученной цены в окне настройки цены
- Добавлен функционал переименования конфигураций (PATCH /api/configs/:uuid/rename)
- Изменён формат имени файла при экспорте: "YYYY-MM-DD NAME SPEC.ext"
- Исправлена сортировка компонентов: перенесена на сервер для корректной работы с пагинацией
- Добавлен расчёт popularity_score на основе котировок из lot_log
- Исправлена потеря настроек (метод, период, коэффициент) при пересчёте цен
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add tab-based configurator (Base, Storage, PCI, Power, Accessories, Other)
- Base tab: single-select with autocomplete for MB, CPU, MEM
- Other tabs: multi-select with autocomplete and quantity input
- Table view with LOT, Description, Price, Quantity, Total columns
- Add configuration list page with create modal (opportunity number)
- Remove Excel export functionality and excelize dependency
- Increase component list limit from 100 to 5000
- Add web templates (base, index, configs, login, admin_pricing)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Go module with Gin, GORM, JWT, excelize dependencies
- Configuration loading from YAML with all settings
- GORM models for users, categories, components, configurations, alerts
- Repository layer for all entities
- Services: auth (JWT), pricing (median/average/weighted), components,
quotes, configurations, export (CSV/XLSX), alerts
- Middleware: JWT auth, role-based access, CORS
- HTTP handlers for all API endpoints
- Main server with dependency injection and graceful shutdown
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>