From 152fbede909256c313a6b70fda030fb9b9833015 Mon Sep 17 00:00:00 2001 From: RajuGangitla Date: Tue, 17 Sep 2024 13:05:19 +0530 Subject: [PATCH] feat: info about new formbricks version (#3126) Co-authored-by: Johannes Co-authored-by: Johannes <72809645+jobenjada@users.noreply.github.com> Co-authored-by: pandeymangg Co-authored-by: Anshuman Pandey <54475686+pandeymangg@users.noreply.github.com> Co-authored-by: Piyush Gupta --- .../[environmentId]/actions/actions.ts | 33 ++++++++++++++- .../components/MainNavigation.tsx | 41 +++++++++++++++++-- 2 files changed, 69 insertions(+), 5 deletions(-) diff --git a/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts b/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts index 3ff5965856..3c35933638 100644 --- a/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts +++ b/apps/web/app/(app)/environments/[environmentId]/actions/actions.ts @@ -2,8 +2,9 @@ import { z } from "zod"; import { deleteActionClass, getActionClass, updateActionClass } from "@formbricks/lib/actionClass/service"; -import { authenticatedActionClient } from "@formbricks/lib/actionClient"; +import { actionClient, authenticatedActionClient } from "@formbricks/lib/actionClient"; import { checkAuthorization } from "@formbricks/lib/actionClient/utils"; +import { cache } from "@formbricks/lib/cache"; import { getOrganizationIdFromActionClassId } from "@formbricks/lib/organization/utils"; import { getSurveysByActionClassId } from "@formbricks/lib/survey/service"; import { ZActionClassInput } from "@formbricks/types/action-classes"; @@ -72,3 +73,33 @@ export const getActiveInactiveSurveysAction = authenticatedActionClient }; return response; }); + +const getLatestStableFbRelease = async (): Promise => + cache( + async () => { + try { + const res = await fetch("https://api.github.com/repos/formbricks/formbricks/releases"); + const releases = await res.json(); + + if (Array.isArray(releases)) { + const latestStableReleaseTag = releases.filter((release) => !release.prerelease)?.[0] + ?.tag_name as string; + if (latestStableReleaseTag) { + return latestStableReleaseTag; + } + } + + return null; + } catch (err) { + return null; + } + }, + ["latest-fb-release"], + { + revalidate: 60 * 60 * 24, // 24 hours + } + )(); + +export const getLatestStableFbReleaseAction = actionClient.action(async () => { + return await getLatestStableFbRelease(); +}); diff --git a/apps/web/app/(app)/environments/[environmentId]/components/MainNavigation.tsx b/apps/web/app/(app)/environments/[environmentId]/components/MainNavigation.tsx index 90136ea077..a76dbd38cf 100644 --- a/apps/web/app/(app)/environments/[environmentId]/components/MainNavigation.tsx +++ b/apps/web/app/(app)/environments/[environmentId]/components/MainNavigation.tsx @@ -1,5 +1,6 @@ "use client"; +import { getLatestStableFbReleaseAction } from "@/app/(app)/environments/[environmentId]/actions/actions"; import { NavigationLink } from "@/app/(app)/environments/[environmentId]/components/NavigationLink"; import { formbricksLogout } from "@/app/lib/formbricks"; import FBLogo from "@/images/formbricks-wordmark.svg"; @@ -20,6 +21,7 @@ import { PanelLeftCloseIcon, PanelLeftOpenIcon, PlusIcon, + RocketIcon, UserCircleIcon, UserIcon, UsersIcon, @@ -54,6 +56,7 @@ import { DropdownMenuSubTrigger, DropdownMenuTrigger, } from "@formbricks/ui/DropdownMenu"; +import { version } from "../../../../../package.json"; interface NavigationProps { environment: TEnvironment; @@ -61,9 +64,9 @@ interface NavigationProps { user: TUser; organization: TOrganization; products: TProduct[]; - isFormbricksCloud: boolean; - membershipRole?: TMembershipRole; isMultiOrgEnabled: boolean; + isFormbricksCloud?: boolean; + membershipRole?: TMembershipRole; } export const MainNavigation = ({ @@ -72,9 +75,9 @@ export const MainNavigation = ({ organization, user, products, - isFormbricksCloud, - membershipRole, isMultiOrgEnabled, + isFormbricksCloud = true, + membershipRole, }: NavigationProps) => { const router = useRouter(); const pathname = usePathname(); @@ -84,6 +87,7 @@ export const MainNavigation = ({ const [showCreateOrganizationModal, setShowCreateOrganizationModal] = useState(false); const [isCollapsed, setIsCollapsed] = useState(true); const [isTextVisible, setIsTextVisible] = useState(true); + const [latestVersion, setLatestVersion] = useState(""); const product = products.find((product) => product.id === environment.productId); const { isAdmin, isOwner, isViewer } = getAccessFlags(membershipRole); @@ -248,6 +252,21 @@ export const MainNavigation = ({ }, ]; + useEffect(() => { + async function loadReleases() { + const res = await getLatestStableFbReleaseAction(); + if (res?.data) { + const latestVersionTag = res.data; + const currentVersionTag = `v${version}`; + + if (currentVersionTag !== latestVersionTag) { + setLatestVersion(latestVersionTag); + } + } + } + if (isOwnerOrAdmin) loadReleases(); + }, [isOwnerOrAdmin]); + return ( <> {product && ( @@ -305,8 +324,22 @@ export const MainNavigation = ({ )} + {/* Product Switch */}
+ {/* New Version Available */} + {!isCollapsed && isOwnerOrAdmin && latestVersion && !isFormbricksCloud && ( + +

+ + Formbricks {latestVersion} is here. Upgrade now! +

+ + )} +