From 2e3fd067f80b834c05825a3ebf2f0aece944b52c Mon Sep 17 00:00:00 2001 From: Mark Phelps <209477+markphelps@users.noreply.github.com> Date: Sun, 15 Dec 2024 11:39:47 -0500 Subject: [PATCH] refactor(ui): make flags/segments views take up more screen space (#3726) * chore: have flags/segments take up more horizontal space Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> * chore: rm nav border Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> * chore: set max-w Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> * chore: fix ui lint errors Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> * chore: make better use of space for bottom of pages Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> --------- Signed-off-by: Mark Phelps <209477+markphelps@users.noreply.github.com> --- go.work.sum | 18 +- ui/src/app/Layout.tsx | 2 +- ui/src/app/flags/Flag.tsx | 153 ++++------ ui/src/app/flags/NewFlag.tsx | 25 +- ui/src/app/flags/analytics/Analytics.tsx | 8 +- ui/src/app/flags/rules/Rules.tsx | 23 +- ui/src/app/segments/NewSegment.tsx | 25 +- ui/src/app/segments/Segment.tsx | 320 +++++++++----------- ui/src/components/MoreInfo.tsx | 10 +- ui/src/components/Well.tsx | 4 +- ui/src/components/flags/FlagTable.tsx | 22 +- ui/src/components/rollouts/Rollouts.tsx | 22 +- ui/src/components/segments/SegmentTable.tsx | 22 +- ui/src/components/ui/guide.tsx | 19 -- ui/src/components/variants/Variants.tsx | 2 +- 15 files changed, 281 insertions(+), 394 deletions(-) delete mode 100644 ui/src/components/ui/guide.tsx diff --git a/go.work.sum b/go.work.sum index 44bb08cfa4..f19bd590c9 100644 --- a/go.work.sum +++ b/go.work.sum @@ -74,12 +74,9 @@ cloud.google.com/go/auth v0.7.3/go.mod h1:HJtWUx1P5eqjy/f6Iq5KeytNpbAcGolPhOgyop cloud.google.com/go/auth v0.8.0/go.mod h1:qGVp/Y3kDRSDZ5gFD/XPUfYQ9xW1iI7q8RIRoCyBbJc= cloud.google.com/go/auth v0.9.0/go.mod h1:2HsApZBr9zGZhC9QAXsYVYaWk8kNUt37uny+XVKi7wM= cloud.google.com/go/auth v0.9.4/go.mod h1:SHia8n6//Ya940F1rLimhJCjjx7KE17t0ctFEci3HkA= -cloud.google.com/go/auth v0.11.0 h1:Ic5SZz2lsvbYcWT5dfjNWgw6tTlGi2Wc8hyQSC9BstA= -cloud.google.com/go/auth v0.11.0/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= +cloud.google.com/go/auth v0.9.9/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= cloud.google.com/go/auth/oauth2adapt v0.2.2/go.mod h1:wcYjgpZI9+Yu7LyYBg4pqSiaRkfEK3GQcpb7C/uyF1Q= cloud.google.com/go/auth/oauth2adapt v0.2.3/go.mod h1:tMQXOfZzFuNuUxOypHlQEXgdfX5cuhwU+ffUuXRJE8I= -cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= -cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= cloud.google.com/go/automl v1.13.6/go.mod h1:/0VtkKis6KhFJuPzi45e0E+e9AdQE09SNieChjJqU18= cloud.google.com/go/automl v1.13.11/go.mod h1:oMJdXRDOVC+Eq3PnGhhxSut5Hm9TSyVx1aLEOgerOw8= cloud.google.com/go/automl v1.14.0/go.mod h1:Kr7rN9ANSjlHyBLGvwhrnt35/vVZy3n/CP4Xmyj0shM= @@ -1175,8 +1172,6 @@ github.com/googleapis/gax-go/v2 v2.12.0/go.mod h1:y+aIqrI5eb1YGMVJfuV3185Ts/D7qK github.com/googleapis/gax-go/v2 v2.12.2/go.mod h1:61M8vcyyXR2kqKFxKrfA22jaA8JGF7Dc8App1U3H6jc= github.com/googleapis/gax-go/v2 v2.12.3/go.mod h1:AKloxT6GtNbaLm8QTNSidHUVsHYcBHwWRvkNFJUQcS4= github.com/googleapis/gax-go/v2 v2.12.5/go.mod h1:BUDKcWo+RaKq5SC9vVYL0wLADa3VcfswbOMMRmB9H3E= -github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o= -github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk= github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU= github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= @@ -1940,8 +1935,7 @@ golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/ golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= -golang.org/x/crypto v0.31.0 h1:ihbySMvVjLAeSH1IbfcRTkD/iNscyz8rGzjF/E5hV6U= -golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= +golang.org/x/crypto v0.30.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk= golang.org/x/exp v0.0.0-20200331195152-e8c3332aa8e5/go.mod h1:4M0jN8W1tt0AVLNr8HDosyJCDCDuyL9N9+3m7wDWgKw= golang.org/x/exp v0.0.0-20230224173230-c95f2b4c22f2/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20230515195305-f3d0a9c9a5cc/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= @@ -2128,8 +2122,6 @@ golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxb golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= -golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= -golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -2176,9 +2168,8 @@ golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c golang.org/x/tools v0.23.0/go.mod h1:pnu6ufv6vQkll6szChhK3C3L/ruaIv5eBeztNG8wtsI= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.27.0/go.mod h1:sUi0ZgbwW9ZPAq26Ekut+weQPR5eIM6GQLQ1Yjm1H0Q= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= -golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY= -golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.10.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= @@ -2200,9 +2191,8 @@ google.golang.org/api v0.189.0/go.mod h1:FLWGJKb0hb+pU2j+rJqwbnsF+ym+fQs73rbJ+KA google.golang.org/api v0.191.0/go.mod h1:tD5dsFGxFza0hnQveGfVk9QQYKcfp+VzgRqyXFxE0+E= google.golang.org/api v0.193.0/go.mod h1:Po3YMV1XZx+mTku3cfJrlIYR03wiGrCOsdpC67hjZvw= google.golang.org/api v0.196.0/go.mod h1:g9IL21uGkYgvQ5BZg6BAtoGJQIm8r6EgaAbpNey5wBE= +google.golang.org/api v0.203.0/go.mod h1:BuOVyCSYEPwJb3npWvDnNmFI92f3GeRnHNkETneT3SI= google.golang.org/api v0.205.0/go.mod h1:NrK1EMqO8Xk6l6QwRAmrXXg2v6dzukhlOyvkYtnvUuc= -google.golang.org/api v0.210.0 h1:HMNffZ57OoZCRYSbdWVRoqOa8V8NIHLL0CzdBPLztWk= -google.golang.org/api v0.210.0/go.mod h1:B9XDZGnx2NtyjzVkOVTGrFSAVZgPcbedzKg/gTLwqBs= google.golang.org/appengine v1.6.2/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= diff --git a/ui/src/app/Layout.tsx b/ui/src/app/Layout.tsx index 9aa08afe1e..11161cf0b6 100644 --- a/ui/src/app/Layout.tsx +++ b/ui/src/app/Layout.tsx @@ -81,7 +81,7 @@ function InnerLayout() { /> )}
-
+
diff --git a/ui/src/app/flags/Flag.tsx b/ui/src/app/flags/Flag.tsx index 05c1c8f735..fef044db3c 100644 --- a/ui/src/app/flags/Flag.tsx +++ b/ui/src/app/flags/Flag.tsx @@ -18,7 +18,6 @@ import CopyToNamespacePanel from '~/components/panels/CopyToNamespacePanel'; import DeletePanel from '~/components/panels/DeletePanel'; import { useError } from '~/data/hooks/error'; import { useSuccess } from '~/data/hooks/success'; -import { useTimezone } from '~/data/hooks/timezone'; import { useCopyFlagMutation, useDeleteFlagMutation, @@ -41,7 +40,6 @@ const booleanFlagTabs = [ export default function Flag() { let { flagKey } = useParams(); - const { inTimezone } = useTimezone(); const [showDeleteFlagModal, setShowDeleteFlagModal] = useState(false); const [showCopyFlagModal, setShowCopyFlagModal] = useState(false); @@ -155,98 +153,71 @@ export default function Flag() { ]} /> -
-
-
-
+ + {/* Info Section */} +
+
+ + Created{' '} + {formatDistanceToNowStrict(parseISO(flag.createdAt), { + addSuffix: true + })}
+ + + Learn more about flags +
-
- {/* flag details */} -
-
-
-

- Basic information about the flag and its status. -

- - Learn more about flags - -
-
- -
-
-
- <> -
-
- -
-
- - + {/* Form Section - Full Width */} +
+ +
+ + {/* Tabs Section */} +
+ +
); diff --git a/ui/src/app/flags/NewFlag.tsx b/ui/src/app/flags/NewFlag.tsx index 4962993777..b18b95c1d6 100644 --- a/ui/src/app/flags/NewFlag.tsx +++ b/ui/src/app/flags/NewFlag.tsx @@ -6,23 +6,14 @@ export default function NewFlag() { return ( <> -
-
-
-

- Basic information about the flag and its status. -

- - Learn more about flags - -
-
- -
-
+
+ + Learn more about flags + +
+ +
+
); diff --git a/ui/src/app/flags/analytics/Analytics.tsx b/ui/src/app/flags/analytics/Analytics.tsx index 6d20263a0d..c2ef0fccc5 100644 --- a/ui/src/app/flags/analytics/Analytics.tsx +++ b/ui/src/app/flags/analytics/Analytics.tsx @@ -101,7 +101,7 @@ export default function Analytics() { }, [selectedDuration]); return ( -
+
{config.analyticsEnabled ? ( <>
@@ -141,10 +141,10 @@ export default function Analytics() {
) : ( -
+
{(rules && rules.length > 0) || showDefaultVariant ? ( -
-
-

- Rules are evaluated in order from{' '} - top to bottom. The - first rule that matches will be applied. -

-

- Rules can be rearranged by clicking on a rule header and{' '} - dragging and dropping{' '} - it into place. -

-
-
+
+
{rules && rules.length > 0 && ( -
-
-
-

- Basic information about the segment. -

- - Learn more about segmentation - -
-
- -
-
+
+ + Learn more about segments + +
+ +
+
); diff --git a/ui/src/app/segments/Segment.tsx b/ui/src/app/segments/Segment.tsx index e9c167fdcd..a3f61a8091 100644 --- a/ui/src/app/segments/Segment.tsx +++ b/ui/src/app/segments/Segment.tsx @@ -69,7 +69,6 @@ function ConstraintValue({ constraint }: { constraint: IConstraint }) { export default function Segment() { let { segmentKey } = useParams(); - const { inTimezone } = useTimezone(); const [showConstraintForm, setShowConstraintForm] = useState(false); const [editingConstraint, setEditingConstraint] = @@ -215,7 +214,6 @@ export default function Segment() { /> - {/* segment header / delete button */} -
-
-
-
+ +
+
+ + Created{' '} + {formatDistanceToNowStrict(parseISO(segment.createdAt), { + addSuffix: true + })}
+ + + Learn more about segments +
-
- {/* segment details */} -
-
-
-

- Basic information about the segment -

- + +
+ +
+
+
+

Constraints

+

+ Determine if a request matches a segment. +

+
+ {segment.constraints && segment.constraints.length > 0 && ( +
+
-
- + New Constraint +
-
+ )}
- {/* constraints */} -
-
-
-
-

- Constraints -

-

- Determine if a request matches a segment -

-
- {segment.constraints && segment.constraints.length > 0 && ( -
-
diff --git a/ui/src/components/MoreInfo.tsx b/ui/src/components/MoreInfo.tsx index 7c3e1de58d..bb6ea572f0 100644 --- a/ui/src/components/MoreInfo.tsx +++ b/ui/src/components/MoreInfo.tsx @@ -1,5 +1,3 @@ -import { QuestionMarkCircleIcon } from '@heroicons/react/20/solid'; - type MoreInfoProps = { className?: string; href: string; @@ -15,13 +13,9 @@ export default function MoreInfo(props: MoreInfoProps) { href={href} target="_blank" rel="noreferrer" - className="group inline-flex items-center text-gray-500 hover:text-gray-600" + className="group inline-flex items-center text-gray-500 underline underline-offset-4 hover:text-gray-600" > -
); diff --git a/ui/src/components/Well.tsx b/ui/src/components/Well.tsx index a4ae10f0d5..a5b75785e9 100644 --- a/ui/src/components/Well.tsx +++ b/ui/src/components/Well.tsx @@ -8,7 +8,9 @@ export default function Well(props: WellProps) { return (
-
{children}
+
+ {children} +
); } diff --git a/ui/src/components/flags/FlagTable.tsx b/ui/src/components/flags/FlagTable.tsx index e0585c3e84..eeb1bc8470 100644 --- a/ui/src/components/flags/FlagTable.tsx +++ b/ui/src/components/flags/FlagTable.tsx @@ -10,7 +10,7 @@ import { } from '@tanstack/react-table'; import { useState, useEffect, useMemo } from 'react'; import { useSelector, useDispatch } from 'react-redux'; -import { useNavigate } from 'react-router-dom'; +import { Link, useNavigate } from 'react-router-dom'; import { DataTablePagination } from '~/components/ui/table-pagination'; import { useTimezone } from '~/data/hooks/timezone'; import { FlagType, flagTypeToLabel, IFlag } from '~/types/Flag'; @@ -24,11 +24,11 @@ import { Badge } from '~/components/ui/badge'; import { formatDistanceToNowStrict, parseISO } from 'date-fns'; import { Search } from '~/components/ui/search'; import { DataTableViewOptions } from '~/components/ui/table-view-options'; -import Guide from '~/components/ui/guide'; import { VariableIcon, ToggleLeftIcon } from 'lucide-react'; import { useError } from '~/data/hooks/error'; import { INamespaceBase } from '~/types/Namespace'; import { TableSkeleton } from '~/components/ui/table-skeleton'; +import Well from '../Well'; type FlagTableProps = { namespace: INamespaceBase; @@ -186,13 +186,21 @@ export default function FlagTable(props: FlagTableProps) {
{table.getRowCount() === 0 && filter.length === 0 && ( - - Flags enable you to control and roll out new functionality - dynamically. Create a new flag to get started. - + +

+ Flags enable you to control and roll out new functionality + dynamically. Create a{' '} + + new flag + {' '} + to get started. +

+
)} {table.getRowCount() === 0 && filter.length > 0 && ( - No flags matched your search. + +

No flags matched your search.

+
)} {table.getRowModel().rows.map((row) => { const item = row.original; diff --git a/ui/src/components/rollouts/Rollouts.tsx b/ui/src/components/rollouts/Rollouts.tsx index 0215a2c603..7c44f44e77 100644 --- a/ui/src/components/rollouts/Rollouts.tsx +++ b/ui/src/components/rollouts/Rollouts.tsx @@ -228,7 +228,6 @@ export default function Rollouts(props: RolloutsProps) { const rollouts = useMemo(() => { // Combine both segmentKey and segmentKeys for legacy purposes. - // TODO(yquansah): Should be removed once there are no more references to `segmentKey`. return rolloutsRules.map((rollout) => { if (rollout.segment) { let segmentKeys: string[] = []; @@ -383,7 +382,12 @@ export default function Rollouts(props: RolloutsProps) {

- Return boolean values based on rules you define + Return boolean values based on rules you define. Rules are + evaluated in order from top to bottom. +

+

+ Rules can be rearranged by clicking on the header and dragging and + dropping it into place.

@@ -402,19 +406,7 @@ export default function Rollouts(props: RolloutsProps) {
-
-
-

- Rollout rules are evaluated in order from{' '} - top to bottom. The first - rule that matches will be applied. -

-

- Rollouts can be rearranged by clicking on a rollout header and{' '} - dragging and dropping it - into place. -

-
+
{rollouts && rollouts.length > 0 && (
{table.getRowCount() === 0 && filter.length !== 0 && ( - No segments matched your search. + +

No segments matched your search.

+
)} {table.getRowCount() === 0 && filter.length === 0 && ( - - Segments enable request targeting based on defined criteria. Create - new segment to get started. - + +

+ Segments enable request targeting based on defined criteria. Create + a{' '} + + new segment + {' '} + to get started. +

+
)} {table.getRowModel().rows.map((row) => { const item = row.original; diff --git a/ui/src/components/ui/guide.tsx b/ui/src/components/ui/guide.tsx deleted file mode 100644 index 4302bf7773..0000000000 --- a/ui/src/components/ui/guide.tsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from 'react'; -import { cn } from '~/lib/utils'; -import Well from '~/components/Well'; - -export interface GuideProps extends React.HTMLAttributes {} - -const Guide = ({ children, className, ...props }: GuideProps) => ( -
- -

- {children} -

-
-
-); - -Guide.displayName = 'Guide'; - -export default Guide; diff --git a/ui/src/components/variants/Variants.tsx b/ui/src/components/variants/Variants.tsx index 43b041835e..f69db2ea26 100644 --- a/ui/src/components/variants/Variants.tsx +++ b/ui/src/components/variants/Variants.tsx @@ -78,7 +78,7 @@ export default function Variants({ flag }: VariantsProps) {

- Return different values based on rules you define + Return different values based on rules you define.

{flag.variants && flag.variants.length > 0 && (