Skip to content

Commit

Permalink
feat(ui): new table view for flags and segments
Browse files Browse the repository at this point in the history
Signed-off-by: Roman Dmytrenko <[email protected]>
  • Loading branch information
erka committed Nov 30, 2024
1 parent 70295f8 commit 2bdfa2c
Show file tree
Hide file tree
Showing 20 changed files with 1,310 additions and 453 deletions.
539 changes: 539 additions & 0 deletions ui/package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@heroicons/react": "^2.1.5",
"@loadable/component": "^5.16.4",
"@radix-ui/react-dropdown-menu": "^2.1.2",
"@radix-ui/react-select": "^2.1.2",
"@radix-ui/react-slot": "^1.1.0",
"@reduxjs/toolkit": "^2.3.0",
"@tanstack/react-table": "^8.20.5",
Expand Down
4 changes: 2 additions & 2 deletions ui/src/app/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -85,8 +85,8 @@ function InnerLayout() {
href="https://docs.flipt.io/cloud/overview"
/>
)}
<main className="flex px-6 py-10">
<div className="w-full overflow-x-auto px-4 sm:px-6 lg:px-8">
<main className="flex sm:pt-4">
<div className="mx-auto w-full max-w-7xl overflow-x-auto px-4 sm:px-6 lg:px-8">
<Outlet />
</div>
</main>
Expand Down
13 changes: 5 additions & 8 deletions ui/src/app/console/Console.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
generateCurlCommand,
getErrorMessage
} from '~/utils/helpers';
import { PageHeader } from '~/components/ui/page';

