datajunction-ui 0.0.1-rc.18 → 0.0.1-rc.19

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.
@@ -13,6 +13,54 @@ exports[`<NamespacePage /> should render and match the snapshot 1`] = `
13
13
  <h2>
14
14
  Explore
15
15
  </h2>
16
+ <span
17
+ className="menu-link"
18
+ >
19
+ <span
20
+ className="menu-title"
21
+ >
22
+ <div
23
+ className="dropdown"
24
+ >
25
+ <span
26
+ className="add_node"
27
+ >
28
+ + Add Node
29
+ </span>
30
+ <div
31
+ className="dropdown-content"
32
+ >
33
+ <a
34
+ href="/create/transform/undefined"
35
+ >
36
+ <div
37
+ className="node_type__transform node_type_creation_heading"
38
+ >
39
+ Transform
40
+ </div>
41
+ </a>
42
+ <a
43
+ href="/create/metric/undefined"
44
+ >
45
+ <div
46
+ className="node_type__metric node_type_creation_heading"
47
+ >
48
+ Metric
49
+ </div>
50
+ </a>
51
+ <a
52
+ href="/create/dimension/undefined"
53
+ >
54
+ <div
55
+ className="node_type__dimension node_type_creation_heading"
56
+ >
57
+ Dimension
58
+ </div>
59
+ </a>
60
+ </div>
61
+ </div>
62
+ </span>
63
+ </span>
16
64
  <div
17
65
  className="table-responsive"
18
66
  >
@@ -56,6 +104,9 @@ exports[`<NamespacePage /> should render and match the snapshot 1`] = `
56
104
  <th>
57
105
  Last Updated
58
106
  </th>
107
+ <th>
108
+ Actions
109
+ </th>
59
110
  </tr>
60
111
  </thead>
61
112
  <tbody />
@@ -4,6 +4,8 @@ import { useContext, useEffect, useState } from 'react';
4
4
  import NodeStatus from '../NodePage/NodeStatus';
5
5
  import DJClientContext from '../../providers/djclient';
6
6
  import Explorer from '../NamespacePage/Explorer';
7
+ import EditIcon from '../../icons/EditIcon';
8
+ import DeleteIcon from '../../icons/DeleteIcon';
7
9
 
8
10
  export function NamespacePage() {
9
11
  const djClient = useContext(DJClientContext).DataJunctionAPI;
@@ -102,6 +104,14 @@ export function NamespacePage() {
102
104
  {new Date(node.updated_at).toLocaleString('en-us')}
103
105
  </span>
104
106
  </td>
107
+ <td>
108
+ <a href={`/nodes/${node?.name}/edit`} style={{ marginLeft: '0.5rem' }}>
109
+ <EditIcon />
110
+ </a>
111
+ <a href="#" style={{ marginLeft: '0.5rem' }}>
112
+ <DeleteIcon />
113
+ </a>
114
+ </td>
105
115
  </tr>
106
116
  ));
107
117
 
