Skip to content

Commit 4278a1f

Browse files
betegonclaude
andcommitted
refactor(dashboard): co-locate widget types formatter with its command
Move formatWidgetTypes from human.ts into the types.ts command file, matching the pattern used by dashboard list. Removes the unusual command→formatter cross-import from human.ts. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 694aa14 commit 4278a1f

2 files changed

Lines changed: 70 additions & 66 deletions

File tree

src/commands/dashboard/widget/types.ts

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@
77

88
import type { SentryContext } from "../../../context.js";
99
import { buildCommand } from "../../../lib/command.js";
10-
import { formatWidgetTypes } from "../../../lib/formatters/human.js";
10+
import { renderMarkdown } from "../../../lib/formatters/markdown.js";
1111
import { CommandOutput } from "../../../lib/formatters/output.js";
12+
import { type Column, writeTable } from "../../../lib/formatters/table.js";
1213
import {
1314
AGGREGATE_ALIASES,
1415
DEFAULT_WIDGET_SIZE,
@@ -21,6 +22,7 @@ import {
2122
SPAN_AGGREGATE_FUNCTIONS,
2223
WIDGET_TYPES,
2324
} from "../../../types/dashboard.js";
25+
import type { Writer } from "../../../types/index.js";
2426

2527
/** Category classification for display types */
2628
const DISPLAY_TYPE_CATEGORY: Record<
@@ -59,6 +61,72 @@ export type WidgetTypesResult = {
5961
aggregateAliases: Record<string, string>;
6062
};
6163

64+
/**
65+
* Format widget types info for human-readable terminal output.
66+
*
67+
* Renders grid info, a display types table, a datasets table, and
68+
* aggregate function summaries.
69+
*/
70+
function formatWidgetTypesHuman(result: WidgetTypesResult): string {
71+
const parts: string[] = [];
72+
const buffer: Writer = { write: (s) => parts.push(s) };
73+
74+
// Grid header
75+
parts.push(renderMarkdown(`**Grid:** ${result.grid.columns} columns`));
76+
parts.push("\n");
77+
78+
// Display types table
79+
type DisplayRow = WidgetTypesResult["displayTypes"][number];
80+
const dtColumns: Column<DisplayRow>[] = [
81+
{ header: "DISPLAY TYPE", value: (r) => r.name },
82+
{ header: "WIDTH:", value: (r) => String(r.defaultWidth), align: "right" },
83+
{
84+
header: "HEIGHT:",
85+
value: (r) => String(r.defaultHeight),
86+
align: "right",
87+
},
88+
{ header: "CATEGORY", value: (r) => r.category },
89+
];
90+
writeTable(buffer, result.displayTypes, dtColumns);
91+
92+
parts.push("\n");
93+
94+
// Datasets table
95+
type DatasetRow = WidgetTypesResult["datasets"][number];
96+
const dsColumns: Column<DatasetRow>[] = [
97+
{ header: "DATASET", value: (r) => r.name },
98+
{ header: "DEFAULT", value: (r) => (r.isDefault ? "✓" : "") },
99+
];
100+
writeTable(buffer, result.datasets, dsColumns);
101+
102+
parts.push("\n");
103+
104+
// Aggregate functions
105+
const aggLines: string[] = [];
106+
aggLines.push(
107+
`**Aggregates (spans):** ${result.aggregateFunctions.spans.join(", ")}`
108+
);
109+
110+
const spanSet = new Set(result.aggregateFunctions.spans);
111+
const discoverOnly = result.aggregateFunctions.discover.filter(
112+
(f) => !spanSet.has(f)
113+
);
114+
if (discoverOnly.length > 0) {
115+
aggLines.push(`**Aggregates (discover):** + ${discoverOnly.join(", ")}`);
116+
}
117+
118+
const aliasEntries = Object.entries(result.aggregateAliases);
119+
if (aliasEntries.length > 0) {
120+
aggLines.push(
121+
`**Aliases:** ${aliasEntries.map(([k, v]) => `${k}${v}`).join(", ")}`
122+
);
123+
}
124+
125+
parts.push(renderMarkdown(aggLines.join("\n")));
126+
127+
return parts.join("").trimEnd();
128+
}
129+
62130
export const typesCommand = buildCommand({
63131
docs: {
64132
brief: "Show available widget display types and layout info",
@@ -76,7 +144,7 @@ export const typesCommand = buildCommand({
76144
" sentry dashboard widget types --json",
77145
},
78146
output: {
79-
human: formatWidgetTypes,
147+
human: formatWidgetTypesHuman,
80148
},
81149
parameters: {
82150
positional: { kind: "array", parameter: { brief: "", parse: String } },

src/lib/formatters/human.ts

Lines changed: 0 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@
1010
// biome-ignore lint/performance/noNamespaceImport: Sentry SDK recommends namespace import
1111
import * as Sentry from "@sentry/node-core/light";
1212
import prettyMs from "pretty-ms";
13-
import type { WidgetTypesResult } from "../../commands/dashboard/widget/types.js";
1413
import type {
1514
DashboardDetail,
1615
DashboardWidget,
@@ -2277,66 +2276,3 @@ export function formatWidgetEdited(result: {
22772276
];
22782277
return renderMarkdown(lines.join("\n"));
22792278
}
2280-
2281-
/**
2282-
* Format widget types info for human-readable output.
2283-
*/
2284-
export function formatWidgetTypes(result: WidgetTypesResult): string {
2285-
const parts: string[] = [];
2286-
const buffer: Writer = { write: (s) => parts.push(s) };
2287-
2288-
// Grid header
2289-
parts.push(renderMarkdown(`**Grid:** ${result.grid.columns} columns`));
2290-
parts.push("\n");
2291-
2292-
// Display types table
2293-
type DisplayRow = WidgetTypesResult["displayTypes"][number];
2294-
const dtColumns: Column<DisplayRow>[] = [
2295-
{ header: "DISPLAY TYPE", value: (r) => r.name },
2296-
{ header: "WIDTH:", value: (r) => String(r.defaultWidth), align: "right" },
2297-
{
2298-
header: "HEIGHT:",
2299-
value: (r) => String(r.defaultHeight),
2300-
align: "right",
2301-
},
2302-
{ header: "CATEGORY", value: (r) => r.category },
2303-
];
2304-
writeTable(buffer, result.displayTypes, dtColumns);
2305-
2306-
parts.push("\n");
2307-
2308-
// Datasets table
2309-
type DatasetRow = WidgetTypesResult["datasets"][number];
2310-
const dsColumns: Column<DatasetRow>[] = [
2311-
{ header: "DATASET", value: (r) => r.name },
2312-
{ header: "DEFAULT", value: (r) => (r.isDefault ? "✓" : "") },
2313-
];
2314-
writeTable(buffer, result.datasets, dsColumns);
2315-
2316-
parts.push("\n");
2317-
2318-
// Aggregate functions
2319-
const aggLines: string[] = [];
2320-
aggLines.push(
2321-
`**Aggregates (spans):** ${result.aggregateFunctions.spans.join(", ")}`
2322-
);
2323-
2324-
const spanSet = new Set(result.aggregateFunctions.spans);
2325-
const discoverOnly = result.aggregateFunctions.discover.filter(
2326-
(f) => !spanSet.has(f)
2327-
);
2328-
if (discoverOnly.length > 0) {
2329-
aggLines.push(`**Aggregates (discover):** + ${discoverOnly.join(", ")}`);
2330-
}
2331-
2332-
const aliasEntries = Object.entries(result.aggregateAliases);
2333-
if (aliasEntries.length > 0) {
2334-
aggLines.push(
2335-
`**Aliases:** ${aliasEntries.map(([k, v]) => `${k}${v}`).join(", ")}`
2336-
);
2337-
}
2338-
2339-
parts.push(renderMarkdown(aggLines.join("\n")));
2340-
2341-
return parts.join("").trimEnd();
2342-
}

0 commit comments

Comments
 (0)