@devtron-labs/devtron-fe-common-lib 0.0.6-beta-8 → 0.0.6-beta-9
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/dist/Common/Drawer/Drawer.d.ts +16 -0
- package/dist/Common/EmptyState/EmptyState.d.ts +24 -0
- package/dist/Common/Error.d.ts +15 -0
- package/dist/Common/Helper.d.ts +1 -0
- package/dist/Common/Modals/Modal.d.ts +9 -0
- package/dist/Common/Modals/OpaqueModal.d.ts +12 -0
- package/dist/Common/Modals/VisibleModal.d.ts +15 -0
- package/dist/Common/Modals/VisibleModal2.d.ts +12 -0
- package/dist/Common/Progressing.d.ts +13 -0
- package/dist/Common/Reload.d.ts +5 -0
- package/dist/Common/ToastBody.d.ts +23 -0
- package/dist/Common/index.d.ts +11 -0
- package/dist/b5d52f036bdcd3ed.png +0 -0
- package/dist/index.js +1 -1
- package/package.json +44 -39
- package/rollup.config.js +16 -14
- package/src/Assets/Icon/ic-close.svg +9 -0
- package/src/Assets/Icon/ic-progressing.svg +6 -0
- package/src/Assets/Img/ic-loading-failure.png +0 -0
- package/src/Assets/Img/ic-not-authorized.svg +62 -0
- package/src/Common/Constants.ts +1 -1
- package/src/Common/Drawer/Drawer.scss +39 -0
- package/src/Common/Drawer/Drawer.tsx +51 -0
- package/src/Common/EmptyState/EmptyState.tsx +42 -0
- package/src/Common/EmptyState/emptyState.scss +83 -0
- package/src/Common/Error.tsx +55 -0
- package/src/Common/Helper.tsx +31 -0
- package/src/Common/Modals/Modal.tsx +53 -0
- package/src/Common/Modals/OpaqueModal.tsx +36 -0
- package/src/Common/Modals/VisibleModal.tsx +55 -0
- package/src/Common/Modals/VisibleModal2.tsx +35 -0
- package/src/Common/Progressing.tsx +60 -0
- package/src/Common/Reload.tsx +18 -0
- package/src/Common/ServerError.ts +19 -18
- package/src/Common/ToastBody.tsx +64 -0
- package/src/Common/Types.ts +8 -9
- package/src/Common/index.ts +12 -1
- package/src/custom.d.ts +13 -0
- package/tsconfig.json +2 -1
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
.drawer{
|
|
2
|
+
position: absolute;
|
|
3
|
+
overflow: hidden;
|
|
4
|
+
&.left, &.right{
|
|
5
|
+
top: 0;
|
|
6
|
+
bottom: 0;
|
|
7
|
+
width: 0;
|
|
8
|
+
transition: width 200ms;
|
|
9
|
+
&.show{
|
|
10
|
+
min-width: var(--minWidth);
|
|
11
|
+
width: var(--width);
|
|
12
|
+
max-width: var(--maxWidth);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
&.top, &.bottom{
|
|
17
|
+
left: 0;
|
|
18
|
+
right: 0;
|
|
19
|
+
height: 0;
|
|
20
|
+
transition: height 200ms;
|
|
21
|
+
&.show{
|
|
22
|
+
height: var(--height);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
&.right{
|
|
26
|
+
right: 0;
|
|
27
|
+
}
|
|
28
|
+
&.left{
|
|
29
|
+
left: 0;
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
&.top{
|
|
33
|
+
top: 0;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
&.bottom{
|
|
37
|
+
bottom: 0;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import React, { useRef, useEffect } from 'react'
|
|
2
|
+
import { VisibleModal } from '../Modals/VisibleModal'
|
|
3
|
+
import './Drawer.scss'
|
|
4
|
+
|
|
5
|
+
interface drawerInterface {
|
|
6
|
+
position: 'left' | 'right' | 'bottom' | 'top'
|
|
7
|
+
children?: any
|
|
8
|
+
backdrop?: boolean
|
|
9
|
+
onClose?: (e: any) => void
|
|
10
|
+
width?: string
|
|
11
|
+
minWidth?: string
|
|
12
|
+
maxWidth?: string
|
|
13
|
+
height?: string
|
|
14
|
+
parentClassName?: string
|
|
15
|
+
onEscape?: (e?: any) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const Drawer: React.FC<drawerInterface> = ({
|
|
19
|
+
children,
|
|
20
|
+
position,
|
|
21
|
+
height,
|
|
22
|
+
width,
|
|
23
|
+
minWidth,
|
|
24
|
+
maxWidth,
|
|
25
|
+
parentClassName,
|
|
26
|
+
onEscape,
|
|
27
|
+
}) => {
|
|
28
|
+
const drawerRef = useRef(null)
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
setTimeout(() => drawerRef.current?.classList?.add('show'), 1)
|
|
31
|
+
return () => drawerRef.current?.classList?.remove('show')
|
|
32
|
+
}, [])
|
|
33
|
+
const style = {}
|
|
34
|
+
if (position === 'left' || position === 'right') {
|
|
35
|
+
style['--width'] = width
|
|
36
|
+
style['--minWidth'] = minWidth
|
|
37
|
+
style['--maxWidth'] = maxWidth
|
|
38
|
+
}
|
|
39
|
+
if (position === 'top' || position === 'bottom') {
|
|
40
|
+
style['--height'] = height
|
|
41
|
+
}
|
|
42
|
+
return (
|
|
43
|
+
<VisibleModal className="drawer--container" parentClassName={parentClassName || ''} onEscape={onEscape}>
|
|
44
|
+
<aside style={style} ref={drawerRef} className={`drawer ${position}`}>
|
|
45
|
+
{children}
|
|
46
|
+
</aside>
|
|
47
|
+
</VisibleModal>
|
|
48
|
+
)
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export default Drawer
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import './emptyState.scss';
|
|
3
|
+
import { ReactComponent as Progressing } from '../../Assets/Icon/ic-progressing.svg';
|
|
4
|
+
|
|
5
|
+
function EmptyState({ children }) {
|
|
6
|
+
return (
|
|
7
|
+
<div className="flex column empty-state" style={{ width: '100%', height: '100%' }}>
|
|
8
|
+
{children}
|
|
9
|
+
</div>
|
|
10
|
+
)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function Image({ children }) {
|
|
14
|
+
return children
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function Title({ children }) {
|
|
18
|
+
return children
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function Subtitle({ children, className }:{children: any, className?: string}) {
|
|
22
|
+
return <p className={`subtitle ${className}`}>{children}</p>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function Button({ children }) {
|
|
26
|
+
return children
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const Loading: React.FC<{ text: string }> = function (props) {
|
|
30
|
+
return <>
|
|
31
|
+
<Progressing className="dc__block empty-state__loader" />
|
|
32
|
+
<p className="empty-state__loading-text">{props.text}</p>
|
|
33
|
+
</>
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
EmptyState.Image = Image
|
|
37
|
+
EmptyState.Title = Title
|
|
38
|
+
EmptyState.Subtitle = Subtitle
|
|
39
|
+
EmptyState.Button = Button
|
|
40
|
+
EmptyState.Loading = Loading
|
|
41
|
+
|
|
42
|
+
export default EmptyState
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
.empty-state {
|
|
2
|
+
height: 100%;
|
|
3
|
+
img {
|
|
4
|
+
height: 40%;
|
|
5
|
+
max-height: 200px;
|
|
6
|
+
width: auto;
|
|
7
|
+
max-width: 250px;
|
|
8
|
+
}
|
|
9
|
+
svg {
|
|
10
|
+
height: 40%;
|
|
11
|
+
max-height: 200px;
|
|
12
|
+
width: auto;
|
|
13
|
+
}
|
|
14
|
+
h1,
|
|
15
|
+
h2,
|
|
16
|
+
h3,
|
|
17
|
+
h4,
|
|
18
|
+
h5,
|
|
19
|
+
h6 {
|
|
20
|
+
color: var(--N900);
|
|
21
|
+
}
|
|
22
|
+
.title {
|
|
23
|
+
font-weight: 600;
|
|
24
|
+
}
|
|
25
|
+
.subtitle {
|
|
26
|
+
font-size: 13px;
|
|
27
|
+
width: 250px;
|
|
28
|
+
font-weight: 400;
|
|
29
|
+
text-align: center;
|
|
30
|
+
line-height: 1.5;
|
|
31
|
+
margin-bottom: 20px;
|
|
32
|
+
}
|
|
33
|
+
.cta {
|
|
34
|
+
height: 36px;
|
|
35
|
+
}
|
|
36
|
+
p {
|
|
37
|
+
width: 250px;
|
|
38
|
+
text-align: center;
|
|
39
|
+
font-size: 13px;
|
|
40
|
+
color: var(--N700);
|
|
41
|
+
}
|
|
42
|
+
strong {
|
|
43
|
+
font-size: 16px;
|
|
44
|
+
margin-bottom: 4px;
|
|
45
|
+
color: var(--N900);
|
|
46
|
+
font-weight: normal;
|
|
47
|
+
}
|
|
48
|
+
.select-popup {
|
|
49
|
+
height: min-content;
|
|
50
|
+
max-height: 250px;
|
|
51
|
+
overflow: auto;
|
|
52
|
+
padding: 8px 0;
|
|
53
|
+
}
|
|
54
|
+
.empty-state__loader {
|
|
55
|
+
width: 34px;
|
|
56
|
+
height: 34px;
|
|
57
|
+
margin-bottom: 16px;
|
|
58
|
+
}
|
|
59
|
+
.empty-state__loading-text {
|
|
60
|
+
font-size: 12px;
|
|
61
|
+
width: 200px;
|
|
62
|
+
font-weight: normal;
|
|
63
|
+
font-stretch: normal;
|
|
64
|
+
font-style: normal;
|
|
65
|
+
line-height: 1.5;
|
|
66
|
+
letter-spacing: normal;
|
|
67
|
+
text-align: center;
|
|
68
|
+
color: var(--N900);
|
|
69
|
+
}
|
|
70
|
+
.button__icon {
|
|
71
|
+
width:16px;
|
|
72
|
+
height:16px;
|
|
73
|
+
margin-left: 8px;
|
|
74
|
+
}
|
|
75
|
+
//TODO: use name
|
|
76
|
+
// .subtitle a {
|
|
77
|
+
// color: inherit;
|
|
78
|
+
// }
|
|
79
|
+
// .subtitle a:focus,
|
|
80
|
+
// .subtitle a:hover {
|
|
81
|
+
// color: inherit;
|
|
82
|
+
// }
|
|
83
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React, { Component } from 'react'
|
|
2
|
+
import EmptyState from './EmptyState/EmptyState'
|
|
3
|
+
import notAuthorized from '../Assets/Img/ic-not-authorized.svg'
|
|
4
|
+
import Reload from './Reload'
|
|
5
|
+
|
|
6
|
+
export class ErrorScreenManager extends Component<{ code?: number; reload?: (...args) => any; subtitle?: string ;reloadClass?: string}> {
|
|
7
|
+
getMessage() {
|
|
8
|
+
switch (this.props.code) {
|
|
9
|
+
case 400:
|
|
10
|
+
return 'Bad Request'
|
|
11
|
+
case 401:
|
|
12
|
+
return 'Unauthorized'
|
|
13
|
+
case 403:
|
|
14
|
+
return <ErrorScreenNotAuthorized subtitle={this.props.subtitle} />
|
|
15
|
+
case 404:
|
|
16
|
+
return 'Not Found'
|
|
17
|
+
case 500:
|
|
18
|
+
return 'Internal Server Error'
|
|
19
|
+
case 502:
|
|
20
|
+
return 'Bad Gateway'
|
|
21
|
+
case 503:
|
|
22
|
+
return 'Service Temporarily Unavailable'
|
|
23
|
+
default:
|
|
24
|
+
return <Reload className={this.props.reloadClass} />
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
render() {
|
|
28
|
+
let msg = this.getMessage()
|
|
29
|
+
return (
|
|
30
|
+
<div>
|
|
31
|
+
<h1>{msg}</h1>
|
|
32
|
+
</div>
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export class ErrorScreenNotAuthorized extends Component<{ subtitle: string }> {
|
|
38
|
+
render() {
|
|
39
|
+
return (
|
|
40
|
+
<EmptyState>
|
|
41
|
+
<EmptyState.Image>
|
|
42
|
+
<img src={notAuthorized} alt="Not Authorized" />
|
|
43
|
+
</EmptyState.Image>
|
|
44
|
+
<EmptyState.Title>
|
|
45
|
+
<h3 className="title">Not authorized</h3>
|
|
46
|
+
</EmptyState.Title>
|
|
47
|
+
<EmptyState.Subtitle>
|
|
48
|
+
{this.props.subtitle
|
|
49
|
+
? this.props.subtitle
|
|
50
|
+
: "Looks like you don't have access to information on this page. Please contact your manager to request access."}
|
|
51
|
+
</EmptyState.Subtitle>
|
|
52
|
+
</EmptyState>
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { ServerErrors } from "./ServerError"
|
|
2
|
+
import { toast } from 'react-toastify'
|
|
3
|
+
import * as Sentry from '@sentry/browser'
|
|
4
|
+
import { toastAccessDenied } from "./ToastBody"
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
export function showError(serverError, showToastOnUnknownError = true, hideAccessError = false) {
|
|
8
|
+
if (serverError instanceof ServerErrors && Array.isArray(serverError.errors)) {
|
|
9
|
+
serverError.errors.map(({ userMessage, internalMessage }) => {
|
|
10
|
+
if (serverError.code === 403 && userMessage === 'unauthorized') {
|
|
11
|
+
if (!hideAccessError) {
|
|
12
|
+
toastAccessDenied()
|
|
13
|
+
}
|
|
14
|
+
} else {
|
|
15
|
+
toast.error(userMessage || internalMessage)
|
|
16
|
+
}
|
|
17
|
+
})
|
|
18
|
+
} else {
|
|
19
|
+
if (serverError.code !== 403 && serverError.code !== 408) {
|
|
20
|
+
Sentry.captureException(serverError)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (showToastOnUnknownError) {
|
|
24
|
+
if (serverError.message) {
|
|
25
|
+
toast.error(serverError.message)
|
|
26
|
+
} else {
|
|
27
|
+
toast.error('Some Error Occurred')
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom'
|
|
3
|
+
export const Modal = ({ style = {}, children, modal = false, rootClassName = "", onClick = null, callbackRef = null }) => {
|
|
4
|
+
const innerRef = React.useRef(null)
|
|
5
|
+
function handleClick(e) {
|
|
6
|
+
if (typeof onClick !== 'function') return
|
|
7
|
+
if (innerRef && innerRef.current.contains(e.target)) {
|
|
8
|
+
onClick(e, 'in')
|
|
9
|
+
}
|
|
10
|
+
else {
|
|
11
|
+
onClick(e, 'out')
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function disableWheel(e) {
|
|
16
|
+
if (innerRef && innerRef.current.contains(e.target)) {
|
|
17
|
+
if (innerRef.current.clientHeight === innerRef.current.scrollHeight) {
|
|
18
|
+
e.preventDefault();
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
e.preventDefault();
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
document.addEventListener('click', handleClick)
|
|
27
|
+
let modal = document.getElementById("visible-modal");
|
|
28
|
+
if (modal) modal.classList.add("show")
|
|
29
|
+
document.body.addEventListener('wheel', disableWheel, { passive: false })
|
|
30
|
+
return () => {
|
|
31
|
+
document.body.removeEventListener('wheel', disableWheel)
|
|
32
|
+
document.removeEventListener('click', handleClick)
|
|
33
|
+
if (modal) modal.classList.remove("show")
|
|
34
|
+
|
|
35
|
+
}
|
|
36
|
+
}, []
|
|
37
|
+
)
|
|
38
|
+
return ReactDOM.createPortal(
|
|
39
|
+
<div tabIndex={0} onClick={onClick}
|
|
40
|
+
ref={
|
|
41
|
+
el => {
|
|
42
|
+
if (typeof callbackRef === 'function') {
|
|
43
|
+
callbackRef(el);
|
|
44
|
+
}
|
|
45
|
+
innerRef.current = el;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
id="popup"
|
|
49
|
+
className={`${rootClassName} popup ${modal ? 'modal' : ''}`}
|
|
50
|
+
style={{ ...style }}>
|
|
51
|
+
{children}
|
|
52
|
+
</div>, document.getElementById('visible-modal'));
|
|
53
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
import closeIcon from '../../Assets/Icon/ic-close.svg'
|
|
4
|
+
|
|
5
|
+
export interface OpaqueModalProps {
|
|
6
|
+
className?: string;
|
|
7
|
+
onHide?: any;
|
|
8
|
+
noBackground?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class OpaqueModal extends React.Component<OpaqueModalProps> {
|
|
12
|
+
modalRef = document.getElementById('full-screen-modal');
|
|
13
|
+
|
|
14
|
+
componentDidMount() {
|
|
15
|
+
if (this.props.noBackground)
|
|
16
|
+
this.modalRef.classList.add("show");
|
|
17
|
+
else this.modalRef.classList.add("show-with-bg");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
componentWillUnmount() {
|
|
21
|
+
this.modalRef.classList.remove("show");
|
|
22
|
+
this.modalRef.classList.remove("show-with-bg");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
render() {
|
|
26
|
+
const { className = "", onHide = null } = { ...this.props };
|
|
27
|
+
|
|
28
|
+
return ReactDOM.createPortal(
|
|
29
|
+
<div className={`full-screen-modal__body-container ${className}`}>
|
|
30
|
+
{this.props.children}
|
|
31
|
+
{typeof onHide === 'function' && <div className="close-btn icon-dim-24" onClick={e => onHide(false)}>
|
|
32
|
+
<img className="close-img" src={closeIcon} alt="close" />
|
|
33
|
+
</div>}
|
|
34
|
+
</div>, document.getElementById('full-screen-modal'))
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import ReactDOM from 'react-dom'
|
|
3
|
+
|
|
4
|
+
export class VisibleModal extends React.Component<{
|
|
5
|
+
className: string
|
|
6
|
+
parentClassName?: string
|
|
7
|
+
noBackground?: boolean
|
|
8
|
+
close?: (e) => void
|
|
9
|
+
onEscape?: (e) => void
|
|
10
|
+
}> {
|
|
11
|
+
modalRef = document.getElementById('visible-modal')
|
|
12
|
+
|
|
13
|
+
constructor(props) {
|
|
14
|
+
super(props)
|
|
15
|
+
this.escFunction = this.escFunction.bind(this)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
escFunction(event) {
|
|
19
|
+
if (event.keyCode === 27 || event.key === 'Escape') {
|
|
20
|
+
if (this.props.onEscape) {
|
|
21
|
+
this.props.onEscape(event)
|
|
22
|
+
} else if (this.props.close) {
|
|
23
|
+
this.props.close(event)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
componentDidMount() {
|
|
29
|
+
document.addEventListener('keydown', this.escFunction)
|
|
30
|
+
this.modalRef.classList.add(this.props.noBackground ? 'show' : 'show-with-bg')
|
|
31
|
+
|
|
32
|
+
if (this.props.parentClassName) {
|
|
33
|
+
this.modalRef.classList.add(this.props.parentClassName)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
componentWillUnmount() {
|
|
38
|
+
document.removeEventListener('keydown', this.escFunction)
|
|
39
|
+
this.modalRef.classList.remove('show')
|
|
40
|
+
this.modalRef.classList.remove('show-with-bg')
|
|
41
|
+
|
|
42
|
+
if (this.props.parentClassName) {
|
|
43
|
+
this.modalRef.classList.remove(this.props.parentClassName)
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
render() {
|
|
48
|
+
return ReactDOM.createPortal(
|
|
49
|
+
<div className={`visible-modal__body ${this.props.className}`} onClick={this.props?.close}>
|
|
50
|
+
{this.props.children}
|
|
51
|
+
</div>,
|
|
52
|
+
document.getElementById('visible-modal'),
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import ReactDOM from 'react-dom';
|
|
3
|
+
|
|
4
|
+
export class VisibleModal2 extends React.Component<{ className: string; close?: (e) => void; }> {
|
|
5
|
+
|
|
6
|
+
modalRef = document.getElementById('visible-modal-2');
|
|
7
|
+
|
|
8
|
+
constructor(props) {
|
|
9
|
+
super(props);
|
|
10
|
+
this.escFunction = this.escFunction.bind(this);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
escFunction(event) {
|
|
14
|
+
if (event.keyCode === 27 && this.props.close) {
|
|
15
|
+
this.props.close(event);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
componentDidMount() {
|
|
20
|
+
document.addEventListener("keydown", this.escFunction);
|
|
21
|
+
this.modalRef.classList.add("show-with-bg");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
componentWillUnmount() {
|
|
25
|
+
document.removeEventListener("keydown", this.escFunction);
|
|
26
|
+
this.modalRef.classList.remove("show-with-bg");
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
render() {
|
|
30
|
+
return ReactDOM.createPortal(
|
|
31
|
+
<div className={`visible-modal__body ${this.props.className}`} onClick={this.props?.close}>
|
|
32
|
+
{this.props.children}
|
|
33
|
+
</div>, document.getElementById('visible-modal-2'))
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
interface ProgressingProps {
|
|
4
|
+
pageLoader?: boolean
|
|
5
|
+
loadingText?: string
|
|
6
|
+
size?: number
|
|
7
|
+
fullHeight?: boolean
|
|
8
|
+
theme?: 'white' | 'default'
|
|
9
|
+
styles?: React.CSSProperties
|
|
10
|
+
children?: React.ReactNode
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function Progressing({ pageLoader, size, theme, styles }: ProgressingProps): JSX.Element {
|
|
14
|
+
const loaderSize = size ? `${size}px` : pageLoader ? '48px' : '20px'
|
|
15
|
+
return (
|
|
16
|
+
<div className={`loader ${theme || 'default'}-background`} style={styles}>
|
|
17
|
+
<div style={{ width: loaderSize, height: loaderSize }}>
|
|
18
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" className="loader__svg">
|
|
19
|
+
<g fill="none" fillRule="evenodd" strokeLinecap="round">
|
|
20
|
+
<animateTransform
|
|
21
|
+
attributeName="transform"
|
|
22
|
+
attributeType="XML"
|
|
23
|
+
dur="0.5s"
|
|
24
|
+
from="0 12 12"
|
|
25
|
+
repeatCount="indefinite"
|
|
26
|
+
to="360 12 12"
|
|
27
|
+
type="rotate"
|
|
28
|
+
/>
|
|
29
|
+
<path
|
|
30
|
+
fill="#06C"
|
|
31
|
+
fillRule="nonzero"
|
|
32
|
+
d="M12 2.5A9.5 9.5 0 1 1 2.5 12a1.5 1.5 0 0 1 3 0A6.5 6.5 0 1 0 12 5.5a1.5 1.5 0 0 1 0-3z"
|
|
33
|
+
/>
|
|
34
|
+
</g>
|
|
35
|
+
</svg>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function DetailsProgressing({
|
|
42
|
+
loadingText,
|
|
43
|
+
size = 24,
|
|
44
|
+
fullHeight = false,
|
|
45
|
+
children,
|
|
46
|
+
styles,
|
|
47
|
+
}: ProgressingProps): JSX.Element {
|
|
48
|
+
return (
|
|
49
|
+
<div
|
|
50
|
+
className={`details-loader bcn-0 flex column fs-14 fw-6 ${fullHeight ? 'h-100' : 'details-loader-height'}`}
|
|
51
|
+
style={styles}
|
|
52
|
+
>
|
|
53
|
+
<span style={{ width: `${size}px`, height: `${size}px` }}>
|
|
54
|
+
<Progressing size={size} />
|
|
55
|
+
</span>
|
|
56
|
+
{loadingText && <span className="mt-10">{loadingText}</span>}
|
|
57
|
+
{children}
|
|
58
|
+
</div>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import loadingFailure from '../Assets/Img/ic-loading-failure.png';
|
|
3
|
+
|
|
4
|
+
export default function Reload({ reload = null, className = '' }) {
|
|
5
|
+
function refresh(e){
|
|
6
|
+
window.location.reload();
|
|
7
|
+
}
|
|
8
|
+
return (
|
|
9
|
+
<article className={`flex ${className}`} style={{ width: '100%', height: '100%' }}>
|
|
10
|
+
<div className="flex column" style={{ width: '250px', textAlign: 'center' }}>
|
|
11
|
+
<img src={loadingFailure} style={{ width: '100%', height: 'auto', marginBottom: '12px' }} alt="load-error" />
|
|
12
|
+
<h3 className="title dc__bold">Failed to load</h3>
|
|
13
|
+
<div className="dc__empty__subtitle" style={{ marginBottom: '20px' }}>We could not load this page. Please give us another try.</div>
|
|
14
|
+
<button type="button" className="cta ghosted" onClick={typeof reload === 'function' ? reload : refresh}>Retry</button>
|
|
15
|
+
</div>
|
|
16
|
+
</article>
|
|
17
|
+
)
|
|
18
|
+
}
|
|
@@ -1,30 +1,31 @@
|
|
|
1
1
|
export class ServerError {
|
|
2
|
-
code: number
|
|
3
|
-
internalMessage: string | null
|
|
4
|
-
userMessage: string | null
|
|
5
|
-
moreInfo?: string | null
|
|
2
|
+
code: number
|
|
3
|
+
internalMessage: string | null
|
|
4
|
+
userMessage: string | null
|
|
5
|
+
moreInfo?: string | null
|
|
6
6
|
|
|
7
7
|
constructor(error) {
|
|
8
|
-
this.code = error.code || 0
|
|
9
|
-
this.userMessage = error.userMessage || ''
|
|
10
|
-
this.internalMessage = error.internalMessage || ''
|
|
11
|
-
this.moreInfo = error.moreInfo || ''
|
|
8
|
+
this.code = error.code || 0
|
|
9
|
+
this.userMessage = error.userMessage || ''
|
|
10
|
+
this.internalMessage = error.internalMessage || ''
|
|
11
|
+
this.moreInfo = error.moreInfo || ''
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
export class ServerErrors extends Error {
|
|
16
|
-
code: number
|
|
17
|
-
errors: ServerError[]
|
|
16
|
+
code: number
|
|
17
|
+
errors: ServerError[]
|
|
18
18
|
|
|
19
19
|
constructor(obj: { code: number; errors: ServerError[] }) {
|
|
20
|
-
super()
|
|
21
|
-
this.code = obj.code
|
|
20
|
+
super()
|
|
21
|
+
this.code = obj.code
|
|
22
22
|
let message = obj.errors.reduce((str: string, err: ServerError) => {
|
|
23
|
-
str += `${err.internalMessage || err.userMessage}
|
|
24
|
-
return str
|
|
25
|
-
}, '')
|
|
26
|
-
this.name = `[${obj.code.toString()}]
|
|
27
|
-
this.message = message
|
|
28
|
-
this.errors = obj.errors.map((err) => new ServerError(err))
|
|
23
|
+
str += `${err.internalMessage || err.userMessage}`
|
|
24
|
+
return str
|
|
25
|
+
}, '')
|
|
26
|
+
this.name = `[${obj.code.toString()}]`
|
|
27
|
+
this.message = message
|
|
28
|
+
this.errors = obj.errors.map((err) => new ServerError(err))
|
|
29
|
+
Object.setPrototypeOf(this, ServerErrors.prototype)
|
|
29
30
|
}
|
|
30
31
|
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { toast } from 'react-toastify'
|
|
3
|
+
|
|
4
|
+
export class ToastBody extends React.Component<{
|
|
5
|
+
title: string
|
|
6
|
+
subtitle?: string
|
|
7
|
+
}> {
|
|
8
|
+
render() {
|
|
9
|
+
return (
|
|
10
|
+
<div className="toast">
|
|
11
|
+
<div className="toast__title">{this.props.title}</div>
|
|
12
|
+
{this.props.subtitle && <div className="toast__subtitle">{this.props.subtitle}</div>}
|
|
13
|
+
</div>
|
|
14
|
+
)
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export class ToastBody3 extends React.Component<{
|
|
19
|
+
text: string
|
|
20
|
+
onClick: (...args) => void
|
|
21
|
+
buttonText: string
|
|
22
|
+
}> {
|
|
23
|
+
render() {
|
|
24
|
+
return (
|
|
25
|
+
<div className="flex left column dc__app-update-toast">
|
|
26
|
+
<span className="info">{this.props.text}</span>
|
|
27
|
+
<button type="button" onClick={this.props.onClick}>
|
|
28
|
+
{this.props.buttonText}
|
|
29
|
+
</button>
|
|
30
|
+
</div>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export class ToastBodyWithButton extends React.Component<{
|
|
36
|
+
title: string
|
|
37
|
+
subtitle?: string
|
|
38
|
+
onClick: (...args) => void
|
|
39
|
+
buttonText: string
|
|
40
|
+
}> {
|
|
41
|
+
render() {
|
|
42
|
+
return (
|
|
43
|
+
<div className="toast dc__app-update-toast">
|
|
44
|
+
<div className="toast__title">{this.props.title}</div>
|
|
45
|
+
{this.props.subtitle && <div className="toast__subtitle">{this.props.subtitle}</div>}
|
|
46
|
+
<button type="button" onClick={this.props.onClick} style={{ float: 'right' }}>
|
|
47
|
+
{this.props.buttonText}
|
|
48
|
+
</button>
|
|
49
|
+
</div>
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const toastAccessDenied = (subtitle?: string) => {
|
|
55
|
+
return toast.info(
|
|
56
|
+
<ToastBody
|
|
57
|
+
title="Access denied"
|
|
58
|
+
subtitle={subtitle || "You do not have required access to perform this action"}
|
|
59
|
+
/>,
|
|
60
|
+
{
|
|
61
|
+
className: 'devtron-toast unauthorized',
|
|
62
|
+
},
|
|
63
|
+
)
|
|
64
|
+
}
|