chore: merge executor worktree (04-03)
This commit is contained in:
commit
422ea2bd0e
2 changed files with 136 additions and 4 deletions
110
.planning/phases/04-tickets-restyle/04-03-SUMMARY.md
Normal file
110
.planning/phases/04-tickets-restyle/04-03-SUMMARY.md
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
---
|
||||
phase: 04
|
||||
plan: 03
|
||||
subsystem: mobile-tickets-detail
|
||||
tags: [mobile, tickets, header, navigation, accessibility]
|
||||
dependency_graph:
|
||||
requires: []
|
||||
provides: [ticket-detail-header-D18]
|
||||
affects: [app/mobile/tickets/[id]/page.tsx]
|
||||
tech_stack:
|
||||
added: []
|
||||
patterns: [three-slot-header, external-link-with-noopener]
|
||||
key_files:
|
||||
modified:
|
||||
- app/mobile/tickets/[id]/page.tsx
|
||||
decisions:
|
||||
- Desktop URL chosen as /analyzer/ticket/{id} (in-app, stays in auth boundary; UI-SPEC permits either Autotask direct URL or analyzer URL)
|
||||
- rel="noopener noreferrer" added per T-04-11 threat mitigation (reverse tabnabbing defense-in-depth, even on same-origin)
|
||||
- -mx-4 px-4 technique extends header band to parent div's edges so border-b runs full-width under the new header bar
|
||||
metrics:
|
||||
duration_minutes: 5
|
||||
completed_date: "2026-05-03"
|
||||
tasks_completed: 1
|
||||
files_changed: 1
|
||||
---
|
||||
|
||||
# Phase 4 Plan 3: Detail Page In-Page Header Reskin Summary
|
||||
|
||||
**One-liner:** Three-slot in-page header on `/mobile/tickets/[id]` — back chevron + breadcrumb + external-link icon — replacing the legacy `Back` button per D-18.
|
||||
|
||||
---
|
||||
|
||||
## What Changed
|
||||
|
||||
### Exact Lines Modified in `app/mobile/tickets/[id]/page.tsx`
|
||||
|
||||
**Import line (line 7):** Added `ExternalLink` to the named lucide-react import, alphabetically between `Eye` and `Mail`. All existing imports retained.
|
||||
|
||||
**Header bar (lines ~240-264, replacing ~240-242):** The legacy single-line back button:
|
||||
|
||||
```tsx
|
||||
<button onClick={() => router.back()} className="flex items-center gap-1 text-sm text-muted-foreground mb-3 hover:text-foreground">
|
||||
<ArrowLeft className="w-4 h-4" /> Back
|
||||
</button>
|
||||
```
|
||||
|
||||
Was replaced with the D-18 three-slot header bar (25 lines). The replacement lives inside the existing `<div className="px-4 pt-4 pb-3 border-b">` parent — that wrapper was NOT changed. The new inner `div` uses `-mx-4 px-4` to extend to the parent's edges so its `border-b` runs full-width as a visual separator between the new header and the body below.
|
||||
|
||||
---
|
||||
|
||||
## Desktop URL Choice
|
||||
|
||||
`/analyzer/ticket/{id}` was used as the desktop link target.
|
||||
|
||||
**Rationale:** The analyzer ticket view (`/analyzer/ticket/{id}`) is the canonical in-app desktop view of a ticket in this codebase. It stays within the auth boundary (same domain, same session cookie), so the user lands on a fully functional desktop page rather than being redirected to an external system. The UI-SPEC permits either the Autotask direct URL or the analyzer URL — the analyzer URL is preferred per the plan's spec note.
|
||||
|
||||
---
|
||||
|
||||
## D-19 Body Untouched — Confirmation
|
||||
|
||||
The following sections were preserved verbatim (not modified):
|
||||
|
||||
| Section | Preserved |
|
||||
|---------|-----------|
|
||||
| Badges row (`flex items-start gap-2 mb-2` — priority + ticket# + status pills) | Yes |
|
||||
| `<h1 className="text-base font-bold leading-snug">` ticket title | Yes |
|
||||
| Metadata icons row (Briefcase / User / Clock) | Yes |
|
||||
| 3-card stats grid (Notes / Time entries / Hours logged) | Yes |
|
||||
| Description Collapsible block (`AlignLeft` icon, raw/formatted toggle) | Yes |
|
||||
| Timeline section (filter toggles, `TimelineCard` list) | Yes |
|
||||
| `TimelineCard` component definition | Yes |
|
||||
| `loading` and `error` states | Yes |
|
||||
| Helper functions: `fmtDate`, `fmtHours`, `renderContent`, `relTime` | Yes (note: `relTime` is not in this file — lives in the list page; all helpers in this file preserved) |
|
||||
| Constant maps: `STATUS_LABEL`, `PRIORITY_LABEL`, `PRIORITY_COLOR` | Yes |
|
||||
|
||||
---
|
||||
|
||||
## Parallel Execution
|
||||
|
||||
This plan (04-03) ran in parallel with 04-01 and 04-02 in wave 1. There is no shared file conflict — 04-03 only touches `app/mobile/tickets/[id]/page.tsx`, while 04-01 touches `app/api/mobile/tickets/route.ts` and 04-02 touches `app/mobile/tickets/page.tsx`. No merge conflicts expected.
|
||||
|
||||
---
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
None — plan executed exactly as written.
|
||||
|
||||
---
|
||||
|
||||
## Known Stubs
|
||||
|
||||
None. The header bar is fully wired: `router.back()` for navigation, `ticket.ticket_number` for breadcrumb text, `ticket.id` for the external link href. No placeholder values.
|
||||
|
||||
---
|
||||
|
||||
## Threat Flags
|
||||
|
||||
No new threat surface beyond what was declared in the plan's threat model (T-04-11, T-04-12). T-04-11 mitigation (`rel="noopener noreferrer"`) is implemented.
|
||||
|
||||
---
|
||||
|
||||
## Self-Check
|
||||
|
||||
Checking created file and commit:
|
||||
|
||||
- `app/mobile/tickets/[id]/page.tsx` — FOUND
|
||||
- Commit `b1ff866` — FOUND
|
||||
- `04-03-SUMMARY.md` — FOUND
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
|
@ -4,7 +4,7 @@ import { useEffect, useState, use } from 'react';
|
|||
import { useRouter } from 'next/navigation';
|
||||
import {
|
||||
ArrowLeft, RefreshCw, Clock, FileText, Timer, CheckCircle2,
|
||||
ChevronDown, ChevronRight, User, Briefcase, AlertCircle, EyeOff, Eye, Mail, AlignLeft, Code2,
|
||||
ChevronDown, ChevronRight, User, Briefcase, AlertCircle, EyeOff, Eye, ExternalLink, Mail, AlignLeft, Code2,
|
||||
} from 'lucide-react';
|
||||
|
||||
const PRIORITY_LABEL: Record<number, string> = {
|
||||
|
|
@ -237,9 +237,31 @@ export default function TicketTimeline({ params }: { params: Promise<{ id: strin
|
|||
<div>
|
||||
{/* Ticket header */}
|
||||
<div className="px-4 pt-4 pb-3 border-b">
|
||||
<button onClick={() => router.back()} className="flex items-center gap-1 text-sm text-muted-foreground mb-3 hover:text-foreground">
|
||||
<ArrowLeft className="w-4 h-4" /> Back
|
||||
</button>
|
||||
<div className="flex items-center justify-between -mx-4 px-4 py-3 border-b mb-3">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => router.back()}
|
||||
aria-label="Back to Tickets"
|
||||
className="flex items-center gap-1 text-sm text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<ArrowLeft className="w-4 h-4" aria-hidden="true" />
|
||||
<span>Tickets</span>
|
||||
</button>
|
||||
|
||||
<p className="text-sm font-semibold truncate mx-2 flex-1 text-center">
|
||||
Tickets / #{ticket.ticket_number}
|
||||
</p>
|
||||
|
||||
<a
|
||||
href={`/analyzer/ticket/${ticket.id}`}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
aria-label="Open ticket on desktop"
|
||||
className="text-muted-foreground hover:text-foreground transition-colors shrink-0"
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" aria-hidden="true" />
|
||||
</a>
|
||||
</div>
|
||||
<div className="flex items-start gap-2 mb-2">
|
||||
<span className={`shrink-0 text-xs font-medium px-2 py-0.5 rounded-full border ${priClass}`}>
|
||||
{PRIORITY_LABEL[ticket.priority] ?? `P${ticket.priority}`}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue