From 9853c7dd320192be26cd1c218b762e3031d5dab8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Sans=C3=B3n?= <57395395+csansoon@users.noreply.github.com> Date: Thu, 26 Sep 2024 10:40:00 +0200 Subject: [PATCH] Usage Indicator (#278) --- apps/web/src/actions/workspaces/usage.ts | 11 + .../AppLayout/Header/UsageIndicator/index.tsx | 160 +++++++++ .../layouts/AppLayout/Header/index.tsx | 4 +- apps/web/src/stores/workspaceUsage.ts | 53 +++ apps/websockets/src/server.ts | 5 + packages/core/src/constants.ts | 7 + .../events/handlers/documentLogs/createJob.ts | 18 +- .../core/src/services/documentLogs/create.ts | 3 + .../core/src/services/workspaces/create.ts | 4 +- .../core/src/services/workspaces/index.ts | 1 + .../src/services/workspaces/usage.test.ts | 319 ++++++++++++++++++ .../core/src/services/workspaces/usage.ts | 61 ++++ .../core/src/tests/factories/documentLogs.ts | 3 + .../core/src/tests/factories/workspaces.ts | 2 + packages/core/src/websockets/constants.ts | 13 + .../src/ds/atoms/CircularProgress/index.tsx | 80 +++++ packages/web-ui/src/ds/atoms/index.ts | 1 + 17 files changed, 741 insertions(+), 4 deletions(-) create mode 100644 apps/web/src/actions/workspaces/usage.ts create mode 100644 apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx create mode 100644 apps/web/src/stores/workspaceUsage.ts create mode 100644 packages/core/src/services/workspaces/usage.test.ts create mode 100644 packages/core/src/services/workspaces/usage.ts create mode 100644 packages/web-ui/src/ds/atoms/CircularProgress/index.tsx diff --git a/apps/web/src/actions/workspaces/usage.ts b/apps/web/src/actions/workspaces/usage.ts new file mode 100644 index 000000000..1c3463e25 --- /dev/null +++ b/apps/web/src/actions/workspaces/usage.ts @@ -0,0 +1,11 @@ +'use server' + +import { computeWorkspaceUsage } from '@latitude-data/core/services/workspaces/usage' + +import { authProcedure } from '../procedures' + +export const fetchWorkspaceUsageAction = authProcedure + .createServerAction() + .handler(async ({ ctx }) => { + return await computeWorkspaceUsage(ctx.workspace).then((r) => r.unwrap()) + }) diff --git a/apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx new file mode 100644 index 000000000..1e1103825 --- /dev/null +++ b/apps/web/src/components/layouts/AppLayout/Header/UsageIndicator/index.tsx @@ -0,0 +1,160 @@ +'use client' + +import { ReactNode, useMemo } from 'react' + +import { WorkspaceUsage } from '@latitude-data/core/browser' +import { + Badge, + Button, + CircularProgress, + CircularProgressProps, + Skeleton, + Text, +} from '@latitude-data/web-ui' +import useWorkspaceUsage from '$/stores/workspaceUsage' +import Link from 'next/link' +import Popover from 'node_modules/@latitude-data/web-ui/src/ds/atoms/Popover' + +function UsageIndicatorCircle({ + data, + isLoading, + ...props +}: Omit & { + data: WorkspaceUsage | undefined + isLoading: boolean +}) { + const ratio = useMemo(() => { + if (!data) return 0 + if (data.max === 0) return 0 + const actualRatio = (data.max - data.usage) / data.max + + if (actualRatio <= 0) return 0 + if (actualRatio >= 1) return 1 + + if (actualRatio < 0.01) return 0.01 // Too low of a ratio makes the circle so small it disappears + return actualRatio + }, [data]) + + if (isLoading) { + return ( + + ) + } + if (ratio <= 0) { + return + } + + return ( + 0.25 + ? 'primary' + : ratio === 0 + ? 'destructive' + : 'warningMutedForeground' + } + {...props} + /> + ) +} + +function LoadingText({ + isLoading, + children, +}: { + isLoading: boolean + children: ReactNode +}) { + if (isLoading) { + return + } + + return children +} + +function descriptionText({ usage, max }: WorkspaceUsage) { + const ratio = (max - usage) / max + + if (ratio <= 0) { + return "You've reached the maximum number of runs. Your app will continue working, the Latitude limit reached means you'll no longer be able to run tests, view new logs or evaluation results." + } + if (ratio < 0.25) { + return "You're running low on runs in your current plan. Your app will continue working, the Latitude limit reached means you'll no longer be able to run tests, view new logs or evaluation results." + } + + return `Your plan has included ${max} runs. You can upgrade your plan to get more runs.` +} + +export function UsageIndicator() { + const { data, isLoading } = useWorkspaceUsage() + + return ( + + + + + +
+
+ + +
+ {data?.usage} + + {' '} + / {data?.max} runs + +
+ + Team Plan + +
+
+
+
+ {data ? ( + {descriptionText(data)} + ) : ( +
+ + + +
+ )} +
+ + + +
+
+
+
+ ) +} diff --git a/apps/web/src/components/layouts/AppLayout/Header/index.tsx b/apps/web/src/components/layouts/AppLayout/Header/index.tsx index 61bcf3f40..eb2310276 100644 --- a/apps/web/src/components/layouts/AppLayout/Header/index.tsx +++ b/apps/web/src/components/layouts/AppLayout/Header/index.tsx @@ -10,6 +10,7 @@ import Link from 'next/link' import { Fragment } from 'react/jsx-runtime' import AvatarDropdown from './AvatarDropdown' +import { UsageIndicator } from './UsageIndicator' function BreadcrumbSeparator() { return ( @@ -110,7 +111,8 @@ export default function AppHeader({
-