datajunction-ui 0.0.151 → 0.0.153
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
CHANGED
|
@@ -1143,21 +1143,38 @@ export function NamespacePage() {
|
|
|
1143
1143
|
namespace={namespace}
|
|
1144
1144
|
onGitConfigLoaded={setGitConfig}
|
|
1145
1145
|
>
|
|
1146
|
-
<
|
|
1147
|
-
|
|
1148
|
-
|
|
1146
|
+
<button
|
|
1147
|
+
type="button"
|
|
1148
|
+
onClick={async () => {
|
|
1149
|
+
const response = await fetch(
|
|
1150
|
+
`${getDJUrl()}/namespaces/${namespace}/export/yaml`,
|
|
1151
|
+
{ method: 'POST', credentials: 'include' },
|
|
1152
|
+
);
|
|
1153
|
+
if (!response.ok) {
|
|
1154
|
+
return;
|
|
1155
|
+
}
|
|
1156
|
+
const blob = await response.blob();
|
|
1157
|
+
const url = URL.createObjectURL(blob);
|
|
1158
|
+
const link = document.createElement('a');
|
|
1159
|
+
const safeName = namespace.replace(/\./g, '_');
|
|
1160
|
+
link.href = url;
|
|
1161
|
+
link.download = `${safeName}_export.zip`;
|
|
1162
|
+
document.body.appendChild(link);
|
|
1163
|
+
link.click();
|
|
1164
|
+
document.body.removeChild(link);
|
|
1165
|
+
URL.revokeObjectURL(url);
|
|
1166
|
+
}}
|
|
1149
1167
|
style={{
|
|
1150
1168
|
display: 'inline-flex',
|
|
1151
1169
|
alignItems: 'center',
|
|
1152
1170
|
gap: '4px',
|
|
1153
|
-
// padding: '6px 12px',
|
|
1154
1171
|
fontSize: '13px',
|
|
1155
1172
|
fontWeight: '500',
|
|
1156
1173
|
color: '#475569',
|
|
1157
|
-
|
|
1158
|
-
|
|
1174
|
+
background: 'none',
|
|
1175
|
+
border: 'none',
|
|
1176
|
+
padding: 0,
|
|
1159
1177
|
borderRadius: '6px',
|
|
1160
|
-
textDecoration: 'none',
|
|
1161
1178
|
cursor: 'pointer',
|
|
1162
1179
|
transition: 'all 0.15s ease',
|
|
1163
1180
|
margin: '0.5em 0px 0px 1em',
|
|
@@ -1184,7 +1201,7 @@ export function NamespacePage() {
|
|
|
1184
1201
|
<polyline points="7 10 12 15 17 10"></polyline>
|
|
1185
1202
|
<line x1="12" y1="15" x2="12" y2="3"></line>
|
|
1186
1203
|
</svg>
|
|
1187
|
-
</
|
|
1204
|
+
</button>
|
|
1188
1205
|
{showEditControls && <AddNodeDropdown namespace={namespace} />}
|
|
1189
1206
|
</NamespaceHeader>
|
|
1190
1207
|
|
|
@@ -61,26 +61,74 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
const showColumnPartition = col => {
|
|
64
|
-
if (col.partition)
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<b>Format:</b> <code>{col.partition.format}</code>
|
|
74
|
-
</span>
|
|
75
|
-
<br />
|
|
76
|
-
<span className="partition_value badge">
|
|
77
|
-
<b>Granularity:</b> <code>{col.partition.granularity}</code>
|
|
78
|
-
</span>
|
|
79
|
-
</span>
|
|
80
|
-
</>
|
|
81
|
-
);
|
|
64
|
+
if (!col.partition) return '';
|
|
65
|
+
const isTemporal = col.partition.type_ === 'temporal';
|
|
66
|
+
const typeLabel =
|
|
67
|
+
col.partition.type_.charAt(0).toUpperCase() +
|
|
68
|
+
col.partition.type_.slice(1);
|
|
69
|
+
const details = [];
|
|
70
|
+
if (isTemporal) {
|
|
71
|
+
if (col.partition.granularity) details.push(col.partition.granularity);
|
|
72
|
+
if (col.partition.format) details.push(col.partition.format);
|
|
82
73
|
}
|
|
83
|
-
|
|
74
|
+
const icon = isTemporal ? (
|
|
75
|
+
<svg
|
|
76
|
+
className="partition-cell__icon"
|
|
77
|
+
viewBox="0 0 24 24"
|
|
78
|
+
width="14"
|
|
79
|
+
height="14"
|
|
80
|
+
fill="none"
|
|
81
|
+
stroke="currentColor"
|
|
82
|
+
strokeWidth="2"
|
|
83
|
+
strokeLinecap="round"
|
|
84
|
+
strokeLinejoin="round"
|
|
85
|
+
aria-hidden="true"
|
|
86
|
+
>
|
|
87
|
+
<rect x="3" y="4" width="18" height="18" rx="2" ry="2" />
|
|
88
|
+
<line x1="16" y1="2" x2="16" y2="6" />
|
|
89
|
+
<line x1="8" y1="2" x2="8" y2="6" />
|
|
90
|
+
<line x1="3" y1="10" x2="21" y2="10" />
|
|
91
|
+
</svg>
|
|
92
|
+
) : (
|
|
93
|
+
<svg
|
|
94
|
+
className="partition-cell__icon"
|
|
95
|
+
viewBox="0 0 24 24"
|
|
96
|
+
width="14"
|
|
97
|
+
height="14"
|
|
98
|
+
fill="none"
|
|
99
|
+
stroke="currentColor"
|
|
100
|
+
strokeWidth="2"
|
|
101
|
+
strokeLinecap="round"
|
|
102
|
+
strokeLinejoin="round"
|
|
103
|
+
aria-hidden="true"
|
|
104
|
+
>
|
|
105
|
+
<line x1="8" y1="6" x2="21" y2="6" />
|
|
106
|
+
<line x1="8" y1="12" x2="21" y2="12" />
|
|
107
|
+
<line x1="8" y1="18" x2="21" y2="18" />
|
|
108
|
+
<line x1="3" y1="6" x2="3.01" y2="6" />
|
|
109
|
+
<line x1="3" y1="12" x2="3.01" y2="12" />
|
|
110
|
+
<line x1="3" y1="18" x2="3.01" y2="18" />
|
|
111
|
+
</svg>
|
|
112
|
+
);
|
|
113
|
+
return (
|
|
114
|
+
<div className="partition-cell">
|
|
115
|
+
{icon}
|
|
116
|
+
<span className="partition-cell__type">{typeLabel}</span>
|
|
117
|
+
{details.length > 0 ? (
|
|
118
|
+
<>
|
|
119
|
+
<span className="partition-cell__dash">—</span>
|
|
120
|
+
<span className="partition-cell__details">
|
|
121
|
+
{details.map((d, i) => (
|
|
122
|
+
<React.Fragment key={i}>
|
|
123
|
+
{i > 0 ? ', ' : ''}
|
|
124
|
+
<code>{d}</code>
|
|
125
|
+
</React.Fragment>
|
|
126
|
+
))}
|
|
127
|
+
</span>
|
|
128
|
+
</>
|
|
129
|
+
) : null}
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
84
132
|
};
|
|
85
133
|
|
|
86
134
|
const columnList = columns => {
|
|
@@ -2,9 +2,8 @@ import { useContext, useEffect, useRef, useState } from 'react';
|
|
|
2
2
|
import * as React from 'react';
|
|
3
3
|
import DJClientContext from '../../providers/djclient';
|
|
4
4
|
import { Field, Form, Formik } from 'formik';
|
|
5
|
-
import { FormikSelect } from '../AddEditNodePage/FormikSelect';
|
|
6
5
|
import EditIcon from '../../icons/EditIcon';
|
|
7
|
-
import { displayMessageAfterSubmit
|
|
6
|
+
import { displayMessageAfterSubmit } from '../../../utils/form';
|
|
8
7
|
|
|
9
8
|
export default function PartitionColumnPopover({ column, node, onSubmit }) {
|
|
10
9
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
@@ -43,7 +42,17 @@ export default function PartitionColumnPopover({ column, node, onSubmit }) {
|
|
|
43
42
|
});
|
|
44
43
|
}
|
|
45
44
|
onSubmit();
|
|
46
|
-
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const removePartition = async setStatus => {
|
|
48
|
+
const response = await djClient.removePartition(node.name, column.name);
|
|
49
|
+
if (response.status === 200 || response.status === 201) {
|
|
50
|
+
setStatus({ success: 'Partition removed' });
|
|
51
|
+
onSubmit();
|
|
52
|
+
setPopoverAnchor(false);
|
|
53
|
+
} else {
|
|
54
|
+
setStatus({ failure: `${response.json.message}` });
|
|
55
|
+
}
|
|
47
56
|
};
|
|
48
57
|
|
|
49
58
|
return (
|
|
@@ -59,7 +68,7 @@ export default function PartitionColumnPopover({ column, node, onSubmit }) {
|
|
|
59
68
|
<EditIcon />
|
|
60
69
|
</button>
|
|
61
70
|
<div
|
|
62
|
-
className="popover"
|
|
71
|
+
className="popover partition-popover"
|
|
63
72
|
role="dialog"
|
|
64
73
|
aria-label="client-code"
|
|
65
74
|
style={{ display: popoverAnchor === false ? 'none' : 'block' }}
|
|
@@ -69,17 +78,23 @@ export default function PartitionColumnPopover({ column, node, onSubmit }) {
|
|
|
69
78
|
initialValues={{
|
|
70
79
|
column: column.name,
|
|
71
80
|
node: node.name,
|
|
72
|
-
partition_type: '',
|
|
73
|
-
format: 'yyyyMMdd',
|
|
74
|
-
granularity: 'day',
|
|
81
|
+
partition_type: column.partition?.type_ ?? '',
|
|
82
|
+
format: column.partition?.format ?? 'yyyyMMdd',
|
|
83
|
+
granularity: column.partition?.granularity ?? 'day',
|
|
75
84
|
}}
|
|
76
85
|
onSubmit={savePartition}
|
|
77
86
|
>
|
|
78
|
-
{function Render({ values, isSubmitting, status,
|
|
87
|
+
{function Render({ values, isSubmitting, status, setStatus }) {
|
|
79
88
|
return (
|
|
80
89
|
<Form>
|
|
90
|
+
<div className="popover-header">
|
|
91
|
+
<div className="popover-title">Partition column</div>
|
|
92
|
+
<div className="popover-subtitle">
|
|
93
|
+
{popoverAnchor ? column.name : null}
|
|
94
|
+
</div>
|
|
95
|
+
</div>
|
|
81
96
|
{displayMessageAfterSubmit(status)}
|
|
82
|
-
<
|
|
97
|
+
<div className="field-group" data-testid="edit-partition">
|
|
83
98
|
<label htmlFor="partitionType">Partition Type</label>
|
|
84
99
|
<Field
|
|
85
100
|
as="select"
|
|
@@ -91,7 +106,7 @@ export default function PartitionColumnPopover({ column, node, onSubmit }) {
|
|
|
91
106
|
<option value="temporal">Temporal</option>
|
|
92
107
|
<option value="categorical">Categorical</option>
|
|
93
108
|
</Field>
|
|
94
|
-
</
|
|
109
|
+
</div>
|
|
95
110
|
<input
|
|
96
111
|
hidden={true}
|
|
97
112
|
name="column"
|
|
@@ -104,70 +119,60 @@ export default function PartitionColumnPopover({ column, node, onSubmit }) {
|
|
|
104
119
|
value={node.name}
|
|
105
120
|
readOnly={true}
|
|
106
121
|
/>
|
|
107
|
-
<br />
|
|
108
|
-
<br />
|
|
109
122
|
{values.partition_type === 'temporal' ? (
|
|
110
123
|
<>
|
|
111
|
-
<
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
<
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
124
|
+
<div className="field-group">
|
|
125
|
+
<label htmlFor="partitionFormat">Partition Format</label>
|
|
126
|
+
<Field
|
|
127
|
+
type="text"
|
|
128
|
+
name="format"
|
|
129
|
+
id="partitionFormat"
|
|
130
|
+
placeholder="e.g. yyyyMMdd"
|
|
131
|
+
/>
|
|
132
|
+
</div>
|
|
133
|
+
<div className="field-group">
|
|
134
|
+
<label htmlFor="partitionGranularity">
|
|
135
|
+
Partition Granularity
|
|
136
|
+
</label>
|
|
137
|
+
<Field
|
|
138
|
+
as="select"
|
|
139
|
+
name="granularity"
|
|
140
|
+
id="partitionGranularity"
|
|
141
|
+
placeholder="Granularity"
|
|
142
|
+
>
|
|
143
|
+
<option value="day">Day</option>
|
|
144
|
+
<option value="hour">Hour</option>
|
|
145
|
+
</Field>
|
|
146
|
+
</div>
|
|
132
147
|
</>
|
|
133
|
-
) :
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
<button
|
|
137
|
-
className="add_node"
|
|
138
|
-
type="submit"
|
|
139
|
-
aria-label="SaveEditColumn"
|
|
140
|
-
aria-hidden="false"
|
|
141
|
-
>
|
|
142
|
-
Save
|
|
143
|
-
</button>
|
|
144
|
-
<button
|
|
145
|
-
className="delete_button"
|
|
146
|
-
type="button"
|
|
147
|
-
aria-label="RemovePartition"
|
|
148
|
-
aria-hidden="false"
|
|
149
|
-
onClick={() => {
|
|
150
|
-
setFieldValue('partition_type', '');
|
|
151
|
-
setFieldValue('format', '');
|
|
152
|
-
setFieldValue('granularity', '');
|
|
153
|
-
savePartition(
|
|
154
|
-
{
|
|
155
|
-
node: node.name,
|
|
156
|
-
column: column.name,
|
|
157
|
-
partition_type: '',
|
|
158
|
-
format: '',
|
|
159
|
-
granularity: '',
|
|
160
|
-
},
|
|
161
|
-
{ setSubmitting: () => {}, setStatus: s => {} },
|
|
162
|
-
);
|
|
163
|
-
}}
|
|
148
|
+
) : null}
|
|
149
|
+
<div
|
|
150
|
+
className="button-row"
|
|
164
151
|
style={{
|
|
165
|
-
|
|
166
|
-
|
|
152
|
+
justifyContent: column.partition
|
|
153
|
+
? 'space-between'
|
|
154
|
+
: 'flex-end',
|
|
167
155
|
}}
|
|
168
156
|
>
|
|
169
|
-
|
|
170
|
-
|
|
157
|
+
{column.partition ? (
|
|
158
|
+
<button
|
|
159
|
+
className="remove-link"
|
|
160
|
+
type="button"
|
|
161
|
+
aria-label="RemovePartition"
|
|
162
|
+
onClick={() => removePartition(setStatus)}
|
|
163
|
+
>
|
|
164
|
+
Remove partition
|
|
165
|
+
</button>
|
|
166
|
+
) : null}
|
|
167
|
+
<button
|
|
168
|
+
className="add_node"
|
|
169
|
+
type="submit"
|
|
170
|
+
aria-label="SaveEditColumn"
|
|
171
|
+
aria-hidden="false"
|
|
172
|
+
>
|
|
173
|
+
Save
|
|
174
|
+
</button>
|
|
175
|
+
</div>
|
|
171
176
|
</Form>
|
|
172
177
|
);
|
|
173
178
|
}}
|
|
@@ -1999,6 +1999,16 @@ export const DataJunctionAPI = {
|
|
|
1999
1999
|
);
|
|
2000
2000
|
return { status: response.status, json: await response.json() };
|
|
2001
2001
|
},
|
|
2002
|
+
removePartition: async function (nodeName, columnName) {
|
|
2003
|
+
const response = await fetch(
|
|
2004
|
+
`${DJ_URL}/nodes/${nodeName}/columns/${columnName}/partition`,
|
|
2005
|
+
{
|
|
2006
|
+
method: 'DELETE',
|
|
2007
|
+
credentials: 'include',
|
|
2008
|
+
},
|
|
2009
|
+
);
|
|
2010
|
+
return { status: response.status, json: await response.json() };
|
|
2011
|
+
},
|
|
2002
2012
|
materialize: async function (nodeName, jobType, strategy, schedule, config) {
|
|
2003
2013
|
const response = await fetch(
|
|
2004
2014
|
`${DJ_URL}/nodes/${nodeName}/materialization`,
|
package/src/styles/index.css
CHANGED
|
@@ -568,6 +568,47 @@ tbody th {
|
|
|
568
568
|
background-color: #ccf7e525;
|
|
569
569
|
color: #00b368;
|
|
570
570
|
margin: 0.25rem;
|
|
571
|
+
font-size: 0.85rem;
|
|
572
|
+
text-transform: none;
|
|
573
|
+
padding: 0.35em 0.6em;
|
|
574
|
+
}
|
|
575
|
+
.partition_value code {
|
|
576
|
+
font-size: 0.9em;
|
|
577
|
+
color: inherit;
|
|
578
|
+
background: none;
|
|
579
|
+
padding: 0;
|
|
580
|
+
}
|
|
581
|
+
.partition-cell {
|
|
582
|
+
display: inline-flex;
|
|
583
|
+
align-items: center;
|
|
584
|
+
gap: 6px;
|
|
585
|
+
font-size: 0.85rem;
|
|
586
|
+
color: #334155;
|
|
587
|
+
text-transform: none;
|
|
588
|
+
letter-spacing: 0;
|
|
589
|
+
line-height: 1.4;
|
|
590
|
+
white-space: nowrap;
|
|
591
|
+
}
|
|
592
|
+
.partition-cell__icon {
|
|
593
|
+
color: #00794a;
|
|
594
|
+
flex-shrink: 0;
|
|
595
|
+
}
|
|
596
|
+
.partition-cell__type {
|
|
597
|
+
font-weight: 600;
|
|
598
|
+
color: #1e293b;
|
|
599
|
+
}
|
|
600
|
+
.partition-cell__dash {
|
|
601
|
+
color: #cbd5e1;
|
|
602
|
+
}
|
|
603
|
+
.partition-cell__details {
|
|
604
|
+
color: #64748b;
|
|
605
|
+
}
|
|
606
|
+
.partition-cell__details code {
|
|
607
|
+
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
|
|
608
|
+
font-size: 0.92em;
|
|
609
|
+
background: none;
|
|
610
|
+
padding: 0;
|
|
611
|
+
color: #475569;
|
|
571
612
|
}
|
|
572
613
|
|
|
573
614
|
.partition_value_highlight {
|
|
@@ -1096,6 +1137,87 @@ pre {
|
|
|
1096
1137
|
background-color: #f3eeff !important;
|
|
1097
1138
|
}
|
|
1098
1139
|
|
|
1140
|
+
.partition-popover {
|
|
1141
|
+
min-width: 260px;
|
|
1142
|
+
}
|
|
1143
|
+
.partition-popover .popover-header {
|
|
1144
|
+
margin: -4px 0 14px 0;
|
|
1145
|
+
padding-bottom: 10px;
|
|
1146
|
+
border-bottom: 1px solid #f1f5f9;
|
|
1147
|
+
}
|
|
1148
|
+
.partition-popover .popover-title {
|
|
1149
|
+
font-size: 0.7rem;
|
|
1150
|
+
font-weight: 600;
|
|
1151
|
+
text-transform: uppercase;
|
|
1152
|
+
letter-spacing: 0.06em;
|
|
1153
|
+
color: #94a3b8;
|
|
1154
|
+
margin-bottom: 2px;
|
|
1155
|
+
}
|
|
1156
|
+
.partition-popover .popover-subtitle {
|
|
1157
|
+
font-size: 0.95rem;
|
|
1158
|
+
font-weight: 600;
|
|
1159
|
+
color: #1e293b;
|
|
1160
|
+
font-family: 'Menlo', 'Monaco', 'Courier New', monospace;
|
|
1161
|
+
}
|
|
1162
|
+
.partition-popover label {
|
|
1163
|
+
display: block;
|
|
1164
|
+
text-transform: none;
|
|
1165
|
+
font-size: 0.8rem;
|
|
1166
|
+
font-weight: 600;
|
|
1167
|
+
color: #475569;
|
|
1168
|
+
margin-bottom: 4px;
|
|
1169
|
+
letter-spacing: 0.02em;
|
|
1170
|
+
}
|
|
1171
|
+
.partition-popover select,
|
|
1172
|
+
.partition-popover input[type='text'] {
|
|
1173
|
+
width: 100%;
|
|
1174
|
+
background-color: #fff;
|
|
1175
|
+
border: 1px solid #d4d4d8;
|
|
1176
|
+
border-radius: 6px;
|
|
1177
|
+
padding: 6px 10px;
|
|
1178
|
+
font-size: 0.875rem;
|
|
1179
|
+
font-family: inherit;
|
|
1180
|
+
box-shadow: none;
|
|
1181
|
+
box-sizing: border-box;
|
|
1182
|
+
}
|
|
1183
|
+
.partition-popover select:focus,
|
|
1184
|
+
.partition-popover input[type='text']:focus {
|
|
1185
|
+
outline: none;
|
|
1186
|
+
border-color: #5d2f86;
|
|
1187
|
+
}
|
|
1188
|
+
.partition-popover .field-group {
|
|
1189
|
+
margin-bottom: 12px;
|
|
1190
|
+
}
|
|
1191
|
+
.partition-popover .button-row {
|
|
1192
|
+
display: flex;
|
|
1193
|
+
align-items: center;
|
|
1194
|
+
justify-content: space-between;
|
|
1195
|
+
margin-top: 16px;
|
|
1196
|
+
padding-top: 8px;
|
|
1197
|
+
border-top: 1px solid #f1f5f9;
|
|
1198
|
+
}
|
|
1199
|
+
.partition-popover .button-row .add_node {
|
|
1200
|
+
margin: 0 !important;
|
|
1201
|
+
padding: 6px 14px !important;
|
|
1202
|
+
font-size: 0.875rem !important;
|
|
1203
|
+
border-radius: 6px !important;
|
|
1204
|
+
}
|
|
1205
|
+
.partition-popover .remove-link {
|
|
1206
|
+
background: none !important;
|
|
1207
|
+
border: none;
|
|
1208
|
+
color: #b91c1c;
|
|
1209
|
+
font-size: 0.8125rem;
|
|
1210
|
+
font-weight: 500;
|
|
1211
|
+
cursor: pointer;
|
|
1212
|
+
padding: 4px 6px !important;
|
|
1213
|
+
text-transform: none;
|
|
1214
|
+
border-radius: 4px;
|
|
1215
|
+
}
|
|
1216
|
+
.partition-popover .remove-link:hover {
|
|
1217
|
+
background-color: #fef2f2 !important;
|
|
1218
|
+
color: #991b1b;
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1099
1221
|
.add_node {
|
|
1100
1222
|
background-color: #5d2f86 !important;
|
|
1101
1223
|
color: #fff;
|