When user A opens an offer, a lock is acquired (locked_by + locked_at). User B opening the same offer sees a warning banner and the form is read-only. Lock expires after 5 minutes without heartbeat. Backend: - POST /:id/lock — acquire lock (returns 423 if locked by another) - POST /:id/heartbeat — refresh lock timestamp (every 2 min) - POST /:id/unlock — release lock - GET /:id — includes locked_by info - PUT /:id — auto-releases lock on save Frontend: - Acquires lock on page load (edit mode only) - Sends heartbeat every 2 minutes - Releases lock on page unmount (navigate away) - Shows warning banner with locker's name - All inputs read-only + action buttons hidden when locked Migration: adds locked_by (INT) and locked_at (DATETIME) to quotations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4 lines
228 B
SQL
4 lines
228 B
SQL
-- Add lock fields to quotations table for pessimistic locking
|
|
ALTER TABLE `quotations` ADD COLUMN `locked_by` INT NULL AFTER `scope_description`;
|
|
ALTER TABLE `quotations` ADD COLUMN `locked_at` DATETIME NULL AFTER `locked_by`;
|