function ResetOnNamespaceChange({ namespace }: { namespace: INamespace }) {
const { resetForm } = useFormikContext();
Expand Down Expand Up @@ -213,14 +214,10 @@ export default function Console() {

return (
<>
<div className="relative flex flex-col">
<h1 className="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl">
Console
</h1>
<p className="mt-2 text-sm text-gray-500">
See the results of your flag evaluations and debug any issues
</p>
</div>
<PageHeader title="Console" />
<p className="mt-2 text-sm text-gray-500">
See the results of your flag evaluations and debug any issues
</p>
<div className="flex flex-col md:flex-row">
{flags.length > 0 && (
<>
Expand Down
80 changes: 38 additions & 42 deletions ui/src/app/flags/Flag.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import {
} from './flagsApi';
import { FlagType } from '~/types/Flag';
import { cls } from '~/utils/helpers';
import { PageHeader } from '~/components/ui/page';

const variantFlagTabs = [
{ name: 'Variants', to: '' },
Expand Down Expand Up @@ -130,51 +131,46 @@ export default function Flag() {
</Modal>

{/* flag header / actions */}
<PageHeader title={flag.name}>
<Dropdown
label="Actions"
actions={[
{
id: 'flag-copy',
label: 'Copy to Namespace',
disabled: readOnly || namespaces.length < 2,
onClick: () => {
setShowCopyFlagModal(true);
},
icon: FilesIcon
},
{
id: 'flag-delete',
label: 'Delete',
disabled: readOnly,
onClick: () => setShowDeleteFlagModal(true),
icon: Trash2Icon,
variant: 'destructive'
}
]}
/>
</PageHeader>
<div className="flex items-center justify-between">
<div className="min-w-0 flex-1">
<h2 className="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight">
{flag.name}
</h2>
<div className="mt-1 flex flex-col sm:mt-0 sm:flex-row sm:flex-wrap sm:space-x-6">
<div
title={inTimezone(flag.createdAt)}
className="mt-2 flex items-center text-sm text-gray-500"
>
<CalendarIcon
className="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
aria-hidden="true"
/>
Created{' '}
{formatDistanceToNowStrict(parseISO(flag.createdAt), {
addSuffix: true
})}
</div>
<div className="mt-1 flex flex-col sm:mt-0 sm:flex-row sm:flex-wrap sm:space-x-6">
<div
title={inTimezone(flag.createdAt)}
className="mt-2 flex items-center text-sm text-gray-500"
>
<CalendarIcon
className="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
aria-hidden="true"
/>
Created{' '}
{formatDistanceToNowStrict(parseISO(flag.createdAt), {
addSuffix: true
})}
</div>
</div>
<div className="flex flex-none">
<Dropdown
label="Actions"
actions={[
{
id: 'flag-copy',
label: 'Copy to Namespace',
disabled: readOnly || namespaces.length < 2,
onClick: () => {
setShowCopyFlagModal(true);
},
icon: FilesIcon
},
{
id: 'flag-delete',
label: 'Delete',
disabled: readOnly,
onClick: () => setShowDeleteFlagModal(true),
icon: Trash2Icon,
variant: 'destructive'
}
]}
/>
</div>
</div>

<div className="flex flex-col">
Expand Down
54 changes: 19 additions & 35 deletions ui/src/app/flags/Flags.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import { useEffect } from 'react';
import { useSelector } from 'react-redux';
import { Link, useNavigate } from 'react-router-dom';
import { useNavigate } from 'react-router-dom';
import { selectReadonly } from '~/app/meta/metaSlice';
import { selectCurrentNamespace } from '~/app/namespaces/namespacesSlice';
import EmptyState from '~/components/EmptyState';
import FlagTable from '~/components/flags/FlagTable';
import { ButtonWithPlus } from '~/components/forms/buttons/Button';
import { useError } from '~/data/hooks/error';
import { useListFlagsQuery } from './flagsApi';
import { Plus } from 'lucide-react';
import { Button } from '~/components/ui/button';
import FlagTable from '~/components/flags/FlagTable';
import Guide from '~/components/ui/guide';
import { PageHeader } from '~/components/ui/page';

export default function Flags() {
const namespace = useSelector(selectCurrentNamespace);
Expand All @@ -17,50 +19,32 @@ export default function Flags() {
const flags = data?.flags || [];

const navigate = useNavigate();
const { setError, clearError } = useError();
const { setError } = useError();

const readOnly = useSelector(selectReadonly);

useEffect(() => {
if (error) {
setError(error);
return;
}
clearError();
}, [clearError, error, setError]);
}, [error, setError]);

return (
<>
<div className="flex-row justify-between pb-5 sm:flex sm:items-center">
<div className="flex flex-col">
<h1 className="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl">
Flags
</h1>
<p className="mt-2 text-sm text-gray-500">
Flags represent features that you can easily enable or disable
</p>
</div>
<div className="mt-4">
<Link to={`${path}/new`}>
<ButtonWithPlus
variant="primary"
disabled={readOnly}
title={readOnly ? 'Not allowed in Read-Only mode' : undefined}
>
New Flag
</ButtonWithPlus>
</Link>
</div>
</div>
<div className="mt-4 flex flex-col">
<PageHeader title="Flags">
<Button onClick={() => navigate(`${path}/new`)} disabled={readOnly}>
<Plus />
New Flag
</Button>
</PageHeader>
<div className="flex flex-col gap-1 py-2">
{flags && flags.length > 0 ? (
<FlagTable flags={flags} />
) : (
<EmptyState
text="Create Flag"
disabled={readOnly}
onClick={() => navigate(`${path}/new`)}
/>
<Guide className="mt-6">
Flags enable you to control and roll out new functionality
dynamically. Create a new flag to get started.
</Guide>
)}
</div>
</>
Expand Down
11 changes: 3 additions & 8 deletions ui/src/app/flags/NewFlag.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import FlagForm from '~/components/flags/forms/FlagForm';
import MoreInfo from '~/components/MoreInfo';
import { PageHeader } from '~/components/ui/page';

export default function NewFlag() {
return (
<>
<div className="lg:flex lg:items-center lg:justify-between">
<div className="min-w-0 flex-1">
<h2 className="text-2xl font-semibold leading-6 text-gray-900">
New Flag
</h2>
</div>
</div>
<div className="my-10">
<PageHeader title="New Flag" />
<div className="my-6">
<div className="md:grid md:grid-cols-3 md:gap-6">
<div className="md:col-span-1">
<p className="mt-2 text-sm text-gray-500">
Expand Down
11 changes: 3 additions & 8 deletions ui/src/app/segments/NewSegment.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,12 @@
import MoreInfo from '~/components/MoreInfo';
import SegmentForm from '~/components/segments/SegmentForm';
import { PageHeader } from '~/components/ui/page';

export default function NewSegment() {
return (
<>
<div className="lg:flex lg:items-center lg:justify-between">
<div className="min-w-0 flex-1">
<h2 className="text-2xl font-semibold leading-6 text-gray-900">
New Segment
</h2>
</div>
</div>
<div className="my-10">
<PageHeader title="New Segment" />
<div className="my-6">
<div className="md:grid md:grid-cols-3 md:gap-6">
<div className="md:col-span-1">
<p className="mt-2 text-sm text-gray-500">
Expand Down
78 changes: 37 additions & 41 deletions ui/src/app/segments/Segment.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import {
constraintTypeToLabel,
IConstraint
} from '~/types/Constraint';
import { PageHeader } from '~/components/ui/page';

function ConstraintArrayValue({ value }: { value: string | undefined }) {
const items: string[] | number[] = useMemo(() => {
Expand Down Expand Up @@ -215,54 +216,49 @@ export default function Segment() {
</Modal>

{/* segment header / delete button */}
<PageHeader title={segment.name}>
<Dropdown
label="Actions"
actions={[
{
id: 'segment-copy',
label: 'Copy to Namespace',
disabled: readOnly || namespaces.length < 2,
onClick: () => setShowCopySegmentModal(true),
icon: FilesIcon
},
{
id: 'segement-delete',
label: 'Delete',
disabled: readOnly,
onClick: () => setShowDeleteSegmentModal(true),
icon: Trash2Icon,
variant: 'destructive'
}
]}
/>
</PageHeader>
<div className="flex items-center justify-between">
<div className="min-w-0 flex-1">
<h2 className="text-2xl font-bold leading-7 text-gray-900 sm:truncate sm:text-3xl sm:tracking-tight">
{segment.name}
</h2>
<div className="mt-1 flex flex-col sm:mt-0 sm:flex-row sm:flex-wrap sm:space-x-6">
<div
title={inTimezone(segment.createdAt)}
className="mt-2 flex items-center text-sm text-gray-500"
>
<CalendarIcon
className="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
aria-hidden="true"
/>
Created{' '}
{formatDistanceToNowStrict(parseISO(segment.createdAt), {
addSuffix: true
})}
</div>
<div className="mt-1 flex flex-col sm:mt-0 sm:flex-row sm:flex-wrap sm:space-x-6">
<div
title={inTimezone(segment.createdAt)}
className="mt-2 flex items-center text-sm text-gray-500"
>
<CalendarIcon
className="mr-1.5 h-5 w-5 flex-shrink-0 text-gray-400"
aria-hidden="true"
/>
Created{' '}
{formatDistanceToNowStrict(parseISO(segment.createdAt), {
addSuffix: true
})}
</div>
</div>
<div className="flex flex-none">
<Dropdown
label="Actions"
actions={[
{
id: 'segment-copy',
label: 'Copy to Namespace',
disabled: readOnly || namespaces.length < 2,
onClick: () => setShowCopySegmentModal(true),
icon: FilesIcon
},
{
id: 'segement-delete',
label: 'Delete',
disabled: readOnly,
onClick: () => setShowDeleteSegmentModal(true),
icon: Trash2Icon,
variant: 'destructive'
}
]}
/>
</div>
</div>

<div className="flex flex-col">
{/* segment details */}
<div className="mb-5 mt-10">
<div className="b-5">
<div className="md:grid md:grid-cols-3 md:gap-6">
<div className="md:col-span-1">
<p className="mt-1 text-sm text-gray-500">
Expand Down
Loading

0 comments on commit 2bdfa2c

Please sign in to comment.