16 KiB
Button Consistency Normalization
TL;DR
Quick Summary: Replace all raw
<button>elements across the dashboard withNgButtoncomponent, normalize sizing/spacing, removeflex-1stretching, and ensure consistent button patterns everywhere.Deliverables:
- All raw
<button>replaced withNgButton- DeviceDetail.vue buttons de-stretched (no more
flex-1)- Servers.vue modal buttons use NgButton
- TrafficHistory.vue buttons + hardcoded colors fixed
- Consistent button hierarchy: primary/secondary/ghost/danger
Estimated Effort: Quick Parallel Execution: YES - 1 wave Critical Path: All tasks independent, can run in parallel
Context
Original Request
User says: "jadikan semua tombol ini sama. seperti di traffic menu. contoh di view device. tombol besar jelek. tombol diskonek jelen buat semua tombol setara"
Translation: Make all buttons the same. Like in the traffic menu. Example in device view — big ugly buttons, disconnect button looks bad. Make all buttons equal.
Interview Summary
Key Discussions:
- DeviceDetail.vue has
flex-1buttons that stretch to fill container — visually heavy - Multiple views still use raw
<button>with hardcoded Tailwind classes instead of NgButton - TrafficHistory.vue itself still has hardcoded colors (
text-cyan-400,bg-cyan-600,bg-black/30) - Inconsistent button patterns: some NgButton, some raw, some with
flex-1, some without
Button Hierarchy Standard
| Context | NgButton Config | Rationale |
|---|---|---|
| Form submit (Save, Create, Register) | variant="primary" size="md" |
Primary action |
| Cancel / Close / secondary | variant="secondary" size="md" |
Destructive-neutral |
| Delete (dangerous) | variant="danger" size="md" |
Destructive |
| Table row actions (Edit, Config, Firewall) | variant="ghost" size="sm" |
Inline, low visual weight |
| Small utility (Copy, Refresh) | size="sm" |
Compact |
| Action button row | gap-2 NOT space-x-3 OR flex-1 |
Consistent spacing |
Work Objectives
Core Objective
Make every button in the dashboard use NgButton with consistent sizing, spacing, and variant hierarchy.
Concrete Deliverables
apps/dashboard-ui/src/views/DeviceDetail.vue— buttons de-stretchedapps/dashboard-ui/src/views/Servers.vue— modal buttons use NgButtonapps/dashboard-ui/src/views/Users.vue— delete button uses NgButtonapps/dashboard-ui/src/views/Devices.vue— action buttons use NgButtonapps/dashboard-ui/src/views/TrafficHistory.vue— buttons use NgButton + design tokensapps/dashboard-ui/src/App.vue— logout button uses NgButton
Must Have
- Zero raw
<button>with hardcoded Tailwind classes (except toggle switches and accordion chevrons) - All action buttons use
NgButtonwith appropriate variant/size - No
flex-1on button rows (causes ugly stretching) - Consistent
gap-2spacing between button groups
Must NOT Have (Guardrails)
- Do NOT change NgButton component itself
- Do NOT change any API calls or data flow
- Do NOT change toggle switches (they're custom CSS, not buttons)
- Do NOT change accordion chevron toggles (functional, not action buttons)
- Do NOT touch Login.vue (already redesigned)
- Do NOT touch FirewallEditor.vue (already redesigned)
Verification Strategy
ZERO HUMAN INTERVENTION - ALL verification is agent-executed.
QA Policy
Every task includes agent-executed QA scenarios.
Evidence saved to .sisyphus/evidence/task-{N}-{scenario-slug}.{ext}.
- Frontend/UI: Build check via
npm run build - Grep checks: Verify zero raw
<button class=patterns (excluding known exceptions)
Execution Strategy
Parallel Execution Waves
Wave 1 (Start Immediately — all independent):
├── Task 1: Normalize DeviceDetail.vue buttons [quick]
├── Task 2: Normalize Servers.vue modal buttons [quick]
├── Task 3: Normalize Users.vue delete button [quick]
├── Task 4: Normalize Devices.vue action buttons [quick]
├── Task 5: Normalize TrafficHistory.vue buttons + tokens [quick]
├── Task 6: Normalize App.vue logout button [quick]
Wave FINAL (After ALL tasks):
├── Build verify: npm run build
├── Grep verify: zero raw button patterns
└── Present results to user
Dependency Matrix
- All tasks (1-6): No dependencies — can all run in parallel
- Final verification: After all tasks complete
TODOs
-
1. Normalize DeviceDetail.vue buttons
What to do:
- Line 93: Keep
w-fullon save settings button (it's inside a form, full-width is correct) - Lines 114-124: Remove
flex-1from all 3 action buttons (Regenerate Token, Regenerate Keys, Delete Device). Changeflex space-x-3toflex items-center gap-2 - Lines 141-145: Remove
flex-1from Config & QR button. Remove the wrapping<div class="flex space-x-3">since it's a single button — just use<NgButton>directly - Line 153: Copy token button already
size="sm"— OK - Line 187: Refresh button already
size="sm"— OK
Must NOT do:
- Do NOT change the save settings
w-full(form submit, full-width is correct) - Do NOT change toggle switches or accordion chevrons
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 2-6)
- Blocks: Final verification
- Blocked By: None
References:
apps/dashboard-ui/src/views/DeviceDetail.vue— Lines 114-124 (action buttons), 141-145 (config button)apps/dashboard-ui/src/components/ui/NgButton.vue— API: variant, size, loading, disabled
Acceptance Criteria:
QA Scenarios:
Scenario: No flex-1 on NgButton in DeviceDetail Tool: Bash Steps: 1. Run: grep "flex-1" apps/dashboard-ui/src/views/DeviceDetail.vue Expected Result: 0 matches Evidence: .sisyphus/evidence/task-1-no-flex1.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-1-build.txtCommit: YES (groups with 2-6)
- Line 93: Keep
-
2. Normalize Servers.vue modal buttons
What to do:
- Line 127:
<button type="submit" ... class="flex-1 bg-accent ...">Register Node</button>→<NgButton type="submit" :loading="loading">Register Node</NgButton> - Line 128:
<button type="button" @click="showAddModal = false" ... class="flex-1 bg-bg-elevated ...">Cancel</button>→<NgButton variant="secondary" @click="showAddModal = false">Cancel</NgButton> - Line 281:
<button type="submit" ... class="flex-1 bg-accent ...">Save</button>→<NgButton type="submit" :loading="loading">Save</NgButton> - Line 282:
<button type="button" @click="closeEdit" ... class="flex-1 bg-bg-elevated ...">Cancel</button>→<NgButton variant="secondary" @click="closeEdit">Cancel</NgButton> - Button rows in modals: wrap in
<div class="flex items-center gap-2 pt-4"> - Lines 156-157: Table action buttons (Edit, Delete) →
NgButton variant="ghost" size="sm"
Must NOT do:
- Do NOT change form inputs or validation logic
- Do NOT change toggle switches
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 1, 3-6)
- Blocks: Final verification
- Blocked By: None
References:
apps/dashboard-ui/src/views/Servers.vue— Lines 127-128 (add modal buttons), 156-157 (table actions), 281-282 (edit modal buttons)
Acceptance Criteria:
QA Scenarios:
Scenario: No raw button in Servers modal Tool: Bash Steps: 1. Run: grep -n '<button' apps/dashboard-ui/src/views/Servers.vue | grep -v 'NgButton' | grep -v 'toggle' | grep -v 'chevron' | grep -v 'accordion' Expected Result: 0 matches (excluding toggle/accordion) Evidence: .sisyphus/evidence/task-2-no-raw-button.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-2-build.txtCommit: YES (groups with 1, 3-6)
- Line 127:
-
3. Normalize Users.vue delete button
What to do:
- Line 51:
<button v-if="user.Username !== 'admin'" @click="handleDelete(user.ID)" class="text-danger hover:text-danger text-sm font-semibold">Delete</button>→<NgButton v-if="user.Username !== 'admin'" variant="ghost" size="sm" @click="handleDelete(user.ID)">Delete</NgButton> - Import NgButton if not already imported (it is — line 67)
Must NOT do:
- Do NOT change the delete confirmation logic
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 1-2, 4-6)
- Blocks: Final verification
- Blocked By: None
References:
apps/dashboard-ui/src/views/Users.vue— Line 51
Acceptance Criteria:
QA Scenarios:
Scenario: No raw button in Users Tool: Bash Steps: 1. Run: grep -n '<button' apps/dashboard-ui/src/views/Users.vue | grep -v 'NgButton' Expected Result: 0 matches Evidence: .sisyphus/evidence/task-3-no-raw-button.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-3-build.txtCommit: YES (groups with 1-2, 4-6)
- Line 51:
-
4. Normalize Devices.vue action buttons
What to do:
- Lines 45-47: Replace raw
<button>withNgButton variant="ghost" size="sm":<NgButton v-if="device.InternalIP && authStore.isAdmin" variant="ghost" size="sm" @click="openConfigModal(device.ID)">Config</NgButton> <NgButton variant="ghost" size="sm" @click="openFirewall(device.ID)">Firewall</NgButton> <NgButton variant="ghost" size="sm" @click="handleDelete(device.ID, device.Name)">Delete</NgButton> - Import NgButton if not already imported (it is — line 78)
Must NOT do:
- Do NOT change the delete confirmation logic
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 1-3, 5-6)
- Blocks: Final verification
- Blocked By: None
References:
apps/dashboard-ui/src/views/Devices.vue— Lines 45-47
Acceptance Criteria:
QA Scenarios:
Scenario: No raw button in Devices Tool: Bash Steps: 1. Run: grep -n '<button' apps/dashboard-ui/src/views/Devices.vue | grep -v 'NgButton' Expected Result: 0 matches Evidence: .sisyphus/evidence/task-4-no-raw-button.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-4-build.txtCommit: YES (groups with 1-3, 5-6)
- Lines 45-47: Replace raw
-
5. Normalize TrafficHistory.vue buttons + hardcoded colors
What to do:
- Import NgButton and useToast/useConfirm if needed
- Line 5-11: Export CSV button →
<NgButton @click="exportToCSV" :disabled="trafficData.length === 0">Export CSV</NgButton> - Lines 53-59: Apply Filters button →
<NgButton type="submit" :loading="loading">{{ loading ? 'Loading...' : 'Apply Filters' }}</NgButton> - Lines 110-116: Previous button →
<NgButton size="sm" @click="currentPage--" :disabled="currentPage === 1">Previous</NgButton> - Lines 118-124: Next button →
<NgButton size="sm" @click="currentPage++" :disabled="currentPage >= totalPages">Next</NgButton> - Replace ALL hardcoded colors with design tokens:
text-cyan-400→text-accentbg-cyan-600→bg-accentbg-black/30→bg-bg-base/30border-white/5→border-border-subtle/50text-gray-400→text-text-mutedtext-gray-500→text-text-mutedbg-gradient-to-br from-gray-900/90 to-gray-800/90→bg-gradient-to-br from-bg-surface/90 to-bg-elevated/90border-white/10→border-border-subtletext-white→text-text-primarybg-black/50→bg-bg-base/50hover:bg-white/5→hover:bg-bg-elevated/50hover:bg-black/50→hover:bg-bg-base/50hover:text-white→hover:text-text-primarytext-cyan-400→text-accenttext-blue-400→text-infohover:shadow-cyan-500/50→hover:shadow-accent/50
- Update
<NgButton>import
Must NOT do:
- Do NOT change the CSV export logic
- Do NOT change the pagination logic
- Do NOT change the date filtering logic
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 1-4, 6)
- Blocks: Final verification
- Blocked By: None
References:
apps/dashboard-ui/src/views/TrafficHistory.vue— Full file (326 lines). Raw buttons + extensive hardcoded colorsapps/dashboard-ui/src/components/ui/NgButton.vue— API reference
Acceptance Criteria:
QA Scenarios:
Scenario: No raw button in TrafficHistory Tool: Bash Steps: 1. Run: grep -n '<button' apps/dashboard-ui/src/views/TrafficHistory.vue | grep -v 'NgButton' Expected Result: 0 matches Evidence: .sisyphus/evidence/task-5-no-raw-button.txt Scenario: No hardcoded colors Tool: Bash Steps: 1. Run: grep -cE "text-cyan-|bg-cyan-|bg-black|border-white/|text-gray-|text-blue-|hover:bg-white" apps/dashboard-ui/src/views/TrafficHistory.vue Expected Result: 0 matches Evidence: .sisyphus/evidence/task-5-no-hardcoded.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-5-build.txtCommit: YES (groups with 1-4, 6)
-
6. Normalize App.vue logout button
What to do:
- Line 16:
<button @click="authStore.logout" class="w-full text-left px-4 py-2 text-sm text-red-400 hover:bg-red-500/10 rounded-lg transition">Logout</button>→<NgButton variant="ghost" @click="authStore.logout" class="w-full justify-start">Logout</NgButton> - Line 24:
<button @click="authStore.logout" class="text-red-400 text-sm">Logout</button>→<NgButton variant="ghost" size="sm" @click="authStore.logout">Logout</NgButton> - Import NgButton
Must NOT do:
- Do NOT change the logout logic
- Do NOT change sidebar behavior
Recommended Agent Profile:
- Category:
quick - Skills: []
Parallelization:
- Can Run In Parallel: YES
- Parallel Group: Wave 1 (with Tasks 1-5)
- Blocks: Final verification
- Blocked By: None
References:
apps/dashboard-ui/src/App.vue— Lines 16, 24
Acceptance Criteria:
QA Scenarios:
Scenario: No raw button in App.vue Tool: Bash Steps: 1. Run: grep -n '<button' apps/dashboard-ui/src/App.vue | grep -v 'NgButton' Expected Result: 0 matches Evidence: .sisyphus/evidence/task-6-no-raw-button.txt Scenario: Build succeeds Tool: Bash Steps: 1. Run: cd apps/dashboard-ui && npm run build Expected Result: Exit code 0 Evidence: .sisyphus/evidence/task-6-build.txtCommit: YES (groups with 1-5)
- Line 16:
Final Verification Wave
After ALL tasks complete:
Wave FINAL:
├── F1: Build verify — npm run build passes
├── F2: Grep verify — zero raw <button class= across all .vue files
├── F3: Grep verify — zero hardcoded colors in TrafficHistory.vue
└── F4: Present results to user
Commit Strategy
- Commit D: All button normalization changes
- Files:
DeviceDetail.vue,Servers.vue,Users.vue,Devices.vue,TrafficHistory.vue,App.vue - Pre-commit:
cd apps/dashboard-ui && npm run build
- Files:
Success Criteria
Verification Commands
cd apps/dashboard-ui && npm run build # Expected: ✓ built in Xs
grep -rn '<button' src/ --include="*.vue" | grep -v 'NgButton' | grep -v 'toggle' | grep -v 'chevron' | grep -v 'sr-only' # Expected: 0 matches (excluding known exceptions)
grep -cE "text-cyan-|bg-cyan-|bg-black|border-white/" src/views/TrafficHistory.vue # Expected: 0
Final Checklist
- All raw
<button>replaced withNgButton - No
flex-1on button rows - Consistent
gap-2spacing - TrafficHistory.vue hardcoded colors replaced with tokens
- Frontend builds without errors