- Top nav: link to /partnumber-books
- Page: summary cards (active version, unique LOTs, total PN, primary PN)
+ searchable items table for active book
+ collapsible history of all snapshots
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Show supported column formats and auto-detection rules so users
know what to copy from Excel.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Previous description was wrong: migrations/*.sql are MariaDB-only.
Document the actual 3-level SQLite migration flow:
1. GORM AutoMigrate (primary, runs on every start)
2. runLocalMigrations Go functions (data backfill, index creation)
3. Centralized remote migrations via qt_client_local_migrations
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
LocalPartnumberBook and LocalPartnumberBookItem added to AutoMigrate list
in localdb.go — consistent with all other local tables. Removed incorrectly
added addPartnumberBooks/addVendorSpecColumn functions from migrations.go
(vendor_spec column is handled by AutoMigrate via the LocalConfiguration model field).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Features:
- Configuration versioning: immutable snapshots in local_configuration_versions
- Revisions UI: /configs/:uuid/revisions page to view version history
- Clone from version: ability to clone configuration from specific revision
- Project variant deletion: DELETE /api/projects/:uuid endpoint
- Updated CLAUDE.md with new architecture details and endpoints
Architecture updates:
- local_configuration_versions table for immutable snapshots
- Version tracking on each configuration save
- Rollback capability to previous versions
- Variant deletion with main variant protection
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Root cause: Projects with duplicate (code, variant) pairs fail to sync
due to unique constraint on server. Example: multiple "OPS-1934" projects
with variant="Dell" where one already exists on server.
Fixes:
1. Sync service now detects duplicate (code, variant) on server and links
local project to existing server project instead of failing
2. Local repair checks for duplicate (code, variant) pairs and deduplicates
by appending UUID suffix to variant
3. Modal now scrollable with fixed header/footer (max-h-90vh)
This allows users to sync projects that were created offline with
conflicting codes/variants without losing data.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Implements automatic repair mechanism for pending changes with sync errors:
- Projects: validates and fixes empty name/code fields
- Configurations: ensures project references exist or assigns system project
- Clears errors and resets attempts to give changes another sync chance
Backend:
- LocalDB.RepairPendingChanges() with smart validation logic
- POST /api/sync/repair endpoint
- Detailed repair results with remaining errors
Frontend:
- Auto-repair section in sync modal shown when errors exist
- "ИСПРАВИТЬ" button with clear explanation of actions
- Real-time feedback with result messages
Co-Authored-By: Claude Opus 4.6 <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>
**Problem:**
Pricelist page showed empty list in offline mode even though
local pricelists existed in SQLite cache.
**Solution:**
Modified PricelistHandler.List() to fallback to local pricelists:
1. Check if server list is empty (offline)
2. Load from localDB.GetLocalPricelists()
3. Convert LocalPricelist to summary format
4. Add "synced_from": "local" field
5. Add "offline": true flag
**Response format:**
```json
{
"offline": true,
"total": 4,
"pricelists": [
{
"version": "2026-02-02-002",
"created_by": "sync",
"synced_from": "local",
"is_active": true
}
]
}
```
**Impact:**
- ✅ Local pricelists visible in offline mode
- ✅ UI can show cached pricelist versions
- ✅ Users can browse pricelists without connection
- ✅ Clear indication of local/remote source
Part of Phase 2.5: Full Offline Mode
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
**Problem:**
Configurator was broken in offline mode - no component search
and no price calculation because /api/components returned empty list.
**Solution:**
Added local component fallback to ComponentHandler:
1. **ComponentHandler with localDB** (component.go)
- Added localDB parameter to NewComponentHandler
- List() now fallbacks to local_components when offline
- Converts LocalComponent to ComponentView format
- Preserves prices from local cache
2. **Updated initialization** (main.go)
- Pass localDB to NewComponentHandler
**Impact:**
- ✅ Component search works offline
- ✅ Prices load from local_components table
- ✅ Configuration creation fully functional offline
- ✅ Price calculation works with cached prices
**Testing:**
- Verified /api/components returns local components
- Verified current_price field populated from cache
- Search, filtering, and pagination work correctly
Fixes critical Phase 2.5 offline mode issue.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
**Changes:**
1. **Admin menu always visible** (base.html)
- Removed 'hidden' class from "Администратор цен" link
- Menu no longer depends on write permission check
- Users can access pricing/pricelists pages in offline mode
2. **Online status checks for mutations** (admin_pricing.html)
- Added checkOnlineStatus() helper function
- createPricelist() checks online before creating
- deletePricelist() checks online before deleting
- Clear user feedback when operations blocked offline
**User Impact:**
- Admin menu accessible in both online and offline modes
- View-only access to pricelists when offline
- Clear error messages when attempting mutations offline
- Better offline-first UX
Part of Phase 2.5: Full Offline Mode
Co-Authored-By: Claude Sonnet 4.5 <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>
- Create htmx-powered partial template for sync status display
- Show Online/Offline indicator with color coding (green/red)
- Display pending changes count badge when there are unsynced items
- Add Sync button to push pending changes (appears only when needed)
- Auto-refresh every 30 seconds via htmx polling
- Replace JavaScript-based sync indicator with server-rendered partial
- Integrate SyncStatusPartial handler with template rendering
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Consolidate UI TODO items into single sync status partial task
- Move conflict resolution to Phase 4
- Add LOCAL_FIRST_INTEGRATION.md with architecture guide
- Add unified repository interface for future use
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add development phases (pricelists, projects, local SQLite, price versioning)
- Add new table schemas (qt_pricelists, qt_projects, qt_specifications)
- Add local SQLite database structure for offline work
- Remove Docker files (distributing as binary only)
- Disable RBAC for initial phases
Co-Authored-By: Claude Opus 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>
- Add is_hidden field to hide components from configurator
- Add colored dot indicator showing component usage status:
- Green: available in configurator
- Cyan: used as source for meta-articles
- Gray: hidden from configurator
- Optimize price recalculation with caching and skip unchanged
- Show current lot name during price recalculation
- Add Dockerfile (Alpine-based multi-stage build)
- Add docker-compose.yml and .dockerignore
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>