datajunction-ui 0.0.1-rc.24 → 0.0.1-rc.25
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/.env +1 -0
- package/package.json +3 -2
- package/src/app/components/Tab.jsx +0 -1
- package/src/app/constants.js +2 -2
- package/src/app/icons/LoadingIcon.jsx +14 -0
- package/src/app/index.tsx +11 -1
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +28 -2
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +44 -9
- package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormFailed.test.jsx.snap +1 -0
- package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormSuccess.test.jsx.snap +0 -50
- package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +2 -0
- package/src/app/pages/AddEditNodePage/index.jsx +60 -6
- package/src/app/pages/AddEditTagPage/Loadable.jsx +16 -0
- package/src/app/pages/AddEditTagPage/__tests__/AddEditTagPage.test.jsx +107 -0
- package/src/app/pages/AddEditTagPage/index.jsx +132 -0
- package/src/app/pages/LoginPage/LoginForm.jsx +116 -0
- package/src/app/pages/LoginPage/SignupForm.jsx +144 -0
- package/src/app/pages/LoginPage/__tests__/index.test.jsx +34 -2
- package/src/app/pages/LoginPage/index.jsx +9 -82
- package/src/app/pages/NamespacePage/index.jsx +5 -0
- package/src/app/pages/NodePage/EditColumnPopover.jsx +1 -1
- package/src/app/pages/NodePage/NodeColumnTab.jsx +11 -0
- package/src/app/pages/NodePage/NodeInfoTab.jsx +5 -1
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +6 -3
- package/src/app/pages/RegisterTablePage/__tests__/__snapshots__/RegisterTablePage.test.jsx.snap +1 -0
- package/src/app/pages/Root/index.tsx +1 -1
- package/src/app/pages/TagPage/Loadable.jsx +16 -0
- package/src/app/pages/TagPage/__tests__/TagPage.test.jsx +70 -0
- package/src/app/pages/TagPage/index.jsx +79 -0
- package/src/app/services/DJService.js +79 -1
- package/src/app/services/__tests__/DJService.test.jsx +84 -1
- package/src/mocks/mockNodes.jsx +88 -44
- package/src/styles/index.css +19 -0
- package/src/styles/loading.css +35 -0
- package/src/styles/login.css +17 -3
- package/src/utils/form.jsx +2 -2
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add or edit tags
|
|
3
|
+
*/
|
|
4
|
+
import { ErrorMessage, Field, Form, Formik } from 'formik';
|
|
5
|
+
|
|
6
|
+
import NamespaceHeader from '../../components/NamespaceHeader';
|
|
7
|
+
import React, { useContext } from 'react';
|
|
8
|
+
import DJClientContext from '../../providers/djclient';
|
|
9
|
+
import 'styles/node-creation.scss';
|
|
10
|
+
import { displayMessageAfterSubmit } from '../../../utils/form';
|
|
11
|
+
|
|
12
|
+
export function AddEditTagPage() {
|
|
13
|
+
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
14
|
+
const initialValues = {
|
|
15
|
+
name: '',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
const validator = values => {
|
|
19
|
+
const errors = {};
|
|
20
|
+
if (!values.name) {
|
|
21
|
+
errors.name = 'Required';
|
|
22
|
+
}
|
|
23
|
+
return errors;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const handleSubmit = async (values, { setSubmitting, setStatus }) => {
|
|
27
|
+
const { status, json } = await djClient.addTag(
|
|
28
|
+
values.name,
|
|
29
|
+
values.display_name,
|
|
30
|
+
values.tag_type,
|
|
31
|
+
values.description,
|
|
32
|
+
);
|
|
33
|
+
if (status === 200 || status === 201) {
|
|
34
|
+
setStatus({
|
|
35
|
+
success: (
|
|
36
|
+
<>
|
|
37
|
+
Successfully added tag{' '}
|
|
38
|
+
<a href={`/tags/${json.name}`}>{json.display_name}</a>.
|
|
39
|
+
</>
|
|
40
|
+
),
|
|
41
|
+
});
|
|
42
|
+
} else {
|
|
43
|
+
setStatus({
|
|
44
|
+
failure: `${json.message}`,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
setSubmitting(false);
|
|
48
|
+
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' });
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<div className="mid">
|
|
53
|
+
<NamespaceHeader namespace="" />
|
|
54
|
+
<div className="card">
|
|
55
|
+
<div className="card-header">
|
|
56
|
+
<h2>
|
|
57
|
+
Add{' '}
|
|
58
|
+
<span className={`node_type__source node_type_creation_heading`}>
|
|
59
|
+
Tag
|
|
60
|
+
</span>
|
|
61
|
+
</h2>
|
|
62
|
+
<center>
|
|
63
|
+
<Formik
|
|
64
|
+
initialValues={initialValues}
|
|
65
|
+
validate={validator}
|
|
66
|
+
onSubmit={handleSubmit}
|
|
67
|
+
>
|
|
68
|
+
{function Render({ isSubmitting, status }) {
|
|
69
|
+
return (
|
|
70
|
+
<Form>
|
|
71
|
+
{displayMessageAfterSubmit(status)}
|
|
72
|
+
{
|
|
73
|
+
<>
|
|
74
|
+
<div className="NodeCreationInput">
|
|
75
|
+
<ErrorMessage name="name" component="span" />
|
|
76
|
+
<label htmlFor="name">Name</label>
|
|
77
|
+
<Field
|
|
78
|
+
type="text"
|
|
79
|
+
name="name"
|
|
80
|
+
id="name"
|
|
81
|
+
placeholder="Tag Name"
|
|
82
|
+
/>
|
|
83
|
+
</div>
|
|
84
|
+
<br />
|
|
85
|
+
<div className="FullNameInput NodeCreationInput">
|
|
86
|
+
<ErrorMessage name="display_name" component="span" />
|
|
87
|
+
<label htmlFor="display_name">Display Name</label>
|
|
88
|
+
<Field
|
|
89
|
+
type="text"
|
|
90
|
+
name="display_name"
|
|
91
|
+
id="display_name"
|
|
92
|
+
placeholder="Display Name"
|
|
93
|
+
class="FullNameField"
|
|
94
|
+
/>
|
|
95
|
+
</div>
|
|
96
|
+
<br />
|
|
97
|
+
<div className="NodeCreationInput">
|
|
98
|
+
<ErrorMessage name="tag_type" component="span" />
|
|
99
|
+
<label htmlFor="tag_type">Tag Type</label>
|
|
100
|
+
<Field
|
|
101
|
+
type="text"
|
|
102
|
+
name="tag_type"
|
|
103
|
+
id="tag_type"
|
|
104
|
+
placeholder="Tag Type"
|
|
105
|
+
/>
|
|
106
|
+
</div>
|
|
107
|
+
<div className="DescriptionInput NodeCreationInput">
|
|
108
|
+
<ErrorMessage name="description" component="span" />
|
|
109
|
+
<label htmlFor="description">Description</label>
|
|
110
|
+
<Field
|
|
111
|
+
type="textarea"
|
|
112
|
+
as="textarea"
|
|
113
|
+
name="description"
|
|
114
|
+
id="Description"
|
|
115
|
+
placeholder="Describe the tag"
|
|
116
|
+
/>
|
|
117
|
+
</div>
|
|
118
|
+
<button type="submit" disabled={isSubmitting}>
|
|
119
|
+
Add Tag
|
|
120
|
+
</button>
|
|
121
|
+
</>
|
|
122
|
+
}
|
|
123
|
+
</Form>
|
|
124
|
+
);
|
|
125
|
+
}}
|
|
126
|
+
</Formik>
|
|
127
|
+
</center>
|
|
128
|
+
</div>
|
|
129
|
+
</div>
|
|
130
|
+
</div>
|
|
131
|
+
);
|
|
132
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Formik, Form, Field, ErrorMessage } from 'formik';
|
|
3
|
+
import '../../../styles/login.css';
|
|
4
|
+
import LoadingIcon from '../../icons/LoadingIcon';
|
|
5
|
+
import logo from '../Root/assets/dj-logo.png';
|
|
6
|
+
import GitHubLoginButton from './assets/sign-in-with-github.png';
|
|
7
|
+
import GoogleLoginButton from './assets/sign-in-with-google.png';
|
|
8
|
+
import * as Yup from 'yup';
|
|
9
|
+
|
|
10
|
+
const githubLoginURL = new URL('/github/login/', process.env.REACT_APP_DJ_URL);
|
|
11
|
+
const googleLoginURL = new URL('/google/login/', process.env.REACT_APP_DJ_URL);
|
|
12
|
+
|
|
13
|
+
const LoginSchema = Yup.object().shape({
|
|
14
|
+
username: Yup.string()
|
|
15
|
+
.min(2, 'Must be at least 2 characters')
|
|
16
|
+
.required('Username is required'),
|
|
17
|
+
password: Yup.string().required('Password is required'),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export default function LoginForm({ setShowSignup }) {
|
|
21
|
+
const [, setError] = useState('');
|
|
22
|
+
|
|
23
|
+
// Add the path that the user was trying to access in order to properly redirect after auth
|
|
24
|
+
githubLoginURL.searchParams.append('target', window.location.pathname);
|
|
25
|
+
googleLoginURL.searchParams.append('target', window.location.pathname);
|
|
26
|
+
|
|
27
|
+
const handleBasicLogin = async ({ username, password }) => {
|
|
28
|
+
const data = new FormData();
|
|
29
|
+
data.append('username', username);
|
|
30
|
+
data.append('password', password);
|
|
31
|
+
await fetch(`${process.env.REACT_APP_DJ_URL}/basic/login/`, {
|
|
32
|
+
method: 'POST',
|
|
33
|
+
body: data,
|
|
34
|
+
credentials: 'include',
|
|
35
|
+
}).catch(error => {
|
|
36
|
+
setError(error ? JSON.stringify(error) : '');
|
|
37
|
+
});
|
|
38
|
+
window.location.reload();
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
return (
|
|
42
|
+
<Formik
|
|
43
|
+
initialValues={{
|
|
44
|
+
username: '',
|
|
45
|
+
password: '',
|
|
46
|
+
target: window.location.pathname,
|
|
47
|
+
}}
|
|
48
|
+
validationSchema={LoginSchema}
|
|
49
|
+
onSubmit={(values, { setSubmitting }) => {
|
|
50
|
+
setTimeout(() => {
|
|
51
|
+
handleBasicLogin(values);
|
|
52
|
+
setSubmitting(false);
|
|
53
|
+
}, 400);
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
{({ isSubmitting }) => (
|
|
57
|
+
<Form>
|
|
58
|
+
<div className="logo-title">
|
|
59
|
+
<img src={logo} alt="DJ Logo" width="75px" height="75px" />
|
|
60
|
+
<h2>DataJunction</h2>
|
|
61
|
+
</div>
|
|
62
|
+
<div>
|
|
63
|
+
<Field type="text" name="username" placeholder="Username" />
|
|
64
|
+
</div>
|
|
65
|
+
<div>
|
|
66
|
+
<ErrorMessage className="form-error" name="username" component="span" />
|
|
67
|
+
</div>
|
|
68
|
+
<div>
|
|
69
|
+
<Field type="password" name="password" placeholder="Password" />
|
|
70
|
+
</div>
|
|
71
|
+
<div>
|
|
72
|
+
<ErrorMessage className="form-error" name="password" component="span" />
|
|
73
|
+
</div>
|
|
74
|
+
<div>
|
|
75
|
+
<p>
|
|
76
|
+
Don't have an account yet?{' '}
|
|
77
|
+
<a onClick={() => setShowSignup(true)}>Sign Up</a>
|
|
78
|
+
</p>
|
|
79
|
+
</div>
|
|
80
|
+
<button type="submit" disabled={isSubmitting}>
|
|
81
|
+
{isSubmitting ? <LoadingIcon /> : 'Login'}
|
|
82
|
+
</button>
|
|
83
|
+
<div>
|
|
84
|
+
<p>Or</p>
|
|
85
|
+
</div>
|
|
86
|
+
{process.env.REACT_ENABLE_GITHUB_OAUTH === 'true' ? (
|
|
87
|
+
<div>
|
|
88
|
+
<a href={githubLoginURL.href}>
|
|
89
|
+
<img
|
|
90
|
+
src={GitHubLoginButton}
|
|
91
|
+
alt="Sign in with GitHub"
|
|
92
|
+
width="200px"
|
|
93
|
+
/>
|
|
94
|
+
</a>
|
|
95
|
+
</div>
|
|
96
|
+
) : (
|
|
97
|
+
''
|
|
98
|
+
)}
|
|
99
|
+
{process.env.REACT_ENABLE_GOOGLE_OAUTH === 'true' ? (
|
|
100
|
+
<div>
|
|
101
|
+
<a href={googleLoginURL.href}>
|
|
102
|
+
<img
|
|
103
|
+
src={GoogleLoginButton}
|
|
104
|
+
alt="Sign in with Google"
|
|
105
|
+
width="200px"
|
|
106
|
+
/>
|
|
107
|
+
</a>
|
|
108
|
+
</div>
|
|
109
|
+
) : (
|
|
110
|
+
''
|
|
111
|
+
)}
|
|
112
|
+
</Form>
|
|
113
|
+
)}
|
|
114
|
+
</Formik>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { useState } from 'react';
|
|
2
|
+
import { Formik, Form, Field, ErrorMessage } from 'formik';
|
|
3
|
+
import '../../../styles/login.css';
|
|
4
|
+
import logo from '../Root/assets/dj-logo.png';
|
|
5
|
+
import LoadingIcon from '../../icons/LoadingIcon';
|
|
6
|
+
import GitHubLoginButton from './assets/sign-in-with-github.png';
|
|
7
|
+
import GoogleLoginButton from './assets/sign-in-with-google.png';
|
|
8
|
+
import * as Yup from 'yup';
|
|
9
|
+
|
|
10
|
+
const githubLoginURL = new URL('/github/login/', process.env.REACT_APP_DJ_URL);
|
|
11
|
+
const googleLoginURL = new URL('/google/login/', process.env.REACT_APP_DJ_URL);
|
|
12
|
+
|
|
13
|
+
const SignupSchema = Yup.object().shape({
|
|
14
|
+
email: Yup.string().email('Invalid email').required('Email is required'),
|
|
15
|
+
signupUsername: Yup.string()
|
|
16
|
+
.min(3, 'Must be at least 2 characters')
|
|
17
|
+
.max(20, 'Must be less than 20 characters')
|
|
18
|
+
.required('Username is required'),
|
|
19
|
+
signupPassword: Yup.string().required('Password is required'),
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
export default function SignupForm({ setShowSignup }) {
|
|
23
|
+
const [, setError] = useState('');
|
|
24
|
+
|
|
25
|
+
// Add the path that the user was trying to access in order to properly redirect after auth
|
|
26
|
+
githubLoginURL.searchParams.append('target', window.location.pathname);
|
|
27
|
+
googleLoginURL.searchParams.append('target', window.location.pathname);
|
|
28
|
+
|
|
29
|
+
const handleBasicSignup = async ({
|
|
30
|
+
email,
|
|
31
|
+
signupUsername,
|
|
32
|
+
signupPassword,
|
|
33
|
+
}) => {
|
|
34
|
+
const data = new FormData();
|
|
35
|
+
data.append('email', email);
|
|
36
|
+
data.append('username', signupUsername);
|
|
37
|
+
data.append('password', signupPassword);
|
|
38
|
+
await fetch(`${process.env.REACT_APP_DJ_URL}/basic/user/`, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
body: data,
|
|
41
|
+
credentials: 'include',
|
|
42
|
+
}).catch(error => {
|
|
43
|
+
setError(error ? JSON.stringify(error) : '');
|
|
44
|
+
});
|
|
45
|
+
const loginData = new FormData();
|
|
46
|
+
loginData.append('username', signupUsername);
|
|
47
|
+
loginData.append('password', signupPassword);
|
|
48
|
+
await fetch(`${process.env.REACT_APP_DJ_URL}/basic/login/`, {
|
|
49
|
+
method: 'POST',
|
|
50
|
+
body: data,
|
|
51
|
+
credentials: 'include',
|
|
52
|
+
}).catch(error => {
|
|
53
|
+
setError(error ? JSON.stringify(error) : '');
|
|
54
|
+
});
|
|
55
|
+
window.location.reload();
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
return (
|
|
59
|
+
<Formik
|
|
60
|
+
initialValues={{
|
|
61
|
+
email: '',
|
|
62
|
+
signupUsername: '',
|
|
63
|
+
signupPassword: '',
|
|
64
|
+
target: window.location.pathname,
|
|
65
|
+
}}
|
|
66
|
+
validationSchema={SignupSchema}
|
|
67
|
+
onSubmit={(values, { setSubmitting }) => {
|
|
68
|
+
setTimeout(() => {
|
|
69
|
+
handleBasicSignup(values);
|
|
70
|
+
setSubmitting(false);
|
|
71
|
+
}, 400);
|
|
72
|
+
}}
|
|
73
|
+
>
|
|
74
|
+
{({ isSubmitting }) => (
|
|
75
|
+
<Form>
|
|
76
|
+
<div className="logo-title">
|
|
77
|
+
<img src={logo} alt="DJ Logo" width="75px" height="75px" />
|
|
78
|
+
<h2>DataJunction</h2>
|
|
79
|
+
</div>
|
|
80
|
+
<div>
|
|
81
|
+
<Field type="text" name="email" placeholder="Email" />
|
|
82
|
+
</div>
|
|
83
|
+
<div>
|
|
84
|
+
<ErrorMessage className="form-error" name="email" component="span" />
|
|
85
|
+
</div>
|
|
86
|
+
<div>
|
|
87
|
+
<Field type="text" name="signupUsername" placeholder="Username" />
|
|
88
|
+
</div>
|
|
89
|
+
<div>
|
|
90
|
+
<ErrorMessage className="form-error" name="signupUsername" component="span" />
|
|
91
|
+
</div>
|
|
92
|
+
<div>
|
|
93
|
+
<Field
|
|
94
|
+
type="password"
|
|
95
|
+
name="signupPassword"
|
|
96
|
+
placeholder="Password"
|
|
97
|
+
/>
|
|
98
|
+
</div>
|
|
99
|
+
<div>
|
|
100
|
+
<ErrorMessage className="form-error" name="signupPassword" component="span" />
|
|
101
|
+
</div>
|
|
102
|
+
<div>
|
|
103
|
+
<p>
|
|
104
|
+
Have an account already?{' '}
|
|
105
|
+
<a onClick={() => setShowSignup(false)}>Login</a>
|
|
106
|
+
</p>
|
|
107
|
+
</div>
|
|
108
|
+
<button type="submit" disabled={isSubmitting}>
|
|
109
|
+
{isSubmitting ? <LoadingIcon /> : 'Sign Up'}
|
|
110
|
+
</button>
|
|
111
|
+
<div>
|
|
112
|
+
<p>Or</p>
|
|
113
|
+
</div>
|
|
114
|
+
{process.env.REACT_ENABLE_GITHUB_OAUTH === 'true' ? (
|
|
115
|
+
<div>
|
|
116
|
+
<a href={githubLoginURL.href}>
|
|
117
|
+
<img
|
|
118
|
+
src={GitHubLoginButton}
|
|
119
|
+
alt="Sign in with GitHub"
|
|
120
|
+
width="200px"
|
|
121
|
+
/>
|
|
122
|
+
</a>
|
|
123
|
+
</div>
|
|
124
|
+
) : (
|
|
125
|
+
''
|
|
126
|
+
)}
|
|
127
|
+
{process.env.REACT_ENABLE_GOOGLE_OAUTH === 'true' ? (
|
|
128
|
+
<div>
|
|
129
|
+
<a href={googleLoginURL.href}>
|
|
130
|
+
<img
|
|
131
|
+
src={GoogleLoginButton}
|
|
132
|
+
alt="Sign in with Google"
|
|
133
|
+
width="200px"
|
|
134
|
+
/>
|
|
135
|
+
</a>
|
|
136
|
+
</div>
|
|
137
|
+
) : (
|
|
138
|
+
''
|
|
139
|
+
)}
|
|
140
|
+
</Form>
|
|
141
|
+
)}
|
|
142
|
+
</Formik>
|
|
143
|
+
);
|
|
144
|
+
}
|
|
@@ -33,11 +33,12 @@ describe('LoginPage', () => {
|
|
|
33
33
|
|
|
34
34
|
await waitFor(() => {
|
|
35
35
|
expect(getByText('DataJunction')).toBeInTheDocument();
|
|
36
|
-
expect(
|
|
36
|
+
expect(getByText('Username is required')).toBeInTheDocument();
|
|
37
|
+
expect(getByText('Password is required')).toBeInTheDocument();
|
|
37
38
|
});
|
|
38
39
|
});
|
|
39
40
|
|
|
40
|
-
it('calls fetch with correct data on
|
|
41
|
+
it('calls fetch with correct data on login', async () => {
|
|
41
42
|
const username = 'testUser';
|
|
42
43
|
const password = 'testPassword';
|
|
43
44
|
|
|
@@ -62,4 +63,35 @@ describe('LoginPage', () => {
|
|
|
62
63
|
expect(window.location.reload).toHaveBeenCalled();
|
|
63
64
|
});
|
|
64
65
|
});
|
|
66
|
+
|
|
67
|
+
it('calls fetch with correct data on signup', async () => {
|
|
68
|
+
const email = 'testEmail@testEmail.com';
|
|
69
|
+
const username = 'testUser';
|
|
70
|
+
const password = 'testPassword';
|
|
71
|
+
|
|
72
|
+
const { getByText, getByPlaceholderText } = render(<LoginPage />);
|
|
73
|
+
fireEvent.click(getByText('Sign Up'));
|
|
74
|
+
fireEvent.change(getByPlaceholderText('Email'), {
|
|
75
|
+
target: { value: email },
|
|
76
|
+
});
|
|
77
|
+
fireEvent.change(getByPlaceholderText('Username'), {
|
|
78
|
+
target: { value: username },
|
|
79
|
+
});
|
|
80
|
+
fireEvent.change(getByPlaceholderText('Password'), {
|
|
81
|
+
target: { value: password },
|
|
82
|
+
});
|
|
83
|
+
fireEvent.click(getByText('Sign Up'));
|
|
84
|
+
|
|
85
|
+
await waitFor(() => {
|
|
86
|
+
expect(fetch).toHaveBeenCalledWith(
|
|
87
|
+
`${process.env.REACT_APP_DJ_URL}/basic/user/`,
|
|
88
|
+
expect.objectContaining({
|
|
89
|
+
method: 'POST',
|
|
90
|
+
body: expect.any(FormData),
|
|
91
|
+
credentials: 'include',
|
|
92
|
+
}),
|
|
93
|
+
);
|
|
94
|
+
expect(window.location.reload).toHaveBeenCalled();
|
|
95
|
+
});
|
|
96
|
+
});
|
|
65
97
|
});
|
|
@@ -1,90 +1,17 @@
|
|
|
1
1
|
import { useState } from 'react';
|
|
2
|
-
import { Formik, Form, Field, ErrorMessage } from 'formik';
|
|
3
2
|
import '../../../styles/login.css';
|
|
4
|
-
import
|
|
5
|
-
import
|
|
3
|
+
import SignupForm from './SignupForm';
|
|
4
|
+
import LoginForm from './LoginForm';
|
|
6
5
|
|
|
7
6
|
export function LoginPage() {
|
|
8
|
-
const [,
|
|
9
|
-
const githubLoginURL = new URL('/github/login/', process.env.REACT_APP_DJ_URL)
|
|
10
|
-
.href;
|
|
11
|
-
|
|
12
|
-
const handleBasicLogin = async ({ username, password }) => {
|
|
13
|
-
const data = new FormData();
|
|
14
|
-
data.append('username', username);
|
|
15
|
-
data.append('password', password);
|
|
16
|
-
await fetch(`${process.env.REACT_APP_DJ_URL}/basic/login/`, {
|
|
17
|
-
method: 'POST',
|
|
18
|
-
body: data,
|
|
19
|
-
credentials: 'include',
|
|
20
|
-
}).catch(error => {
|
|
21
|
-
setError(error ? JSON.stringify(error) : '');
|
|
22
|
-
});
|
|
23
|
-
window.location.reload();
|
|
24
|
-
};
|
|
25
|
-
|
|
7
|
+
const [showSignup, setShowSignup] = useState(false);
|
|
26
8
|
return (
|
|
27
|
-
<div className="container">
|
|
28
|
-
|
|
29
|
-
<
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const errors = {};
|
|
34
|
-
if (!values.username) {
|
|
35
|
-
errors.username = 'Required';
|
|
36
|
-
}
|
|
37
|
-
if (!values.password) {
|
|
38
|
-
errors.password = 'Required';
|
|
39
|
-
}
|
|
40
|
-
return errors;
|
|
41
|
-
}}
|
|
42
|
-
onSubmit={(values, { setSubmitting }) => {
|
|
43
|
-
setTimeout(() => {
|
|
44
|
-
handleBasicLogin(values);
|
|
45
|
-
setSubmitting(false);
|
|
46
|
-
}, 400);
|
|
47
|
-
}}
|
|
48
|
-
>
|
|
49
|
-
{({ isSubmitting }) => (
|
|
50
|
-
<Form>
|
|
51
|
-
<div className="logo-title">
|
|
52
|
-
<img src={logo} alt="DJ Logo" width="75px" height="75px" />
|
|
53
|
-
<h2>DataJunction</h2>
|
|
54
|
-
</div>
|
|
55
|
-
<div className="inputContainer">
|
|
56
|
-
<ErrorMessage name="username" component="span" />
|
|
57
|
-
<Field type="text" name="username" placeholder="Username" />
|
|
58
|
-
</div>
|
|
59
|
-
<div>
|
|
60
|
-
<ErrorMessage name="password" component="span" />
|
|
61
|
-
<Field
|
|
62
|
-
type="password"
|
|
63
|
-
name="password"
|
|
64
|
-
placeholder="Password"
|
|
65
|
-
/>
|
|
66
|
-
</div>
|
|
67
|
-
<button type="submit" disabled={isSubmitting}>
|
|
68
|
-
Login
|
|
69
|
-
</button>
|
|
70
|
-
{process.env.REACT_ENABLE_GITHUB_OAUTH === 'true' ? (
|
|
71
|
-
<div>
|
|
72
|
-
<a href={githubLoginURL}>
|
|
73
|
-
<img
|
|
74
|
-
src={GitHubLoginButton}
|
|
75
|
-
alt="Sign in with GitHub"
|
|
76
|
-
width="200px"
|
|
77
|
-
/>
|
|
78
|
-
</a>
|
|
79
|
-
</div>
|
|
80
|
-
) : (
|
|
81
|
-
''
|
|
82
|
-
)}
|
|
83
|
-
</Form>
|
|
84
|
-
)}
|
|
85
|
-
</Formik>
|
|
86
|
-
</center>
|
|
87
|
-
</div>
|
|
9
|
+
<div className="container login">
|
|
10
|
+
{showSignup ? (
|
|
11
|
+
<SignupForm setShowSignup={setShowSignup} />
|
|
12
|
+
) : (
|
|
13
|
+
<LoginForm setShowSignup={setShowSignup} />
|
|
14
|
+
)}
|
|
88
15
|
</div>
|
|
89
16
|
);
|
|
90
17
|
}
|
|
@@ -60,6 +60,16 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
60
60
|
>
|
|
61
61
|
{col.name}
|
|
62
62
|
</td>
|
|
63
|
+
<td>
|
|
64
|
+
<span
|
|
65
|
+
className=""
|
|
66
|
+
role="columnheader"
|
|
67
|
+
aria-label="ColumnDisplayName"
|
|
68
|
+
aria-hidden="false"
|
|
69
|
+
>
|
|
70
|
+
{col.display_name}
|
|
71
|
+
</span>
|
|
72
|
+
</td>
|
|
63
73
|
<td>
|
|
64
74
|
<span
|
|
65
75
|
className="node_type__transform badge node_type"
|
|
@@ -111,6 +121,7 @@ export default function NodeColumnTab({ node, djClient }) {
|
|
|
111
121
|
<thead className="fs-7 fw-bold text-gray-400 border-bottom-0">
|
|
112
122
|
<tr>
|
|
113
123
|
<th className="text-start">Column</th>
|
|
124
|
+
<th>Display Name</th>
|
|
114
125
|
<th>Type</th>
|
|
115
126
|
<th>Linked Dimension</th>
|
|
116
127
|
<th>Attributes</th>
|
|
@@ -13,7 +13,11 @@ foundation.hljs['padding'] = '2rem';
|
|
|
13
13
|
export default function NodeInfoTab({ node }) {
|
|
14
14
|
const [compiledSQL, setCompiledSQL] = useState('');
|
|
15
15
|
const [checked, setChecked] = useState(false);
|
|
16
|
-
const nodeTags = node?.tags.map(tag =>
|
|
16
|
+
const nodeTags = node?.tags.map(tag => (
|
|
17
|
+
<div className={'badge tag_value'}>
|
|
18
|
+
<a href={`/tags/${tag.name}`}>{tag.display_name}</a>
|
|
19
|
+
</div>
|
|
20
|
+
));
|
|
17
21
|
const djClient = useContext(DJClientContext).DataJunctionAPI;
|
|
18
22
|
useEffect(() => {
|
|
19
23
|
const fetchData = async () => {
|
|
@@ -70,6 +70,7 @@ describe('<NodePage />', () => {
|
|
|
70
70
|
{
|
|
71
71
|
name: 'default_DOT_avg_repair_price',
|
|
72
72
|
type: 'double',
|
|
73
|
+
display_name: 'Default DOT avg repair price',
|
|
73
74
|
attributes: [],
|
|
74
75
|
dimension: null,
|
|
75
76
|
},
|
|
@@ -82,7 +83,7 @@ describe('<NodePage />', () => {
|
|
|
82
83
|
},
|
|
83
84
|
],
|
|
84
85
|
created_at: '2023-08-21T16:48:56.932162+00:00',
|
|
85
|
-
tags: [],
|
|
86
|
+
tags: [{ name: 'purpose', display_name: 'Purpose' }],
|
|
86
87
|
primary_key: [],
|
|
87
88
|
createNodeClientCode:
|
|
88
89
|
'dj = DJBuilder(DJ_URL)\n\navg_repair_price = dj.create_metric(\n description="Average repair price",\n display_name="Default: Avg Repair Price",\n name="default.avg_repair_price",\n primary_key=[],\n query="""SELECT avg(price) default_DOT_avg_repair_price \n FROM default.repair_order_details\n\n"""\n)',
|
|
@@ -303,13 +304,12 @@ describe('<NodePage />', () => {
|
|
|
303
304
|
'v1.0',
|
|
304
305
|
);
|
|
305
306
|
|
|
306
|
-
// expect(screen.getByRole('dialog', { name: 'Table' })).not.toBeInTheDocument();
|
|
307
307
|
expect(
|
|
308
308
|
screen.getByRole('dialog', { name: 'NodeStatus' }),
|
|
309
309
|
).toBeInTheDocument();
|
|
310
310
|
|
|
311
311
|
expect(screen.getByRole('dialog', { name: 'Tags' })).toHaveTextContent(
|
|
312
|
-
'',
|
|
312
|
+
'Purpose',
|
|
313
313
|
);
|
|
314
314
|
|
|
315
315
|
expect(
|
|
@@ -416,6 +416,9 @@ describe('<NodePage />', () => {
|
|
|
416
416
|
expect(
|
|
417
417
|
screen.getByRole('columnheader', { name: 'ColumnName' }),
|
|
418
418
|
).toHaveTextContent('default_DOT_avg_repair_price');
|
|
419
|
+
expect(
|
|
420
|
+
screen.getByRole('columnheader', { name: 'ColumnDisplayName' }),
|
|
421
|
+
).toHaveTextContent('Default DOT avg repair price');
|
|
419
422
|
expect(
|
|
420
423
|
screen.getByRole('columnheader', { name: 'ColumnType' }),
|
|
421
424
|
).toHaveTextContent('double');
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Asynchronously loads the component for the Node page
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as React from 'react';
|
|
6
|
+
import { lazyLoad } from '../../../utils/loadable';
|
|
7
|
+
|
|
8
|
+
export const TagPage = () => {
|
|
9
|
+
return lazyLoad(
|
|
10
|
+
() => import('./index'),
|
|
11
|
+
module => module.TagPage,
|
|
12
|
+
{
|
|
13
|
+
fallback: <div></div>,
|
|
14
|
+
},
|
|
15
|
+
)();
|
|
16
|
+
};
|