@bakers-build/addon-bakers-ui 0.2.1-next.211 → 0.3.0

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.
@@ -23,13 +23,20 @@ export interface StatisticFilter {
23
23
  value: string;
24
24
  options: StatisticFilterOption[];
25
25
  }
26
+ declare const GROUPINGS: readonly ["day", "week", "month", "quarter"];
27
+ declare const WIDGETS: readonly ["matrix", "line", "grouped_horizontal_bars", "bars", "tiles", "flow", "table"];
26
28
  export interface StatisticFigure {
27
29
  id: string;
28
30
  label: string;
29
31
  description: string;
30
- widget: 'matrix' | 'line' | 'grouped_horizontal_bars';
32
+ /** `tiles`, `flow`, `table` and `bars` render through the bars fallback. */
33
+ widget: (typeof WIDGETS)[number];
31
34
  generated: string;
32
35
  filters: Record<string, StatisticFilter>;
36
+ /** Bucket size of the selected period; absent on `windows` figures. */
37
+ period?: {
38
+ grouping: (typeof GROUPINGS)[number];
39
+ };
33
40
  categories: StatisticCategory[];
34
41
  measurements: StatisticMeasurement[];
35
42
  empty: {
@@ -63,3 +70,4 @@ export declare function errorResultOf(caught: unknown): {
63
70
  message: string;
64
71
  };
65
72
  export declare function loadGroupStatistics(client: DropshClient, contexts: StatisticsContexts, filters?: StatisticsFilters): Promise<GroupStatisticsResult>;
73
+ export {};
@@ -1,4 +1,14 @@
1
1
  import { DrupalJsonApiParams } from 'drupal-jsonapi-params';
2
+ const GROUPINGS = ['day', 'week', 'month', 'quarter'];
3
+ const WIDGETS = [
4
+ 'matrix',
5
+ 'line',
6
+ 'grouped_horizontal_bars',
7
+ 'bars',
8
+ 'tiles',
9
+ 'flow',
10
+ 'table',
11
+ ];
2
12
  function statusOf(caught) {
3
13
  if (typeof caught !== 'object' || caught === null)
4
14
  return undefined;
@@ -113,7 +123,7 @@ function figureOf(value) {
113
123
  typeof row.id !== 'string' ||
114
124
  typeof row.label !== 'string' ||
115
125
  typeof row.description !== 'string' ||
116
- !['matrix', 'line', 'grouped_horizontal_bars'].includes(String(row.widget)) ||
126
+ !WIDGETS.includes(String(row.widget)) ||
117
127
  typeof row.generated !== 'string' ||
118
128
  !Array.isArray(row.categories) ||
119
129
  !Array.isArray(row.measurements) ||
@@ -129,8 +139,14 @@ function figureOf(value) {
129
139
  const measurements = row.measurements.map(measurementOf);
130
140
  if (categories.includes(undefined) || measurements.includes(undefined))
131
141
  return undefined;
142
+ const grouping = recordOf(row.period)?.grouping;
143
+ const period = GROUPINGS.find((item) => item === grouping);
132
144
  const parsedFilters = {};
133
145
  for (const [key, item] of Object.entries(filters)) {
146
+ // An entity filter (project, team, person) picks UUIDs in the web UI; the
147
+ // cockpit offers no picker for it, so it is left out, not refused.
148
+ if (recordOf(item)?.type === 'entities')
149
+ continue;
134
150
  const parsed = filterOf(item);
135
151
  if (!parsed)
136
152
  return undefined;
@@ -143,6 +159,7 @@ function figureOf(value) {
143
159
  widget: row.widget,
144
160
  generated: row.generated,
145
161
  filters: parsedFilters,
162
+ ...(period ? { period: { grouping: period } } : {}),
146
163
  categories: categories,
147
164
  measurements: measurements,
148
165
  empty: {
@@ -163,7 +180,10 @@ function groupOf(value) {
163
180
  typeof row.label !== 'string' ||
164
181
  !Array.isArray(row.figures))
165
182
  return undefined;
166
- const figures = row.figures.map(figureOf);
183
+ // A figure whose widget the CLI cannot draw is skipped, not malformed.
184
+ const figures = row.figures
185
+ .filter((figure) => WIDGETS.includes(String(recordOf(figure)?.widget)))
186
+ .map(figureOf);
167
187
  if (figures.includes(undefined))
168
188
  return undefined;
169
189
  return {
@@ -193,5 +213,8 @@ export async function loadGroupStatistics(client, contexts, filters = {}) {
193
213
  const groups = source.map(groupOf);
194
214
  if (groups.includes(undefined))
195
215
  return { kind: 'error', message: 'malformed statistics document' };
196
- return { kind: 'ok', groups: groups };
216
+ return {
217
+ kind: 'ok',
218
+ groups: groups.filter((group) => group.figures.length > 0),
219
+ };
197
220
  }
@@ -50,11 +50,11 @@ function matrixDisplay(id, figure, width) {
50
50
  });
51
51
  }
52
52
  function partialPeriodUnit(figure) {
53
- return figure.filters.period?.value === '365' ? 'month' : 'day';
53
+ return figure.period?.grouping ?? 'day';
54
54
  }
55
55
  // Shared above every widget: which trailing period is still filling in (the
56
56
  // figure's own categories carry the `partial` flag — see
57
- // MeasurementEvaluator::days()/months()) and what the figure means, in the
57
+ // MeasurementEvaluator::buckets()) and what the figure means, in the
58
58
  // figure's own words. Rendered once here rather than per-widget so every
59
59
  // shipped widget (matrix, grouped_horizontal_bars) gets both without having
60
60
  // to know about them.
@@ -20,14 +20,9 @@ export declare abstract class StatisticsScreen<T> extends AsyncScreen<T> {
20
20
  private activeGroup;
21
21
  private shiftActiveGroup;
22
22
  protected statisticsFilters(): StatisticsFilters;
23
- /**
24
- * Maps a preset's raw period value to the granularity legible label
25
- * (BB-548 scope change 7): `7`/`30` are daily buckets, `365` is monthly.
26
- */
27
- private static rangeLabel;
28
23
  private periodFilter;
29
24
  /**
30
- * One boxed filter chip cycling the three fixed period+granularity presets
25
+ * One boxed filter chip cycling the period kinds
31
26
  * (BB-548 scope change 7, replacing the former two independent
32
27
  * period/granularity chips of addendum 4). Rendered via
33
28
  * `filterBadgeStrip` inside `renderReady`, below Overview — see the
@@ -60,13 +60,6 @@ export class StatisticsScreen extends AsyncScreen {
60
60
  filters.period = this.period;
61
61
  return filters;
62
62
  }
63
- /**
64
- * Maps a preset's raw period value to the granularity legible label
65
- * (BB-548 scope change 7): `7`/`30` are daily buckets, `365` is monthly.
66
- */
67
- static rangeLabel(value) {
68
- return value === '365' ? '1y·monthly' : `${value}d·daily`;
69
- }
70
63
  periodFilter() {
71
64
  const result = this.readyValue === undefined
72
65
  ? undefined
@@ -79,14 +72,19 @@ export class StatisticsScreen extends AsyncScreen {
79
72
  if (filter)
80
73
  return {
81
74
  value: filter.value,
82
- options: filter.options.map((option) => option.value),
75
+ label: filter.options.find((option) => option.value === filter.value)
76
+ ?.label ?? filter.value,
77
+ // Custom needs start and end dates, which this screen cannot supply.
78
+ options: filter.options
79
+ .map((option) => option.value)
80
+ .filter((value) => value !== 'custom'),
83
81
  };
84
82
  }
85
83
  }
86
84
  return undefined;
87
85
  }
88
86
  /**
89
- * One boxed filter chip cycling the three fixed period+granularity presets
87
+ * One boxed filter chip cycling the period kinds
90
88
  * (BB-548 scope change 7, replacing the former two independent
91
89
  * period/granularity chips of addendum 4). Rendered via
92
90
  * `filterBadgeStrip` inside `renderReady`, below Overview — see the
@@ -99,7 +97,7 @@ export class StatisticsScreen extends AsyncScreen {
99
97
  return [
100
98
  {
101
99
  id: 'period',
102
- label: `t Range(${StatisticsScreen.rangeLabel(period.value)})`,
100
+ label: `t Range(${period.label})`,
103
101
  },
104
102
  ];
105
103
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bakers-build/addon-bakers-ui",
3
- "version": "0.2.1-next.211",
3
+ "version": "0.3.0",
4
4
  "description": "bakers.build TUI: interactive terminal UI (dashboard, projects, tickets) as a dropsh `tui` renderer plugin.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -29,7 +29,7 @@
29
29
  "zustand": "^5.0.15"
30
30
  },
31
31
  "peerDependencies": {
32
- "@bakers-build/core": "^0.2.1-next.211",
33
- "@bakers-build/message": "^0.2.1-next.211"
32
+ "@bakers-build/core": "^0.3.0",
33
+ "@bakers-build/message": "^0.3.0"
34
34
  }
35
35
  }