datajunction-ui 0.0.14 → 0.0.16
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/package.json +1 -1
- package/src/app/components/__tests__/NodeMaterializationDelete.test.jsx +263 -0
- package/src/app/components/__tests__/QueryInfo.test.jsx +174 -46
- package/src/app/components/__tests__/Search.test.jsx +300 -56
- package/src/app/pages/AddEditNodePage/FormikSelect.jsx +15 -2
- package/src/app/pages/NamespacePage/Explorer.jsx +192 -21
- package/src/app/pages/NamespacePage/__tests__/AddNamespacePopover.test.jsx +283 -0
- package/src/app/pages/NamespacePage/__tests__/index.test.jsx +74 -41
- package/src/app/pages/NamespacePage/index.jsx +13 -7
- package/src/app/pages/NodePage/AddComplexDimensionLinkPopover.jsx +367 -0
- package/src/app/pages/NodePage/LinkDimensionPopover.jsx +1 -1
- package/src/app/pages/NodePage/ManageDimensionLinksDialog.jsx +526 -0
- package/src/app/pages/NodePage/NodeColumnTab.jsx +223 -58
- package/src/app/pages/NodePage/__tests__/AddComplexDimensionLinkPopover.test.jsx +459 -0
- package/src/app/pages/NodePage/__tests__/EditColumnPopover.test.jsx +2 -6
- package/src/app/pages/NodePage/__tests__/LinkDimensionPopover.test.jsx +19 -48
- package/src/app/pages/NodePage/__tests__/ManageDimensionLinksDialog.test.jsx +390 -0
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +22 -12
- package/src/app/pages/RegisterTablePage/__tests__/RegisterTablePage.test.jsx +4 -2
- package/src/app/services/DJService.js +46 -6
- package/src/app/services/__tests__/DJService.test.jsx +551 -5
- package/webpack.config.js +1 -0
|
@@ -2,12 +2,12 @@ import { useEffect, useState } from 'react';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import EditColumnPopover from './EditColumnPopover';
|
|
4
4
|
import EditColumnDescriptionPopover from './EditColumnDescriptionPopover';
|
|
5
|
-
import
|
|
5
|
+
import ManageDimensionLinksDialog from './ManageDimensionLinksDialog';
|
|
6
|
+
import AddComplexDimensionLinkPopover from './AddComplexDimensionLinkPopover';
|
|
6
7
|
import { labelize } from '../../../utils/form';
|
|
7
8
|
import PartitionColumnPopover from './PartitionColumnPopover';
|
|
8
9
|
import { Light as SyntaxHighlighter } from 'react-syntax-highlighter';
|
|
9
10
|
import { foundation } from 'react-syntax-highlighter/src/styles/hljs';
|
|
10
|
-
import { link } from 'fs';
|
|
11
11
|
|
|
12
12
|
export default function NodeColumnTab({ node, djClient }) {
|
|
13
13
|
const [attributes, setAttributes] = useState([]);
|
|
@@ -85,18 +85,29 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
85
85
|
|
|
86
86
|
const columnList = columns => {
|
|
87
87
|
return columns?.map(col => {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
88
|
+
// FK Links: Only show links that specifically reference THIS column's foreign keys
|
|
89
|
+
// Filter out complex dimension links that may join on multiple columns
|
|
90
|
+
const fkLinksForColumn = (
|
|
91
|
+
links.length > 0 ? links : node?.dimension_links
|
|
92
|
+
)
|
|
93
|
+
.filter(link => {
|
|
94
|
+
// Check if this link has a foreign key entry for this specific column
|
|
95
|
+
const foreignKeys = Object.keys(link.foreign_keys || {});
|
|
96
|
+
const columnKey = `${node.name}.${col.name}`;
|
|
97
|
+
return foreignKeys.includes(columnKey);
|
|
98
|
+
})
|
|
99
|
+
.map(link => link.dimension.name);
|
|
100
|
+
|
|
101
|
+
// Check if this column has a reference dimension link
|
|
102
|
+
const referenceLink = col.dimension
|
|
103
|
+
? {
|
|
104
|
+
dimension: col.dimension.name,
|
|
105
|
+
dimension_column: col.dimension_column,
|
|
106
|
+
role: col.dimension.role,
|
|
107
|
+
}
|
|
108
|
+
: null;
|
|
98
109
|
return (
|
|
99
|
-
<tr key={col.name}>
|
|
110
|
+
<tr key={col.name} className="column-row">
|
|
100
111
|
<td
|
|
101
112
|
className="text-start"
|
|
102
113
|
role="columnheader"
|
|
@@ -146,28 +157,60 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
146
157
|
</span>
|
|
147
158
|
</td>
|
|
148
159
|
{node.type !== 'cube' ? (
|
|
149
|
-
<td>
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
<a href={`/nodes/${link[0]}`}>{link[0]}</a>
|
|
158
|
-
</span>
|
|
159
|
-
))
|
|
160
|
-
: ''}
|
|
161
|
-
<LinkDimensionPopover
|
|
162
|
-
column={col}
|
|
163
|
-
dimensionNodes={dimensionLinks.map(link => link[0])}
|
|
164
|
-
node={node}
|
|
165
|
-
options={dimensions}
|
|
166
|
-
onSubmit={async () => {
|
|
167
|
-
const res = await djClient.node(node.name);
|
|
168
|
-
setLinks(res.dimension_links);
|
|
160
|
+
<td className="dimension-links-cell">
|
|
161
|
+
<div
|
|
162
|
+
style={{
|
|
163
|
+
display: 'flex',
|
|
164
|
+
alignItems: 'center',
|
|
165
|
+
flexWrap: 'wrap',
|
|
166
|
+
gap: '0.25rem',
|
|
167
|
+
position: 'relative',
|
|
169
168
|
}}
|
|
170
|
-
|
|
169
|
+
>
|
|
170
|
+
{fkLinksForColumn.length > 0 && (
|
|
171
|
+
<div
|
|
172
|
+
style={{
|
|
173
|
+
display: 'flex',
|
|
174
|
+
flexWrap: 'wrap',
|
|
175
|
+
gap: '0.25rem',
|
|
176
|
+
}}
|
|
177
|
+
>
|
|
178
|
+
{fkLinksForColumn.map(dimName => (
|
|
179
|
+
<span
|
|
180
|
+
className="rounded-pill badge bg-secondary-soft dimension-badge"
|
|
181
|
+
style={{ fontSize: '14px', position: 'relative' }}
|
|
182
|
+
key={dimName}
|
|
183
|
+
title="FK Link (via primary key)"
|
|
184
|
+
>
|
|
185
|
+
<a href={`/nodes/${dimName}`}>{dimName}</a>
|
|
186
|
+
</span>
|
|
187
|
+
))}
|
|
188
|
+
</div>
|
|
189
|
+
)}
|
|
190
|
+
{referenceLink && (
|
|
191
|
+
<span
|
|
192
|
+
className="rounded-pill badge bg-info dimension-badge"
|
|
193
|
+
style={{ fontSize: '14px', position: 'relative' }}
|
|
194
|
+
title={`Reference Link: ${referenceLink.dimension}.${referenceLink.dimension_column}`}
|
|
195
|
+
>
|
|
196
|
+
<a href={`/nodes/${referenceLink.dimension}`}>
|
|
197
|
+
{referenceLink.dimension}.{referenceLink.dimension_column}
|
|
198
|
+
</a>
|
|
199
|
+
</span>
|
|
200
|
+
)}
|
|
201
|
+
<ManageDimensionLinksDialog
|
|
202
|
+
column={col}
|
|
203
|
+
node={node}
|
|
204
|
+
dimensions={dimensions}
|
|
205
|
+
fkLinks={fkLinksForColumn}
|
|
206
|
+
referenceLink={referenceLink}
|
|
207
|
+
onSubmit={async () => {
|
|
208
|
+
const res = await djClient.node(node.name);
|
|
209
|
+
setLinks(res.dimension_links);
|
|
210
|
+
setColumns(res.columns);
|
|
211
|
+
}}
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
171
214
|
</td>
|
|
172
215
|
) : (
|
|
173
216
|
''
|
|
@@ -206,6 +249,24 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
206
249
|
|
|
207
250
|
return (
|
|
208
251
|
<>
|
|
252
|
+
<style>
|
|
253
|
+
{`
|
|
254
|
+
.dimension-link-edit:hover {
|
|
255
|
+
opacity: 1 !important;
|
|
256
|
+
color: #007bff !important;
|
|
257
|
+
}
|
|
258
|
+
.dimension-badge a {
|
|
259
|
+
max-width: 300px;
|
|
260
|
+
display: inline-block;
|
|
261
|
+
overflow: hidden;
|
|
262
|
+
text-overflow: ellipsis;
|
|
263
|
+
white-space: nowrap;
|
|
264
|
+
vertical-align: bottom;
|
|
265
|
+
direction: rtl;
|
|
266
|
+
text-align: left;
|
|
267
|
+
}
|
|
268
|
+
`}
|
|
269
|
+
</style>
|
|
209
270
|
<div className="table-responsive">
|
|
210
271
|
<table className="card-inner-table table">
|
|
211
272
|
<thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
|
|
@@ -216,7 +277,7 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
216
277
|
<th>Type</th>
|
|
217
278
|
{node?.type !== 'cube' ? (
|
|
218
279
|
<>
|
|
219
|
-
<th>
|
|
280
|
+
<th>Dimension Links</th>
|
|
220
281
|
<th>Attributes</th>
|
|
221
282
|
</>
|
|
222
283
|
) : (
|
|
@@ -229,39 +290,143 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
229
290
|
</table>
|
|
230
291
|
</div>
|
|
231
292
|
<div>
|
|
232
|
-
<
|
|
293
|
+
<div
|
|
294
|
+
style={{
|
|
295
|
+
display: 'flex',
|
|
296
|
+
alignItems: 'center',
|
|
297
|
+
justifyContent: 'space-between',
|
|
298
|
+
marginBottom: '1rem',
|
|
299
|
+
}}
|
|
300
|
+
>
|
|
301
|
+
<h3 style={{ margin: 0 }}>
|
|
302
|
+
Complex Dimension Links (Custom Join SQL)
|
|
303
|
+
</h3>
|
|
304
|
+
<AddComplexDimensionLinkPopover
|
|
305
|
+
node={node}
|
|
306
|
+
dimensions={dimensions}
|
|
307
|
+
onSubmit={async () => {
|
|
308
|
+
const res = await djClient.node(node.name);
|
|
309
|
+
setLinks(res.dimension_links);
|
|
310
|
+
}}
|
|
311
|
+
/>
|
|
312
|
+
</div>
|
|
233
313
|
<table className="card-inner-table table">
|
|
234
314
|
<thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
|
|
235
315
|
<tr>
|
|
236
316
|
<th className="text-start">Dimension Node</th>
|
|
237
317
|
<th>Join Type</th>
|
|
238
318
|
<th>Join SQL</th>
|
|
319
|
+
<th>Join Cardinality</th>
|
|
239
320
|
<th>Role</th>
|
|
321
|
+
<th>Actions</th>
|
|
240
322
|
</tr>
|
|
241
323
|
</thead>
|
|
242
324
|
<tbody>
|
|
243
|
-
{node?.dimension_links.
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
<
|
|
247
|
-
<
|
|
248
|
-
{link.dimension.name}
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
<
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
325
|
+
{node?.dimension_links && node.dimension_links.length > 0 ? (
|
|
326
|
+
node.dimension_links.map((link, idx) => {
|
|
327
|
+
return (
|
|
328
|
+
<tr key={`${link.dimension.name}-${link.role || idx}`}>
|
|
329
|
+
<td>
|
|
330
|
+
<a href={'/nodes/' + link.dimension.name}>
|
|
331
|
+
{link.dimension.name}
|
|
332
|
+
</a>
|
|
333
|
+
</td>
|
|
334
|
+
<td>{link.join_type.toUpperCase()}</td>
|
|
335
|
+
<td style={{ maxWidth: '400px' }}>
|
|
336
|
+
<SyntaxHighlighter
|
|
337
|
+
language="sql"
|
|
338
|
+
style={foundation}
|
|
339
|
+
wrapLongLines={true}
|
|
340
|
+
>
|
|
341
|
+
{link.join_sql?.trim()}
|
|
342
|
+
</SyntaxHighlighter>
|
|
343
|
+
</td>
|
|
344
|
+
<td>
|
|
345
|
+
{link.join_cardinality
|
|
346
|
+
? link.join_cardinality.replace(/_/g, ' ').toUpperCase()
|
|
347
|
+
: 'N/A'}
|
|
348
|
+
</td>
|
|
349
|
+
<td>{link.role || '-'}</td>
|
|
350
|
+
<td>
|
|
351
|
+
<div style={{ display: 'flex', gap: '0.5rem' }}>
|
|
352
|
+
<AddComplexDimensionLinkPopover
|
|
353
|
+
node={node}
|
|
354
|
+
dimensions={dimensions}
|
|
355
|
+
existingLink={link}
|
|
356
|
+
isEditMode={true}
|
|
357
|
+
onSubmit={async () => {
|
|
358
|
+
const res = await djClient.node(node.name);
|
|
359
|
+
setLinks(res.dimension_links);
|
|
360
|
+
}}
|
|
361
|
+
/>
|
|
362
|
+
<button
|
|
363
|
+
onClick={async () => {
|
|
364
|
+
if (
|
|
365
|
+
window.confirm(
|
|
366
|
+
`Remove link to ${link.dimension.name}?`,
|
|
367
|
+
)
|
|
368
|
+
) {
|
|
369
|
+
try {
|
|
370
|
+
const response =
|
|
371
|
+
await djClient.removeComplexDimensionLink(
|
|
372
|
+
node.name,
|
|
373
|
+
link.dimension.name,
|
|
374
|
+
link.role || null,
|
|
375
|
+
);
|
|
376
|
+
if (
|
|
377
|
+
response.status === 200 ||
|
|
378
|
+
response.status === 201 ||
|
|
379
|
+
response.status === 204
|
|
380
|
+
) {
|
|
381
|
+
alert(
|
|
382
|
+
'Complex dimension link removed successfully!',
|
|
383
|
+
);
|
|
384
|
+
window.location.reload();
|
|
385
|
+
} else {
|
|
386
|
+
console.error('Remove link error:', response);
|
|
387
|
+
alert(
|
|
388
|
+
response.json?.message ||
|
|
389
|
+
`Failed to remove link (status: ${response.status})`,
|
|
390
|
+
);
|
|
391
|
+
}
|
|
392
|
+
} catch (error) {
|
|
393
|
+
console.error('Remove link exception:', error);
|
|
394
|
+
alert(`Error removing link: ${error.message}`);
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
}}
|
|
398
|
+
style={{
|
|
399
|
+
padding: '0.25rem 0.5rem',
|
|
400
|
+
fontSize: '0.75rem',
|
|
401
|
+
background: '#dc3545',
|
|
402
|
+
color: 'white',
|
|
403
|
+
border: 'none',
|
|
404
|
+
borderRadius: '4px',
|
|
405
|
+
cursor: 'pointer',
|
|
406
|
+
}}
|
|
407
|
+
>
|
|
408
|
+
Remove
|
|
409
|
+
</button>
|
|
410
|
+
</div>
|
|
411
|
+
</td>
|
|
412
|
+
</tr>
|
|
413
|
+
);
|
|
414
|
+
})
|
|
415
|
+
) : (
|
|
416
|
+
<tr>
|
|
417
|
+
<td
|
|
418
|
+
colSpan="6"
|
|
419
|
+
style={{
|
|
420
|
+
textAlign: 'center',
|
|
421
|
+
padding: '2rem',
|
|
422
|
+
color: '#6c757d',
|
|
423
|
+
}}
|
|
424
|
+
>
|
|
425
|
+
No complex dimension links. Click the + button above to add
|
|
426
|
+
one.
|
|
427
|
+
</td>
|
|
428
|
+
</tr>
|
|
429
|
+
)}
|
|
265
430
|
</tbody>
|
|
266
431
|
</table>
|
|
267
432
|
</div>
|