**Windows compatibility:**
- Added filepath.Join for all template and static paths
- Fixes "path not found" errors on Windows
**Localhost binding:**
- Changed default host from 0.0.0.0 to 127.0.0.1
- Browser always opens on 127.0.0.1 (localhost)
- Setup mode now listens on 127.0.0.1:8080
- Updated config.example.yaml with comment about 0.0.0.0
This ensures the app works correctly on Windows and opens
browser on the correct localhost address.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add make build-windows for Windows AMD64
- Update make build-all to include Windows
- Update release script to package Windows binary as .zip
- Add Windows installation instructions to docs
- Windows binary: qfs-windows-amd64.exe (~17MB)
All platforms now supported:
- Linux AMD64 (.tar.gz)
- macOS Intel/ARM (.tar.gz)
- Windows AMD64 (.zip)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add scripts/release.sh for automated release builds
- Creates tar.gz packages for Linux and macOS
- Generates SHA256 checksums
- Add 'make release' target
- Add releases/ to .gitignore
Usage:
make release # Build and package for all platforms
Output: releases/v0.2.5/*.tar.gz + SHA256SUMS.txt
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add -version flag to show build version
- Add Makefile with build targets:
- make build-release: optimized build with version
- make build-all: cross-compile for Linux/macOS
- make run/test/clean: dev commands
- Update documentation with build commands
- Version is embedded via ldflags during build
Usage:
make build-release # Build with version
./bin/qfs -version # Show version
Version format: v0.2.5-1-gfa0f5e3 (tag-commits-hash)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Rename cmd/server to cmd/qfs for shorter binary name
- Update all documentation references (README, CLAUDE.md, etc.)
- Update build commands to output bin/qfs
- Binary name now matches directory name
Usage:
go run ./cmd/qfs # Development
go build -o bin/qfs ./cmd/qfs # Production
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This merge brings Phase 2.5 (Full Offline Mode) with the following improvements:
- Local-first architecture: all operations work through SQLite
- Background sync worker for automatic synchronization
- Sync queue (pending_changes table) for reliable data push
- LocalConfigurationService for offline-capable CRUD operations
- Pre-create pricelist check before configuration creation
- RefreshPrices works in offline mode using local_components
- UI improvements: sync status indicator, pricelist badge, unified admin tabs
- Fixed online mode: automatic MariaDB connection on startup
- Fixed nil pointer dereference in PricingHandler alert methods
- Improved setup flow with restart requirement notification
Phase 2.5 is now complete. Ready for production.
- Fix nil pointer dereference in PricingHandler alert methods
- Add automatic MariaDB connection on startup if settings exist
- Update setupRouter to accept mariaDB as parameter
- Fix offline mode checks: use h.db instead of h.alertService
- Update setup handler to show restart required message
- Add warning status support in setup.html UI
This ensures that after saving connection settings, the application
works correctly in online mode after restart. All repositories are
properly initialized with MariaDB connection on startup.
Co-Authored-By: Claude Sonnet 4.5 <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>
- Added CountErroredChanges() method to count only pending changes with LastError
- Previously, error count included all pending changes, not just failed ones
- Added /api/sync/info endpoint with proper error count and error list
- Added sync info modal to display sync status, error count, and error details
- Made sync status indicators clickable to open the modal
- Fixed disconnect between "Error count: 4" and "No errors" in the list
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Root cause: admin_pricing.html declared 'const showToast' while base.html
already defined 'function showToast', causing SyntaxError that prevented
all JavaScript from executing on the admin pricing page.
Changes:
- Removed duplicate showToast declaration from admin_pricing.html (lines 206-210)
- Removed debug logging added in previous commit
- Kept immediate function calls in base.html to ensure early initialization
This fixes the issue where username and "Администратор цен" link
disappeared when navigating to /admin/pricing.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added immediate calls to checkDbStatus() and checkWritePermission() in base.html
- Calls happen right after function definitions, before DOMContentLoaded
- Added console.log statements to track function execution and API responses
- Removed duplicate calls from admin_pricing.html to avoid conflicts
- This will help diagnose why username and admin link disappear on admin pricing page
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Parse URLSearchParams to detect ?tab=pricelists on page load
- Load tab from URL or default to 'alerts'
- Fixes redirect from /pricelists to /admin/pricing?tab=pricelists
This resolves the critical UX issue where users redirected from
/pricelists would see the 'alerts' tab instead of 'pricelists'.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Pass originalHTML through syncAction function chain
- Simplify finally block by restoring original button innerHTML
- Remove hardcoded button HTML values (5 lines reduction)
- Improve maintainability: button text changes won't break code
- Preserve any custom classes, attributes, or nested elements
This fixes the issue where originalHTML was declared but never used.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Fix critical race condition in sync dropdown actions
- Add loading states and spinners for sync operations
- Implement proper event delegation to prevent memory leaks
- Add accessibility attributes (aria-label, aria-haspopup, aria-expanded)
- Add keyboard navigation (Escape to close dropdown)
- Reduce code duplication in sync functions (70% reduction)
- Improve error handling for pricelist badge
- Fix z-index issues in dropdown menu
- Maintain full backward compatibility
Addresses all issues identified in the TODO list and bug reports
- Replace text 'Online/Offline' with SVG icons in sync status
- Change sync button to circular arrow icon
- Add dropdown menu with push changes, full sync, and last sync status
- Add pricelist version badge to configuration page
- Load pricelist version via /api/pricelists/latest on DOMContentLoaded
This completes task 1 of Phase 2.5 (UI Improvements) as specified in CLAUDE.md
- 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>
- 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>