diff --git a/RFCP-Iteration-1.1.1-UX-Safety-Undo-Redo.md b/RFCP-Iteration-1.1.1-UX-Safety-Undo-Redo.md new file mode 100644 index 0000000..31e9b4e --- /dev/null +++ b/RFCP-Iteration-1.1.1-UX-Safety-Undo-Redo.md @@ -0,0 +1,308 @@ +# RFCP Frontend - Iteration 1.1.1: UX Safety & Undo/Redo + +**Date:** January 30, 2025 +**Type:** Frontend Enhancement +**Estimated:** 2-3 hours +**Location:** `/opt/rfcp/frontend/` + +--- + +## 🎯 Goal + +Add safety confirmations for destructive actions and implement full Undo/Redo system. + +--- + +## 📋 Pre-reading + +1. Review current state management in `src/store/` (Zustand) +2. Check existing toast implementation for delete actions + +--- + +## 📊 Current State + +- Toast exists for delete actions (basic) +- No unsaved changes detection +- No confirmation dialogs +- No undo/redo system + +--- + +## ✅ Tasks + +### 1. Unsaved Changes Detection + +**Add dirty state tracking to store:** + +```typescript +// src/store/projectStore.ts (or similar) + +interface ProjectState { + // ... existing + isDirty: boolean; + lastSavedState: string | null; // JSON snapshot + + markDirty: () => void; + markClean: () => void; + checkDirty: () => boolean; +} + +// On any change to sites/settings: +markDirty() + +// On save: +markClean() +lastSavedState = JSON.stringify(currentState) +``` + +**Browser beforeunload warning:** + +```typescript +// src/App.tsx or dedicated hook + +useEffect(() => { + const handleBeforeUnload = (e: BeforeUnloadEvent) => { + if (store.isDirty) { + e.preventDefault(); + e.returnValue = ''; // Required for Chrome + } + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + return () => window.removeEventListener('beforeunload', handleBeforeUnload); +}, []); +``` + +--- + +### 2. Confirmation Dialogs + +**Create reusable ConfirmDialog component:** + +```typescript +// src/components/ui/ConfirmDialog.tsx + +interface ConfirmDialogProps { + isOpen: boolean; + title: string; + message: string; + confirmText?: string; // default: "Confirm" + cancelText?: string; // default: "Cancel" + variant?: 'danger' | 'warning' | 'info'; + onConfirm: () => void; + onCancel: () => void; +} + +// Styling: +// - danger: red confirm button (for delete) +// - warning: yellow/orange (for discard changes) +// - info: blue (for info confirmations) +``` + +**Apply to actions:** + +| Action | Dialog | +|--------|--------| +| Load project (when dirty) | "Є незбережені зміни. Завантажити інший проект?" | +| Delete project | "Видалити проект '{name}'? Цю дію не можна скасувати." | +| Delete site | "Видалити станцію '{name}'?" | +| Delete sector | "Видалити сектор '{name}'?" | +| New project (when dirty) | "Є незбережені зміни. Створити новий проект?" | +| Page refresh (handled by beforeunload) | Browser native dialog | + +--- + +### 3. Undo/Redo System + +**Create history store:** + +```typescript +// src/store/historyStore.ts + +interface HistoryState { + past: ProjectSnapshot[]; + future: ProjectSnapshot[]; + maxHistory: number; // default: 50 + + // Actions + push: (snapshot: ProjectSnapshot) => void; + undo: () => ProjectSnapshot | null; + redo: () => ProjectSnapshot | null; + clear: () => void; + + // Selectors + canUndo: boolean; + canRedo: boolean; +} + +type ProjectSnapshot = { + sites: Site[]; + settings: CoverageSettings; + timestamp: number; + action: string; // "add site", "delete sector", "move site", etc. +}; +``` + +**Integration points — push snapshot BEFORE these actions:** + +- Add site +- Delete site +- Update site (position, params) +- Add sector +- Delete sector +- Update sector +- Update coverage settings +- Import project +- Clear all sites + +**Keyboard shortcuts:** + +```typescript +// src/hooks/useKeyboardShortcuts.ts + +useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + // Undo: Ctrl+Z (Windows/Linux) or Cmd+Z (Mac) + if ((e.ctrlKey || e.metaKey) && e.key === 'z' && !e.shiftKey) { + e.preventDefault(); + handleUndo(); + } + + // Redo: Ctrl+Shift+Z or Ctrl+Y + if ((e.ctrlKey || e.metaKey) && ( + (e.key === 'z' && e.shiftKey) || + e.key === 'y' + )) { + e.preventDefault(); + handleRedo(); + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); +}, []); +``` + +**UI indicators:** + +```typescript +// Toolbar buttons (disabled when can't undo/redo) + + + +``` + +--- + +### 4. Enhanced Toast Notifications + +**Extend existing toast for all actions:** + +```typescript +// Success toasts +"Проект збережено" +"Станцію додано" +"Станцію видалено" +"Зміни скасовано" (undo) +"Зміни повернено" (redo) + +// Error toasts +"Помилка збереження" +"Помилка завантаження" + +// Info toasts +"Проект завантажено" +``` + +**Toast with undo action (optional enhancement):** + +```typescript +// When deleting, show toast with undo button +toast({ + message: "Станцію видалено", + action: { + label: "Скасувати", + onClick: () => handleUndo() + }, + duration: 5000 +}); +``` + +--- + +## 📁 Files to Create/Modify + +``` +src/ +├── components/ui/ +│ └── ConfirmDialog.tsx # NEW +├── store/ +│ ├── historyStore.ts # NEW +│ └── projectStore.ts # MODIFY (add isDirty) +├── hooks/ +│ ├── useUnsavedChanges.ts # NEW +│ └── useKeyboardShortcuts.ts # NEW or MODIFY +└── App.tsx # MODIFY (add beforeunload) +``` + +--- + +## ✅ Success Criteria + +- [ ] Refresh page with unsaved changes → browser warning +- [ ] Load project with unsaved changes → confirmation dialog +- [ ] Delete project → confirmation dialog (danger style) +- [ ] Delete site/sector → confirmation dialog +- [ ] Ctrl+Z undoes last action +- [ ] Ctrl+Shift+Z redoes +- [ ] Undo/Redo buttons in toolbar (with disabled states) +- [ ] Toast shows on save/delete/undo/redo +- [ ] History limited to 50 states (memory management) + +--- + +## 🧪 Test Scenarios + +1. **Unsaved changes:** + - Add site → refresh → browser warning appears + - Add site → save → refresh → no warning + +2. **Confirmations:** + - Add site → click Load → dialog appears → Cancel → site still there + - Add site → click Load → dialog appears → Confirm → new project loads + +3. **Undo/Redo:** + - Add 3 sites → Ctrl+Z → 2 sites remain + - Ctrl+Z again → 1 site + - Ctrl+Shift+Z → 2 sites back + - Add new site after undo → redo history cleared + +4. **Edge cases:** + - Undo when nothing to undo → button disabled, no action + - 51 actions → oldest dropped from history + +--- + +## 📝 Notes + +- Keep snapshots lightweight (only sites + settings, not UI state) +- Debounce position changes (don't snapshot every pixel of drag) +- Consider grouping rapid changes (e.g., typing in input) +- Ukrainian UI text for all dialogs/toasts + +--- + +**Ready for Claude Code** 🚀