# 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
