@lunit/oui 2.2.30 → 2.3.0
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/presentations/AnalysisBar/AnalysisBar.d.ts +1 -1
- package/dist/presentations/AnalysisBar/AnalysisBar.js +2 -2
- package/dist/presentations/AnalysisBar/AnalysisBar.types.d.ts +1 -0
- package/dist/presentations/ModelType/ModelType.d.ts +3 -0
- package/dist/presentations/ModelType/ModelType.js +43 -0
- package/dist/presentations/ModelType/ModelType.styled.d.ts +4 -0
- package/dist/presentations/ModelType/ModelType.styled.js +6 -0
- package/dist/presentations/ModelType/ModelType.types.d.ts +31 -0
- package/dist/presentations/ModelType/ModelType.types.js +1 -0
- package/dist/presentations/ModelType/index.d.ts +4 -0
- package/dist/presentations/ModelType/index.js +2 -0
- package/dist/presentations/Preview/ModelTypeControl.d.ts +3 -0
- package/dist/presentations/Preview/ModelTypeControl.js +28 -0
- package/dist/presentations/Preview/Preview.js +2 -1
- package/dist/presentations/Preview/Preview.types.d.ts +2 -1
- package/dist/presentations/Score/Score.js +1 -1
- package/dist/presentations/Threshold/Threshold.js +3 -1
- package/dist/presentations/ToggleControl/ToggleControl.js +1 -1
- package/dist/presentations/index.d.ts +1 -0
- package/dist/presentations/index.js +1 -0
- package/dist/presentations/presentations.styled.js +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import type { AnalysisBarProps } from './AnalysisBar.types';
|
|
2
|
-
declare const AnalysisBar: ({ componentType, expressionValue, caption, expressionLevels, segments, isDimmed, }: AnalysisBarProps) => import("react/jsx-runtime").JSX.Element;
|
|
2
|
+
declare const AnalysisBar: ({ componentType, expressionValue, caption, expressionLevels, expressionLabels, segments, isDimmed, }: AnalysisBarProps) => import("react/jsx-runtime").JSX.Element;
|
|
3
3
|
export default AnalysisBar;
|
|
@@ -17,7 +17,7 @@ const Segments = ({ segments, adjustForOnePercent }) => {
|
|
|
17
17
|
return (_jsx(GraphSegment, { color: segment.color, width: adjustForOnePercent ? adjustBarSegmentWidth(segment.width) : segment.width }, i));
|
|
18
18
|
}) }));
|
|
19
19
|
};
|
|
20
|
-
const AnalysisBar = ({ componentType = 'analysisBar', expressionValue, caption, expressionLevels = [], segments, isDimmed = false, }) => {
|
|
20
|
+
const AnalysisBar = ({ componentType = 'analysisBar', expressionValue, caption, expressionLevels = [], expressionLabels = [], segments, isDimmed = false, }) => {
|
|
21
21
|
if (!componentType)
|
|
22
22
|
throw new Error("The 'componentType' prop is required.");
|
|
23
23
|
if (!expressionLevels.length)
|
|
@@ -25,7 +25,7 @@ const AnalysisBar = ({ componentType = 'analysisBar', expressionValue, caption,
|
|
|
25
25
|
const hasAdjustedOnePercentSegment = segments?.[0].width === 1;
|
|
26
26
|
return (_jsxs(AnalysisBarOuter, { className: "analysis-bar", isDimmed: isDimmed, children: [_jsxs(AnalysisBarSegmentContainer, { children: [expressionValue && (_jsx(GraphScoreIndicator, { adjustForOnePercent: hasAdjustedOnePercentSegment, value: expressionValue, isJet: segments?.[0].color === 'jet' })), _jsx(Segments, { segments: segments, adjustForOnePercent: hasAdjustedOnePercentSegment })] }), (expressionLevels.length > 0 || caption) && (_jsxs(AxisLabels, { children: [expressionLevels.length > 0 &&
|
|
27
27
|
expressionLevels.map((val, i) => {
|
|
28
|
-
return (_jsx(GraphAxisLabel, { label: val, adjustForOnePercent: hasAdjustedOnePercentSegment, leftPos: parseStringValToNumber(val) }, i));
|
|
28
|
+
return (_jsx(GraphAxisLabel, { label: expressionLabels[i] || val, adjustForOnePercent: hasAdjustedOnePercentSegment, leftPos: parseStringValToNumber(val) }, i));
|
|
29
29
|
}), caption && (_jsx(GraphAxisLabel, { label: caption, adjustForOnePercent: hasAdjustedOnePercentSegment, leftPos: 50 }))] }))] }));
|
|
30
30
|
};
|
|
31
31
|
export default AnalysisBar;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { ModelTypeProps } from './ModelType.types';
|
|
2
|
+
declare const ModelType: ({ componentType, title, optionTitle, scoreItems, currentSensitivity, currentSpecificity, userModelType, options, description, onChangeDropdown, onClickUpdateConfiguration, }: ModelTypeProps) => import("react/jsx-runtime").JSX.Element;
|
|
3
|
+
export default ModelType;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useState } from 'react';
|
|
3
|
+
import { v4 as uuidV4 } from 'uuid';
|
|
4
|
+
import { Score } from '../Score';
|
|
5
|
+
import { Container } from './ModelType.styled';
|
|
6
|
+
import { FreeText } from '../FreeText';
|
|
7
|
+
import { HorizonDivider } from '../HorizonDivider';
|
|
8
|
+
import { PresentationTypography } from '../presentations.styled';
|
|
9
|
+
import { Button, Dropdown, DropdownItem, DropdownSubtitle } from '../../components';
|
|
10
|
+
const ModelType = ({ componentType = 'modelType', title, optionTitle, scoreItems, currentSensitivity, currentSpecificity, userModelType, options, description, onChangeDropdown, onClickUpdateConfiguration, }) => {
|
|
11
|
+
if (!componentType)
|
|
12
|
+
throw new Error("The 'componentType' prop is required.");
|
|
13
|
+
if (!title)
|
|
14
|
+
throw new Error("The 'title' prop is required.");
|
|
15
|
+
if (!Object.keys(scoreItems).length)
|
|
16
|
+
throw new Error("The 'scoreItems' prop must contain at least one item.");
|
|
17
|
+
if (!currentSensitivity)
|
|
18
|
+
throw new Error("The 'currentSensitivity' prop is required.");
|
|
19
|
+
if (!currentSpecificity)
|
|
20
|
+
throw new Error("The 'currentSpecificity' prop is required.");
|
|
21
|
+
if (!options.length)
|
|
22
|
+
throw new Error("The 'options' prop must contain at least one item.");
|
|
23
|
+
const [selectedItem, setSelectedItem] = useState(userModelType);
|
|
24
|
+
const [isModelTypeChanged, setIsModelTypeChanged] = useState(false);
|
|
25
|
+
const handleDropdownChange = (event) => {
|
|
26
|
+
onChangeDropdown(event);
|
|
27
|
+
const newOption = options.find((option) => option.target.annotation.dimension === event.target.value)?.target.annotation.dimension;
|
|
28
|
+
setSelectedItem(newOption);
|
|
29
|
+
setIsModelTypeChanged(userModelType !== newOption);
|
|
30
|
+
};
|
|
31
|
+
const handleUpdateConfiguration = () => {
|
|
32
|
+
onClickUpdateConfiguration();
|
|
33
|
+
setIsModelTypeChanged(false);
|
|
34
|
+
};
|
|
35
|
+
return (_jsxs(Container, { children: [scoreItems[selectedItem].map((item) => {
|
|
36
|
+
return (_jsx(Score, { ...item }, uuidV4()));
|
|
37
|
+
}), _jsx(HorizonDivider, { componentType: "divider" }), _jsx(PresentationTypography, { size: "medium-bold", children: title }), _jsx(Score, { ...currentSensitivity }), _jsx(Score, { ...currentSpecificity }), _jsxs(Dropdown, { value: selectedItem, onChange: handleDropdownChange, sx: { width: '258px' }, children: [optionTitle && _jsx(DropdownSubtitle, { label: optionTitle }), options.map((option) => {
|
|
38
|
+
return (_jsx(DropdownItem, { value: option.target.annotation.dimension, children: option.title }, option.target.annotation.dimension));
|
|
39
|
+
})] }), _jsx(Button, { id: "update-threshold-config-btn", label: "Update Configuration", sx: {
|
|
40
|
+
width: '100%',
|
|
41
|
+
}, disabled: !isModelTypeChanged, onClick: handleUpdateConfiguration }), description && description.length && (_jsx(FreeText, { componentType: "freeText", size: "small-dimmed", content: description }))] }));
|
|
42
|
+
};
|
|
43
|
+
export default ModelType;
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
export declare const Container: import("@emotion/styled").StyledComponent<import("@mui/system").BoxOwnProps<import("@mui/material").Theme> & Omit<Omit<import("react").DetailedHTMLProps<import("react").HTMLAttributes<HTMLDivElement>, HTMLDivElement>, "ref"> & {
|
|
3
|
+
ref?: ((instance: HTMLDivElement | null) => void) | import("react").RefObject<HTMLDivElement> | null | undefined;
|
|
4
|
+
}, keyof import("@mui/system").BoxOwnProps<import("@mui/material").Theme>> & import("@mui/system").MUIStyledCommonProps<import("@mui/material").Theme>, {}, {}>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { SelectChangeEvent } from '@mui/material/Select';
|
|
2
|
+
import { ScoreProps } from '../Score';
|
|
3
|
+
interface ModelTypeOption {
|
|
4
|
+
title: string;
|
|
5
|
+
target: {
|
|
6
|
+
visualizationType: string;
|
|
7
|
+
annotation: {
|
|
8
|
+
name: string;
|
|
9
|
+
dimension: string;
|
|
10
|
+
color: string;
|
|
11
|
+
};
|
|
12
|
+
};
|
|
13
|
+
sensitivity: number;
|
|
14
|
+
specificity: number;
|
|
15
|
+
}
|
|
16
|
+
export interface ModelTypeSpec {
|
|
17
|
+
componentType: 'modelType';
|
|
18
|
+
title: string;
|
|
19
|
+
optionTitle?: string;
|
|
20
|
+
scoreItems: Record<string, ScoreProps[]>;
|
|
21
|
+
currentSensitivity: ScoreProps;
|
|
22
|
+
currentSpecificity: ScoreProps;
|
|
23
|
+
options: ModelTypeOption[];
|
|
24
|
+
userModelType: string;
|
|
25
|
+
description?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface ModelTypeProps extends ModelTypeSpec {
|
|
28
|
+
onChangeDropdown: (event: SelectChangeEvent) => void;
|
|
29
|
+
onClickUpdateConfiguration: () => void;
|
|
30
|
+
}
|
|
31
|
+
export {};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
3
|
+
import { find as _find } from 'lodash';
|
|
4
|
+
import { ModelType } from '../ModelType';
|
|
5
|
+
const ModelTypeControl = (modelType) => {
|
|
6
|
+
const [modelTypeState, setModelTypeState] = useState(modelType);
|
|
7
|
+
const updateModelType = useCallback((type) => {
|
|
8
|
+
const currentModelType = { ...modelType };
|
|
9
|
+
const currentSensitivity = currentModelType.options.find((option) => option.target.annotation.dimension === type)?.sensitivity;
|
|
10
|
+
const currentSpecificity = currentModelType.options.find((option) => option.target.annotation.dimension === type)?.specificity;
|
|
11
|
+
currentModelType.currentSensitivity.metrics.value = currentSensitivity?.toString() || '0.0';
|
|
12
|
+
currentModelType.currentSpecificity.metrics.value = currentSpecificity?.toString() || '0.0';
|
|
13
|
+
setModelTypeState(currentModelType);
|
|
14
|
+
}, [modelType]);
|
|
15
|
+
useEffect(() => {
|
|
16
|
+
const item = _find(modelType.options, ['target.annotation.dimension', 'egfrBest']);
|
|
17
|
+
console.log(`default item: pixelAnnotations.${item?.target.annotation.name}.${item?.target.annotation.dimension}`);
|
|
18
|
+
updateModelType('egfrBest');
|
|
19
|
+
}, [updateModelType]);
|
|
20
|
+
const onChangeDropdown = (event) => {
|
|
21
|
+
const item = _find(modelType.options, ['target.annotation.dimension', event.target.value]);
|
|
22
|
+
console.log(`pixelAnnotations.${item?.target.annotation.name}.${item?.target.annotation.dimension}`);
|
|
23
|
+
updateModelType(event.target.value);
|
|
24
|
+
};
|
|
25
|
+
const onClickUpdateConfiguration = () => { };
|
|
26
|
+
return (_jsx(ModelType, { ...modelTypeState, userModelType: 'egfrBest', onChangeDropdown: onChangeDropdown, onClickUpdateConfiguration: onClickUpdateConfiguration }));
|
|
27
|
+
};
|
|
28
|
+
export default ModelTypeControl;
|
|
@@ -13,6 +13,7 @@ import { Score } from '../Score';
|
|
|
13
13
|
import { FreeText } from '../FreeText';
|
|
14
14
|
import { Histogram } from '../Histogram';
|
|
15
15
|
import ThresholdControl from './ThresholdControl';
|
|
16
|
+
import ModelTypeControl from './ModelTypeControl';
|
|
16
17
|
const Preview = ({ visualizationControls = [], analysisSummary = [] }) => {
|
|
17
18
|
const [controls, setControls] = useState(visualizationControls);
|
|
18
19
|
return (_jsxs(Container, { children: [_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }, children: visualizationControls.map((item, index) => {
|
|
@@ -55,6 +56,6 @@ const Preview = ({ visualizationControls = [], analysisSummary = [] }) => {
|
|
|
55
56
|
});
|
|
56
57
|
}
|
|
57
58
|
return (_jsxs(Box, { children: [item.componentType === 'toggle' && (_jsx(ToggleControl, { ...item }, uuidV4())), item.componentType === 'toggleGroup' && (_jsx(ToggleControlGroup, { ...item }, uuidV4())), item.componentType === 'analysisBar' && (_jsx(AnalysisBar, { ...item }, uuidV4()))] }, uuidV4()));
|
|
58
|
-
}) }), _jsx(HorizonDivider, { componentType: "divider" }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }, children: [_jsx(FreeText, { componentType: "freeText", size: "medium-bold", content: "Analysis Summary" }), analysisSummary.map((item) => (_jsxs(Box, { children: [item.componentType === 'accordion' && (_jsx(Accordion, { ...item }, uuidV4())), item.componentType === 'analysisBar' && (_jsx(AnalysisBar, { ...item }, uuidV4())), item.componentType === 'statistic' && (_jsx(Statistic, { ...item }, uuidV4())), item.componentType === 'score' && (_jsx(Score, { ...item }, uuidV4())), item.componentType === 'freeText' && (_jsx(FreeText, { ...item }, uuidV4())), item.componentType === 'threshold' && (_jsx(ThresholdControl, { ...item }, uuidV4())), item.componentType === 'histogram' && (_jsx(Histogram, { ...item }, uuidV4()))] }, uuidV4())))] })] }));
|
|
59
|
+
}) }), _jsx(HorizonDivider, { componentType: "divider" }), _jsxs(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '16px', padding: '16px' }, children: [_jsx(FreeText, { componentType: "freeText", size: "medium-bold", content: "Analysis Summary" }), analysisSummary.map((item) => (_jsxs(Box, { children: [item.componentType === 'accordion' && (_jsx(Accordion, { ...item }, uuidV4())), item.componentType === 'analysisBar' && (_jsx(AnalysisBar, { ...item }, uuidV4())), item.componentType === 'statistic' && (_jsx(Statistic, { ...item }, uuidV4())), item.componentType === 'score' && (_jsx(Score, { ...item }, uuidV4())), item.componentType === 'freeText' && (_jsx(FreeText, { ...item }, uuidV4())), item.componentType === 'threshold' && (_jsx(ThresholdControl, { ...item }, uuidV4())), item.componentType === 'histogram' && (_jsx(Histogram, { ...item }, uuidV4())), item.componentType === 'modelType' && (_jsx(ModelTypeControl, { ...item }, uuidV4()))] }, uuidV4())))] })] }));
|
|
59
60
|
};
|
|
60
61
|
export default Preview;
|
|
@@ -7,8 +7,9 @@ import { AnalysisBarProps } from '../AnalysisBar';
|
|
|
7
7
|
import { FreeTextProps } from '../FreeText';
|
|
8
8
|
import { ThresholdProps } from '../Threshold';
|
|
9
9
|
import { HistogramProps } from '../Histogram';
|
|
10
|
+
import { ModelTypeProps } from '../ModelType';
|
|
10
11
|
interface PreviewProps {
|
|
11
12
|
visualizationControls: (ToggleControlProps | ToggleControlGroupProps | AnalysisBarProps)[];
|
|
12
|
-
analysisSummary: (AccordionProps | AnalysisBarProps | StatisticProps | ScoreProps | FreeTextProps | ThresholdProps | HistogramProps)[];
|
|
13
|
+
analysisSummary: (AccordionProps | AnalysisBarProps | StatisticProps | ScoreProps | FreeTextProps | ThresholdProps | HistogramProps | ModelTypeProps)[];
|
|
13
14
|
}
|
|
14
15
|
export type { PreviewProps };
|
|
@@ -10,6 +10,6 @@ const Score = ({ componentType = 'score', title, size = 'medium', metrics }) =>
|
|
|
10
10
|
throw new Error("The 'title' prop is required.");
|
|
11
11
|
if (!metrics)
|
|
12
12
|
throw new Error("The 'metrics' prop is required.");
|
|
13
|
-
return (
|
|
13
|
+
return (_jsx(PresentationTypography, { size: size, children: _jsxs("span", { style: { color: metrics.isFullColored ? metrics.color : 'inherit' }, children: [title, ": ", metrics.value, metrics.scoreType && (_jsx("span", { style: { color: metrics.color, marginLeft: 4 }, children: capitalize(metrics.scoreType) }))] }) }));
|
|
14
14
|
};
|
|
15
15
|
export default Score;
|
|
@@ -25,7 +25,9 @@ const Threshold = ({ componentType = 'threshold', title, totalScore, totalCells,
|
|
|
25
25
|
const marks = getSliderMarks(presetValue);
|
|
26
26
|
const handleDropdownChange = (event) => {
|
|
27
27
|
onChangeDropdown(event);
|
|
28
|
-
|
|
28
|
+
const newOption = options.find((option) => option.title === event.target.value)?.value;
|
|
29
|
+
setSelectedOptionValue(newOption);
|
|
30
|
+
setIsThresholdChanged(false);
|
|
29
31
|
};
|
|
30
32
|
const handleSliderChange = (event, value) => {
|
|
31
33
|
onChangeSlider(event, value);
|
|
@@ -22,6 +22,6 @@ const ToggleControl = ({ componentType = 'toggle', title, size = 'medium', symbo
|
|
|
22
22
|
return (_jsxs(ToggleControlOuter, { "data-toggle-name": `toggle-control-${title?.replace(/\s/g, '-')}`, hasBar: !!bar || !!content.length, style: { marginBottom: isParent ? '4px' : '0' }, children: [_jsxs(ToggleControlContainer, { className: "toggle-inner-container", size: size, children: [_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: '6px' }, className: "toggle-label", children: [!!tooltip.length && (_jsx(Tooltip, { arrow: true, size: "large", title: _jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '8px' }, children: tooltip.map((item) => (_jsxs(Box, { children: [item.componentType === 'freeText' && (_jsx(FreeText, { componentType: item.componentType, size: item.size, content: item.content })), item.componentType === 'statistic' && (_jsx(Statistic, { componentType: item.componentType, title: item.title, size: item.size, layout: item.layout, symbol: item.symbol, color: item.color }))] }, uuidV4()))) }), children: _jsx(Box, { sx: {
|
|
23
23
|
display: 'flex',
|
|
24
24
|
alignItems: 'center',
|
|
25
|
-
}, children: _jsx(Information, {}) }) })), symbol && (_jsx(PresentationSymbolIcon, { symbol: symbol, color: color, isDimmed: isDimmed || !checked })), _jsx(PresentationTypography, { size: size, isDimmed: isDimmed || !checked, children: title })] }), _jsx(StyledToggle, { disabled: disabled || isDimmed, checked: checked, toggleColor: theme.palette.lunitTeal[70], onChange: onChange })] }), content.length > 0 && (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '8px' }, children: content.map((item) => (_jsx(Statistic, { ...item, isDimmed: isDimmed || !checked }, uuidV4()))) }, uuidV4())), bar && (_jsx(Box, { sx: { padding: '0 8px', fontSize: '14px', lineHeight: '8px' }, children: _jsx(AnalysisBar, { isDimmed: isDimmed || !checked, componentType: bar.componentType, expressionValue: bar.expressionValue, expressionLevels: bar.expressionLevels, caption: bar.caption, segments: bar.segments }) }))] }));
|
|
25
|
+
}, children: _jsx(Information, {}) }) })), symbol && (_jsx(PresentationSymbolIcon, { symbol: symbol, color: color, isDimmed: isDimmed || !checked })), _jsx(PresentationTypography, { size: size, isDimmed: isDimmed || !checked, children: title })] }), _jsx(StyledToggle, { disabled: disabled || isDimmed, checked: checked, toggleColor: theme.palette.lunitTeal[70], onChange: onChange })] }), content.length > 0 && (_jsx(Box, { sx: { display: 'flex', flexDirection: 'column', gap: '8px' }, children: content.map((item) => (_jsx(Statistic, { ...item, isDimmed: isDimmed || !checked }, uuidV4()))) }, uuidV4())), bar && (_jsx(Box, { sx: { padding: '0 8px', fontSize: '14px', lineHeight: '8px' }, children: _jsx(AnalysisBar, { isDimmed: isDimmed || !checked, componentType: bar.componentType, expressionValue: bar.expressionValue, expressionLevels: bar.expressionLevels, expressionLabels: bar.expressionLabels, caption: bar.caption, segments: bar.segments }) }))] }));
|
|
26
26
|
};
|
|
27
27
|
export default ToggleControl;
|
|
@@ -19,7 +19,7 @@ export const PresentationTypography = (props) => {
|
|
|
19
19
|
return (_jsx(Typography, { variant: "h9", ...rest, style: { color: isDimmed ? theme.palette.neutralGrey[40] : 'inherit' } }));
|
|
20
20
|
}
|
|
21
21
|
// Fallback to medium size
|
|
22
|
-
return (_jsx(Typography, { variant: "body5", ...rest,
|
|
22
|
+
return (_jsx(Typography, { variant: "body5", ...rest, style: { color: isDimmed ? theme.palette.neutralGrey[40] : 'inherit' } }));
|
|
23
23
|
};
|
|
24
24
|
export const PresentationSymbolIcon = ({ symbol, color = 'black', isDimmed = false, }) => {
|
|
25
25
|
const symbolColor = color?.length ? color : 'black';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lunit/oui",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.3.0",
|
|
4
4
|
"validate-branch-name": {
|
|
5
5
|
"pattern": "^(main|develop)|(feature|fix|release|hotfix|qe)/.+$",
|
|
6
6
|
"errorMsg": "The branch name is not correct. Please check the pattern. (ex. feature/add-something)"
|