docs: update AGENTS.md, archive plans
NexusGuard CI / server-core-test (push) Failing after 3m30s
NexusGuard CI / server-core-build (push) Has been skipped
NexusGuard CI / device-agent-test (push) Failing after 3s
NexusGuard CI / device-agent-cross-build (amd64, linux) (push) Has been skipped
NexusGuard CI / device-agent-cross-build (amd64, windows) (push) Has been skipped
NexusGuard CI / device-agent-cross-build (arm64, linux) (push) Has been skipped
NexusGuard CI / dashboard-test (push) Failing after 3s
NexusGuard CI / dashboard-dist (push) Has been skipped

This commit is contained in:
datadunia
2026-06-07 06:14:46 +07:00
parent c2f0e53f1d
commit 281ac48d28
10 changed files with 512 additions and 6 deletions
@@ -0,0 +1,469 @@
# Button Consistency Normalization
## TL;DR
> **Quick Summary**: Replace all raw `<button>` elements across the dashboard with `NgButton` component, normalize sizing/spacing, remove `flex-1` stretching, and ensure consistent button patterns everywhere.
>
> **Deliverables**:
> - All raw `<button>` replaced with `NgButton`
> - 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-1` buttons 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-stretched
- `apps/dashboard-ui/src/views/Servers.vue` — modal buttons use NgButton
- `apps/dashboard-ui/src/views/Users.vue` — delete button uses NgButton
- `apps/dashboard-ui/src/views/Devices.vue` — action buttons use NgButton
- `apps/dashboard-ui/src/views/TrafficHistory.vue` — buttons use NgButton + design tokens
- `apps/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 `NgButton` with appropriate variant/size
- No `flex-1` on button rows (causes ugly stretching)
- Consistent `gap-2` spacing 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
- [x] 1. Normalize DeviceDetail.vue buttons
**What to do**:
- Line 93: Keep `w-full` on save settings button (it's inside a form, full-width is correct)
- Lines 114-124: Remove `flex-1` from all 3 action buttons (Regenerate Token, Regenerate Keys, Delete Device). Change `flex space-x-3` to `flex items-center gap-2`
- Lines 141-145: Remove `flex-1` from 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.txt
```
**Commit**: YES (groups with 2-6)
---
- [x] 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.txt
```
**Commit**: YES (groups with 1, 3-6)
---
- [x] 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.txt
```
**Commit**: YES (groups with 1-2, 4-6)
---
- [x] 4. Normalize Devices.vue action buttons
**What to do**:
- Lines 45-47: Replace raw `<button>` with `NgButton variant="ghost" size="sm"`:
```vue
<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.txt
```
**Commit**: YES (groups with 1-3, 5-6)
---
- [x] 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-accent`
- `bg-cyan-600` → `bg-accent`
- `bg-black/30` → `bg-bg-base/30`
- `border-white/5` → `border-border-subtle/50`
- `text-gray-400` → `text-text-muted`
- `text-gray-500` → `text-text-muted`
- `bg-gradient-to-br from-gray-900/90 to-gray-800/90` → `bg-gradient-to-br from-bg-surface/90 to-bg-elevated/90`
- `border-white/10` → `border-border-subtle`
- `text-white` → `text-text-primary`
- `bg-black/50` → `bg-bg-base/50`
- `hover:bg-white/5` → `hover:bg-bg-elevated/50`
- `hover:bg-black/50` → `hover:bg-bg-base/50`
- `hover:text-white` → `hover:text-text-primary`
- `text-cyan-400` → `text-accent`
- `text-blue-400` → `text-info`
- `hover: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 colors
- `apps/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.txt
```
**Commit**: YES (groups with 1-4, 6)
---
- [x] 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.txt
```
**Commit**: YES (groups with 1-5)
---
## 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`
---
## Success Criteria
### Verification Commands
```bash
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
- [x] All raw `<button>` replaced with `NgButton`
- [x] No `flex-1` on button rows
- [x] Consistent `gap-2` spacing
- [x] TrafficHistory.vue hardcoded colors replaced with tokens
- [x] Frontend builds without errors