mirror of
https://github.com/Unleash/unleash.git
synced 2025-03-09 00:18:26 +01:00
fix: misc ui fixes post tables upgrade (#1049)
* fix: misc ui fixes post tables upgrade * fix: update snaps, small cleanup * refactor: ReportCard to styled, misc improvements * cleanup remaining styles file * rename `Feature Toggle Name` to just `name` * refactor: address PR comments
This commit is contained in:
parent
bad17e3606
commit
682921d5bf
@ -1,116 +0,0 @@
|
||||
.card {
|
||||
width: 100%;
|
||||
padding: var(--card-padding);
|
||||
margin: var(--card-margin-y) 0;
|
||||
border-radius: 10px;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.header {
|
||||
font-size: var(--h1-size);
|
||||
font-weight: 'bold';
|
||||
margin: 0 0 0.5rem 0;
|
||||
}
|
||||
|
||||
.reportCardContainer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.reportCardHealth {
|
||||
padding: 10px;
|
||||
}
|
||||
.reportCardAction {
|
||||
padding: 10px;
|
||||
}
|
||||
.reportCardToggle {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.reportCardHealthInnerContainer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 80%;
|
||||
}
|
||||
|
||||
.reportCardHealthRating {
|
||||
font-size: 2rem;
|
||||
font-weight: bold;
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.reportCardList {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.reportCardList li {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin: 0.5rem 0;
|
||||
}
|
||||
|
||||
.reportCardList li span {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-left: 0.5rem;
|
||||
font-size: var(--p-size);
|
||||
}
|
||||
|
||||
.check,
|
||||
.danger {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.check {
|
||||
color: var(--success);
|
||||
}
|
||||
|
||||
.danger {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.reportCardActionContainer {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.reportCardActionText {
|
||||
max-width: 300px;
|
||||
font-size: var(--p-size);
|
||||
margin-left: 35px;
|
||||
}
|
||||
.reportCardNoActionText {
|
||||
max-width: 300px;
|
||||
font-size: var(--p-size);
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.reportCardBtn {
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
.healthDanger {
|
||||
color: var(--danger);
|
||||
}
|
||||
|
||||
.healthWarning {
|
||||
color: var(--warning);
|
||||
}
|
||||
.lastUpdate {
|
||||
color: #585858;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.reportCardContainer {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.reportCardToggle {
|
||||
margin: 10px 5px;
|
||||
flex-basis: 100%;
|
||||
}
|
||||
}
|
@ -1,132 +1,180 @@
|
||||
import classnames from 'classnames';
|
||||
import { Paper } from '@mui/material';
|
||||
import { Box, Paper, styled } from '@mui/material';
|
||||
import CheckIcon from '@mui/icons-material/Check';
|
||||
import ReportProblemOutlinedIcon from '@mui/icons-material/ReportProblemOutlined';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import styles from './ReportCard.module.scss';
|
||||
import ReactTimeAgo from 'react-timeago';
|
||||
import { IProjectHealthReport } from 'interfaces/project';
|
||||
|
||||
const StyledBoxActive = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
color: theme.palette.success.dark,
|
||||
'& svg': {
|
||||
color: theme.palette.success.main,
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledBoxStale = styled(Box)(({ theme }) => ({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
color: theme.palette.warning.dark,
|
||||
'& svg': {
|
||||
color: theme.palette.warning.main,
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledPaper = styled(Paper)(({ theme }) => ({
|
||||
padding: theme.spacing(4),
|
||||
marginBottom: theme.spacing(2),
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
boxShadow: 'none',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
[theme.breakpoints.down('md')]: {
|
||||
flexDirection: 'column',
|
||||
gap: theme.spacing(2),
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledHeader = styled('h2')(({ theme }) => ({
|
||||
fontSize: theme.fontSizes.mainHeader,
|
||||
marginBottom: theme.spacing(1),
|
||||
}));
|
||||
|
||||
const StyledHealthRating = styled('p')(({ theme }) => ({
|
||||
fontSize: '2rem',
|
||||
fontWeight: theme.fontWeight.bold,
|
||||
}));
|
||||
|
||||
const StyledLastUpdated = styled('p')(({ theme }) => ({
|
||||
color: theme.palette.text.secondary,
|
||||
}));
|
||||
|
||||
const StyledList = styled('ul')(({ theme }) => ({
|
||||
listStyleType: 'none',
|
||||
margin: 0,
|
||||
padding: 0,
|
||||
'& svg': {
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledAlignedItem = styled('p')(({ theme }) => ({
|
||||
marginLeft: theme.spacing(4),
|
||||
}));
|
||||
|
||||
interface IReportCardProps {
|
||||
healthReport: IProjectHealthReport;
|
||||
}
|
||||
|
||||
export const ReportCard = ({ healthReport }: IReportCardProps) => {
|
||||
const healthLessThan50 = healthReport.health < 50;
|
||||
const healthLessThan75 = healthReport.health < 75;
|
||||
|
||||
const healthClasses = classnames(styles.reportCardHealthRating, {
|
||||
[styles.healthWarning]: healthLessThan75,
|
||||
[styles.healthDanger]: healthLessThan50,
|
||||
});
|
||||
const healthRatingColor =
|
||||
healthReport.health < 50
|
||||
? 'error.main'
|
||||
: healthReport.health < 75
|
||||
? 'warning.main'
|
||||
: 'success.main';
|
||||
|
||||
const renderActiveToggles = () => (
|
||||
<>
|
||||
<CheckIcon className={styles.check} />
|
||||
<StyledBoxActive>
|
||||
<CheckIcon />
|
||||
<span>{healthReport.activeCount} active toggles</span>
|
||||
</>
|
||||
</StyledBoxActive>
|
||||
);
|
||||
|
||||
const renderStaleToggles = () => (
|
||||
<>
|
||||
<ReportProblemOutlinedIcon className={styles.danger} />
|
||||
<StyledBoxStale>
|
||||
<ReportProblemOutlinedIcon />
|
||||
<span>{healthReport.staleCount} stale toggles</span>
|
||||
</>
|
||||
</StyledBoxStale>
|
||||
);
|
||||
|
||||
const renderPotentiallyStaleToggles = () => (
|
||||
<>
|
||||
<ReportProblemOutlinedIcon className={styles.danger} />
|
||||
<StyledBoxStale>
|
||||
<ReportProblemOutlinedIcon />
|
||||
<span>
|
||||
{healthReport.potentiallyStaleCount} potentially stale toggles
|
||||
</span>
|
||||
</>
|
||||
</StyledBoxStale>
|
||||
);
|
||||
|
||||
return (
|
||||
<Paper className={styles.card}>
|
||||
<div className={styles.reportCardContainer}>
|
||||
<div className={styles.reportCardHealth}>
|
||||
<h2 className={styles.header}>Health rating</h2>
|
||||
<div className={styles.reportCardHealthInnerContainer}>
|
||||
<ConditionallyRender
|
||||
condition={healthReport.health > -1}
|
||||
show={
|
||||
<div>
|
||||
<p className={healthClasses}>
|
||||
{healthReport.health}%
|
||||
</p>
|
||||
<p className={styles.lastUpdate}>
|
||||
Last updated:{' '}
|
||||
<ReactTimeAgo
|
||||
date={healthReport.updatedAt}
|
||||
live={false}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.reportCardToggle}>
|
||||
<h2 className={styles.header}>Toggle report</h2>
|
||||
<ul className={styles.reportCardList}>
|
||||
<li>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(healthReport.activeCount)}
|
||||
show={renderActiveToggles}
|
||||
/>
|
||||
</li>
|
||||
<StyledPaper>
|
||||
<Box>
|
||||
<StyledHeader>Health rating</StyledHeader>
|
||||
<ConditionallyRender
|
||||
condition={healthReport.health > -1}
|
||||
show={
|
||||
<>
|
||||
<StyledHealthRating
|
||||
sx={{ color: healthRatingColor }}
|
||||
>
|
||||
{healthReport.health}%
|
||||
</StyledHealthRating>
|
||||
<StyledLastUpdated>
|
||||
Last updated:{' '}
|
||||
<ReactTimeAgo
|
||||
date={healthReport.updatedAt}
|
||||
live={false}
|
||||
/>
|
||||
</StyledLastUpdated>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
<Box>
|
||||
<StyledHeader>Toggle report</StyledHeader>
|
||||
<StyledList>
|
||||
<li>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(healthReport.activeCount)}
|
||||
show={
|
||||
<p className={styles.reportCardActionText}>
|
||||
Also includes potentially stale toggles.
|
||||
</p>
|
||||
}
|
||||
show={renderActiveToggles}
|
||||
/>
|
||||
</li>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(healthReport.activeCount)}
|
||||
show={
|
||||
<StyledAlignedItem>
|
||||
Also includes potentially stale toggles.
|
||||
</StyledAlignedItem>
|
||||
}
|
||||
/>
|
||||
|
||||
<li>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(healthReport.staleCount)}
|
||||
show={renderStaleToggles}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div className={styles.reportCardAction}>
|
||||
<h2 className={styles.header}>Potential actions</h2>
|
||||
<div className={styles.reportCardActionContainer}>
|
||||
<ul className={styles.reportCardList}>
|
||||
<li>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(
|
||||
healthReport.potentiallyStaleCount
|
||||
)}
|
||||
show={renderPotentiallyStaleToggles}
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
<li>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(healthReport.staleCount)}
|
||||
show={renderStaleToggles}
|
||||
/>
|
||||
</li>
|
||||
</StyledList>
|
||||
</Box>
|
||||
<Box>
|
||||
<StyledHeader>Potential actions</StyledHeader>
|
||||
<StyledList>
|
||||
<li>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(
|
||||
healthReport.potentiallyStaleCount
|
||||
)}
|
||||
show={
|
||||
<p className={styles.reportCardActionText}>
|
||||
Review your feature toggles and delete
|
||||
unused toggles.
|
||||
</p>
|
||||
}
|
||||
elseShow={
|
||||
<p className={styles.reportCardNoActionText}>
|
||||
No action is required
|
||||
</p>
|
||||
}
|
||||
show={renderPotentiallyStaleToggles}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Paper>
|
||||
</li>
|
||||
</StyledList>
|
||||
<ConditionallyRender
|
||||
condition={Boolean(healthReport.potentiallyStaleCount)}
|
||||
show={
|
||||
<StyledAlignedItem>
|
||||
Review your feature toggles and delete unused
|
||||
toggles.
|
||||
</StyledAlignedItem>
|
||||
}
|
||||
elseShow={
|
||||
<StyledAlignedItem>
|
||||
No action is required
|
||||
</StyledAlignedItem>
|
||||
}
|
||||
/>
|
||||
</Box>
|
||||
</StyledPaper>
|
||||
);
|
||||
};
|
||||
|
@ -4,12 +4,20 @@ import { ReportProblemOutlined, Check } from '@mui/icons-material';
|
||||
import { styled } from '@mui/material';
|
||||
import { IReportTableRow } from 'component/Reporting/ReportTable/ReportTable';
|
||||
|
||||
const StyledText = styled('span')(({ theme }) => ({
|
||||
const StyledTextPotentiallyStale = styled('span')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
gap: '1ch',
|
||||
alignItems: 'center',
|
||||
textAlign: 'right',
|
||||
'& svg': { color: theme.palette.inactiveIcon },
|
||||
color: theme.palette.warning.dark,
|
||||
'& svg': { color: theme.palette.warning.main },
|
||||
}));
|
||||
|
||||
const StyledTextHealthy = styled('span')(({ theme }) => ({
|
||||
display: 'flex',
|
||||
gap: '1ch',
|
||||
alignItems: 'center',
|
||||
color: theme.palette.success.dark,
|
||||
'& svg': { color: theme.palette.success.main },
|
||||
}));
|
||||
|
||||
interface IReportStatusCellProps {
|
||||
@ -24,20 +32,20 @@ export const ReportStatusCell: VFC<IReportStatusCellProps> = ({
|
||||
if (row.original.status === 'potentially-stale') {
|
||||
return (
|
||||
<TextCell>
|
||||
<StyledText>
|
||||
<StyledTextPotentiallyStale>
|
||||
<ReportProblemOutlined />
|
||||
<span>Potentially stale</span>
|
||||
</StyledText>
|
||||
</StyledTextPotentiallyStale>
|
||||
</TextCell>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<TextCell>
|
||||
<StyledText>
|
||||
<StyledTextHealthy>
|
||||
<Check />
|
||||
<span>Healthy</span>
|
||||
</StyledText>
|
||||
</StyledTextHealthy>
|
||||
</TextCell>
|
||||
);
|
||||
};
|
||||
|
@ -55,7 +55,7 @@ export const ReportTable = ({ projectId, features }: IReportTableProps) => {
|
||||
const initialState = useMemo(
|
||||
() => ({
|
||||
hiddenColumns: [],
|
||||
sortBy: [{ id: 'name' }],
|
||||
sortBy: [{ id: 'createdAt', desc: true }],
|
||||
}),
|
||||
[]
|
||||
);
|
||||
@ -84,9 +84,11 @@ export const ReportTable = ({ projectId, features }: IReportTableProps) => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const hiddenColumns = [];
|
||||
if (isSmallScreen) {
|
||||
setHiddenColumns(['createdAt', 'expiredAt']);
|
||||
hiddenColumns.push('createdAt', 'expiredAt');
|
||||
}
|
||||
setHiddenColumns(hiddenColumns);
|
||||
}, [setHiddenColumns, isSmallScreen]);
|
||||
|
||||
const header = (
|
||||
@ -101,8 +103,6 @@ export const ReportTable = ({ projectId, features }: IReportTableProps) => {
|
||||
/>
|
||||
);
|
||||
|
||||
console.log(rows);
|
||||
|
||||
return (
|
||||
<PageContent header={header}>
|
||||
<SearchHighlightProvider value={globalFilter}>
|
||||
@ -131,15 +131,15 @@ export const ReportTable = ({ projectId, features }: IReportTableProps) => {
|
||||
condition={globalFilter?.length > 0}
|
||||
show={
|
||||
<TablePlaceholder>
|
||||
No features found matching “
|
||||
No feature toggles found matching “
|
||||
{globalFilter}
|
||||
”
|
||||
</TablePlaceholder>
|
||||
}
|
||||
elseShow={
|
||||
<TablePlaceholder>
|
||||
No features available. Get started by adding a
|
||||
new feature toggle.
|
||||
No feature toggles available. Get started by
|
||||
adding a new feature toggle.
|
||||
</TablePlaceholder>
|
||||
}
|
||||
/>
|
||||
@ -182,7 +182,7 @@ const COLUMNS = [
|
||||
disableGlobalFilter: true,
|
||||
},
|
||||
{
|
||||
Header: 'Feature toggle name',
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
width: '60%',
|
||||
sortType: 'alphanumeric',
|
||||
@ -204,8 +204,8 @@ const COLUMNS = [
|
||||
{
|
||||
Header: 'Status',
|
||||
accessor: 'status',
|
||||
align: 'right',
|
||||
Cell: ReportStatusCell,
|
||||
disableGlobalFilter: true,
|
||||
},
|
||||
{
|
||||
Header: 'State',
|
||||
|
@ -13,7 +13,6 @@ import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightC
|
||||
import { ApiTokenDocs } from 'component/admin/apiToken/ApiTokenDocs/ApiTokenDocs';
|
||||
import { CreateApiTokenButton } from 'component/admin/apiToken/CreateApiTokenButton/CreateApiTokenButton';
|
||||
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
|
||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||
import { Key } from '@mui/icons-material';
|
||||
import { ActionCell } from 'component/common/Table/cells/ActionCell/ActionCell';
|
||||
import { CopyApiTokenButton } from 'component/admin/apiToken/CopyApiTokenButton/CopyApiTokenButton';
|
||||
@ -176,12 +175,10 @@ const COLUMNS = [
|
||||
Header: 'Project',
|
||||
accessor: 'project',
|
||||
Cell: (props: any) => (
|
||||
<TextCell>
|
||||
<ProjectsList
|
||||
project={props.row.original.project}
|
||||
projects={props.row.original.projects}
|
||||
/>
|
||||
</TextCell>
|
||||
<ProjectsList
|
||||
project={props.row.original.project}
|
||||
projects={props.row.original.projects}
|
||||
/>
|
||||
),
|
||||
minWidth: 120,
|
||||
},
|
||||
|
@ -1,8 +1,17 @@
|
||||
import { styled } from '@mui/material';
|
||||
import { Highlighter } from 'component/common/Highlighter/Highlighter';
|
||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||
import { useSearchHighlightContext } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||
import { Fragment, VFC } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
|
||||
const StyledLink = styled(Link)(() => ({
|
||||
textDecoration: 'none',
|
||||
'&:hover, &:focus': {
|
||||
textDecoration: 'underline',
|
||||
},
|
||||
}));
|
||||
|
||||
interface IProjectsListProps {
|
||||
project?: string;
|
||||
projects?: string | string[];
|
||||
@ -22,25 +31,29 @@ export const ProjectsList: VFC<IProjectsListProps> = ({
|
||||
: [];
|
||||
|
||||
if (fields.length === 0) {
|
||||
return <Highlighter search={searchQuery}>*</Highlighter>;
|
||||
return (
|
||||
<TextCell>
|
||||
<Highlighter search={searchQuery}>*</Highlighter>
|
||||
</TextCell>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<TextCell>
|
||||
{fields.map((item, index) => (
|
||||
<Fragment key={item}>
|
||||
{index > 0 && ', '}
|
||||
{!item || item === '*' ? (
|
||||
<Highlighter search={searchQuery}>*</Highlighter>
|
||||
) : (
|
||||
<Link to={`/projects/${item}`}>
|
||||
<StyledLink to={`/projects/${item}`}>
|
||||
<Highlighter search={searchQuery}>
|
||||
{item}
|
||||
</Highlighter>
|
||||
</Link>
|
||||
</StyledLink>
|
||||
)}
|
||||
</Fragment>
|
||||
))}
|
||||
</>
|
||||
</TextCell>
|
||||
);
|
||||
};
|
||||
|
@ -69,7 +69,7 @@ export const BillingHistory: VFC<IBillingHistoryProps> = ({
|
||||
}) => {
|
||||
const initialState = useMemo(
|
||||
() => ({
|
||||
sortBy: [{ id: 'createdAt', desc: false }],
|
||||
sortBy: [{ id: 'dueDate' }],
|
||||
}),
|
||||
[]
|
||||
);
|
||||
|
@ -175,10 +175,11 @@ const ProjectRoleList = () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setHiddenColumns([]);
|
||||
const hiddenColumns = [];
|
||||
if (isExtraSmallScreen) {
|
||||
setHiddenColumns(['Icon']);
|
||||
hiddenColumns.push('Icon');
|
||||
}
|
||||
setHiddenColumns(hiddenColumns);
|
||||
}, [setHiddenColumns, isExtraSmallScreen]);
|
||||
|
||||
return (
|
||||
|
@ -139,6 +139,7 @@ const UsersList = () => {
|
||||
disableSortBy: true,
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
Header: 'Name',
|
||||
accessor: (row: any) => row.name || '',
|
||||
width: '40%',
|
||||
@ -231,10 +232,10 @@ const UsersList = () => {
|
||||
hiddenColumns.push('type');
|
||||
}
|
||||
if (isSmallScreen) {
|
||||
hiddenColumns.push(...['createdAt', 'username']);
|
||||
hiddenColumns.push('createdAt', 'username');
|
||||
}
|
||||
if (isExtraSmallScreen) {
|
||||
hiddenColumns.push(...['imageUrl', 'role', 'last-login']);
|
||||
hiddenColumns.push('imageUrl', 'role', 'last-login');
|
||||
}
|
||||
setHiddenColumns(hiddenColumns);
|
||||
}, [setHiddenColumns, isExtraSmallScreen, isSmallScreen, isBillingUsers]);
|
||||
|
@ -58,6 +58,7 @@ const PermissionIconButton = ({
|
||||
{...tooltipProps}
|
||||
title={formatAccessText(access, tooltipProps?.title)}
|
||||
arrow
|
||||
onClick={e => e.preventDefault()}
|
||||
>
|
||||
<div id={id} role="tooltip">
|
||||
<IconButton
|
||||
|
@ -75,7 +75,7 @@ const CreateEnvironment = () => {
|
||||
show={
|
||||
<FormTemplate
|
||||
loading={loading}
|
||||
title="Create Environment"
|
||||
title="Create environment"
|
||||
description="Environments allow you to manage your
|
||||
product lifecycle from local development
|
||||
through production. Your projects and
|
||||
|
@ -16,7 +16,7 @@ export const CreateEnvironmentButton = () => {
|
||||
permission={ADMIN}
|
||||
disabled={!Boolean(uiConfig.flags.EEA)}
|
||||
>
|
||||
New Environment
|
||||
New environment
|
||||
</ResponsiveButton>
|
||||
);
|
||||
};
|
||||
|
@ -11,7 +11,7 @@ import {
|
||||
} from 'component/common/Table';
|
||||
import { useCallback } from 'react';
|
||||
import { SearchHighlightProvider } from 'component/common/Table/SearchHighlightContext/SearchHighlightContext';
|
||||
import { TableBody } from '@mui/material';
|
||||
import { Alert, styled, TableBody } from '@mui/material';
|
||||
import { CloudCircle } from '@mui/icons-material';
|
||||
import { IconCell } from 'component/common/Table/cells/IconCell/IconCell';
|
||||
import { EnvironmentActionCell } from 'component/environments/EnvironmentActionCell/EnvironmentActionCell';
|
||||
@ -25,6 +25,10 @@ import useEnvironmentApi, {
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
|
||||
const StyledAlert = styled(Alert)(({ theme }) => ({
|
||||
marginBottom: theme.spacing(4),
|
||||
}));
|
||||
|
||||
export const EnvironmentTable = () => {
|
||||
const { changeSortOrder } = useEnvironmentApi();
|
||||
const { setToastApiError } = useToast();
|
||||
@ -82,6 +86,11 @@ export const EnvironmentTable = () => {
|
||||
|
||||
return (
|
||||
<PageContent header={header}>
|
||||
<StyledAlert severity="info">
|
||||
This is the order of environments that you have today in each
|
||||
feature toggle. Rearranging them here will change also the order
|
||||
inside each feature toggle.
|
||||
</StyledAlert>
|
||||
<SearchHighlightProvider value={globalFilter}>
|
||||
<Table {...getTableProps()}>
|
||||
<SortableTableHeader headerGroups={headerGroups as any} />
|
||||
|
@ -104,7 +104,7 @@ export const CopyFeatureToggle = () => {
|
||||
</p>
|
||||
<form onSubmit={onSubmit}>
|
||||
<TextField
|
||||
label="Feature toggle name"
|
||||
label="Name"
|
||||
name="name"
|
||||
value={newToggleName || ''}
|
||||
onBlur={onValidateName}
|
||||
|
@ -64,7 +64,7 @@ const columns = [
|
||||
disableGlobalFilter: true,
|
||||
},
|
||||
{
|
||||
Header: 'Feature toggle name',
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
minWidth: 150,
|
||||
Cell: FeatureNameCell,
|
||||
@ -101,7 +101,7 @@ const columns = [
|
||||
},
|
||||
];
|
||||
|
||||
const defaultSort: SortingRule<string> = { id: 'createdAt', desc: false };
|
||||
const defaultSort: SortingRule<string> = { id: 'createdAt', desc: true };
|
||||
|
||||
export const FeatureToggleListTable: VFC = () => {
|
||||
const theme = useTheme();
|
||||
@ -162,19 +162,14 @@ export const FeatureToggleListTable: VFC = () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isSmallScreen) {
|
||||
setHiddenColumns([
|
||||
'lastSeenAt',
|
||||
'type',
|
||||
'stale',
|
||||
'description',
|
||||
'createdAt',
|
||||
]);
|
||||
} else if (isMediumScreen) {
|
||||
setHiddenColumns(['lastSeenAt', 'stale', 'description']);
|
||||
} else {
|
||||
setHiddenColumns(['description']);
|
||||
const hiddenColumns = ['description'];
|
||||
if (isMediumScreen) {
|
||||
hiddenColumns.push('lastSeenAt', 'stale');
|
||||
}
|
||||
if (isSmallScreen) {
|
||||
hiddenColumns.push('type', 'createdAt');
|
||||
}
|
||||
setHiddenColumns(hiddenColumns);
|
||||
}, [setHiddenColumns, isSmallScreen, isMediumScreen]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -289,15 +284,15 @@ export const FeatureToggleListTable: VFC = () => {
|
||||
condition={globalFilter?.length > 0}
|
||||
show={
|
||||
<TablePlaceholder>
|
||||
No features or projects found matching “
|
||||
No feature toggles found matching “
|
||||
{globalFilter}
|
||||
”
|
||||
</TablePlaceholder>
|
||||
}
|
||||
elseShow={
|
||||
<TablePlaceholder>
|
||||
No features available. Get started by adding a
|
||||
new feature toggle.
|
||||
No feature toggles available. Get started by
|
||||
adding a new feature toggle.
|
||||
</TablePlaceholder>
|
||||
}
|
||||
/>
|
||||
|
@ -45,11 +45,11 @@ export const FeatureMetricsTable = ({
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const hiddenColumns = [];
|
||||
if (isMediumScreen) {
|
||||
setHiddenColumns(['appName', 'environment']);
|
||||
} else {
|
||||
setHiddenColumns([]);
|
||||
hiddenColumns.push('appName', 'environment');
|
||||
}
|
||||
setHiddenColumns(hiddenColumns);
|
||||
}, [setHiddenColumns, isMediumScreen]);
|
||||
|
||||
if (metrics.length === 0) {
|
||||
@ -98,6 +98,7 @@ const COLUMNS = [
|
||||
accessor: 'environment',
|
||||
},
|
||||
{
|
||||
id: 'requested',
|
||||
Header: 'Requested',
|
||||
accessor: (original: any) => original.yes + original.no,
|
||||
},
|
||||
|
@ -2,7 +2,6 @@ import * as jsonpatch from 'fast-json-patch';
|
||||
|
||||
import {
|
||||
Alert,
|
||||
Box,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
@ -27,7 +26,6 @@ import useDeleteVariantMarkup from './useDeleteVariantMarkup';
|
||||
import PermissionButton from 'component/common/PermissionButton/PermissionButton';
|
||||
import { formatUnknownError } from 'utils/formatUnknownError';
|
||||
import { useRequiredPathParam } from 'hooks/useRequiredPathParam';
|
||||
import { Edit, Delete } from '@mui/icons-material';
|
||||
import { useTable, useSortBy, useGlobalFilter } from 'react-table';
|
||||
import { PageContent } from 'component/common/PageContent/PageContent';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
@ -35,7 +33,6 @@ import { SortableTableHeader, TablePlaceholder } from 'component/common/Table';
|
||||
import { sortTypes } from 'utils/sortTypes';
|
||||
import { PayloadOverridesCell } from './PayloadOverridesCell/PayloadOverridesCell';
|
||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||
import PermissionIconButton from 'component/common/PermissionIconButton/PermissionIconButton';
|
||||
import theme from 'themes/theme';
|
||||
import { VariantsActionCell } from './VariantsActionsCell/VariantsActionsCell';
|
||||
|
||||
@ -211,11 +208,14 @@ export const FeatureVariantsList = () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (isMediumScreen) {
|
||||
setHiddenColumns(['weightType', 'data']);
|
||||
} else if (isLargeScreen) {
|
||||
setHiddenColumns(['weightType']);
|
||||
const hiddenColumns = [];
|
||||
if (isLargeScreen) {
|
||||
hiddenColumns.push('weightType');
|
||||
}
|
||||
if (isMediumScreen) {
|
||||
hiddenColumns.push('data');
|
||||
}
|
||||
setHiddenColumns(hiddenColumns);
|
||||
}, [setHiddenColumns, isMediumScreen, isLargeScreen]);
|
||||
|
||||
// @ts-expect-error
|
||||
|
@ -8,8 +8,7 @@ export const useStyles = makeStyles()(theme => ({
|
||||
},
|
||||
menuContainer: {
|
||||
borderRadius: theme.shape.borderRadiusLarge,
|
||||
padding: theme.spacing(1),
|
||||
paddingRight: theme.spacing(3),
|
||||
padding: theme.spacing(1, 1.5),
|
||||
},
|
||||
item: {
|
||||
borderRadius: theme.shape.borderRadius,
|
||||
|
@ -121,6 +121,7 @@ export const ColumnsMenu: VFC<IColumnsMenuProps> = ({
|
||||
aria-expanded={isOpen ? 'true' : undefined}
|
||||
onClick={handleClick}
|
||||
type="button"
|
||||
size="large"
|
||||
className={classes.button}
|
||||
data-loading
|
||||
>
|
||||
|
@ -7,6 +7,7 @@ import {
|
||||
useFlexLayout,
|
||||
useSortBy,
|
||||
useTable,
|
||||
SortingRule,
|
||||
} from 'react-table';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import { PageHeader } from 'component/common/PageHeader/PageHeader';
|
||||
@ -68,6 +69,13 @@ type ListItemType = Pick<
|
||||
|
||||
const staticColumns = ['Actions', 'name'];
|
||||
|
||||
const defaultSort: SortingRule<string> & {
|
||||
columns?: string[];
|
||||
} = {
|
||||
id: 'createdAt',
|
||||
desc: true,
|
||||
};
|
||||
|
||||
export const ProjectFeatureToggles = ({
|
||||
features,
|
||||
loading,
|
||||
@ -205,7 +213,7 @@ export const ProjectFeatureToggles = ({
|
||||
maxWidth: 80,
|
||||
},
|
||||
{
|
||||
Header: 'Feature toggle name',
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
Cell: ({ value }: { value: string }) => (
|
||||
<LinkCell
|
||||
@ -268,9 +276,10 @@ export const ProjectFeatureToggles = ({
|
||||
[projectId, environments, onToggle, loading]
|
||||
);
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [storedParams, setStoredParams] = useLocalStorage<{
|
||||
columns?: string[];
|
||||
}>(`${projectId}:ProjectFeatureToggles`, {});
|
||||
const [storedParams, setStoredParams] = useLocalStorage(
|
||||
`${projectId}:ProjectFeatureToggles`,
|
||||
defaultSort
|
||||
);
|
||||
|
||||
const initialState = useMemo(
|
||||
() => {
|
||||
@ -304,7 +313,7 @@ export const ProjectFeatureToggles = ({
|
||||
id: searchParams.get('sort') || 'createdAt',
|
||||
desc: searchParams.has('order')
|
||||
? searchParams.get('order') === 'desc'
|
||||
: false,
|
||||
: storedParams.desc,
|
||||
},
|
||||
],
|
||||
hiddenColumns,
|
||||
@ -365,14 +374,20 @@ export const ProjectFeatureToggles = ({
|
||||
setSearchParams(tableState, {
|
||||
replace: true,
|
||||
});
|
||||
setStoredParams({
|
||||
id: sortBy[0].id,
|
||||
desc: sortBy[0].desc || false,
|
||||
columns: tableState.columns.split(','),
|
||||
});
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [loading, sortBy, hiddenColumns, globalFilter, setSearchParams]);
|
||||
|
||||
const onCustomizeColumns = useCallback(
|
||||
visibleColumns => {
|
||||
setStoredParams({
|
||||
setStoredParams(storedParams => ({
|
||||
...storedParams,
|
||||
columns: visibleColumns,
|
||||
});
|
||||
}));
|
||||
},
|
||||
[setStoredParams]
|
||||
);
|
||||
@ -490,15 +505,15 @@ export const ProjectFeatureToggles = ({
|
||||
condition={globalFilter?.length > 0}
|
||||
show={
|
||||
<TablePlaceholder>
|
||||
No features found matching “
|
||||
No feature toggles found matching “
|
||||
{globalFilter}
|
||||
”
|
||||
</TablePlaceholder>
|
||||
}
|
||||
elseShow={
|
||||
<TablePlaceholder>
|
||||
No features available. Get started by adding a
|
||||
new feature toggle.
|
||||
No feature toggles available. Get started by
|
||||
adding a new feature toggle.
|
||||
</TablePlaceholder>
|
||||
}
|
||||
/>
|
||||
|
@ -68,10 +68,10 @@ export const useStyles = makeStyles()(theme => ({
|
||||
fontSize: '0.8rem',
|
||||
position: 'relative',
|
||||
padding: '0.8rem',
|
||||
['&:first-of-type']: {
|
||||
'&:first-of-type': {
|
||||
marginLeft: '0',
|
||||
},
|
||||
['&:last-of-type']: {
|
||||
'&:last-of-type': {
|
||||
marginRight: '0',
|
||||
},
|
||||
},
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useMemo, useState, VFC } from 'react';
|
||||
import { useMemo, VFC } from 'react';
|
||||
import { useSortBy, useTable } from 'react-table';
|
||||
import {
|
||||
Table,
|
||||
@ -19,6 +19,10 @@ import PermissionIconButton from 'component/common/PermissionIconButton/Permissi
|
||||
import { UPDATE_PROJECT } from 'component/providers/AccessProvider/permissions';
|
||||
import { TextCell } from 'component/common/Table/cells/TextCell/TextCell';
|
||||
|
||||
const initialState = {
|
||||
sortBy: [{ id: 'name' }],
|
||||
};
|
||||
|
||||
interface IProjectAccessTableProps {
|
||||
access: IProjectAccessOutput;
|
||||
projectId: string;
|
||||
@ -34,7 +38,6 @@ export const ProjectAccessTable: VFC<IProjectAccessTableProps> = ({
|
||||
handleRoleChange,
|
||||
handleRemoveAccess,
|
||||
}) => {
|
||||
const [initialState] = useState({});
|
||||
const data = access.users;
|
||||
|
||||
const columns = useMemo(
|
||||
@ -54,12 +57,13 @@ export const ProjectAccessTable: VFC<IProjectAccessTableProps> = ({
|
||||
align: 'center',
|
||||
},
|
||||
{
|
||||
id: 'name',
|
||||
Header: 'Name',
|
||||
accessor: 'name',
|
||||
accessor: (row: any) => row.name || '',
|
||||
},
|
||||
{
|
||||
Header: 'Username',
|
||||
id: 'username',
|
||||
Header: 'Username',
|
||||
accessor: 'email',
|
||||
Cell: ({ row: { original: user } }: any) => (
|
||||
<TextCell>{user.email || user.username}</TextCell>
|
||||
@ -84,8 +88,8 @@ export const ProjectAccessTable: VFC<IProjectAccessTableProps> = ({
|
||||
),
|
||||
},
|
||||
{
|
||||
Header: 'Actions',
|
||||
id: 'actions',
|
||||
Header: 'Actions',
|
||||
disableSortBy: true,
|
||||
align: 'center',
|
||||
width: 80,
|
||||
|
@ -70,10 +70,12 @@ export const ProjectCard = ({
|
||||
<PermissionIconButton
|
||||
permission={UPDATE_PROJECT}
|
||||
projectId={id}
|
||||
className={classes.actionsBtn}
|
||||
data-loading
|
||||
onClick={handleClick}
|
||||
tooltipProps={{ title: 'Options' }}
|
||||
tooltipProps={{
|
||||
title: 'Options',
|
||||
className: classes.actionsBtn,
|
||||
}}
|
||||
>
|
||||
<MoreVertIcon />
|
||||
</PermissionIconButton>
|
||||
|
@ -73,11 +73,11 @@ export const SegmentTable = () => {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const hiddenColumns = ['description'];
|
||||
if (isSmallScreen) {
|
||||
setHiddenColumns(['description', 'createdAt', 'createdBy']);
|
||||
} else {
|
||||
setHiddenColumns(['description']);
|
||||
hiddenColumns.push('createdAt', 'createdBy');
|
||||
}
|
||||
setHiddenColumns(hiddenColumns);
|
||||
}, [setHiddenColumns, isSmallScreen]);
|
||||
|
||||
return (
|
||||
|
@ -307,6 +307,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
data-mui-internal-clone-element={true}
|
||||
id="useId-1"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
@ -357,6 +358,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
data-mui-internal-clone-element={true}
|
||||
id="useId-2"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
@ -477,6 +479,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
data-mui-internal-clone-element={true}
|
||||
id="useId-3"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
@ -527,6 +530,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
data-mui-internal-clone-element={true}
|
||||
id="useId-4"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
@ -647,6 +651,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
data-mui-internal-clone-element={true}
|
||||
id="useId-5"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
@ -697,6 +702,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
data-mui-internal-clone-element={true}
|
||||
id="useId-6"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
@ -817,6 +823,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
data-mui-internal-clone-element={true}
|
||||
id="useId-7"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
@ -867,6 +874,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
data-mui-internal-clone-element={true}
|
||||
id="useId-8"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
@ -987,6 +995,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
data-mui-internal-clone-element={true}
|
||||
id="useId-9"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
@ -1037,6 +1046,7 @@ exports[`renders an empty list correctly 1`] = `
|
||||
data-mui-internal-clone-element={true}
|
||||
id="useId-10"
|
||||
onBlur={[Function]}
|
||||
onClick={[Function]}
|
||||
onFocus={[Function]}
|
||||
onMouseLeave={[Function]}
|
||||
onMouseOver={[Function]}
|
||||
|
@ -67,7 +67,7 @@ export default createTheme({
|
||||
},
|
||||
success: {
|
||||
light: colors.green[50],
|
||||
main: colors.green[500],
|
||||
main: colors.green[600],
|
||||
dark: colors.green[800],
|
||||
border: colors.green[300],
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user