@stevecastaneda/paseo-progress 0.1.5 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/panel.tsx +54 -5
- package/dist/shared/dashboard.js +12 -0
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
- package/shared/dashboard.ts +14 -0
package/client/panel.tsx
CHANGED
|
@@ -10,7 +10,7 @@ import { raised } from "./surfaces";
|
|
|
10
10
|
import { When } from "./when";
|
|
11
11
|
import React, { useEffect, useRef, useState } from "react";
|
|
12
12
|
import { ActivityIndicator, Animated, Platform, Pressable, ScrollView, Text, View } from "react-native";
|
|
13
|
-
import { shortTitle, type Activity, type Dashboard, type FileIssue, type ProgressSegment, type StuckItem, type Ticket } from "../shared/dashboard";
|
|
13
|
+
import { finishedToFold, isFinished, shortTitle, type Activity, type Dashboard, type FileIssue, type ProgressSegment, type StuckItem, type Ticket } from "../shared/dashboard";
|
|
14
14
|
import { PROGRESS_FILE, type TicketStatus } from "../shared/events";
|
|
15
15
|
import { formatEstimate, formatMinutes, formatWorkDone, minutesSince } from "../shared/format";
|
|
16
16
|
import { markPanelOpened } from "../shared/rpc";
|
|
@@ -214,10 +214,7 @@ function DashboardView({ colors, dashboard, agentRunning, compact, workspaceId,
|
|
|
214
214
|
No {(dashboard.run?.itemLabel ?? "ticket").toLowerCase()}s yet.
|
|
215
215
|
</Text>
|
|
216
216
|
) : (
|
|
217
|
-
dashboard
|
|
218
|
-
<TicketRow key={ticket.id} colors={colors} ticket={ticket} live={live} now={now} onOpen={() => setOpenTicket(ticket.id)}
|
|
219
|
-
deliverables={dashboard.deliverables.filter((deliverable) => deliverable.ticketId === ticket.id).length} />
|
|
220
|
-
))
|
|
217
|
+
<TicketList colors={colors} dashboard={dashboard} live={live} now={now} workspaceId={workspaceId} onOpen={setOpenTicket} />
|
|
221
218
|
)}
|
|
222
219
|
<TicketDialogs colors={colors} dashboard={dashboard} openId={openTicket} setOpenId={setOpenTicket} now={now} live={live} context={attachmentContext} />
|
|
223
220
|
</Card>
|
|
@@ -415,6 +412,58 @@ function StuckSection({ colors, items, now }: { colors: Colors; items: StuckItem
|
|
|
415
412
|
);
|
|
416
413
|
}
|
|
417
414
|
|
|
415
|
+
// Whether the finished tickets at the top are shown, per workspace, kept for
|
|
416
|
+
// the app session like the history tab.
|
|
417
|
+
const finishedShown = new Map<string, boolean>();
|
|
418
|
+
|
|
419
|
+
// Finished tickets fold into one row at the top, so the work still ahead is
|
|
420
|
+
// near the top without scrolling (see finishedToFold).
|
|
421
|
+
function TicketList({ colors, dashboard, live, now, workspaceId, onOpen }: { colors: Colors; dashboard: Dashboard; live: boolean; now: number; workspaceId: string; onOpen(id: string): void }) {
|
|
422
|
+
const [shown, setShown] = useState(() => finishedShown.get(workspaceId) ?? false);
|
|
423
|
+
const toggle = () => {
|
|
424
|
+
finishedShown.set(workspaceId, !shown);
|
|
425
|
+
setShown(!shown);
|
|
426
|
+
};
|
|
427
|
+
const finished = finishedToFold(dashboard.tickets);
|
|
428
|
+
const row = (ticket: Ticket) => (
|
|
429
|
+
<TicketRow key={ticket.id} colors={colors} ticket={ticket} live={live} now={now} onOpen={() => onOpen(ticket.id)}
|
|
430
|
+
deliverables={dashboard.deliverables.filter((deliverable) => deliverable.ticketId === ticket.id).length} />
|
|
431
|
+
);
|
|
432
|
+
if (!finished.length) return <>{dashboard.tickets.map(row)}</>;
|
|
433
|
+
return (
|
|
434
|
+
<>
|
|
435
|
+
<FinishedRow colors={colors} tickets={finished} shown={shown} onPress={toggle} />
|
|
436
|
+
{shown ? finished.map(row) : null}
|
|
437
|
+
{dashboard.tickets.filter((ticket) => !isFinished(ticket)).map(row)}
|
|
438
|
+
</>
|
|
439
|
+
);
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// Sits on the ticket rows' columns: status icon, label, estimate, then the
|
|
443
|
+
// chevron, which points the way the list will move, like "Show 10 older".
|
|
444
|
+
function FinishedRow({ colors, tickets, shown, onPress }: { colors: Colors; tickets: Ticket[]; shown: boolean; onPress(): void }) {
|
|
445
|
+
const done = tickets.filter((ticket) => ticket.status === "done").length;
|
|
446
|
+
const skipped = tickets.length - done;
|
|
447
|
+
const label = [done ? `${done} done` : "", skipped ? `${skipped} skipped` : ""].filter(Boolean).join(", ");
|
|
448
|
+
const estimateMin = tickets.reduce((sum, ticket) => sum + (ticket.status === "done" ? ticket.estimateMin : 0), 0);
|
|
449
|
+
const chevron = shown ? "ChevronUp" : "ChevronDown";
|
|
450
|
+
return (
|
|
451
|
+
<PressableRow colors={colors} accessibilityRole="button" accessibilityState={{ expanded: shown }}
|
|
452
|
+
accessibilityLabel={`${shown ? "Hide" : "Show"} ${label}`} onPress={onPress}
|
|
453
|
+
style={{ flexDirection: "row", alignItems: "flex-start", gap: 10, paddingHorizontal: 10, paddingVertical: 8, borderTopWidth: 1, borderTopColor: colors.border }}>
|
|
454
|
+
<View style={{ width: 16, paddingTop: 2, alignItems: "center" }}>
|
|
455
|
+
<Icon name={done ? "Check" : "CircleSlash"} size={14} color={done ? colors.statusSuccess : colors.foregroundMuted} />
|
|
456
|
+
</View>
|
|
457
|
+
<Text style={{ flex: 1, minWidth: 0, color: colors.foregroundMuted, fontSize: 12, lineHeight: 18, fontWeight: "600", fontVariant: ["tabular-nums"] }}>{label}</Text>
|
|
458
|
+
<View style={{ width: 12 }} />
|
|
459
|
+
<Text style={{ minWidth: 40, flexShrink: 0, textAlign: "right", color: colors.foregroundMuted, fontSize: 12, lineHeight: 18, fontVariant: ["tabular-nums"] }}>{estimateMin ? formatEstimate(estimateMin) : ""}</Text>
|
|
460
|
+
<View style={{ paddingTop: 2 }}>
|
|
461
|
+
<IconSwap swapKey={chevron} size={14}><Icon name={chevron} size={14} color={colors.foregroundMuted} /></IconSwap>
|
|
462
|
+
</View>
|
|
463
|
+
</PressableRow>
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
418
467
|
// Press for the ticket's story. Hover and press only tint the row: it's a list, pressed often.
|
|
419
468
|
function TicketRow({ colors, ticket, live, now, onOpen, deliverables }: { colors: Colors; ticket: Ticket; live: boolean; now: number; onOpen(): void; deliverables: number }) {
|
|
420
469
|
const muted = ticket.status === "skipped";
|
package/dist/shared/dashboard.js
CHANGED
|
@@ -328,6 +328,18 @@ export function shortTitle(title) {
|
|
|
328
328
|
const index = title.indexOf(":");
|
|
329
329
|
return index > 0 && index <= 24 ? title.slice(0, index) : title;
|
|
330
330
|
}
|
|
331
|
+
// The finished tickets (done or skipped) the panel folds into one row at the
|
|
332
|
+
// top, in plan order, so the work still ahead sits near the top. A ticket
|
|
333
|
+
// finished out of order folds too. A single one isn't worth a fold row. Once
|
|
334
|
+
// every ticket is finished nothing is ahead, and the whole list is the record
|
|
335
|
+
// of the run, so nothing folds.
|
|
336
|
+
export function finishedToFold(tickets) {
|
|
337
|
+
const finished = tickets.filter(isFinished);
|
|
338
|
+
return finished.length >= 2 && finished.length < tickets.length ? finished : [];
|
|
339
|
+
}
|
|
340
|
+
export function isFinished(ticket) {
|
|
341
|
+
return ticket.status === "done" || ticket.status === "skipped";
|
|
342
|
+
}
|
|
331
343
|
function plural(count, noun) {
|
|
332
344
|
return `${count} ${count === 1 ? noun : `${noun}s`}`;
|
|
333
345
|
}
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stevecastaneda/paseo-progress",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@stevecastaneda/paseo-progress",
|
|
9
|
-
"version": "0.1.
|
|
9
|
+
"version": "0.1.7",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"@getpaseo/client": "^0.9.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stevecastaneda/paseo-progress",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.7",
|
|
4
4
|
"description": "A live progress dashboard per worktree for Paseo, drawn from a file agents write with the paseo-progress command.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Steve Castaneda",
|
package/shared/dashboard.ts
CHANGED
|
@@ -449,6 +449,20 @@ export function shortTitle(title: string): string {
|
|
|
449
449
|
return index > 0 && index <= 24 ? title.slice(0, index) : title;
|
|
450
450
|
}
|
|
451
451
|
|
|
452
|
+
// The finished tickets (done or skipped) the panel folds into one row at the
|
|
453
|
+
// top, in plan order, so the work still ahead sits near the top. A ticket
|
|
454
|
+
// finished out of order folds too. A single one isn't worth a fold row. Once
|
|
455
|
+
// every ticket is finished nothing is ahead, and the whole list is the record
|
|
456
|
+
// of the run, so nothing folds.
|
|
457
|
+
export function finishedToFold(tickets: Ticket[]): Ticket[] {
|
|
458
|
+
const finished = tickets.filter(isFinished);
|
|
459
|
+
return finished.length >= 2 && finished.length < tickets.length ? finished : [];
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
export function isFinished(ticket: Ticket): boolean {
|
|
463
|
+
return ticket.status === "done" || ticket.status === "skipped";
|
|
464
|
+
}
|
|
465
|
+
|
|
452
466
|
function plural(count: number, noun: string): string {
|
|
453
467
|
return `${count} ${count === 1 ? noun : `${noun}s`}`;
|
|
454
468
|
}
|