diff --git a/apps/backend/src/api/routes/users.controller.ts b/apps/backend/src/api/routes/users.controller.ts index 79882c38..cd467792 100644 --- a/apps/backend/src/api/routes/users.controller.ts +++ b/apps/backend/src/api/routes/users.controller.ts @@ -9,6 +9,7 @@ import { Res, } from '@nestjs/common'; import { GetUserFromRequest } from '@gitroom/nestjs-libraries/user/user.from.request'; +import { sign } from 'jsonwebtoken'; import { Organization, User } from '@prisma/client'; import { SubscriptionService } from '@gitroom/nestjs-libraries/database/prisma/subscriptions/subscription.service'; import { GetOrgFromRequest } from '@gitroom/nestjs-libraries/user/org.from.request'; @@ -42,6 +43,23 @@ export class UsersController { private _userService: UsersService, private _trackService: TrackService ) {} + @Get('/agent-media-sso') + async getAgentMediaSsoUrl( + @GetUserFromRequest() user: User, + @GetOrgFromRequest() organization: Organization + ) { + if (!process.env.AGENT_MEDIA_SSO_KEY) { + throw new HttpException('Agent Media SSO is not configured', 400); + } + + const token = sign( + { id: organization.id, displayName: organization.name }, + process.env.AGENT_MEDIA_SSO_KEY + ); + + return { url: `https://agent-media.ai/sso/${token}` }; + } + @Get('/self') async getSelf( @GetUserFromRequest() user: User, diff --git a/apps/frontend/src/components/layout/top.menu.tsx b/apps/frontend/src/components/layout/top.menu.tsx index 008abb42..4c9f40a2 100644 --- a/apps/frontend/src/components/layout/top.menu.tsx +++ b/apps/frontend/src/components/layout/top.menu.tsx @@ -1,9 +1,10 @@ 'use client'; -import { FC, ReactNode } from 'react'; +import { FC, ReactNode, useCallback } from 'react'; import { useUser } from '@gitroom/frontend/components/layout/user.context'; import { useVariables } from '@gitroom/react/helpers/variable.context'; import { useT } from '@gitroom/react/translation/get.transation.service.client'; +import { useFetch } from '@gitroom/helpers/utils/custom.fetch'; import { MenuItem } from '@gitroom/frontend/components/new-layout/menu-item'; interface MenuItemInterface { @@ -13,11 +14,25 @@ interface MenuItemInterface { role?: string[]; hide?: boolean; requireBilling?: boolean; + onClick?: () => void; } export const useMenuItem = () => { const { isGeneral } = useVariables(); const t = useT(); + const fetch = useFetch(); + + const handleAgentMediaClick = useCallback(async () => { + try { + const response = await fetch('/user/agent-media-sso'); + const data = await response.json(); + if (data.url) { + window.open(data.url, '_blank'); + } + } catch (e) { + // ignore + } + }, [fetch]); const firstMenu = [ { @@ -174,9 +189,10 @@ export const useMenuItem = () => { ), - path: 'https://agent-media.ai', + path: '#', role: ['ADMIN', 'SUPERADMIN', 'USER'], requireBilling: true, + onClick: handleAgentMediaClick, }, { name: t('affiliate', 'Affiliate'), @@ -331,6 +347,7 @@ export const TopMenu: FC = () => { label={item.name} icon={item.icon} key={item.name} + onClick={item.onClick} /> )) } @@ -358,6 +375,7 @@ export const TopMenu: FC = () => { label={item.name} icon={item.icon} key={item.name} + onClick={item.onClick} /> ))} diff --git a/apps/frontend/src/components/new-layout/menu-item.tsx b/apps/frontend/src/components/new-layout/menu-item.tsx index ef36fb3c..2d66795e 100644 --- a/apps/frontend/src/components/new-layout/menu-item.tsx +++ b/apps/frontend/src/components/new-layout/menu-item.tsx @@ -4,23 +4,35 @@ import { usePathname } from 'next/navigation'; import clsx from 'clsx'; import Link from 'next/link'; -export const MenuItem: FC<{ label: string; icon: ReactNode; path: string }> = ({ +export const MenuItem: FC<{ label: string; icon: ReactNode; path: string; onClick?: () => void }> = ({ label, icon, path, + onClick, }) => { const currentPath = usePathname(); const isActive = currentPath.indexOf(path) === 0; + const className = clsx( + 'w-full minCustom:h-[54px] custom:h-[30px] py-[8px] px-[6px] gap-[4px] flex flex-col custom:flex-row text-[10px] font-[600] items-center minCustom:justify-center rounded-[12px] hover:text-textItemFocused hover:bg-boxFocused', + isActive ? 'text-textItemFocused bg-boxFocused' : 'text-textItemBlur' + ); + + if (onClick) { + return ( + + ); + } + return (