@devtron-labs/devtron-fe-common-lib 0.0.45-beta-24 → 0.0.45-beta-27

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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@devtron-labs/devtron-fe-common-lib",
3
- "version": "0.0.45-beta-24",
3
+ "version": "0.0.45-beta-27",
4
4
  "description": "Supporting common component library",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {
@@ -20,25 +20,25 @@
20
20
  "homepage": "https://github.com/devtron-labs/devtron-fe-common-lib#readme",
21
21
  "devDependencies": {
22
22
  "@rollup/plugin-babel": "^6.0.3",
23
- "@rollup/plugin-commonjs": "24.0.1",
24
- "@rollup/plugin-image": "3.0.1",
25
23
  "@rollup/plugin-node-resolve": "^15.0.1",
26
- "@rollup/plugin-url": "8.0.1",
27
- "@sentry/browser": "^7.31.1",
28
- "@svgr/rollup": "6.5.1",
29
- "@tippyjs/react": "^4.2.0",
24
+ "@rollup/plugin-image": "3.0.1",
25
+ "rollup-plugin-peer-deps-external": "^2.2.4",
26
+ "rollup-plugin-postcss": "^4.0.2",
27
+ "rollup-plugin-typescript2": "^0.34.1",
30
28
  "@types/react": "17.0.39",
31
29
  "@types/react-dom": "17.0.13",
32
30
  "@types/react-router-dom": "^5.3.3",
33
- "moment": "^2.29.4",
34
- "react-select": "5.2.2",
35
- "react-toastify": "^8.2.0",
36
31
  "rollup": "^3.10.0",
37
32
  "rollup-plugin-delete": "^2.0.0",
38
- "rollup-plugin-peer-deps-external": "^2.2.4",
39
- "rollup-plugin-postcss": "^4.0.2",
40
- "rollup-plugin-typescript2": "^0.34.1",
41
- "typescript": "^4.9.4"
33
+ "@svgr/rollup": "6.5.1",
34
+ "@rollup/plugin-url": "8.0.1",
35
+ "@rollup/plugin-commonjs": "24.0.1",
36
+ "moment": "^2.29.4",
37
+ "typescript": "^4.9.4",
38
+ "react-toastify": "^8.2.0",
39
+ "@tippyjs/react": "^4.2.0",
40
+ "react-select": "5.2.2",
41
+ "@sentry/browser": "^7.31.1"
42
42
  },
43
43
  "peerDependencies": {
44
44
  "@rjsf/core": "^5.13.3",
@@ -0,0 +1,68 @@
1
+ import React from 'react'
2
+ import Tippy from '@tippyjs/react'
3
+ import { AddCDButtonProps, TooltipContentProps } from './types'
4
+ import { AddCDPositions } from '../Types'
5
+
6
+ function TooltipContent({ tooltipContent }: TooltipContentProps) {
7
+ if (tooltipContent) {
8
+ return (
9
+ <div className="dc__white-space-pre-wrap">
10
+ <p className="m-0 cn-0 fs-12 fw-6 lh-18">Click to add</p>
11
+
12
+ {tooltipContent}
13
+ </div>
14
+ )
15
+ }
16
+
17
+ return <div className="dc__white-space-pre-wrap">Click to add here</div>
18
+ }
19
+
20
+ export default function AddCDButton({
21
+ position,
22
+ addCDButtons,
23
+ endNode,
24
+ startNode,
25
+ handleAddCD,
26
+ tooltipContent,
27
+ }: Readonly<AddCDButtonProps>) {
28
+ const referenceNode = position === AddCDPositions.RIGHT ? endNode : startNode
29
+ const handleAddCDClick = () => {
30
+ handleAddCD(position)
31
+ }
32
+
33
+ if (addCDButtons?.includes(position)) {
34
+ return (
35
+ <Tippy
36
+ placement="top"
37
+ content={<TooltipContent tooltipContent={tooltipContent} />}
38
+ className="default-tt"
39
+ arrow={false}
40
+ >
41
+ <svg
42
+ x={referenceNode.x + (position === AddCDPositions.RIGHT ? -20 - 5 : referenceNode.width + 5)}
43
+ // Here 10 is the height of the button / 2
44
+ y={referenceNode.y + referenceNode.height / 2 - 10}
45
+ width="20"
46
+ height="20"
47
+ viewBox="0 0 20 20"
48
+ fill="none"
49
+ xmlns="http://www.w3.org/2000/svg"
50
+ data-testid={`add-cd-to-${position}`}
51
+ onClick={handleAddCDClick}
52
+ className="dc__outline-none-imp"
53
+ >
54
+ <rect width="20" height="20" rx="10" fill="#664BEE" className="add-cd-edge-btn" />
55
+ <path
56
+ d="M6.5 10H13.5M10 6.5V13.5"
57
+ stroke="white"
58
+ strokeWidth="1.5"
59
+ strokeLinecap="round"
60
+ strokeLinejoin="round"
61
+ />
62
+ </svg>
63
+ </Tippy>
64
+ )
65
+ }
66
+
67
+ return null
68
+ }
@@ -0,0 +1,2 @@
1
+ export { default as AddCDButton } from './AddCDButton'
2
+ export { handleAddCD } from './utils'
@@ -0,0 +1,38 @@
1
+ import { AddCDPositions, AddPipelineType, EdgeEndNodeType, EdgeNodeType, Point } from '../Types'
2
+
3
+ export interface AddCDButtonProps {
4
+ position: AddCDPositions
5
+ addCDButtons: AddCDPositions[]
6
+ endNode: Point & EdgeEndNodeType
7
+ startNode: Point & EdgeNodeType
8
+ handleAddCD: (position: AddCDPositions) => void
9
+ tooltipContent?: string
10
+ }
11
+
12
+ export interface HandleAddCD {
13
+ position: AddCDPositions
14
+ handleCDSelect: (
15
+ workflowId: number | string,
16
+ ciPipelineId: number | string,
17
+ parentPipelineType: string,
18
+ parentPipelineId: number | string,
19
+ isWebhookCD?: boolean,
20
+ childPipelineId?: number | string,
21
+ addType?: AddPipelineType,
22
+ ) => void
23
+ startNode: EdgeNodeType
24
+ endNode: EdgeEndNodeType
25
+ workflowId: number | string
26
+ ciPipelineId: number | string
27
+ isWebhookCD: boolean
28
+ isParallelEdge: boolean
29
+ }
30
+
31
+ export interface GetPipelineType {
32
+ isWebhookCD: boolean
33
+ startNode: EdgeNodeType
34
+ }
35
+
36
+ export interface TooltipContentProps {
37
+ tooltipContent?: string
38
+ }
@@ -0,0 +1,35 @@
1
+ import { AddCDPositions, AddPipelineType, PipelineType, WorkflowNodeType } from '../Types'
2
+ import { HandleAddCD, GetPipelineType } from './types'
3
+
4
+ const getPipelineType = ({ isWebhookCD, startNode }: GetPipelineType) => {
5
+ if (isWebhookCD) {
6
+ return PipelineType.WEBHOOK
7
+ }
8
+
9
+ if (startNode.type === WorkflowNodeType.CI) {
10
+ return PipelineType.CI_PIPELINE
11
+ }
12
+
13
+ return PipelineType.CD_PIPELINE
14
+ }
15
+
16
+ export const handleAddCD = ({
17
+ position,
18
+ handleCDSelect,
19
+ startNode,
20
+ endNode,
21
+ workflowId,
22
+ ciPipelineId,
23
+ isWebhookCD,
24
+ isParallelEdge,
25
+ }: HandleAddCD) => {
26
+ if (!handleCDSelect) {
27
+ return
28
+ }
29
+ const pipelineType = getPipelineType({ isWebhookCD, startNode })
30
+ const addPipelineType =
31
+ isParallelEdge && position === AddCDPositions.RIGHT ? AddPipelineType.PARALLEL : AddPipelineType.SEQUENTIAL
32
+ const endNodeId = !isParallelEdge && position === AddCDPositions.RIGHT ? endNode.id : null
33
+
34
+ handleCDSelect(workflowId, ciPipelineId, pipelineType, startNode.id, isWebhookCD, endNodeId, addPipelineType)
35
+ }
@@ -45,7 +45,7 @@ export const DeleteDialog: React.FC<DeleteDialogProps> & { Description?: React.F
45
45
  type="button"
46
46
  className="cta delete cta-cd-delete-modal ml-16"
47
47
  onClick={handleDelete}
48
- disabled={props.apiCallInProgress}
48
+ disabled={props.apiCallInProgress || props.disabled}
49
49
  data-testid="dialog-delete"
50
50
  >
51
51
  {props.apiCallInProgress ? (
@@ -11,6 +11,7 @@ export interface DeleteDialogProps {
11
11
  dataTestId?: string
12
12
  buttonPrimaryText?: string
13
13
  shouldStopPropagation?: boolean
14
+ disabled?: boolean
14
15
  }
15
16
 
16
17
  export interface ForceDeleteDialogType {
@@ -3,6 +3,7 @@ import moment from 'moment'
3
3
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
4
4
  import { useLocation } from 'react-router-dom'
5
5
  import { toast } from 'react-toastify'
6
+ import { ReactComponent as FormError } from '../Assets/Icon/ic-warning.svg'
6
7
  import { ERROR_EMPTY_SCREEN, TOKEN_COOKIE_NAME } from './Constants'
7
8
  import { ServerErrors } from './ServerError'
8
9
  import { toastAccessDenied } from './ToastBody'
@@ -276,6 +277,64 @@ export function useForm(stateSchema, validationSchema = {}, callback) {
276
277
  return { state, disable, handleOnChange, handleOnSubmit }
277
278
  }
278
279
 
280
+ function handleError(error: any): any[] {
281
+ if (!error) {
282
+ return []
283
+ }
284
+
285
+ if (!Array.isArray(error)) {
286
+ return [error]
287
+ }
288
+
289
+ return error
290
+ }
291
+
292
+ export function CustomInput({
293
+ name,
294
+ value,
295
+ error,
296
+ onChange,
297
+ onBlur = (e) => {},
298
+ onFocus = (e) => {},
299
+ label,
300
+ type = 'text',
301
+ disabled = false,
302
+ autoComplete = 'off',
303
+ labelClassName = '',
304
+ placeholder = '',
305
+ tabIndex = 1,
306
+ dataTestid = '',
307
+ }) {
308
+ return (
309
+ <div className="flex column left top">
310
+ <label className={`form__label ${labelClassName}`}>{label}</label>
311
+ <input
312
+ data-testid={dataTestid}
313
+ type={type}
314
+ name={name}
315
+ autoComplete="off"
316
+ className="form__input"
317
+ onChange={(e) => {
318
+ e.persist()
319
+ onChange(e)
320
+ }}
321
+ onBlur={onBlur}
322
+ onFocus={onFocus}
323
+ placeholder={placeholder}
324
+ value={value}
325
+ disabled={disabled}
326
+ tabIndex={tabIndex}
327
+ />
328
+ {handleError(error).map((err) => (
329
+ <div className="form__error" key={err}>
330
+ <FormError className="form__icon form__icon--error" />
331
+ {err}
332
+ </div>
333
+ ))}
334
+ </div>
335
+ )
336
+ }
337
+
279
338
  export function handleUTCTime(ts: string, isRelativeTime = false) {
280
339
  let timestamp = ''
281
340
  try {
@@ -293,6 +293,11 @@ export interface CDMaterialServiceQueryParams {
293
293
  filter?: CDMaterialFilterQuery
294
294
  }
295
295
 
296
+ export interface DownstreamNodesEnvironmentsType {
297
+ environmentId: number
298
+ environmentName: string
299
+ }
300
+
296
301
  export interface CommonNodeAttr {
297
302
  connectingCiPipelineId?: number
298
303
  parents: string | number[] | string[]
@@ -354,6 +359,9 @@ export interface CommonNodeAttr {
354
359
  }
355
360
  appReleaseTagNames?: string[]
356
361
  tagsEditable?: boolean
362
+ deploymentAppCreated?: boolean
363
+ isLast?: boolean
364
+ downstreamEnvironments?: DownstreamNodesEnvironmentsType[]
357
365
  }
358
366
 
359
367
  export enum DeploymentAppTypes {
@@ -622,4 +630,52 @@ export interface CiPipeline {
622
630
  }
623
631
  isOffendingMandatoryPlugin?: boolean
624
632
  pipelineType?: string
625
- }
633
+ }
634
+
635
+ export enum PipelineType {
636
+ CI_PIPELINE = 'CI_PIPELINE',
637
+ CD_PIPELINE = 'CD_PIPELINE',
638
+ WEBHOOK = 'WEBHOOK',
639
+ LINKED_CD = 'LINKED_CD',
640
+ }
641
+
642
+ export enum WorkflowNodeType {
643
+ GIT = 'GIT',
644
+ CI = 'CI',
645
+ WEBHOOK = 'WEBHOOK',
646
+ PRE_CD = 'PRECD',
647
+ CD = 'CD',
648
+ POST_CD = 'POSTCD',
649
+ }
650
+
651
+ export enum AddCDPositions {
652
+ LEFT = 'left',
653
+ RIGHT = 'right',
654
+ }
655
+
656
+ export interface SelectedNode {
657
+ nodeType: WorkflowNodeType
658
+ id: string
659
+ }
660
+
661
+ export enum AddPipelineType {
662
+ SEQUENTIAL = 'SEQUENTIAL',
663
+ PARALLEL = 'PARALLEL',
664
+ }
665
+
666
+ export interface Point {
667
+ x: number
668
+ y: number
669
+ }
670
+
671
+ export interface EdgeNodeType {
672
+ height: number
673
+ width: number
674
+ userApprovalConfig?: UserApprovalConfigType
675
+ type?: any
676
+ id?: number | string
677
+ }
678
+
679
+ export interface EdgeEndNodeType extends EdgeNodeType {
680
+ userApprovalConfig?: UserApprovalConfigType
681
+ }
@@ -42,4 +42,4 @@ export * from './Hooks'
42
42
  export { AppStatus } from './AppStatus'
43
43
  export * from './RJSF'
44
44
  export * from './DevtronProgressing'
45
- export * from './CustomInput'
45
+ export * from './AddCDButton'
@@ -1,3 +0,0 @@
1
- /// <reference types="react" />
2
- import { CustomInputProps } from './Types';
3
- export declare function CustomInput({ name, value, error, onChange, onFocus, label, type, disabled, labelClassName, placeholder, tabIndex, dataTestid, isRequiredField, autoFocus, rootClassName, autoComplete, showLink, linkText, link, helperText, handleOnBlur, readOnly, noTrim, ref, onKeyPress, defaultValue, onKeyDown, required, additionalErrorInfo }: CustomInputProps): JSX.Element;
@@ -1,32 +0,0 @@
1
- import { HTMLInputTypeAttribute } from "react";
2
- export interface CustomInputProps {
3
- name: string;
4
- value: string | number;
5
- onChange: (e: any) => void;
6
- onFocus?: (e: any) => void;
7
- autoComplete?: string;
8
- label?: React.ReactNode;
9
- labelClassName?: string;
10
- type?: HTMLInputTypeAttribute;
11
- disabled?: boolean;
12
- placeholder?: string;
13
- tabIndex?: number;
14
- dataTestid?: string;
15
- isRequiredField?: boolean;
16
- autoFocus?: boolean;
17
- rootClassName?: string;
18
- showLink?: boolean;
19
- link?: string;
20
- linkText?: string;
21
- error?: string[] | string;
22
- helperText?: string;
23
- handleOnBlur?: (e: any) => void;
24
- readOnly?: boolean;
25
- noTrim?: boolean;
26
- ref?: React.LegacyRef<HTMLInputElement>;
27
- onKeyPress?: (e: any) => void;
28
- defaultValue?: string | number | ReadonlyArray<string> | undefined;
29
- onKeyDown?: (e: any) => void;
30
- required?: boolean;
31
- additionalErrorInfo?: React.ReactNode;
32
- }
@@ -1,2 +0,0 @@
1
- export * from './CustomInput';
2
- export * from './Types';
@@ -1,131 +0,0 @@
1
- import { CustomInputProps } from './Types'
2
- import { ReactComponent as Info } from '../../Assets/Icon/ic-info-filled.svg'
3
- import { ReactComponent as FormError } from '../../Assets/Icon/ic-warning.svg'
4
-
5
- export function CustomInput({
6
- name,
7
- value,
8
- error,
9
- onChange,
10
- onFocus = (e) => {},
11
- label = '',
12
- type = 'text',
13
- disabled = false,
14
- labelClassName = '',
15
- placeholder = '',
16
- tabIndex = 1,
17
- dataTestid = '',
18
- isRequiredField = false,
19
- autoFocus = false,
20
- rootClassName = '',
21
- autoComplete = 'off',
22
- showLink = false,
23
- linkText = '',
24
- link = '',
25
- helperText = '',
26
- handleOnBlur,
27
- readOnly = false,
28
- noTrim = false,
29
- ref,
30
- onKeyPress,
31
- defaultValue,
32
- onKeyDown,
33
- required,
34
- additionalErrorInfo
35
- }: CustomInputProps) {
36
- const renderLabelHelperText = () => {
37
- return (
38
- <span>
39
- <a target="_blank" href={link} className="cursor fs-13 onlink ml-4">
40
- {linkText}
41
- </a>
42
- </span>
43
- )
44
- }
45
-
46
- function handleError(error: any): any[] {
47
- if (!error) {
48
- return []
49
- }
50
-
51
- if (!Array.isArray(error)) {
52
- return [error]
53
- }
54
-
55
- return error
56
- }
57
-
58
- const onBlur = (event) => {
59
- //NOTE: This is to prevent the input from being trimmed when the user do not want to trim the input
60
- if (!noTrim) {
61
- event.stopPropagation()
62
- event.target.value = event.target.value?.trim()
63
- onChange(event)
64
- }
65
- if (typeof handleOnBlur === 'function') {
66
- handleOnBlur(event)
67
- }
68
- }
69
-
70
- const renderFormError = () => {
71
- if (error?.length > 0) {
72
- if (typeof error === 'object') {
73
- return handleError(error).map((err: string) => (
74
- <div className="form__error" key={err}>
75
- <FormError className="form__icon form__icon--error" />
76
- {err}{typeof additionalErrorInfo === "function" && additionalErrorInfo() }
77
- </div>
78
- ))
79
- }
80
- return (
81
- <div className="form__error">
82
- <FormError className="form__icon form__icon--error" />
83
- {error}
84
- </div>
85
- )
86
- }
87
- }
88
-
89
- return (
90
- <div className="flex column left top">
91
- {label && (
92
- <label className={`form__label ${labelClassName}`} data-testid={`label-${dataTestid}`}>
93
- <span className={`${isRequiredField ? 'dc__required-field' : ''}`}>{label}</span>
94
- {showLink && renderLabelHelperText()}
95
- </label>
96
- )}
97
- <input
98
- data-testid={dataTestid}
99
- type={type}
100
- name={name}
101
- autoComplete={autoComplete}
102
- className={`form__input ${rootClassName}`}
103
- onChange={(e) => {
104
- e.persist()
105
- onChange(e)
106
- }}
107
- onBlur={onBlur}
108
- onFocus={onFocus}
109
- placeholder={placeholder}
110
- value={value}
111
- disabled={disabled}
112
- tabIndex={tabIndex}
113
- autoFocus={autoFocus}
114
- readOnly={readOnly}
115
- ref={ref}
116
- onKeyPress={onKeyPress}
117
- defaultValue={defaultValue}
118
- onKeyDown={onKeyDown}
119
- required={required}
120
- />
121
-
122
- {renderFormError()}
123
- {helperText && (
124
- <div className="form__text-field-info">
125
- <Info className="form__icon form__icon--info" />
126
- <p className="sentence-case">{helperText}</p>
127
- </div>
128
- )}
129
- </div>
130
- )
131
- }
@@ -1,33 +0,0 @@
1
- import { HTMLInputTypeAttribute } from "react"
2
-
3
- export interface CustomInputProps {
4
- name: string
5
- value: string | number
6
- onChange: (e: any) => void
7
- onFocus?: (e: any) => void
8
- autoComplete?: string
9
- label?: React.ReactNode
10
- labelClassName?: string
11
- type?: HTMLInputTypeAttribute
12
- disabled?: boolean
13
- placeholder?: string
14
- tabIndex?: number
15
- dataTestid?: string
16
- isRequiredField?: boolean
17
- autoFocus?: boolean
18
- rootClassName?: string
19
- showLink?: boolean
20
- link?: string
21
- linkText?: string
22
- error?: string[] | string
23
- helperText?: string;
24
- handleOnBlur?: (e) => void
25
- readOnly?: boolean
26
- noTrim?: boolean
27
- ref?: React.LegacyRef<HTMLInputElement>
28
- onKeyPress?: (e) => void
29
- defaultValue?: string | number | ReadonlyArray<string> | undefined;
30
- onKeyDown?: (e) => void
31
- required?: boolean
32
- additionalErrorInfo?: React.ReactNode
33
- }
@@ -1,2 +0,0 @@
1
- export * from './CustomInput'
2
- export * from './Types'