datajunction-ui 0.0.1-a45.dev5 → 0.0.1-a46
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/Makefile +5 -0
- package/package.json +2 -2
- package/src/app/components/NodeMaterializationDelete.jsx +80 -0
- package/src/app/pages/CubeBuilderPage/index.jsx +19 -4
- package/src/app/pages/NamespacePage/index.jsx +2 -1
- package/src/app/pages/NodePage/AddBackfillPopover.jsx +46 -51
- package/src/app/pages/NodePage/AddMaterializationPopover.jsx +33 -24
- package/src/app/pages/NodePage/MaterializationConfigField.jsx +38 -31
- package/src/app/pages/NodePage/NodeHistory.jsx +1 -1
- package/src/app/pages/NodePage/NodeInfoTab.jsx +33 -0
- package/src/app/pages/NodePage/NodeMaterializationTab.jsx +179 -110
- package/src/app/pages/NodePage/NodeStatus.jsx +94 -21
- package/src/app/pages/NodePage/PartitionValueForm.jsx +60 -0
- package/src/app/pages/NodePage/RevisionDiff.jsx +1 -1
- package/src/app/pages/NodePage/__tests__/AddBackfillPopover.test.jsx +13 -3
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +13 -5
- package/src/app/pages/NodePage/__tests__/RevisionDiff.test.jsx +32 -47
- package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +276 -191
- package/src/app/pages/NodePage/index.jsx +1 -0
- package/src/app/services/DJService.js +37 -25
- package/src/app/services/__tests__/DJService.test.jsx +53 -21
- package/src/mocks/mockNodes.jsx +62 -7
- package/src/styles/index.css +128 -11
- package/src/styles/node-creation.scss +9 -0
- package/src/styles/node-list.css +4 -0
- package/src/app/components/forms/NodeTagsInput.jsx +0 -61
|
@@ -12,7 +12,7 @@ export const DataJunctionAPI = {
|
|
|
12
12
|
},
|
|
13
13
|
|
|
14
14
|
logout: async function () {
|
|
15
|
-
|
|
15
|
+
return await fetch(`${DJ_URL}/logout/`, {
|
|
16
16
|
credentials: 'include',
|
|
17
17
|
method: 'POST',
|
|
18
18
|
});
|
|
@@ -388,19 +388,7 @@ export const DataJunctionAPI = {
|
|
|
388
388
|
})
|
|
389
389
|
).json();
|
|
390
390
|
|
|
391
|
-
return
|
|
392
|
-
data.map(async materialization => {
|
|
393
|
-
materialization.clientCode = await (
|
|
394
|
-
await fetch(
|
|
395
|
-
`${DJ_URL}/datajunction-clients/python/add_materialization/${node}/${materialization.name}`,
|
|
396
|
-
{
|
|
397
|
-
credentials: 'include',
|
|
398
|
-
},
|
|
399
|
-
)
|
|
400
|
-
).json();
|
|
401
|
-
return materialization;
|
|
402
|
-
}),
|
|
403
|
-
);
|
|
391
|
+
return data;
|
|
404
392
|
},
|
|
405
393
|
|
|
406
394
|
columns: async function (node) {
|
|
@@ -804,13 +792,7 @@ export const DataJunctionAPI = {
|
|
|
804
792
|
);
|
|
805
793
|
return { status: response.status, json: await response.json() };
|
|
806
794
|
},
|
|
807
|
-
runBackfill: async function (
|
|
808
|
-
nodeName,
|
|
809
|
-
materializationName,
|
|
810
|
-
partitionColumn,
|
|
811
|
-
from,
|
|
812
|
-
to,
|
|
813
|
-
) {
|
|
795
|
+
runBackfill: async function (nodeName, materializationName, partitionValues) {
|
|
814
796
|
const response = await fetch(
|
|
815
797
|
`${DJ_URL}/nodes/${nodeName}/materializations/${materializationName}/backfill`,
|
|
816
798
|
{
|
|
@@ -818,10 +800,29 @@ export const DataJunctionAPI = {
|
|
|
818
800
|
headers: {
|
|
819
801
|
'Content-Type': 'application/json',
|
|
820
802
|
},
|
|
821
|
-
body: JSON.stringify(
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
803
|
+
body: JSON.stringify(
|
|
804
|
+
partitionValues.map(partitionValue => {
|
|
805
|
+
return {
|
|
806
|
+
column_name: partitionValue.columnName,
|
|
807
|
+
range: partitionValue.range,
|
|
808
|
+
values: partitionValue.values,
|
|
809
|
+
};
|
|
810
|
+
}),
|
|
811
|
+
),
|
|
812
|
+
credentials: 'include',
|
|
813
|
+
},
|
|
814
|
+
);
|
|
815
|
+
return { status: response.status, json: await response.json() };
|
|
816
|
+
},
|
|
817
|
+
deleteMaterialization: async function (nodeName, materializationName) {
|
|
818
|
+
console.log('deleting materialization', nodeName, materializationName);
|
|
819
|
+
const response = await fetch(
|
|
820
|
+
`${DJ_URL}/nodes/${nodeName}/materializations?materialization_name=${materializationName}`,
|
|
821
|
+
{
|
|
822
|
+
method: 'DELETE',
|
|
823
|
+
headers: {
|
|
824
|
+
'Content-Type': 'application/json',
|
|
825
|
+
},
|
|
825
826
|
credentials: 'include',
|
|
826
827
|
},
|
|
827
828
|
);
|
|
@@ -844,4 +845,15 @@ export const DataJunctionAPI = {
|
|
|
844
845
|
})
|
|
845
846
|
).json();
|
|
846
847
|
},
|
|
848
|
+
revalidate: async function (node) {
|
|
849
|
+
return await (
|
|
850
|
+
await fetch(`${DJ_URL}/nodes/${node}/validate`, {
|
|
851
|
+
method: 'POST',
|
|
852
|
+
headers: {
|
|
853
|
+
'Content-Type': 'application/json',
|
|
854
|
+
},
|
|
855
|
+
credentials: 'include',
|
|
856
|
+
})
|
|
857
|
+
).json();
|
|
858
|
+
},
|
|
847
859
|
};
|
|
@@ -34,6 +34,22 @@ describe('DataJunctionAPI', () => {
|
|
|
34
34
|
});
|
|
35
35
|
});
|
|
36
36
|
|
|
37
|
+
it('calls catalogs correctly', async () => {
|
|
38
|
+
fetch.mockResponseOnce(JSON.stringify({}));
|
|
39
|
+
await DataJunctionAPI.catalogs();
|
|
40
|
+
expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/catalogs`, {
|
|
41
|
+
credentials: 'include',
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it('calls engines correctly', async () => {
|
|
46
|
+
fetch.mockResponseOnce(JSON.stringify({}));
|
|
47
|
+
await DataJunctionAPI.engines();
|
|
48
|
+
expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/engines`, {
|
|
49
|
+
credentials: 'include',
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
37
53
|
it('calls node correctly', async () => {
|
|
38
54
|
fetch.mockResponseOnce(JSON.stringify(mocks.mockMetricNode));
|
|
39
55
|
const nodeData = await DataJunctionAPI.node(mocks.mockMetricNode.name);
|
|
@@ -441,8 +457,8 @@ describe('DataJunctionAPI', () => {
|
|
|
441
457
|
it('calls materializations correctly', async () => {
|
|
442
458
|
const nodeName = 'default.sample_node';
|
|
443
459
|
const mockMaterializations = [
|
|
444
|
-
{ name: 'materialization1'
|
|
445
|
-
{ name: 'materialization2'
|
|
460
|
+
{ name: 'materialization1' },
|
|
461
|
+
{ name: 'materialization2' },
|
|
446
462
|
];
|
|
447
463
|
|
|
448
464
|
// Mock the first fetch call to return the list of materializations
|
|
@@ -465,14 +481,6 @@ describe('DataJunctionAPI', () => {
|
|
|
465
481
|
},
|
|
466
482
|
);
|
|
467
483
|
|
|
468
|
-
// Check the subsequent fetch calls for clientCode
|
|
469
|
-
mockMaterializations.forEach(mat => {
|
|
470
|
-
expect(fetch).toHaveBeenCalledWith(
|
|
471
|
-
`${DJ_URL}/datajunction-clients/python/add_materialization/${nodeName}/${mat.name}`,
|
|
472
|
-
{ credentials: 'include' },
|
|
473
|
-
);
|
|
474
|
-
});
|
|
475
|
-
|
|
476
484
|
// Ensure the result contains the clientCode for each materialization
|
|
477
485
|
expect(result).toEqual(mockMaterializations);
|
|
478
486
|
});
|
|
@@ -965,13 +973,12 @@ describe('DataJunctionAPI', () => {
|
|
|
965
973
|
|
|
966
974
|
it('calls runBackfill correctly', async () => {
|
|
967
975
|
fetch.mockResponseOnce(JSON.stringify({}));
|
|
968
|
-
await DataJunctionAPI.runBackfill(
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
);
|
|
976
|
+
await DataJunctionAPI.runBackfill('default.hard_hat', 'spark', [
|
|
977
|
+
{
|
|
978
|
+
columnName: 'hire_date',
|
|
979
|
+
range: ['20230101', '20230202'],
|
|
980
|
+
},
|
|
981
|
+
]);
|
|
975
982
|
expect(fetch).toHaveBeenCalledWith(
|
|
976
983
|
`${DJ_URL}/nodes/default.hard_hat/materializations/spark/backfill`,
|
|
977
984
|
{
|
|
@@ -979,12 +986,37 @@ describe('DataJunctionAPI', () => {
|
|
|
979
986
|
headers: {
|
|
980
987
|
'Content-Type': 'application/json',
|
|
981
988
|
},
|
|
982
|
-
body: JSON.stringify(
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
989
|
+
body: JSON.stringify([
|
|
990
|
+
{
|
|
991
|
+
column_name: 'hire_date',
|
|
992
|
+
range: ['20230101', '20230202'],
|
|
993
|
+
},
|
|
994
|
+
]),
|
|
986
995
|
method: 'POST',
|
|
987
996
|
},
|
|
988
997
|
);
|
|
989
998
|
});
|
|
999
|
+
|
|
1000
|
+
it('calls materializationInfo correctly', async () => {
|
|
1001
|
+
fetch.mockResponseOnce(JSON.stringify({}));
|
|
1002
|
+
await DataJunctionAPI.materializationInfo();
|
|
1003
|
+
expect(fetch).toHaveBeenCalledWith(`${DJ_URL}/materialization/info`, {
|
|
1004
|
+
credentials: 'include',
|
|
1005
|
+
});
|
|
1006
|
+
});
|
|
1007
|
+
|
|
1008
|
+
it('calls revalidate correctly', async () => {
|
|
1009
|
+
fetch.mockResponseOnce(JSON.stringify({}));
|
|
1010
|
+
await DataJunctionAPI.revalidate('default.hard_hat');
|
|
1011
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
1012
|
+
`${DJ_URL}/nodes/default.hard_hat/validate`,
|
|
1013
|
+
{
|
|
1014
|
+
credentials: 'include',
|
|
1015
|
+
method: 'POST',
|
|
1016
|
+
headers: {
|
|
1017
|
+
'Content-Type': 'application/json',
|
|
1018
|
+
},
|
|
1019
|
+
},
|
|
1020
|
+
);
|
|
1021
|
+
});
|
|
990
1022
|
});
|
package/src/mocks/mockNodes.jsx
CHANGED
|
@@ -41,7 +41,9 @@ export const mocks = {
|
|
|
41
41
|
type: 'string',
|
|
42
42
|
attributes: [],
|
|
43
43
|
dimension: null,
|
|
44
|
-
partition:
|
|
44
|
+
partition: {
|
|
45
|
+
type_: 'categorical',
|
|
46
|
+
},
|
|
45
47
|
},
|
|
46
48
|
],
|
|
47
49
|
updated_at: '2024-01-24T16:39:14.029366+00:00',
|
|
@@ -103,6 +105,7 @@ export const mocks = {
|
|
|
103
105
|
created_at: '2023-08-21T16:48:56.841631+00:00',
|
|
104
106
|
tags: [{ name: 'purpose', display_name: 'Purpose' }],
|
|
105
107
|
dimension_links: [],
|
|
108
|
+
incompatible_druid_functions: ['IF'],
|
|
106
109
|
dimensions: [
|
|
107
110
|
{
|
|
108
111
|
value: 'default.date_dim.dateint',
|
|
@@ -460,12 +463,14 @@ export const mocks = {
|
|
|
460
463
|
{
|
|
461
464
|
backfills: [
|
|
462
465
|
{
|
|
463
|
-
spec:
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
466
|
+
spec: [
|
|
467
|
+
{
|
|
468
|
+
column_name: 'default_DOT_hard_hat_DOT_hire_date',
|
|
469
|
+
values: null,
|
|
470
|
+
range: ['20230101', '20230102'],
|
|
471
|
+
},
|
|
472
|
+
],
|
|
473
|
+
urls: ['a', 'b'],
|
|
469
474
|
},
|
|
470
475
|
],
|
|
471
476
|
name: 'country_birth_date_contractor_id_379232101',
|
|
@@ -476,6 +481,7 @@ export const mocks = {
|
|
|
476
481
|
"SELECT default_DOT_hard_hats.address,\n\tdefault_DOT_hard_hats.birth_date,\n\tdefault_DOT_hard_hats.city,\n\tdefault_DOT_hard_hats.contractor_id,\n\tdefault_DOT_hard_hats.country,\n\tdefault_DOT_hard_hats.first_name,\n\tdefault_DOT_hard_hats.hard_hat_id,\n\tdefault_DOT_hard_hats.hire_date,\n\tdefault_DOT_hard_hats.last_name,\n\tdefault_DOT_hard_hats.manager,\n\tdefault_DOT_hard_hats.postal_code,\n\tdefault_DOT_hard_hats.state,\n\tdefault_DOT_hard_hats.title \n FROM roads.hard_hats AS default_DOT_hard_hats \n WHERE default_DOT_hard_hats.country IN ('DE', 'MY') AND default_DOT_hard_hats.contractor_id BETWEEN 1 AND 10\n\n",
|
|
477
482
|
upstream_tables: ['default.roads.hard_hats'],
|
|
478
483
|
},
|
|
484
|
+
strategy: 'incremental_time',
|
|
479
485
|
schedule: '0 * * * *',
|
|
480
486
|
job: 'SparkSqlMaterializationJob',
|
|
481
487
|
output_tables: ['common.a', 'common.b'],
|
|
@@ -1552,4 +1558,53 @@ export const mocks = {
|
|
|
1552
1558
|
tag_type: 'reports',
|
|
1553
1559
|
},
|
|
1554
1560
|
],
|
|
1561
|
+
materializationInfo: {
|
|
1562
|
+
job_types: [
|
|
1563
|
+
{
|
|
1564
|
+
name: 'spark_sql',
|
|
1565
|
+
label: 'Spark SQL',
|
|
1566
|
+
description: 'Spark SQL materialization job',
|
|
1567
|
+
allowed_node_types: ['transform', 'dimension', 'cube'],
|
|
1568
|
+
job_class: 'SparkSqlMaterializationJob',
|
|
1569
|
+
},
|
|
1570
|
+
{
|
|
1571
|
+
name: 'druid_measures_cube',
|
|
1572
|
+
label: 'Druid Measures Cube (Pre-Agg Cube)',
|
|
1573
|
+
description:
|
|
1574
|
+
"Used to materialize a cube's measures to Druid for low-latency access to a set of metrics and dimensions. While the logical cube definition is at the level of metrics and dimensions, this materialized Druid cube will contain measures and dimensions, with rollup configured on the measures where appropriate.",
|
|
1575
|
+
allowed_node_types: ['cube'],
|
|
1576
|
+
job_class: 'DruidMeasuresCubeMaterializationJob',
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
name: 'druid_metrics_cube',
|
|
1580
|
+
label: 'Druid Metrics Cube (Post-Agg Cube)',
|
|
1581
|
+
description:
|
|
1582
|
+
"Used to materialize a cube of metrics and dimensions to Druid for low-latency access. The materialized cube is at the metric level, meaning that all metrics will be aggregated to the level of the cube's dimensions.",
|
|
1583
|
+
allowed_node_types: ['cube'],
|
|
1584
|
+
job_class: 'DruidMetricsCubeMaterializationJob',
|
|
1585
|
+
},
|
|
1586
|
+
],
|
|
1587
|
+
strategies: [
|
|
1588
|
+
{
|
|
1589
|
+
name: 'full',
|
|
1590
|
+
label: 'Full',
|
|
1591
|
+
},
|
|
1592
|
+
{
|
|
1593
|
+
name: 'snapshot',
|
|
1594
|
+
label: 'Snapshot',
|
|
1595
|
+
},
|
|
1596
|
+
{
|
|
1597
|
+
name: 'snapshot_partition',
|
|
1598
|
+
label: 'Snapshot Partition',
|
|
1599
|
+
},
|
|
1600
|
+
{
|
|
1601
|
+
name: 'incremental_time',
|
|
1602
|
+
label: 'Incremental Time',
|
|
1603
|
+
},
|
|
1604
|
+
{
|
|
1605
|
+
name: 'view',
|
|
1606
|
+
label: 'View',
|
|
1607
|
+
},
|
|
1608
|
+
],
|
|
1609
|
+
},
|
|
1555
1610
|
};
|
package/src/styles/index.css
CHANGED
|
@@ -593,6 +593,12 @@ tbody th {
|
|
|
593
593
|
color: #777777;
|
|
594
594
|
}
|
|
595
595
|
|
|
596
|
+
.strategy {
|
|
597
|
+
background-color: rgb(255, 239, 215) !important;
|
|
598
|
+
color: #6c3b21;
|
|
599
|
+
font-size: 1rem;
|
|
600
|
+
}
|
|
601
|
+
|
|
596
602
|
.status__valid {
|
|
597
603
|
color: #00b368;
|
|
598
604
|
}
|
|
@@ -1018,17 +1024,19 @@ pre {
|
|
|
1018
1024
|
}
|
|
1019
1025
|
|
|
1020
1026
|
.add_button {
|
|
1021
|
-
background-color: #
|
|
1022
|
-
color: #
|
|
1023
|
-
text-transform:
|
|
1027
|
+
background-color: #2f7986 !important;
|
|
1028
|
+
color: #fff;
|
|
1029
|
+
text-transform: none;
|
|
1024
1030
|
vertical-align: middle;
|
|
1025
|
-
padding:
|
|
1026
|
-
|
|
1027
|
-
|
|
1031
|
+
padding-right: 1rem;
|
|
1032
|
+
padding-left: 1rem;
|
|
1033
|
+
padding-top: 0.5rem;
|
|
1034
|
+
padding-bottom: 0.5rem;
|
|
1035
|
+
margin-bottom: 0.5rem !important;
|
|
1036
|
+
font-size: 1rem;
|
|
1028
1037
|
border-radius: 0.5rem;
|
|
1029
1038
|
word-wrap: break-word;
|
|
1030
1039
|
white-space: break-spaces;
|
|
1031
|
-
margin-left: 1rem;
|
|
1032
1040
|
}
|
|
1033
1041
|
|
|
1034
1042
|
.edit_button {
|
|
@@ -1090,10 +1098,6 @@ pre {
|
|
|
1090
1098
|
transition: opacity 0.15s linear;
|
|
1091
1099
|
}
|
|
1092
1100
|
|
|
1093
|
-
.partitionLink:hover {
|
|
1094
|
-
text-decoration: none;
|
|
1095
|
-
}
|
|
1096
|
-
|
|
1097
1101
|
.dimensionsList {
|
|
1098
1102
|
padding: 12px;
|
|
1099
1103
|
opacity: 1;
|
|
@@ -1202,3 +1206,116 @@ pre {
|
|
|
1202
1206
|
text-transform: uppercase;
|
|
1203
1207
|
padding-left: 10px;
|
|
1204
1208
|
}
|
|
1209
|
+
|
|
1210
|
+
.validation_error {
|
|
1211
|
+
border: #b34b0025 1px solid;
|
|
1212
|
+
border-left: #b34b00 5px solid;
|
|
1213
|
+
padding-left: 20px;
|
|
1214
|
+
padding-top: 5px;
|
|
1215
|
+
padding-bottom: 5px;
|
|
1216
|
+
font-size: small;
|
|
1217
|
+
width: 600px;
|
|
1218
|
+
word-wrap: break-word;
|
|
1219
|
+
margin-top: 3px;
|
|
1220
|
+
background-color: #ffffff;
|
|
1221
|
+
margin-bottom: 3px;
|
|
1222
|
+
margin-left: -20px;
|
|
1223
|
+
}
|
|
1224
|
+
|
|
1225
|
+
.partitionLink {
|
|
1226
|
+
/*border: 1px solid #ccc4d5;*/
|
|
1227
|
+
padding: 12px;
|
|
1228
|
+
margin: 5px;
|
|
1229
|
+
border-radius: 0.375rem;
|
|
1230
|
+
background-color: #ccc4d525;
|
|
1231
|
+
}
|
|
1232
|
+
.partitionLink:hover {
|
|
1233
|
+
background-color: rgba(157, 147, 168, 0.15);
|
|
1234
|
+
}
|
|
1235
|
+
.partitionLink a:hover {
|
|
1236
|
+
text-decoration: none;
|
|
1237
|
+
}
|
|
1238
|
+
|
|
1239
|
+
.backfills {
|
|
1240
|
+
margin-left: -4rem;
|
|
1241
|
+
--spacing : 1.5rem;
|
|
1242
|
+
--radius : 10px;
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
.backfills li{
|
|
1246
|
+
display : block;
|
|
1247
|
+
position : relative;
|
|
1248
|
+
padding-left : calc(2 * var(--spacing) - var(--radius) - 2px);
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
.backfills ul{
|
|
1252
|
+
margin-left : calc(var(--radius) - var(--spacing));
|
|
1253
|
+
padding-left : 2rem;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
.backfills ul li{
|
|
1257
|
+
border-left : 2px solid #ddd;
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
.backfills ul li:last-child{
|
|
1261
|
+
border-color : transparent;
|
|
1262
|
+
}
|
|
1263
|
+
|
|
1264
|
+
.backfills ul li::before{
|
|
1265
|
+
content : '';
|
|
1266
|
+
display : block;
|
|
1267
|
+
position : absolute;
|
|
1268
|
+
top : calc(var(--spacing) / -2);
|
|
1269
|
+
left : -2px;
|
|
1270
|
+
width : calc(var(--spacing) + 2px);
|
|
1271
|
+
height : calc(var(--spacing) + 1px);
|
|
1272
|
+
border : solid #ddd;
|
|
1273
|
+
border-width : 0 0 2px 2px;
|
|
1274
|
+
}
|
|
1275
|
+
|
|
1276
|
+
.backfills summary{
|
|
1277
|
+
display : block;
|
|
1278
|
+
cursor : pointer;
|
|
1279
|
+
margin-bottom: 10px;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
.backfills summary::marker,
|
|
1283
|
+
.backfills summary::-webkit-details-marker{
|
|
1284
|
+
display : none;
|
|
1285
|
+
}
|
|
1286
|
+
|
|
1287
|
+
.backfills summary:focus{
|
|
1288
|
+
outline : none;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
.backfills summary:focus-visible{
|
|
1292
|
+
outline : 1px dotted #000;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
.backfills summary::before{
|
|
1296
|
+
z-index : 1;
|
|
1297
|
+
/*background : #696 url('expand-collapse.svg') 0 0;*/
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
.backfills details[open] > summary::before{
|
|
1301
|
+
background-position : calc(-2 * var(--radius)) 0;
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
.backfills_header {
|
|
1305
|
+
font-size: 16px;
|
|
1306
|
+
margin-left: 18px;
|
|
1307
|
+
border-left: 2px solid #ddd;
|
|
1308
|
+
padding-left: 40px;
|
|
1309
|
+
margin-bottom: 10px;
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
.tr {
|
|
1313
|
+
display: inline-flex;
|
|
1314
|
+
}
|
|
1315
|
+
.tr li {
|
|
1316
|
+
padding-right: 10px;
|
|
1317
|
+
}
|
|
1318
|
+
.td {
|
|
1319
|
+
display: table-cell;
|
|
1320
|
+
padding: 8px;
|
|
1321
|
+
}
|
|
@@ -254,6 +254,15 @@ form {
|
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
.warning {
|
|
258
|
+
background-color: rgb(253, 248, 242);
|
|
259
|
+
color: rgb(190, 105, 37);
|
|
260
|
+
svg {
|
|
261
|
+
filter: invert(16%) sepia(68%) saturate(2827%) hue-rotate(344deg)
|
|
262
|
+
brightness(96%) contrast(100%);
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
257
266
|
.SourceCreationInput {
|
|
258
267
|
margin: 0.5rem 0;
|
|
259
268
|
display: inline-grid;
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import { ErrorMessage } from 'formik';
|
|
2
|
-
import { FormikSelect } from '../../pages/AddEditNodePage/FormikSelect';
|
|
3
|
-
import { Action } from './Action';
|
|
4
|
-
import { useContext, useEffect, useState } from 'react';
|
|
5
|
-
import DJClientContext from '../../providers/djclient';
|
|
6
|
-
|
|
7
|
-
export default function NodeTagsInput({ action, node }) {
|
|
8
|
-
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
9
|
-
const [tags, setTags] = useState([]);
|
|
10
|
-
|
|
11
|
-
// Get list of tags
|
|
12
|
-
useEffect(() => {
|
|
13
|
-
const fetchData = async () => {
|
|
14
|
-
const tags = await djClient.listTags();
|
|
15
|
-
setTags(
|
|
16
|
-
tags.map(tag => ({
|
|
17
|
-
value: tag.name,
|
|
18
|
-
label: tag.display_name,
|
|
19
|
-
})),
|
|
20
|
-
);
|
|
21
|
-
};
|
|
22
|
-
fetchData().catch(console.error);
|
|
23
|
-
}, [djClient, djClient.listTags]);
|
|
24
|
-
|
|
25
|
-
return (
|
|
26
|
-
<div
|
|
27
|
-
className="TagsInput"
|
|
28
|
-
style={{ width: '25%', margin: '1rem 0 1rem 1.2rem' }}
|
|
29
|
-
>
|
|
30
|
-
<ErrorMessage name="tags" component="span" />
|
|
31
|
-
<label htmlFor="react-select-3-input">Tags</label>
|
|
32
|
-
<span data-testid="select-tags">
|
|
33
|
-
{action === Action.Edit && node?.tags?.length >= 0 ? (
|
|
34
|
-
<FormikSelect
|
|
35
|
-
className=""
|
|
36
|
-
isMulti={true}
|
|
37
|
-
selectOptions={tags}
|
|
38
|
-
formikFieldName="tags"
|
|
39
|
-
placeholder="Choose Tags"
|
|
40
|
-
defaultValue={node?.tags?.map(t => {
|
|
41
|
-
return { value: t.name, label: t.display_name };
|
|
42
|
-
})}
|
|
43
|
-
/>
|
|
44
|
-
) : (
|
|
45
|
-
''
|
|
46
|
-
)}
|
|
47
|
-
{action === Action.Add ? (
|
|
48
|
-
<FormikSelect
|
|
49
|
-
className=""
|
|
50
|
-
isMulti={true}
|
|
51
|
-
selectOptions={tags}
|
|
52
|
-
formikFieldName="tags"
|
|
53
|
-
placeholder="Choose Tags"
|
|
54
|
-
/>
|
|
55
|
-
) : (
|
|
56
|
-
''
|
|
57
|
-
)}
|
|
58
|
-
</span>
|
|
59
|
-
</div>
|
|
60
|
-
);
|
|
61
|
-
}
|