@@ -110,6 +120,31 @@ export function NamespacePage() {
110
120
  <div className="card">
111
121
  <div className="card-header">
112
122
  <h2>Explore</h2>
123
+
124
+ <span className="menu-link">
125
+ <span className="menu-title">
126
+ <div className="dropdown">
127
+ <span className="add_node">+ Add Node</span>
128
+ <div className="dropdown-content">
129
+ <a href={`/create/transform/${namespace}`}>
130
+ <div className="node_type__transform node_type_creation_heading">
131
+ Transform
132
+ </div>
133
+ </a>
134
+ <a href={`/create/metric/${namespace}`}>
135
+ <div className="node_type__metric node_type_creation_heading">
136
+ Metric
137
+ </div>
138
+ </a>
139
+ <a href={`/create/dimension/${namespace}`}>
140
+ <div className="node_type__dimension node_type_creation_heading">
141
+ Dimension
142
+ </div>
143
+ </a>
144
+ </div>
145
+ </div>
146
+ </span>
147
+ </span>
113
148
  <div className="table-responsive">
114
149
  <div className={`sidebar`}>
115
150
  <span
@@ -142,6 +177,7 @@ export function NamespacePage() {
142
177
  <th>Status</th>
143
178
  <th>Mode</th>
144
179
  <th>Last Updated</th>
180
+ <th>Actions</th>
145
181
  </tr>
146
182
  </thead>
147
183
  <tbody>{nodesList}</tbody>
@@ -15,7 +15,6 @@ export default function NodeHistory({ node, djClient }) {
15
15
  }, [djClient, node]);
16
16
 
17
17
  const eventData = event => {
18
- console.log('event', event);
19
18
  if (
20
19
  event.activity_type === 'set_attribute' &&
21
20
  event.entity_type === 'column_attribute'
@@ -13,6 +13,8 @@ import NodeMaterializationTab from './NodeMaterializationTab';
13
13
  import ClientCodePopover from './ClientCodePopover';
14
14
  import NodesWithDimension from './NodesWithDimension';
15
15
  import NodeColumnLineage from './NodeLineageTab';
16
+ import EditIcon from '../../icons/EditIcon';
17
+ import AlertIcon from '../../icons/AlertIcon';
16
18
 
17
19
  export function NodePage() {
18
20
  const djClient = useContext(DJClientContext).DataJunctionAPI;
@@ -109,7 +111,8 @@ export function NodePage() {
109
111
  let tabToDisplay = null;
110
112
  switch (state.selectedTab) {
111
113
  case 0:
112
- tabToDisplay = node ? <NodeInfoTab node={node} /> : '';
114
+ tabToDisplay =
115
+ node && node.message === undefined ? <NodeInfoTab node={node} /> : '';
113
116
  break;
114
117
  case 1:
115
118
  tabToDisplay = <NodeColumnTab node={node} djClient={djClient} />;
@@ -136,41 +139,55 @@ export function NodePage() {
136
139
  default:
137
140
  tabToDisplay = <NodeInfoTab node={node} />;
138
141
  }
139
-
140
142
  // @ts-ignore
141
143
  return (
142
144
  <div className="node__header">
143
145
  <NamespaceHeader namespace={name.split('.').slice(0, -1).join('.')} />
144
146
  <div className="card">
145
- <div className="card-header">
146
- <h3
147
- className="card-title align-items-start flex-column"
148
- style={{ display: 'inline-block' }}
149
- >
150
- <span className="card-label fw-bold text-gray-800">
151
- {node?.display_name}{' '}
152
- <span className={'node_type__' + node?.type + ' badge node_type'}>
153
- {node?.type}
147
+ {node !== undefined && node.message === undefined ? (
148
+ <div className="card-header">
149
+ <h3
150
+ className="card-title align-items-start flex-column"
151
+ style={{ display: 'inline-block' }}
152
+ >
153
+ <span className="card-label fw-bold text-gray-800">
154
+ {node?.display_name}{' '}
155
+ <span
156
+ className={'node_type__' + node?.type + ' badge node_type'}
157
+ >
158
+ {node?.type}
159
+ </span>
154
160
  </span>
155
- </span>
156
- </h3>
157
- <ClientCodePopover code={node?.createNodeClientCode} />
158
- <div>
159
- <a href={'/nodes/' + node?.name} className="link-table">
160
- {node?.name}
161
- </a>
162
- <span
163
- className="rounded-pill badge bg-secondary-soft"
161
+ </h3>
162
+ <a
163
+ href={`/nodes/${node?.name}/edit`}
164
164
  style={{ marginLeft: '0.5rem' }}
165
165
  >
166
- {node?.version}
167
- </span>
166
+ <EditIcon />
167
+ </a>
168
+ <ClientCodePopover code={node?.createNodeClientCode} />
169
+ <div>
170
+ <a href={'/nodes/' + node?.name} className="link-table">
171
+ {node?.name}
172
+ </a>
173
+ <span
174
+ className="rounded-pill badge bg-secondary-soft"
175
+ style={{ marginLeft: '0.5rem' }}
176
+ >
177
+ {node?.version}
178
+ </span>
179
+ </div>
180
+ <div className="align-items-center row">
181
+ {tabsList(node).map(buildTabs)}
182
+ </div>
183
+ {tabToDisplay}
168
184
  </div>
169
- <div className="align-items-center row">
170
- {tabsList(node).map(buildTabs)}
185
+ ) : (
186
+ <div className="message alert" style={{ margin: '20px' }}>
187
+ <AlertIcon />
188
+ Node `{name}` does not exist!
171
189
  </div>
172
- {tabToDisplay}
173
- </div>
190
+ )}
174
191
  </div>
175
192
  </div>
176
193
  );
@@ -1,6 +1,6 @@
1
1
  import { useContext } from 'react';
2
2
  import { Outlet } from 'react-router-dom';
3
- import logo from './assets/dj-logo.png';
3
+ import DJLogo from '../../icons/DJLogo';
4
4
  import { Helmet } from 'react-helmet-async';
5
5
  import DJClientContext from '../../providers/djclient';
6
6
 
@@ -24,8 +24,8 @@ export function Root() {
24
24
  <div className="header">
25
25
  <div className="logo">
26
26
  <h2>
27
- <img src={logo} alt="DJ Logo" width="15%" />
28
- DataJunction
27
+ <DJLogo />
28
+ Data<b>Junction</b>
29
29
  </h2>
30
30
  </div>
31
31
  <div className="menu">
@@ -25,6 +25,9 @@ export const DataJunctionAPI = {
25
25
  credentials: 'include',
26
26
  })
27
27
  ).json();
28
+ if (data.message !== undefined) {
29
+ return data;
30
+ }
28
31
  data.primary_key = data.columns
29
32
  .filter(col =>
30
33
  col.attributes.some(attr => attr.attribute_type.name === 'primary_key'),
@@ -33,6 +36,72 @@ export const DataJunctionAPI = {
33
36
  return data;
34
37
  },
35
38
 
39
+ nodes: async function (prefix) {
40
+ return await (
41
+ await fetch(`${DJ_URL}/nodes/?prefix=${prefix}`, {
42
+ credentials: 'include',
43
+ })
44
+ ).json();
45
+ },
46
+
47
+ createNode: async function (
48
+ nodeType,
49
+ name,
50
+ display_name,
51
+ description,
52
+ query,
53
+ mode,
54
+ namespace,
55
+ primary_key,
56
+ ) {
57
+ const response = await fetch(`${DJ_URL}/nodes/${nodeType}`, {
58
+ method: 'POST',
59
+ headers: {
60
+ 'Content-Type': 'application/json',
61
+ },
62
+ body: JSON.stringify({
63
+ name: name,
64
+ display_name: display_name,
65
+ description: description,
66
+ query: query,
67
+ mode: mode,
68
+ namespace: namespace,
69
+ primary_key: primary_key,
70
+ }),
71
+ credentials: 'include',
72
+ });
73
+ return { status: response.status, json: await response.json() };
74
+ },
75
+
76
+ patchNode: async function (
77
+ name,
78
+ display_name,
79
+ description,
80
+ query,
81
+ mode,
82
+ primary_key,
83
+ ) {
84
+ try {
85
+ const response = await fetch(`${DJ_URL}/nodes/${name}`, {
86
+ method: 'PATCH',
87
+ headers: {
88
+ 'Content-Type': 'application/json',
89
+ },
90
+ body: JSON.stringify({
91
+ display_name: display_name,
92
+ description: description,
93
+ query: query,
94
+ mode: mode,
95
+ primary_key: primary_key,
96
+ }),
97
+ credentials: 'include',
98
+ });
99
+ return { status: response.status, json: await response.json() };
100
+ } catch (error) {
101
+ return { status: 500, json: { message: 'Update failed' } };
102
+ }
103
+ },
104
+
36
105
  upstreams: async function (name) {
37
106
  const data = await (
38
107
  await fetch(`${DJ_URL}/nodes/${name}/upstream/`, {
@@ -131,7 +200,9 @@ export const DataJunctionAPI = {
131
200
 
132
201
  revisions: async function (name) {
133
202
  const data = await (
134
- await fetch(`${DJ_URL}/nodes/` + name + '/revisions/')
203
+ await fetch(`${DJ_URL}/nodes/` + name + '/revisions/', {
204
+ credentials: 'include',
205
+ })
135
206
  ).json();
136
207
  return data;
137
208
  },
package/src/setupTests.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  // react-testing-library renders your components to document.body,
2
2
  // this adds jest-dom's custom assertions
3
- import '@testing-library/jest-dom/extend-expect';
3
+ import '@testing-library/jest-dom';
4
4
 
5
5
  import 'react-app-polyfill/ie11';
6
6
  import 'react-app-polyfill/stable';
@@ -1,8 +1,12 @@
1
+ @import url('https://fonts.googleapis.com/css?family=Jost');
2
+ @import url('https://fonts.googleapis.com/css2?family=Raleway:wght@300;600&display=swap');
3
+ @import url('https://fonts.googleapis.com/css?family=Lato');
4
+
1
5
  body {
2
6
  margin: 0;
3
- font-family: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue',
4
- 'Noto Sans', 'Liberation Sans', Arial, sans-serif, 'Apple Color Emoji',
5
- 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
7
+ font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell,
8
+ Noto Sans, sans-serif, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial,
9
+ sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol';
6
10
  -webkit-font-smoothing: antialiased;
7
11
  -moz-osx-font-smoothing: grayscale;
8
12
  }
@@ -14,6 +18,13 @@ body {
14
18
  div {
15
19
  display: block;
16
20
  }
21
+ h2 {
22
+ font-family: 'Raleway', system-ui;
23
+ font-weight: 300;
24
+ }
25
+ h2 b {
26
+ font-weight: 600;
27
+ }
17
28
  p {
18
29
  display: block;
19
30
  margin-block-start: 1em;
@@ -26,7 +37,7 @@ a,
26
37
  a:hover {
27
38
  text-decoration: none;
28
39
  color: #005c72;
29
- font-family: 'Cerebri Sans', sans-serif;
40
+ font-family: 'Montserrat', sans-serif;
30
41
  }
31
42
  a:hover {
32
43
  text-decoration: underline;
@@ -153,6 +164,7 @@ ul {
153
164
  .h6,
154
165
  h6 {
155
166
  margin: 0;
167
+ font-family: 'Jost';
156
168
  text-transform: uppercase;
157
169
  font-size: 0.8125rem;
158
170
  font-weight: 600;
@@ -251,6 +263,7 @@ tr {
251
263
  background-clip: border-box;
252
264
  border-radius: 1rem;
253
265
  margin: 2rem;
266
+ padding-bottom: 2rem;
254
267
  }
255
268
 
256
269
  .card-light-shadow {
@@ -313,6 +326,7 @@ tr {
313
326
  font-weight: 600;
314
327
  letter-spacing: 0.08em;
315
328
  color: #95aac9;
329
+ font-family: 'Jost';
316
330
  }
317
331
 
318
332
  .card-table thead th {
@@ -325,7 +339,7 @@ tr {
325
339
  td,
326
340
  tbody th {
327
341
  vertical-align: middle;
328
- text-align: center;
342
+ text-align: left;
329
343
  }
330
344
  .table [data-sort],
331
345
  .table-nowrap td,
@@ -369,6 +383,10 @@ tbody th {
369
383
  border-bottom-width: 1px;
370
384
  box-shadow: inset 0 0 0 9999px transparent;
371
385
  }
386
+ .node_type_creation_heading {
387
+ text-transform: capitalize;
388
+ padding: 5px;
389
+ }
372
390
 
373
391
  /* Nodes */
374
392
  .node_type {
@@ -384,6 +402,7 @@ tbody th {
384
402
 
385
403
  /* Nodes */
386
404
  .node_type__source {
405
+ font-family: 'Jost';
387
406
  background-color: #ccf7e5 !important;
388
407
  color: #00b368;
389
408
  }
@@ -393,15 +412,21 @@ tbody th {
393
412
  }
394
413
 
395
414
  .node_type__transform {
415
+ font-family: 'Jost';
396
416
  background-color: #ccefff !important;
397
417
  color: #0063b4;
398
418
  }
419
+
420
+ .node_type__transform:hover {
421
+ background-color: #ccefff !important;
422
+ }
399
423
  .react-flow__node .node_type__transform {
400
424
  background-color: #6daaa750 !important;
401
425
  color: #6daaa7;
402
426
  }
403
427
 
404
428
  .node_type__metric {
429
+ font-family: 'Jost';
405
430
  background-color: #fad7dd !important;
406
431
  color: #a2283e;
407
432
  }
@@ -411,6 +436,7 @@ tbody th {
411
436
  }
412
437
 
413
438
  .node_type__dimension {
439
+ font-family: 'Jost';
414
440
  background-color: #ffefd0 !important;
415
441
  color: #a96621;
416
442
  }
@@ -420,6 +446,7 @@ tbody th {
420
446
  }
421
447
 
422
448
  .node_type__cube {
449
+ font-family: 'Jost';
423
450
  background-color: #dbafff !important;
424
451
  color: #580076;
425
452
  }
@@ -710,6 +737,10 @@ pre {
710
737
  padding: 0 2.25rem 0 2.25rem;
711
738
  }
712
739
 
740
+ .card-header h2 {
741
+ font-family: 'Jost';
742
+ }
743
+
713
744
  .text-gray-400 {
714
745
  color: #b5b5c3 !important;
715
746
  }
@@ -873,3 +904,49 @@ pre {
873
904
  .inactive {
874
905
  display: none;
875
906
  }
907
+
908
+ .dropdown {
909
+ position: relative;
910
+ display: inline-block;
911
+ float: right;
912
+ }
913
+
914
+ .dropdown-content {
915
+ display: none;
916
+ position: absolute;
917
+ margin-top: 6px;
918
+ box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
919
+ min-width: 115px;
920
+ }
921
+
922
+ .dropdown:hover .dropdown-content {
923
+ display: block;
924
+ }
925
+
926
+ .dropdown-content a:hover {
927
+ text-decoration: none;
928
+ }
929
+
930
+ .dropdown-content a div {
931
+ padding: 12px 16px;
932
+ background-color: #ffffff !important;
933
+ }
934
+
935
+ .dropdown-content a div:hover {
936
+ background-color: #f3eeff !important;
937
+ }
938
+
939
+ .add_node {
940
+ background-color: #5d2f86 !important;
941
+ color: #fff;
942
+ text-transform: none;
943
+ vertical-align: middle;
944
+ padding-right: 1rem;
945
+ padding-left: 1rem;
946
+ padding-top: 0.5rem;
947
+ padding-bottom: 0.5rem;
948
+ font-size: 1rem;
949
+ border-radius: 0.5rem;
950
+ word-wrap: break-word;
951
+ white-space: break-spaces;
952
+ }