Skip to content

Commit

Permalink
feature: show alert when approaching usage limit on dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
vid277 authored and skeptrunedev committed Jan 8, 2025
1 parent 2a37a7d commit d64bd2e
Show file tree
Hide file tree
Showing 4 changed files with 169 additions and 23 deletions.
126 changes: 126 additions & 0 deletions frontends/dashboard/src/components/OrgUpdateAlert.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { FaSolidTriangleExclamation } from "solid-icons/fa";
import { createEffect, createSignal, useContext } from "solid-js";
import { UserContext } from "../contexts/UserContext";
import { useTrieve } from "../hooks/useTrieve";
import {
createUsageQuery,
createSubscriptionQuery,
} from "../utils/fetchOrgUsage";

enum AlertState {
Danger,
Warning,
Hidden,
}

const OrgUpdateAlert = () => {
const [message, setMessage] = createSignal("");
const [alertState, setAlertState] = createSignal(AlertState.Hidden);

const userContext = useContext(UserContext);
const trieve = useTrieve();

const usageQuery = createUsageQuery(userContext, trieve);
const subscriptionQuery = createSubscriptionQuery(userContext, trieve);

const updateAlert = (
currentCount: number,
subscriptionLimit: number,
variableName: string,
) => {
const percentageUsed = ((currentCount / subscriptionLimit) * 100).toFixed(
1,
);

if (currentCount >= subscriptionLimit) {
setMessage(
`Your organization has reached its total ${variableName} limit (${percentageUsed}% used).`,
);
setAlertState(AlertState.Danger);
} else if (currentCount >= Math.floor(subscriptionLimit * 0.8)) {
setMessage(
`Your organization is approaching its total ${variableName} limit (${percentageUsed}% used).`,
);
setAlertState(AlertState.Warning);
} else {
setMessage("");
setAlertState(AlertState.Hidden);
}
};

createEffect(() => {
const orgUsage = usageQuery.data;
const orgLimits = subscriptionQuery.data?.plan;

if (!orgUsage || !orgLimits) return;

const OrganizationUsageVariables = [
{
current: orgUsage.user_count,
limit: orgLimits.user_count,
name: "users",
},
{
current: orgUsage.file_storage,
limit: orgLimits.file_storage,
name: "file storage",
},
{
current: orgUsage.message_count,
limit: orgLimits.message_count,
name: "messages",
},
{
current: orgUsage.chunk_count,
limit: orgLimits.chunk_count,
name: "chunks",
},
];

const approachingUsage = OrganizationUsageVariables.find(
({ current, limit }) =>
current >= limit || current >= Math.floor(limit * 0.8),
);

if (approachingUsage) {
updateAlert(
approachingUsage.current,
approachingUsage.limit,
approachingUsage.name,
);
} else {
setMessage("");
setAlertState(AlertState.Hidden);
}
});

return (
<div>
{alertState() !== AlertState.Hidden && (
<div
class={`flex flex-row items-center justify-between rounded-lg border-2 bg-transparent bg-white p-4 ${
alertState() ? "border-yellow-500" : "border-red-500"
}`}
>
<div
class={`flex flex-row items-center justify-center gap-3 ${
alertState() ? "text-yellow-600" : "text-red-600"
} `}
>
<FaSolidTriangleExclamation />
<span class="text-sm font-semibold">
{message()}
{" To upgrade your subscription, click "}
<a href="/org/billing" class="text-blue-500 underline">
here
</a>
{' or visit the "Billing" tab.'}
</span>
</div>
</div>
)}
</div>
);
};

export default OrgUpdateAlert;
29 changes: 6 additions & 23 deletions frontends/dashboard/src/components/OrganizationUsageOverview.tsx
Original file line number Diff line number Diff line change
@@ -1,36 +1,19 @@
import { useContext } from "solid-js";
import { ProgressBar } from "./ProgressBar";
import { formatNumberWithCommas, formatStorage } from "../utils/formatNumbers";
import { createQuery } from "@tanstack/solid-query";
import { UserContext } from "../contexts/UserContext";
import { OrganizationAndSubAndPlan } from "shared/types";
import { useTrieve } from "../hooks/useTrieve";
import {
createUsageQuery,
createSubscriptionQuery,
} from "../utils/fetchOrgUsage";

export const OrganizationUsageOverview = () => {
const userContext = useContext(UserContext);
const trieve = useTrieve();

const usageQuery = createQuery(() => ({
queryKey: ["org-usage", userContext.selectedOrg().id],
queryFn: async () => {
return trieve.fetch("/api/organization/usage/{organization_id}", "get", {
organizationId: userContext.selectedOrg().id,
});
},
}));

const subscriptionQuery = createQuery(() => ({
queryKey: ["org-subscription", userContext.selectedOrg().id],
queryFn: async () => {
return trieve.fetch<"eject">(
"/api/organization/{organization_id}",
"get",
{
organizationId: userContext.selectedOrg().id,
},
) as Promise<OrganizationAndSubAndPlan>;
},
}));
const usageQuery = createUsageQuery(userContext, trieve);
const subscriptionQuery = createSubscriptionQuery(userContext, trieve);

return (
<div class="mb-3 grid grid-cols-1 gap-5 lg:grid-cols-4">
Expand Down
3 changes: 3 additions & 0 deletions frontends/dashboard/src/pages/orgs/OrganizationHomepage.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { DatasetOverview } from "../../components/DatasetOverview";
import { GettingStartedDocsLinks } from "../../components/GettingStartedDocsLinks";
import { OnboardingSteps } from "../../components/OnboardingSteps";
import OrgUpdateAlert from "../../components/OrgUpdateAlert";

export const OrganizationHomepage = () => {
return (
<div class="pb-8">
<OrgUpdateAlert />
<div class="h-1" />
<OnboardingSteps />
<div class="h-1" />
<DatasetOverview />
Expand Down
34 changes: 34 additions & 0 deletions frontends/dashboard/src/utils/fetchOrgUsage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { createQuery } from "@tanstack/solid-query";
import type { UserStore } from "../contexts/UserContext";
import type { OrganizationAndSubAndPlan } from "shared/types";
import { TrieveFetchClient } from "trieve-ts-sdk";

export const createUsageQuery = (
userContext: UserStore,
trieve: TrieveFetchClient,
) =>
createQuery(() => ({
queryKey: ["org-usage", userContext.selectedOrg().id],
queryFn: async () => {
return trieve.fetch("/api/organization/usage/{organization_id}", "get", {
organizationId: userContext.selectedOrg().id,
});
},
}));

export const createSubscriptionQuery = (
userContext: UserStore,
trieve: TrieveFetchClient,
) =>
createQuery(() => ({
queryKey: ["org-subscription", userContext.selectedOrg().id],
queryFn: async () => {
return trieve.fetch<"eject">(
"/api/organization/{organization_id}",
"get",
{
organizationId: userContext.selectedOrg().id,
},
) as Promise<OrganizationAndSubAndPlan>;
},
}));

0 comments on commit d64bd2e

Please sign in to comment.