mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-08 01:15:49 +02:00
fix: dark theme UI fixes (#3423)
https://linear.app/unleash/issue/2-849/dark-mode-ui-fixes    Also includes misc UI fixes and improvements we identified along the way. Co-authored by @NicolaeUnleash --------- Co-authored-by: NicolaeUnleash <103567375+NicolaeUnleash@users.noreply.github.com>
This commit is contained in:
parent
9db40f50cf
commit
2e6b6cd354
@ -1 +1 @@
|
|||||||
<svg id="bg" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 251.43 251.03"><defs><style>.cls-1{fill:#1a4049;}.cls-2{fill:#fff;}.cls-3{fill:#817afe;}</style></defs><circle class="cls-1" cx="125.71" cy="125.31" r="80"/><polygon class="cls-2" points="137.14 91.03 137.14 113.88 137.14 136.74 160 136.74 160 113.88 160 91.03 137.14 91.03"/><polygon class="cls-2" points="114.29 113.88 114.29 91.03 91.43 91.03 91.43 113.88 91.43 136.74 91.43 159.6 114.29 159.6 137.14 159.6 137.14 136.74 114.29 136.74 114.29 113.88"/><rect class="cls-3" x="137.14" y="136.74" width="22.86" height="22.86"/></svg>
|
<svg width="35" height="35" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M17.5 35C27.165 35 35 27.165 35 17.5S27.165 0 17.5 0 0 7.835 0 17.5 7.835 35 17.5 35Z" fill="#1A4049"/><path d="M15 10h-5v15h15V10h-5v10h-5V10Z" fill="#fff"/><path d="M20 20h5v5h-5v-5Z" fill="#817AFE"/></svg>
|
Before Width: | Height: | Size: 593 B After Width: | Height: | Size: 312 B |
@ -1,7 +1 @@
|
|||||||
<svg width="334" height="334" viewBox="0 0 334 334" fill="none" xmlns="http://www.w3.org/2000/svg">
|
<svg width="35" height="35" viewBox="0 0 35 35" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M17.5 35C27.165 35 35 27.165 35 17.5S27.165 0 17.5 0 0 7.835 0 17.5 7.835 35 17.5 35Z" fill="#fff"/><path d="M15 10h-5v15h15V10h-5v10h-5V10Z" fill="#1A4049"/><path d="M20 20h5v5h-5v-5Z" fill="#817AFE"/></svg>
|
||||||
<circle cx="167" cy="167" r="167" fill="white"/>
|
|
||||||
<rect x="96.3462" y="96.3457" width="44.9615" height="141.308" fill="#1A4049"/>
|
|
||||||
<rect x="192.692" y="96.3457" width="44.9615" height="141.308" fill="#1A4049"/>
|
|
||||||
<path d="M237.654 192.693L237.654 237.655L96.3461 237.655L96.3461 192.693L237.654 192.693Z" fill="#1A4049"/>
|
|
||||||
<rect x="192.692" y="192.693" width="44.9615" height="44.9615" fill="#817AFE"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 505 B After Width: | Height: | Size: 312 B |
@ -10,13 +10,18 @@ import {
|
|||||||
RequestsPerSecondSchemaDataResultItem,
|
RequestsPerSecondSchemaDataResultItem,
|
||||||
} from 'openapi';
|
} from 'openapi';
|
||||||
import logoIcon from 'assets/icons/logoBg.svg';
|
import logoIcon from 'assets/icons/logoBg.svg';
|
||||||
|
import logoWhiteIcon from 'assets/icons/logoWhiteBg.svg';
|
||||||
import { formatAssetPath } from 'utils/formatPath';
|
import { formatAssetPath } from 'utils/formatPath';
|
||||||
|
import { useThemeMode } from 'hooks/useThemeMode';
|
||||||
|
|
||||||
const StyledMermaid = styled(Mermaid)(({ theme }) => ({
|
const StyledMermaid = styled(Mermaid)(({ theme }) => ({
|
||||||
'#mermaid .node rect': {
|
'#mermaid .node rect': {
|
||||||
fill: theme.palette.secondary.light,
|
fill: theme.palette.secondary.light,
|
||||||
stroke: theme.palette.secondary.border,
|
stroke: theme.palette.secondary.border,
|
||||||
},
|
},
|
||||||
|
'#mermaid .unleash-logo': {
|
||||||
|
padding: theme.spacing(1),
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const isRecent = (value: ResultValue) => {
|
const isRecent = (value: ResultValue) => {
|
||||||
@ -79,6 +84,7 @@ const toGraphData = (metrics?: RequestsPerSecondSchema) => {
|
|||||||
|
|
||||||
export const NetworkOverview = () => {
|
export const NetworkOverview = () => {
|
||||||
usePageTitle('Network - Overview');
|
usePageTitle('Network - Overview');
|
||||||
|
const { themeMode } = useThemeMode();
|
||||||
const { metrics } = useInstanceMetrics();
|
const { metrics } = useInstanceMetrics();
|
||||||
const apps = useMemo(() => {
|
const apps = useMemo(() => {
|
||||||
return toGraphData(metrics);
|
return toGraphData(metrics);
|
||||||
@ -89,8 +95,8 @@ export const NetworkOverview = () => {
|
|||||||
subgraph _[ ]
|
subgraph _[ ]
|
||||||
direction BT
|
direction BT
|
||||||
Unleash(<img src='${formatAssetPath(
|
Unleash(<img src='${formatAssetPath(
|
||||||
logoIcon
|
themeMode === 'dark' ? logoWhiteIcon : logoIcon
|
||||||
)}' width='60' height='60' /><br/>Unleash)
|
)}' width='72' height='72' class='unleash-logo'/><br/>Unleash)
|
||||||
${apps
|
${apps
|
||||||
.map(
|
.map(
|
||||||
({ label, reqs, type }, i) =>
|
({ label, reqs, type }, i) =>
|
||||||
|
@ -18,16 +18,18 @@ import {
|
|||||||
ILocationSettings,
|
ILocationSettings,
|
||||||
useLocationSettings,
|
useLocationSettings,
|
||||||
} from 'hooks/useLocationSettings';
|
} from 'hooks/useLocationSettings';
|
||||||
import theme from 'themes/theme';
|
|
||||||
import { formatDateHM } from 'utils/formatDate';
|
import { formatDateHM } from 'utils/formatDate';
|
||||||
import { RequestsPerSecondSchema } from 'openapi';
|
import { RequestsPerSecondSchema } from 'openapi';
|
||||||
import 'chartjs-adapter-date-fns';
|
import 'chartjs-adapter-date-fns';
|
||||||
import { Alert } from '@mui/material';
|
import { Alert, useTheme } from '@mui/material';
|
||||||
import { Box } from '@mui/system';
|
import { Box } from '@mui/system';
|
||||||
import { CyclicIterator } from 'utils/cyclicIterator';
|
import { CyclicIterator } from 'utils/cyclicIterator';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { usePageTitle } from 'hooks/usePageTitle';
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
import { unknownify } from 'utils/unknownify';
|
import { unknownify } from 'utils/unknownify';
|
||||||
|
import { Theme } from '@mui/material/styles/createTheme';
|
||||||
|
|
||||||
|
const pointStyles = ['circle', 'rect', 'rectRounded', 'rectRot', 'triangle'];
|
||||||
|
|
||||||
interface IPoint {
|
interface IPoint {
|
||||||
x: number;
|
x: number;
|
||||||
@ -49,6 +51,7 @@ const createChartPoints = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const createInstanceChartOptions = (
|
const createInstanceChartOptions = (
|
||||||
|
theme: Theme,
|
||||||
locationSettings: ILocationSettings
|
locationSettings: ILocationSettings
|
||||||
): ChartOptions<'line'> => ({
|
): ChartOptions<'line'> => ({
|
||||||
locale: locationSettings.locale,
|
locale: locationSettings.locale,
|
||||||
@ -58,6 +61,7 @@ const createInstanceChartOptions = (
|
|||||||
mode: 'index',
|
mode: 'index',
|
||||||
intersect: false,
|
intersect: false,
|
||||||
},
|
},
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
plugins: {
|
plugins: {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
@ -78,12 +82,13 @@ const createInstanceChartOptions = (
|
|||||||
itemSort: (a, b) => b.parsed.y - a.parsed.y,
|
itemSort: (a, b) => b.parsed.y - a.parsed.y,
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
position: 'top',
|
position: 'bottom',
|
||||||
align: 'end',
|
align: 'start',
|
||||||
labels: {
|
labels: {
|
||||||
boxWidth: 10,
|
boxWidth: 10,
|
||||||
boxHeight: 10,
|
boxHeight: 10,
|
||||||
usePointStyle: true,
|
usePointStyle: true,
|
||||||
|
padding: 24,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
title: {
|
title: {
|
||||||
@ -95,6 +100,10 @@ const createInstanceChartOptions = (
|
|||||||
size: 16,
|
size: 16,
|
||||||
weight: '400',
|
weight: '400',
|
||||||
},
|
},
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
padding: {
|
||||||
|
bottom: 32,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
@ -103,15 +112,24 @@ const createInstanceChartOptions = (
|
|||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: 'Requests per second',
|
text: 'Requests per second',
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
},
|
},
|
||||||
// min: 0,
|
// min: 0,
|
||||||
suggestedMin: 0,
|
suggestedMin: 0,
|
||||||
ticks: { precision: 0 },
|
ticks: { precision: 0, color: theme.palette.text.secondary },
|
||||||
|
grid: {
|
||||||
|
color: theme.palette.divider,
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
type: 'time',
|
type: 'time',
|
||||||
time: { unit: 'minute' },
|
time: { unit: 'minute' },
|
||||||
grid: { display: true },
|
grid: {
|
||||||
|
display: true,
|
||||||
|
color: theme.palette.divider,
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
},
|
||||||
ticks: {
|
ticks: {
|
||||||
callback: (_, i, data) =>
|
callback: (_, i, data) =>
|
||||||
formatDateHM(data[i].value * 1000, locationSettings.locale),
|
formatDateHM(data[i].value * 1000, locationSettings.locale),
|
||||||
@ -135,7 +153,10 @@ class ItemPicker<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const toChartData = (rps?: RequestsPerSecondSchema): ChartDatasetType[] => {
|
const toChartData = (
|
||||||
|
theme: Theme,
|
||||||
|
rps?: RequestsPerSecondSchema
|
||||||
|
): ChartDatasetType[] => {
|
||||||
if (rps?.data?.result) {
|
if (rps?.data?.result) {
|
||||||
const colorPicker = new ItemPicker([
|
const colorPicker = new ItemPicker([
|
||||||
theme.palette.success,
|
theme.palette.success,
|
||||||
@ -143,7 +164,7 @@ const toChartData = (rps?: RequestsPerSecondSchema): ChartDatasetType[] => {
|
|||||||
theme.palette.primary,
|
theme.palette.primary,
|
||||||
theme.palette.warning,
|
theme.palette.warning,
|
||||||
]);
|
]);
|
||||||
return rps.data.result.map(dataset => {
|
return rps.data.result.map((dataset, i) => {
|
||||||
const endpoint = unknownify(dataset.metric?.endpoint);
|
const endpoint = unknownify(dataset.metric?.endpoint);
|
||||||
const appName = unknownify(dataset.metric?.appName);
|
const appName = unknownify(dataset.metric?.appName);
|
||||||
const color = colorPicker.pick(endpoint);
|
const color = colorPicker.pick(endpoint);
|
||||||
@ -156,7 +177,7 @@ const toChartData = (rps?: RequestsPerSecondSchema): ChartDatasetType[] => {
|
|||||||
elements: {
|
elements: {
|
||||||
point: {
|
point: {
|
||||||
radius: 4,
|
radius: 4,
|
||||||
pointStyle: 'circle',
|
pointStyle: pointStyles[i % pointStyles.length],
|
||||||
},
|
},
|
||||||
line: {
|
line: {
|
||||||
borderDash: [8, 4],
|
borderDash: [8, 4],
|
||||||
@ -171,15 +192,16 @@ const toChartData = (rps?: RequestsPerSecondSchema): ChartDatasetType[] => {
|
|||||||
export const NetworkTraffic: VFC = () => {
|
export const NetworkTraffic: VFC = () => {
|
||||||
const { locationSettings } = useLocationSettings();
|
const { locationSettings } = useLocationSettings();
|
||||||
const { metrics } = useInstanceMetrics();
|
const { metrics } = useInstanceMetrics();
|
||||||
|
const theme = useTheme();
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
return createInstanceChartOptions(locationSettings);
|
return createInstanceChartOptions(theme, locationSettings);
|
||||||
}, [locationSettings]);
|
}, [theme, locationSettings]);
|
||||||
|
|
||||||
usePageTitle('Network - Traffic');
|
usePageTitle('Network - Traffic');
|
||||||
|
|
||||||
const data = useMemo(() => {
|
const data = useMemo(() => {
|
||||||
return { datasets: toChartData(metrics) };
|
return { datasets: toChartData(theme, metrics) };
|
||||||
}, [metrics, locationSettings]);
|
}, [theme, metrics, locationSettings]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
|
@ -30,14 +30,14 @@ const StyledBar = styled(Paper)(({ theme }) => ({
|
|||||||
marginRight: 'auto',
|
marginRight: 'auto',
|
||||||
padding: theme.spacing(2, 3),
|
padding: theme.spacing(2, 3),
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
border: `1px solid ${theme.palette.secondary.main}`,
|
border: `1px solid ${theme.palette.background.alternative}`,
|
||||||
borderRadius: theme.shape.borderRadiusLarge,
|
borderRadius: theme.shape.borderRadiusLarge,
|
||||||
gap: theme.spacing(1),
|
gap: theme.spacing(1),
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const StyledCount = styled('span')(({ theme }) => ({
|
const StyledCount = styled('span')(({ theme }) => ({
|
||||||
background: theme.palette.secondary.main,
|
background: theme.palette.background.alternative,
|
||||||
color: theme.palette.common.white,
|
color: theme.palette.common.white,
|
||||||
padding: theme.spacing(0.5, 1),
|
padding: theme.spacing(0.5, 1),
|
||||||
borderRadius: theme.shape.borderRadius,
|
borderRadius: theme.shape.borderRadius,
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Box, Tooltip, useTheme } from '@mui/material';
|
import { Box, Tooltip } from '@mui/material';
|
||||||
import { ReactComponent as NegatedIcon } from 'assets/icons/24_Negator.svg';
|
import { ReactComponent as NegatedIcon } from 'assets/icons/24_Negator.svg';
|
||||||
import { ReactComponent as NegatedIconOff } from 'assets/icons/24_Negator off.svg';
|
import { ReactComponent as NegatedIconOff } from 'assets/icons/24_Negator off.svg';
|
||||||
import { IConstraint } from 'interfaces/strategy';
|
import { IConstraint } from 'interfaces/strategy';
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import { styled, Tooltip, TooltipProps } from '@mui/material';
|
import { styled, Tooltip, TooltipProps } from '@mui/material';
|
||||||
import { HelpOutline } from '@mui/icons-material';
|
import { HelpOutline } from '@mui/icons-material';
|
||||||
|
import { HtmlTooltip } from 'component/common/HtmlTooltip/HtmlTooltip';
|
||||||
|
|
||||||
const StyledContainer = styled('span')(({ theme }) => ({
|
const StyledContainer = styled('span')(({ theme }) => ({
|
||||||
display: 'inline-grid',
|
display: 'inline-grid',
|
||||||
@ -22,12 +23,28 @@ const StyledContainer = styled('span')(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
interface IHelpIconProps {
|
interface IHelpIconProps {
|
||||||
tooltip: string;
|
tooltip: React.ReactNode;
|
||||||
|
htmlTooltip?: boolean;
|
||||||
placement?: TooltipProps['placement'];
|
placement?: TooltipProps['placement'];
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const HelpIcon = ({ tooltip, placement, children }: IHelpIconProps) => {
|
export const HelpIcon = ({
|
||||||
|
tooltip,
|
||||||
|
htmlTooltip,
|
||||||
|
placement,
|
||||||
|
children,
|
||||||
|
}: IHelpIconProps) => {
|
||||||
|
if (htmlTooltip) {
|
||||||
|
return (
|
||||||
|
<HtmlTooltip title={tooltip} placement={placement} arrow>
|
||||||
|
<StyledContainer tabIndex={0} aria-label="Help">
|
||||||
|
{children ?? <HelpOutline />}
|
||||||
|
</StyledContainer>
|
||||||
|
</HtmlTooltip>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Tooltip title={tooltip} placement={placement} arrow>
|
<Tooltip title={tooltip} placement={placement} arrow>
|
||||||
<StyledContainer tabIndex={0} aria-label="Help">
|
<StyledContainer tabIndex={0} aria-label="Help">
|
||||||
|
@ -5,8 +5,25 @@ import { useRef, useEffect } from 'react';
|
|||||||
const StyledMermaid = styled('div')(({ theme }) => ({
|
const StyledMermaid = styled('div')(({ theme }) => ({
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
justifyContent: 'center',
|
justifyContent: 'center',
|
||||||
'#mermaid .edgeLabel': {
|
'#mermaid': {
|
||||||
|
'.edgeLabel': {
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
},
|
||||||
|
'.nodeLabel': {
|
||||||
|
color: theme.palette.secondary.dark,
|
||||||
|
},
|
||||||
|
'.edgePaths > path': {
|
||||||
|
stroke: theme.palette.secondary.dark,
|
||||||
|
},
|
||||||
|
'.arrowMarkerPath': {
|
||||||
|
fill: theme.palette.secondary.dark,
|
||||||
|
stroke: 'transparent',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
'&&& #mermaid .node rect': {
|
||||||
|
stroke: theme.palette.secondary.border,
|
||||||
|
fill: theme.palette.secondary.light,
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ const Toast = ({ title, text, type, confetti }: IToast) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={styles.container}>
|
<div className={classnames(styles.container, 'dropdown-outline')}>
|
||||||
<div className={styles.innerContainer}>
|
<div className={styles.innerContainer}>
|
||||||
<div className={styles.confettiContainer}>
|
<div className={styles.confettiContainer}>
|
||||||
{confetti && renderConfetti()}
|
{confetti && renderConfetti()}
|
||||||
|
@ -6,7 +6,7 @@ import {
|
|||||||
FeatureMetricsHours,
|
FeatureMetricsHours,
|
||||||
} from './FeatureMetricsHours/FeatureMetricsHours';
|
} from './FeatureMetricsHours/FeatureMetricsHours';
|
||||||
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
||||||
import { Grid, styled } from '@mui/material';
|
import { Grid } from '@mui/material';
|
||||||
import { FeatureMetricsContent } from './FeatureMetricsContent/FeatureMetricsContent';
|
import { FeatureMetricsContent } from './FeatureMetricsContent/FeatureMetricsContent';
|
||||||
import { useQueryStringNumberState } from 'hooks/useQueryStringNumberState';
|
import { useQueryStringNumberState } from 'hooks/useQueryStringNumberState';
|
||||||
import { useQueryStringState } from 'hooks/useQueryStringState';
|
import { useQueryStringState } from 'hooks/useQueryStringState';
|
||||||
@ -16,12 +16,6 @@ import { ConditionallyRender } from 'component/common/ConditionallyRender/Condit
|
|||||||
import { usePageTitle } from 'hooks/usePageTitle';
|
import { usePageTitle } from 'hooks/usePageTitle';
|
||||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||||
|
|
||||||
const StyledContainer = styled('div')(({ theme }) => ({
|
|
||||||
[theme.breakpoints.down('md')]: {
|
|
||||||
marginTop: theme.spacing(2),
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
|
|
||||||
export const FeatureMetrics = () => {
|
export const FeatureMetrics = () => {
|
||||||
const projectId = useRequiredPathParam('projectId');
|
const projectId = useRequiredPathParam('projectId');
|
||||||
const featureId = useRequiredPathParam('featureId');
|
const featureId = useRequiredPathParam('featureId');
|
||||||
@ -62,12 +56,7 @@ export const FeatureMetrics = () => {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContent>
|
<PageContent>
|
||||||
<Grid
|
<Grid container component="header" spacing={2}>
|
||||||
container
|
|
||||||
component="header"
|
|
||||||
spacing={2}
|
|
||||||
alignItems="flex-end"
|
|
||||||
>
|
|
||||||
<Grid item xs={12} md={5}>
|
<Grid item xs={12} md={5}>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={environments.size > 0}
|
condition={environments.size > 0}
|
||||||
@ -95,12 +84,10 @@ export const FeatureMetrics = () => {
|
|||||||
/>
|
/>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} md={2}>
|
<Grid item xs={12} md={2}>
|
||||||
<StyledContainer>
|
|
||||||
<FeatureMetricsHours
|
<FeatureMetricsHours
|
||||||
hoursBack={hoursBack}
|
hoursBack={hoursBack}
|
||||||
setHoursBack={setHoursBack}
|
setHoursBack={setHoursBack}
|
||||||
/>
|
/>
|
||||||
</StyledContainer>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
<FeatureMetricsContent
|
<FeatureMetricsContent
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
||||||
import React, { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { Line } from 'react-chartjs-2';
|
import { Line } from 'react-chartjs-2';
|
||||||
import {
|
import {
|
||||||
CategoryScale,
|
CategoryScale,
|
||||||
@ -16,6 +16,7 @@ import { useLocationSettings } from 'hooks/useLocationSettings';
|
|||||||
import 'chartjs-adapter-date-fns';
|
import 'chartjs-adapter-date-fns';
|
||||||
import { createChartData } from './createChartData';
|
import { createChartData } from './createChartData';
|
||||||
import { createChartOptions } from './createChartOptions';
|
import { createChartOptions } from './createChartOptions';
|
||||||
|
import { useTheme } from '@mui/material';
|
||||||
|
|
||||||
interface IFeatureMetricsChartProps {
|
interface IFeatureMetricsChartProps {
|
||||||
metrics: IFeatureMetricsRaw[];
|
metrics: IFeatureMetricsRaw[];
|
||||||
@ -28,6 +29,7 @@ export const FeatureMetricsChart = ({
|
|||||||
hoursBack,
|
hoursBack,
|
||||||
statsSectionId,
|
statsSectionId,
|
||||||
}: IFeatureMetricsChartProps) => {
|
}: IFeatureMetricsChartProps) => {
|
||||||
|
const theme = useTheme();
|
||||||
const { locationSettings } = useLocationSettings();
|
const { locationSettings } = useLocationSettings();
|
||||||
|
|
||||||
const sortedMetrics = useMemo(() => {
|
const sortedMetrics = useMemo(() => {
|
||||||
@ -37,12 +39,17 @@ export const FeatureMetricsChart = ({
|
|||||||
}, [metrics]);
|
}, [metrics]);
|
||||||
|
|
||||||
const options = useMemo(() => {
|
const options = useMemo(() => {
|
||||||
return createChartOptions(sortedMetrics, hoursBack, locationSettings);
|
return createChartOptions(
|
||||||
}, [sortedMetrics, hoursBack, locationSettings]);
|
theme,
|
||||||
|
sortedMetrics,
|
||||||
|
hoursBack,
|
||||||
|
locationSettings
|
||||||
|
);
|
||||||
|
}, [theme, sortedMetrics, hoursBack, locationSettings]);
|
||||||
|
|
||||||
const data = useMemo(() => {
|
const data = useMemo(() => {
|
||||||
return createChartData(sortedMetrics, locationSettings);
|
return createChartData(theme, sortedMetrics, locationSettings);
|
||||||
}, [sortedMetrics, locationSettings]);
|
}, [theme, sortedMetrics, locationSettings]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div style={{ height: 400 }}>
|
<div style={{ height: 400 }}>
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
||||||
import { ChartData } from 'chart.js';
|
import { ChartData } from 'chart.js';
|
||||||
import { ILocationSettings } from 'hooks/useLocationSettings';
|
import { ILocationSettings } from 'hooks/useLocationSettings';
|
||||||
import theme from 'themes/theme';
|
|
||||||
import 'chartjs-adapter-date-fns';
|
import 'chartjs-adapter-date-fns';
|
||||||
|
import { Theme } from '@mui/material/styles/createTheme';
|
||||||
|
|
||||||
interface IPoint {
|
interface IPoint {
|
||||||
x: string;
|
x: string;
|
||||||
@ -10,6 +10,7 @@ interface IPoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const createChartData = (
|
export const createChartData = (
|
||||||
|
theme: Theme,
|
||||||
metrics: IFeatureMetricsRaw[],
|
metrics: IFeatureMetricsRaw[],
|
||||||
locationSettings: ILocationSettings
|
locationSettings: ILocationSettings
|
||||||
): ChartData<'line', IPoint[], string> => {
|
): ChartData<'line', IPoint[], string> => {
|
||||||
|
@ -2,10 +2,11 @@ import { ILocationSettings } from 'hooks/useLocationSettings';
|
|||||||
import 'chartjs-adapter-date-fns';
|
import 'chartjs-adapter-date-fns';
|
||||||
import { ChartOptions, defaults } from 'chart.js';
|
import { ChartOptions, defaults } from 'chart.js';
|
||||||
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
||||||
import theme from 'themes/theme';
|
|
||||||
import { formatDateHM } from 'utils/formatDate';
|
import { formatDateHM } from 'utils/formatDate';
|
||||||
|
import { Theme } from '@mui/material/styles/createTheme';
|
||||||
|
|
||||||
export const createChartOptions = (
|
export const createChartOptions = (
|
||||||
|
theme: Theme,
|
||||||
metrics: IFeatureMetricsRaw[],
|
metrics: IFeatureMetricsRaw[],
|
||||||
hoursBack: number,
|
hoursBack: number,
|
||||||
locationSettings: ILocationSettings
|
locationSettings: ILocationSettings
|
||||||
@ -18,6 +19,7 @@ export const createChartOptions = (
|
|||||||
mode: 'index',
|
mode: 'index',
|
||||||
intersect: false,
|
intersect: false,
|
||||||
},
|
},
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
plugins: {
|
plugins: {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
@ -56,6 +58,7 @@ export const createChartOptions = (
|
|||||||
size: 16,
|
size: 16,
|
||||||
weight: '400',
|
weight: '400',
|
||||||
},
|
},
|
||||||
|
color: theme.palette.text.primary,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
scales: {
|
scales: {
|
||||||
@ -64,10 +67,15 @@ export const createChartOptions = (
|
|||||||
title: {
|
title: {
|
||||||
display: true,
|
display: true,
|
||||||
text: 'Number of requests',
|
text: 'Number of requests',
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
},
|
},
|
||||||
// min: 0,
|
// min: 0,
|
||||||
suggestedMin: 0,
|
suggestedMin: 0,
|
||||||
ticks: { precision: 0 },
|
ticks: { precision: 0, color: theme.palette.text.secondary },
|
||||||
|
grid: {
|
||||||
|
color: theme.palette.divider,
|
||||||
|
borderColor: theme.palette.divider,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
x: {
|
x: {
|
||||||
type: 'time',
|
type: 'time',
|
||||||
@ -76,6 +84,7 @@ export const createChartOptions = (
|
|||||||
ticks: {
|
ticks: {
|
||||||
callback: (_, i, data) =>
|
callback: (_, i, data) =>
|
||||||
formatDateHM(data[i].value, locationSettings.locale),
|
formatDateHM(data[i].value, locationSettings.locale),
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -11,7 +11,7 @@ interface IFeatureMetricsChipsProps {
|
|||||||
|
|
||||||
const StyledTitle = styled('h2')(({ theme }) => ({
|
const StyledTitle = styled('h2')(({ theme }) => ({
|
||||||
margin: 0,
|
margin: 0,
|
||||||
marginBottom: theme.spacing(1),
|
marginBottom: theme.spacing(1.5),
|
||||||
fontSize: theme.fontSizes.smallBody,
|
fontSize: theme.fontSizes.smallBody,
|
||||||
fontWeight: theme.fontWeight.thin,
|
fontWeight: theme.fontWeight.thin,
|
||||||
color: theme.palette.text.secondary,
|
color: theme.palette.text.secondary,
|
||||||
|
@ -2,7 +2,6 @@ import { FeatureMetricsTable } from '../FeatureMetricsTable/FeatureMetricsTable'
|
|||||||
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
import { IFeatureMetricsRaw } from 'interfaces/featureToggle';
|
||||||
import { FeatureMetricsStatsRaw } from '../FeatureMetricsStats/FeatureMetricsStatsRaw';
|
import { FeatureMetricsStatsRaw } from '../FeatureMetricsStats/FeatureMetricsStatsRaw';
|
||||||
import { Box, Typography } from '@mui/material';
|
import { Box, Typography } from '@mui/material';
|
||||||
import theme from 'themes/theme';
|
|
||||||
import { useId } from 'hooks/useId';
|
import { useId } from 'hooks/useId';
|
||||||
import React, { Suspense } from 'react';
|
import React, { Suspense } from 'react';
|
||||||
|
|
||||||
@ -35,12 +34,7 @@ export const FeatureMetricsContent = ({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Suspense fallback={null}>
|
<Suspense fallback={null}>
|
||||||
<Box
|
<Box borderTop={1} pt={2} mt={3} borderColor="divider">
|
||||||
borderTop={1}
|
|
||||||
pt={2}
|
|
||||||
mt={3}
|
|
||||||
borderColor={theme.palette.divider}
|
|
||||||
>
|
|
||||||
<LazyFeatureMetricsChart
|
<LazyFeatureMetricsChart
|
||||||
metrics={metrics}
|
metrics={metrics}
|
||||||
hoursBack={hoursBack}
|
hoursBack={hoursBack}
|
||||||
|
@ -1,7 +1,16 @@
|
|||||||
|
import { styled } from '@mui/material';
|
||||||
import GeneralSelect, {
|
import GeneralSelect, {
|
||||||
IGeneralSelectProps,
|
IGeneralSelectProps,
|
||||||
} from 'component/common/GeneralSelect/GeneralSelect';
|
} from 'component/common/GeneralSelect/GeneralSelect';
|
||||||
|
|
||||||
|
const StyledTitle = styled('h2')(({ theme }) => ({
|
||||||
|
margin: 0,
|
||||||
|
marginBottom: theme.spacing(1),
|
||||||
|
fontSize: theme.fontSizes.smallBody,
|
||||||
|
fontWeight: theme.fontWeight.thin,
|
||||||
|
color: theme.palette.text.secondary,
|
||||||
|
}));
|
||||||
|
|
||||||
interface IFeatureMetricsHoursProps {
|
interface IFeatureMetricsHoursProps {
|
||||||
hoursBack: number;
|
hoursBack: number;
|
||||||
setHoursBack: (value: number) => void;
|
setHoursBack: (value: number) => void;
|
||||||
@ -18,8 +27,9 @@ export const FeatureMetricsHours = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<div>
|
||||||
|
<StyledTitle>Period</StyledTitle>
|
||||||
<GeneralSelect
|
<GeneralSelect
|
||||||
label="Period"
|
|
||||||
name="feature-metrics-period"
|
name="feature-metrics-period"
|
||||||
id="feature-metrics-period"
|
id="feature-metrics-period"
|
||||||
options={hourOptions}
|
options={hourOptions}
|
||||||
@ -27,6 +37,7 @@ export const FeatureMetricsHours = ({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1,11 +1,18 @@
|
|||||||
import { useContext, useMemo, useState } from 'react';
|
import { useContext, useMemo, useState } from 'react';
|
||||||
import { Box, Button, IconButton, Tooltip, useTheme } from '@mui/material';
|
import {
|
||||||
|
Box,
|
||||||
|
Button,
|
||||||
|
IconButton,
|
||||||
|
Tooltip,
|
||||||
|
useTheme,
|
||||||
|
styled,
|
||||||
|
} from '@mui/material';
|
||||||
import CloseIcon from '@mui/icons-material/Close';
|
import CloseIcon from '@mui/icons-material/Close';
|
||||||
import { ReactComponent as Logo } from 'assets/icons/logoPlain.svg';
|
import { ReactComponent as UnleashLogo } from 'assets/icons/logoBg.svg';
|
||||||
|
import { ReactComponent as UnleashLogoWhite } from 'assets/icons/logoWhiteBg.svg';
|
||||||
import AnimateOnMount from 'component/common/AnimateOnMount/AnimateOnMount';
|
import AnimateOnMount from 'component/common/AnimateOnMount/AnimateOnMount';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import {
|
import {
|
||||||
contentSpacingY,
|
|
||||||
fadeInTopEnter,
|
fadeInTopEnter,
|
||||||
fadeInTopLeave,
|
fadeInTopLeave,
|
||||||
fadeInTopStart,
|
fadeInTopStart,
|
||||||
@ -17,6 +24,13 @@ import {
|
|||||||
} from 'component/feedback/FeedbackNPS/showNPSFeedback';
|
} from 'component/feedback/FeedbackNPS/showNPSFeedback';
|
||||||
import { useAuthFeedback } from 'hooks/api/getters/useAuth/useAuthFeedback';
|
import { useAuthFeedback } from 'hooks/api/getters/useAuth/useAuthFeedback';
|
||||||
import { useAuthFeedbackApi } from 'hooks/api/actions/useAuthFeedbackApi/useAuthFeedbackApi';
|
import { useAuthFeedbackApi } from 'hooks/api/actions/useAuthFeedbackApi/useAuthFeedbackApi';
|
||||||
|
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
|
||||||
|
|
||||||
|
const StyledHeader = styled('h3')(({ theme }) => ({
|
||||||
|
margin: 0,
|
||||||
|
color: theme.palette.text.primary,
|
||||||
|
marginLeft: theme.spacing(1),
|
||||||
|
}));
|
||||||
|
|
||||||
interface IFeedbackNPSProps {
|
interface IFeedbackNPSProps {
|
||||||
openUrl: string;
|
openUrl: string;
|
||||||
@ -77,12 +91,13 @@ export const FeedbackNPS = ({ openUrl }: IFeedbackNPSProps) => {
|
|||||||
leave={animations.leave}
|
leave={animations.leave}
|
||||||
>
|
>
|
||||||
<Box
|
<Box
|
||||||
|
className="dropdown-outline"
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: `${theme.shape.borderRadiusLarge}px`,
|
borderRadius: `${theme.shape.borderRadiusLarge}px`,
|
||||||
backgroundColor: theme.palette.background.paper,
|
backgroundColor: theme.palette.background.paper,
|
||||||
zIndex: 9999,
|
zIndex: 9999,
|
||||||
boxShadow: theme.boxShadows.elevated,
|
boxShadow: theme.boxShadows.elevated,
|
||||||
padding: theme.spacing(3),
|
padding: theme.spacing(4),
|
||||||
maxWidth: '400px',
|
maxWidth: '400px',
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@ -90,35 +105,37 @@ export const FeedbackNPS = ({ openUrl }: IFeedbackNPSProps) => {
|
|||||||
sx={{
|
sx={{
|
||||||
display: 'flex',
|
display: 'flex',
|
||||||
flexDirection: 'column',
|
flexDirection: 'column',
|
||||||
|
rowGap: theme.spacing(1.5),
|
||||||
position: 'relative',
|
position: 'relative',
|
||||||
...contentSpacingY(theme),
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Tooltip title="Close" arrow>
|
<Tooltip title="Close" arrow>
|
||||||
<IconButton
|
<IconButton
|
||||||
sx={{
|
sx={theme => ({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
right: '-38px',
|
right: theme.spacing(-4),
|
||||||
top: '-47px',
|
top: theme.spacing(-4),
|
||||||
backgroundColor: theme.palette.background.paper,
|
})}
|
||||||
boxShadow: theme.boxShadows.elevated,
|
|
||||||
'&:hover': {
|
|
||||||
backgroundColor:
|
|
||||||
theme.palette.background.paper,
|
|
||||||
},
|
|
||||||
}}
|
|
||||||
onClick={() => setShowFeedback(false)}
|
onClick={() => setShowFeedback(false)}
|
||||||
size="large"
|
size="large"
|
||||||
>
|
>
|
||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Logo
|
<Box
|
||||||
style={{
|
sx={{
|
||||||
width: '25px',
|
display: 'flex',
|
||||||
height: '25px',
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
}}
|
}}
|
||||||
|
>
|
||||||
|
<ThemeMode
|
||||||
|
darkmode={<UnleashLogoWhite />}
|
||||||
|
lightmode={<UnleashLogo />}
|
||||||
/>
|
/>
|
||||||
|
<StyledHeader>Unleash feedback</StyledHeader>
|
||||||
|
</Box>
|
||||||
|
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={answeredNotNow}
|
condition={answeredNotNow}
|
||||||
show={
|
show={
|
||||||
@ -135,7 +152,12 @@ export const FeedbackNPS = ({ openUrl }: IFeedbackNPSProps) => {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<Box>
|
<Box
|
||||||
|
sx={{
|
||||||
|
textAlign: 'right',
|
||||||
|
marginTop: theme.spacing(2.5),
|
||||||
|
}}
|
||||||
|
>
|
||||||
<ConditionallyRender
|
<ConditionallyRender
|
||||||
condition={answeredNotNow}
|
condition={answeredNotNow}
|
||||||
show={
|
show={
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { Box, styled, Typography } from '@mui/material';
|
import { Box, styled, Typography } from '@mui/material';
|
||||||
import { HelpOutline } from '@mui/icons-material';
|
import { FC } from 'react';
|
||||||
import { HtmlTooltip } from '../../../common/HtmlTooltip/HtmlTooltip';
|
import { HelpIcon } from 'component/common/HelpIcon/HelpIcon';
|
||||||
import React, { FC } from 'react';
|
|
||||||
|
|
||||||
const StyledTitle = styled(Typography)(({ theme }) => ({
|
const StyledTitle = styled(Typography)(({ theme }) => ({
|
||||||
fontWeight: theme.fontWeight.bold,
|
fontWeight: theme.fontWeight.bold,
|
||||||
@ -13,8 +12,9 @@ const StyledDescription = styled(Typography)(({ theme }) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
export const CollaborationModeTooltip: FC = () => (
|
export const CollaborationModeTooltip: FC = () => (
|
||||||
<HtmlTooltip
|
<HelpIcon
|
||||||
title={
|
htmlTooltip
|
||||||
|
tooltip={
|
||||||
<>
|
<>
|
||||||
<Box>
|
<Box>
|
||||||
<StyledTitle>open: </StyledTitle>
|
<StyledTitle>open: </StyledTitle>
|
||||||
@ -31,9 +31,5 @@ export const CollaborationModeTooltip: FC = () => (
|
|||||||
</Box>
|
</Box>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
arrow
|
/>
|
||||||
describeChild
|
|
||||||
>
|
|
||||||
<HelpOutline />
|
|
||||||
</HtmlTooltip>
|
|
||||||
);
|
);
|
||||||
|
@ -140,10 +140,15 @@ const ProjectForm: React.FC<IProjectForm> = ({
|
|||||||
condition={Boolean(projectModeFlag)}
|
condition={Boolean(projectModeFlag)}
|
||||||
show={
|
show={
|
||||||
<>
|
<>
|
||||||
<Box sx={{ display: 'flex' }}>
|
<Box
|
||||||
<StyledDescription>
|
sx={{
|
||||||
What is your project collaboration mode?
|
display: 'flex',
|
||||||
</StyledDescription>
|
alignItems: 'center',
|
||||||
|
marginBottom: 1,
|
||||||
|
gap: 1,
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<p>What is your project collaboration mode?</p>
|
||||||
<CollaborationModeTooltip />
|
<CollaborationModeTooltip />
|
||||||
</Box>
|
</Box>
|
||||||
<Select
|
<Select
|
||||||
|
@ -3,7 +3,7 @@ import { useContext } from 'react';
|
|||||||
import { setLocalStorageItem } from 'utils/storage';
|
import { setLocalStorageItem } from 'utils/storage';
|
||||||
import mainTheme from 'themes/theme';
|
import mainTheme from 'themes/theme';
|
||||||
import darkTheme from 'themes/dark-theme';
|
import darkTheme from 'themes/dark-theme';
|
||||||
import { Theme } from '@emotion/react';
|
import { Theme } from '@mui/material/styles/createTheme';
|
||||||
|
|
||||||
interface IUseThemeModeOutput {
|
interface IUseThemeModeOutput {
|
||||||
resolveTheme: () => Theme;
|
resolveTheme: () => Theme;
|
||||||
|
@ -9,9 +9,6 @@ const actionColors = {
|
|||||||
0.08: 'rgba(223, 222, 255, 0.08)',
|
0.08: 'rgba(223, 222, 255, 0.08)',
|
||||||
0.05: 'rgba(223, 222, 255, 0.05)',
|
0.05: 'rgba(223, 222, 255, 0.05)',
|
||||||
};
|
};
|
||||||
const purpleColor = {
|
|
||||||
0.5: 'rgba(151, 146, 237, 0.5))',
|
|
||||||
};
|
|
||||||
|
|
||||||
const theme = {
|
const theme = {
|
||||||
breakpoints: {
|
breakpoints: {
|
||||||
@ -95,7 +92,7 @@ const theme = {
|
|||||||
},
|
},
|
||||||
secondary: {
|
secondary: {
|
||||||
// Used for purple badges and puple light elements
|
// Used for purple badges and puple light elements
|
||||||
main: '#57549C', // used on icons on these elements
|
main: '#9792ED', // used on icons on these elements
|
||||||
light: '#34325E', // used as a bakground on these elements
|
light: '#34325E', // used as a bakground on these elements
|
||||||
dark: '#EEEEFC', // used for text on these elements
|
dark: '#EEEEFC', // used for text on these elements
|
||||||
border: '#4C4992',
|
border: '#4C4992',
|
||||||
@ -543,7 +540,7 @@ export default createTheme({
|
|||||||
'&:not(.Mui-disabled).MuiButton-containedPrimary': {
|
'&:not(.Mui-disabled).MuiButton-containedPrimary': {
|
||||||
backgroundColor: theme.palette.background.alternative,
|
backgroundColor: theme.palette.background.alternative,
|
||||||
'&:hover': {
|
'&:hover': {
|
||||||
backgroundColor: theme.palette.secondary.main,
|
backgroundColor: theme.palette.secondary.light,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
Loading…
Reference in New Issue
Block a user