datajunction-ui 0.0.153 → 0.0.155

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.
@@ -0,0 +1,477 @@
1
+ .sme-page {
2
+ --sme-bg: #f8fafc;
3
+ --sme-surface: #ffffff;
4
+ --sme-surface-hover: #f1f5f9;
5
+ --sme-border: #e2e8f0;
6
+ --sme-text: #1e293b;
7
+ --sme-text-muted: #64748b;
8
+ --sme-text-dim: #94a3b8;
9
+ --sme-accent: #3b82f6;
10
+ --sme-accent-soft: #dbeafe;
11
+ --sme-font-body: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
12
+ --sme-font-display: 'JetBrains Mono', 'Fira Code', 'SF Mono', monospace;
13
+ --sme-radius: 8px;
14
+
15
+ display: flex;
16
+ height: calc(100vh - 90px);
17
+ border-top: 1px solid var(--sme-border);
18
+ font-family: var(--sme-font-body);
19
+ color: var(--sme-text);
20
+ background: var(--sme-bg);
21
+ font-size: 13px;
22
+ line-height: 1.4;
23
+ }
24
+
25
+ /* ===== Left rail ===== */
26
+ .sme-rail {
27
+ width: 260px;
28
+ min-width: 260px;
29
+ border-right: 1px solid var(--sme-border);
30
+ background: var(--sme-surface);
31
+ display: flex;
32
+ flex-direction: column;
33
+ overflow: hidden;
34
+ }
35
+
36
+ .sme-rail-section {
37
+ display: flex;
38
+ flex-direction: column;
39
+ min-height: 0;
40
+ }
41
+
42
+ .sme-rail-section.grow {
43
+ flex: 1;
44
+ }
45
+
46
+ .sme-rail-header {
47
+ font-size: 11px;
48
+ font-weight: 600;
49
+ color: var(--sme-text-muted);
50
+ letter-spacing: 0.05em;
51
+ padding: 14px 16px 8px;
52
+ text-transform: uppercase;
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: space-between;
56
+ }
57
+
58
+ .sme-rail-count {
59
+ font-size: 11px;
60
+ color: var(--sme-text-dim);
61
+ font-weight: 500;
62
+ letter-spacing: 0;
63
+ }
64
+
65
+ .sme-rail-search {
66
+ margin: 0 12px 6px;
67
+ padding: 6px 10px;
68
+ font-size: 12px;
69
+ font-family: inherit;
70
+ border: 1px solid var(--sme-border);
71
+ border-radius: 6px;
72
+ background: var(--sme-surface);
73
+ outline: none;
74
+ color: var(--sme-text);
75
+ }
76
+
77
+ .sme-rail-search:focus {
78
+ border-color: var(--sme-accent);
79
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
80
+ }
81
+
82
+ .sme-rail-list {
83
+ overflow-y: auto;
84
+ padding: 2px 8px 10px;
85
+ }
86
+
87
+ .sme-rail-group {
88
+ margin-bottom: 4px;
89
+ }
90
+
91
+ .sme-rail-group-header {
92
+ font-size: 10.5px;
93
+ font-weight: 600;
94
+ text-transform: uppercase;
95
+ letter-spacing: 0.04em;
96
+ color: var(--sme-text-muted);
97
+ padding: 8px 10px 2px;
98
+ }
99
+
100
+ .sme-rail-subgroup {
101
+ margin: 2px 0 4px;
102
+ }
103
+
104
+ .sme-rail-subgroup-header {
105
+ font-size: 11px;
106
+ font-weight: 600;
107
+ color: var(--sme-text-dim);
108
+ letter-spacing: 0.02em;
109
+ padding: 4px 10px 1px 14px;
110
+ }
111
+
112
+ .sme-rail-subgroup .sme-rail-item.indent {
113
+ padding-left: 18px;
114
+ }
115
+
116
+ .sme-rail-item {
117
+ display: flex;
118
+ align-items: center;
119
+ gap: 8px;
120
+ padding: 5px 10px;
121
+ font-size: 12px;
122
+ color: var(--sme-text);
123
+ cursor: pointer;
124
+ border-radius: 5px;
125
+ word-break: break-word;
126
+ outline: none;
127
+ border: none;
128
+ box-shadow: none;
129
+ }
130
+
131
+ .sme-rail-item:focus,
132
+ .sme-rail-item:focus-visible,
133
+ .sme-rail-item.active:focus,
134
+ .sme-rail-item.active:focus-visible {
135
+ outline: none;
136
+ border: none;
137
+ box-shadow: none;
138
+ }
139
+
140
+ .sme-rail-item.indent {
141
+ padding-left: 18px;
142
+ }
143
+
144
+ .sme-rail-item:hover {
145
+ background: var(--sme-surface-hover);
146
+ }
147
+
148
+ .sme-rail-item.active {
149
+ background: var(--sme-accent-soft);
150
+ color: var(--sme-accent);
151
+ font-weight: 500;
152
+ }
153
+
154
+ .sme-rail-icon {
155
+ color: var(--sme-text-dim);
156
+ font-family: var(--sme-font-display);
157
+ font-size: 10px;
158
+ flex-shrink: 0;
159
+ text-align: center;
160
+ }
161
+
162
+ .sme-rail-item.active .sme-rail-icon {
163
+ color: var(--sme-accent);
164
+ }
165
+
166
+ /* ===== Main pane ===== */
167
+ .sme-main {
168
+ flex: 1;
169
+ display: flex;
170
+ flex-direction: column;
171
+ overflow: hidden;
172
+ }
173
+
174
+ .sme-toolbar {
175
+ display: flex;
176
+ flex-direction: column;
177
+ gap: 8px;
178
+ padding: 14px 24px;
179
+ border-bottom: 1px solid var(--sme-border);
180
+ background: var(--sme-surface);
181
+ }
182
+
183
+ .sme-row {
184
+ display: flex;
185
+ align-items: center;
186
+ gap: 10px;
187
+ flex-wrap: wrap;
188
+ }
189
+
190
+ .sme-label {
191
+ font-size: 12px;
192
+ color: var(--sme-text-muted);
193
+ font-weight: 500;
194
+ min-width: 80px;
195
+ letter-spacing: 0.01em;
196
+ }
197
+
198
+ .sme-select {
199
+ min-width: 200px;
200
+ font-size: 12px;
201
+ }
202
+
203
+ .sme-select-wide {
204
+ flex: 1;
205
+ min-width: 260px;
206
+ font-size: 12px;
207
+ }
208
+
209
+ .sme-filter-row {
210
+ display: flex;
211
+ gap: 6px;
212
+ align-items: center;
213
+ flex-wrap: wrap;
214
+ }
215
+
216
+ .sme-add-filter {
217
+ background: var(--sme-accent-soft);
218
+ color: var(--sme-accent);
219
+ border: none;
220
+ border-radius: 6px;
221
+ padding: 5px 12px;
222
+ cursor: pointer;
223
+ font-size: 12px;
224
+ font-weight: 500;
225
+ font-family: inherit;
226
+ }
227
+
228
+ .sme-add-filter:hover:not(:disabled) {
229
+ background: #bfdbfe;
230
+ }
231
+
232
+ .sme-add-filter:disabled {
233
+ opacity: 0.4;
234
+ cursor: not-allowed;
235
+ }
236
+
237
+ .sme-remove-filter {
238
+ background: transparent;
239
+ border: none;
240
+ color: var(--sme-text-dim);
241
+ cursor: pointer;
242
+ font-size: 16px;
243
+ padding: 0 6px;
244
+ line-height: 1;
245
+ }
246
+
247
+ .sme-remove-filter:hover {
248
+ color: var(--sme-text);
249
+ }
250
+
251
+ .sme-text-input {
252
+ padding: 6px 10px;
253
+ border: 1px solid var(--sme-border);
254
+ border-radius: 6px;
255
+ font-size: 12px;
256
+ font-family: inherit;
257
+ background: var(--sme-surface);
258
+ min-width: 160px;
259
+ color: var(--sme-text);
260
+ }
261
+
262
+ .sme-text-input:focus {
263
+ outline: none;
264
+ border-color: var(--sme-accent);
265
+ box-shadow: 0 0 0 2px rgba(59, 130, 246, 0.15);
266
+ }
267
+
268
+ /* ===== Chart card ===== */
269
+ .sme-content {
270
+ flex: 1;
271
+ overflow-y: auto;
272
+ padding: 20px 24px;
273
+ }
274
+
275
+ .sme-chart-controls {
276
+ display: flex;
277
+ justify-content: space-between;
278
+ align-items: center;
279
+ margin-bottom: 14px;
280
+ flex-wrap: wrap;
281
+ gap: 10px;
282
+ }
283
+
284
+ .sme-view-toggle {
285
+ display: inline-flex;
286
+ border: 1px solid var(--sme-border);
287
+ border-radius: 7px;
288
+ overflow: hidden;
289
+ background: var(--sme-surface);
290
+ padding: 2px;
291
+ }
292
+
293
+ .sme-view-btn {
294
+ padding: 5px 12px;
295
+ background: transparent;
296
+ border: none;
297
+ cursor: pointer;
298
+ font-size: 12px;
299
+ font-family: inherit;
300
+ color: var(--sme-text-muted);
301
+ display: inline-flex;
302
+ align-items: center;
303
+ gap: 5px;
304
+ border-radius: 5px;
305
+ }
306
+
307
+ .sme-view-btn:hover:not(.active) {
308
+ color: var(--sme-text);
309
+ }
310
+
311
+ .sme-view-btn.active {
312
+ color: var(--sme-accent);
313
+ background: var(--sme-accent-soft);
314
+ font-weight: 500;
315
+ }
316
+
317
+ .sme-options {
318
+ display: inline-flex;
319
+ gap: 14px;
320
+ align-items: center;
321
+ font-size: 12px;
322
+ color: var(--sme-text-muted);
323
+ }
324
+
325
+ .sme-options label {
326
+ display: inline-flex;
327
+ align-items: center;
328
+ gap: 6px;
329
+ cursor: pointer;
330
+ }
331
+
332
+ .sme-options input[type='checkbox'] {
333
+ accent-color: var(--sme-accent);
334
+ }
335
+
336
+ .sme-chart-title {
337
+ font-family: var(--sme-font-body);
338
+ font-size: 22px;
339
+ font-weight: 700;
340
+ letter-spacing: -0.3px;
341
+ margin: 0;
342
+ color: var(--sme-text);
343
+ }
344
+
345
+ .sme-chart-title-link,
346
+ .sme-chart-title-link:visited {
347
+ color: inherit;
348
+ text-decoration: underline dotted;
349
+ text-decoration-color: var(--sme-text-dim);
350
+ text-underline-offset: 4px;
351
+ text-decoration-thickness: 1.5px;
352
+ transition: color 120ms ease-out, text-decoration-color 120ms ease-out;
353
+ }
354
+
355
+ .sme-chart-title-link:hover {
356
+ color: var(--sme-accent);
357
+ text-decoration-color: var(--sme-accent);
358
+ }
359
+
360
+ .sme-chart-subtitle {
361
+ color: var(--sme-text-muted);
362
+ margin: 4px 0 0;
363
+ font-size: 13px;
364
+ }
365
+
366
+ .sme-chart-description {
367
+ color: var(--sme-text-muted);
368
+ margin: 6px 0 14px;
369
+ font-size: 12.5px;
370
+ line-height: 1.5;
371
+ display: block;
372
+ width: 100%;
373
+ max-width: 100%;
374
+ }
375
+
376
+ .sme-chart-body {
377
+ width: 100%;
378
+ height: 460px;
379
+ background: var(--sme-surface);
380
+ border: 1px solid var(--sme-border);
381
+ border-radius: var(--sme-radius);
382
+ padding: 12px 8px 8px;
383
+ }
384
+
385
+ .sme-empty {
386
+ display: flex;
387
+ flex-direction: column;
388
+ align-items: center;
389
+ justify-content: center;
390
+ height: 360px;
391
+ color: var(--sme-text-dim);
392
+ font-size: 13px;
393
+ gap: 8px;
394
+ background: var(--sme-surface);
395
+ border: 1px solid var(--sme-border);
396
+ border-radius: var(--sme-radius);
397
+ }
398
+
399
+ .sme-empty-icon {
400
+ font-size: 32px;
401
+ opacity: 0.5;
402
+ }
403
+
404
+ .sme-table-wrap {
405
+ max-height: 540px;
406
+ overflow: auto;
407
+ border: 1px solid var(--sme-border);
408
+ border-radius: var(--sme-radius);
409
+ background: var(--sme-surface);
410
+ }
411
+
412
+ .sme-table {
413
+ width: 100%;
414
+ border-collapse: collapse;
415
+ font-size: 12px;
416
+ font-family: inherit;
417
+ }
418
+
419
+ .sme-table th,
420
+ .sme-table td {
421
+ text-align: left;
422
+ padding: 8px 12px;
423
+ border-bottom: 1px solid var(--sme-border);
424
+ }
425
+
426
+ .sme-table th {
427
+ background: var(--sme-bg);
428
+ color: var(--sme-text-muted);
429
+ font-weight: 600;
430
+ position: sticky;
431
+ top: 0;
432
+ font-size: 11px;
433
+ text-transform: uppercase;
434
+ letter-spacing: 0.04em;
435
+ }
436
+
437
+ .sme-table tbody tr:hover {
438
+ background: var(--sme-surface-hover);
439
+ }
440
+
441
+ .sme-error {
442
+ color: #dc2626;
443
+ margin: 8px 0;
444
+ font-size: 12px;
445
+ }
446
+
447
+ .sme-spinner {
448
+ display: inline-block;
449
+ width: 14px;
450
+ height: 14px;
451
+ border: 2px solid var(--sme-border);
452
+ border-top-color: var(--sme-accent);
453
+ border-radius: 50%;
454
+ animation: sme-spin 0.8s linear infinite;
455
+ margin-right: 6px;
456
+ vertical-align: middle;
457
+ }
458
+
459
+ @keyframes sme-spin {
460
+ to {
461
+ transform: rotate(360deg);
462
+ }
463
+ }
464
+
465
+ /* Constrain react-select fonts/heights for consistency */
466
+ .sme-page .css-1nmdiq5-menu,
467
+ .sme-page .css-13cymwt-control,
468
+ .sme-page .css-t3ipsp-control {
469
+ font-size: 12px;
470
+ font-family: var(--sme-font-body);
471
+ min-height: 32px;
472
+ }
473
+
474
+ .sme-page .css-1jqq78o-placeholder,
475
+ .sme-page .css-1dimb5e-singleValue {
476
+ font-size: 12px;
477
+ }
@@ -288,20 +288,21 @@ export const DataJunctionAPI = {
288
288
  filters = [],
289
289
  orderby = [],
290
290
  }) {
291
- const results = await DataJunctionAPI.querySystemMetric({
291
+ const { columns, rows } = await DataJunctionAPI.querySystemMetric({
292
292
  metric: metric,
293
293
  dimensions: [dimension],
294
294
  filters: filters,
295
295
  orderby: orderby,
296
296
  });
297
- return results.map(row => {
298
- return {
299
- name:
300
- row.find(entry => entry.col === dimension)?.value?.toString() ??
301
- 'unknown',
302
- value: row.find(entry => entry.col === metric)?.value ?? 0,
303
- };
304
- });
297
+ const dimIdx = columns.indexOf(dimension);
298
+ const metricIdx = columns.indexOf(metric);
299
+ return rows.map(row => ({
300
+ name:
301
+ dimIdx >= 0 && row[dimIdx] !== undefined && row[dimIdx] !== null
302
+ ? String(row[dimIdx])
303
+ : 'unknown',
304
+ value: metricIdx >= 0 ? row[metricIdx] ?? 0 : 0,
305
+ }));
305
306
  },
306
307
 
307
308
  system: {
@@ -336,35 +337,28 @@ export const DataJunctionAPI = {
336
337
  });
337
338
  },
338
339
  node_trends: async function () {
339
- const results = await (
340
+ const { columns, rows } = await (
340
341
  await fetch(
341
342
  `${DJ_URL}/system/data/system.dj.number_of_nodes?dimensions=system.dj.nodes.created_at_week&dimensions=system.dj.node_type.type&filters=system.dj.nodes.created_at_week>=20240101&orderby=system.dj.nodes.created_at_week`,
342
343
  { credentials: 'include' },
343
344
  )
344
345
  ).json();
346
+ const dateIdx = columns.indexOf('system.dj.nodes.created_at_week');
347
+ const typeIdx = columns.indexOf('system.dj.node_type.type');
348
+ const countIdx = columns.indexOf('system.dj.number_of_nodes');
345
349
  const byDateint = {};
346
- results.forEach(row => {
347
- const dateint = row.find(
348
- r => r.col === 'system.dj.nodes.created_at_week',
349
- )?.value;
350
- const nodeType = row.find(
351
- r => r.col === 'system.dj.node_type.type',
352
- )?.value;
353
- const count = row.find(
354
- r => r.col === 'system.dj.number_of_nodes',
355
- )?.value;
356
- if (!byDateint[dateint]) {
357
- byDateint[dateint] = { date: dateint };
358
- }
350
+ rows.forEach(row => {
351
+ const dateint = row[dateIdx];
352
+ const nodeType = row[typeIdx];
353
+ const count = row[countIdx];
354
+ if (!byDateint[dateint]) byDateint[dateint] = { date: dateint };
359
355
  byDateint[dateint][nodeType] =
360
356
  (byDateint[dateint][nodeType] || 0) + count;
361
357
  });
362
- return Object.entries(byDateint).map(([dateint, data]) => {
363
- return {
364
- date: dateint,
365
- ...data,
366
- };
367
- });
358
+ return Object.entries(byDateint).map(([dateint, data]) => ({
359
+ date: dateint,
360
+ ...data,
361
+ }));
368
362
  },
369
363
  materialization_counts_by_type: async function () {
370
364
  return DataJunctionAPI.querySystemMetricSingleDimension({
@@ -382,6 +376,14 @@ export const DataJunctionAPI = {
382
376
  })
383
377
  ).json();
384
378
  },
379
+
380
+ list: async function () {
381
+ return await (
382
+ await fetch(`${DJ_URL}/system/metrics`, {
383
+ credentials: 'include',
384
+ })
385
+ ).json();
386
+ },
385
387
  },
386
388
 
387
389
  logout: async function () {