From 90d30c93af6cb1295fb2ff6bc77a652696fc0ffb Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Mon, 27 Jan 2025 16:23:08 -0700 Subject: [PATCH] add additional context provider for chats --- .../chat/[chatId]/layout.tsx | 5 ++ .../ChatContainer/ChatHeader/ChatHeader.tsx | 4 +- .../ChatHeaderOptions/ChatHeaderDropdown.tsx | 4 +- .../ChatHeader/ChatHeaderOptions/config.tsx | 10 ++-- .../ChatHeader/ChatHeaderTitle.tsx | 8 +-- .../ChatLayout/ChatContext/ChatContext.tsx | 51 +++++++++++++++++++ .../_layouts/ChatLayout/ChatContext/index.ts | 1 + .../app/_layouts/ChatLayout/ChatLayout.tsx | 30 ++++++----- .../ChatLayoutContext/ChatLayoutContext.tsx | 14 +---- .../FileContainerHeader.tsx | 1 - .../ChatLayout/hooks/useDefaultFile.ts | 10 ++-- .../collection/[collectionId]/page.tsx | 3 -- .../[chatId]/dashboard/[dashboardId]/page.tsx | 3 -- .../[chatId]/dataset/[datasetId]/page.tsx | 3 -- .../chat/[chatId]/metric/[metricId]/page.tsx | 3 -- .../app/test/splitter/chat/[chatId]/page.tsx | 3 -- .../collection/[collectionId]/page.tsx | 3 -- .../splitter/dashboard/[dashboardId]/page.tsx | 3 -- .../splitter/dataset/[datasetId]/page.tsx | 3 -- web/src/app/test/splitter/layout.tsx | 43 ---------------- .../test/splitter/metric/[metricId]/page.tsx | 3 -- web/src/app/test/splitter/page.tsx | 5 -- web/src/context/Chats/ChatProvider.tsx | 37 ++++---------- .../routes/busterRoutes/busterAppRoutes.ts | 2 + 24 files changed, 107 insertions(+), 145 deletions(-) create mode 100644 web/src/app/app/(chat_experience)/chat/[chatId]/layout.tsx create mode 100644 web/src/app/app/_layouts/ChatLayout/ChatContext/ChatContext.tsx create mode 100644 web/src/app/app/_layouts/ChatLayout/ChatContext/index.ts delete mode 100644 web/src/app/test/splitter/chat/[chatId]/collection/[collectionId]/page.tsx delete mode 100644 web/src/app/test/splitter/chat/[chatId]/dashboard/[dashboardId]/page.tsx delete mode 100644 web/src/app/test/splitter/chat/[chatId]/dataset/[datasetId]/page.tsx delete mode 100644 web/src/app/test/splitter/chat/[chatId]/metric/[metricId]/page.tsx delete mode 100644 web/src/app/test/splitter/chat/[chatId]/page.tsx delete mode 100644 web/src/app/test/splitter/collection/[collectionId]/page.tsx delete mode 100644 web/src/app/test/splitter/dashboard/[dashboardId]/page.tsx delete mode 100644 web/src/app/test/splitter/dataset/[datasetId]/page.tsx delete mode 100644 web/src/app/test/splitter/layout.tsx delete mode 100644 web/src/app/test/splitter/metric/[metricId]/page.tsx delete mode 100644 web/src/app/test/splitter/page.tsx diff --git a/web/src/app/app/(chat_experience)/chat/[chatId]/layout.tsx b/web/src/app/app/(chat_experience)/chat/[chatId]/layout.tsx new file mode 100644 index 000000000..227516f0b --- /dev/null +++ b/web/src/app/app/(chat_experience)/chat/[chatId]/layout.tsx @@ -0,0 +1,5 @@ +import React from 'react'; + +export default function Layout({ children }: { children: React.ReactNode }) { + return <>{children}; +} diff --git a/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeader.tsx b/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeader.tsx index abf9db9b5..29fd5edb8 100644 --- a/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeader.tsx +++ b/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeader.tsx @@ -1,15 +1,15 @@ import { appContentHeaderHeight } from '@/components/layout/AppContentHeader'; import { createStyles } from 'antd-style'; import React from 'react'; -import { useChatSplitterContextSelector } from '../../ChatLayoutContext'; import { ChatHeaderOptions } from './ChatHeaderOptions'; import { ChatHeaderTitle } from './ChatHeaderTitle'; +import { useChatContextSelector } from '../../ChatContext'; export const ChatHeader: React.FC<{ showScrollOverflow: boolean; }> = React.memo(({ showScrollOverflow }) => { const { cx, styles } = useStyles(); - const hasFile = useChatSplitterContextSelector((state) => state.hasFile); + const hasFile = useChatContextSelector((state) => state.hasFile); return (
= React.memo(({ children }) => { - const selectedFileType = useChatSplitterContextSelector((state) => state.selectedFileType); + const selectedFileType = useChatContextSelector((state) => state.selectedFileType); const menuItem: MenuProps['items'] = useMemo(() => { if (!selectedFileType || !(selectedFileType in HeaderOptionsRecord)) diff --git a/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeaderOptions/config.tsx b/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeaderOptions/config.tsx index f67e2a437..433ba779e 100644 --- a/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeaderOptions/config.tsx +++ b/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeaderOptions/config.tsx @@ -1,8 +1,8 @@ +import type { FileType } from '@/api/buster_socket/chats'; import { AppMaterialIcons } from '@/components/icons'; -import type { AppChatMessageFileType } from '@/components/messages/AppChatMessageContainer'; -import { MenuProps } from 'antd'; +import type { MenuProps } from 'antd'; -export const HeaderOptionsRecord: Record MenuProps['items']> = { +export const HeaderOptionsRecord: Record MenuProps['items']> = { dataset: () => [ { label: 'Delete', @@ -30,5 +30,7 @@ export const HeaderOptionsRecord: Record MenuProps key: 'delete', icon: } - ] + ], + term: () => [], + value: () => [] }; diff --git a/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeaderTitle.tsx b/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeaderTitle.tsx index 5e45623f5..bbbdef977 100644 --- a/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeaderTitle.tsx +++ b/web/src/app/app/_layouts/ChatLayout/ChatContainer/ChatHeader/ChatHeaderTitle.tsx @@ -1,7 +1,7 @@ import { Text } from '@/components/text'; import React from 'react'; -import { useChatSplitterContextSelector } from '../../ChatLayoutContext'; import { AnimatePresence, motion } from 'framer-motion'; +import { useChatContextSelector } from '../../ChatContext'; const animation = { initial: { opacity: 0 }, @@ -11,12 +11,12 @@ const animation = { }; export const ChatHeaderTitle: React.FC<{}> = React.memo(() => { - const selectedFileTitle = useChatSplitterContextSelector((state) => state.selectedFileTitle); + const chatTitle = useChatContextSelector((state) => state.chatTitle); return ( - - {selectedFileTitle} + + {chatTitle} ); diff --git a/web/src/app/app/_layouts/ChatLayout/ChatContext/ChatContext.tsx b/web/src/app/app/_layouts/ChatLayout/ChatContext/ChatContext.tsx new file mode 100644 index 000000000..5c791e45c --- /dev/null +++ b/web/src/app/app/_layouts/ChatLayout/ChatContext/ChatContext.tsx @@ -0,0 +1,51 @@ +import React, { PropsWithChildren } from 'react'; +import { + ContextSelector, + createContext, + useContextSelector +} from '@fluentui/react-context-selector'; +import { useBusterChatIndividual } from '@/context/Chats'; +import type { SelectedFile } from '../interfaces'; + +export const useChatContext = ({ + chatId, + defaultSelectedFile +}: { + chatId?: string; + defaultSelectedFile?: SelectedFile; +}) => { + const selectedFileId = defaultSelectedFile?.id; + const selectedFileType = defaultSelectedFile?.type; + + //CHAT + const { chat } = useBusterChatIndividual({ + chatId + }); + const chatTitle = chat?.title; + + //FILE + const hasFile = !!defaultSelectedFile?.id; + + return { + hasFile, + selectedFileId, + chatTitle, + selectedFileType + }; +}; + +export const ChatContext = createContext>( + {} as ReturnType +); + +export const ChatContextProvider = React.memo( + ({ value, children }: PropsWithChildren<{ value: ReturnType }>) => { + return {children}; + } +); + +ChatContextProvider.displayName = 'ChatContextProvider'; + +export const useChatContextSelector = ( + selector: ContextSelector, T> +) => useContextSelector(ChatContext, selector); diff --git a/web/src/app/app/_layouts/ChatLayout/ChatContext/index.ts b/web/src/app/app/_layouts/ChatLayout/ChatContext/index.ts new file mode 100644 index 000000000..accef6a69 --- /dev/null +++ b/web/src/app/app/_layouts/ChatLayout/ChatContext/index.ts @@ -0,0 +1 @@ +export * from './ChatContext'; diff --git a/web/src/app/app/_layouts/ChatLayout/ChatLayout.tsx b/web/src/app/app/_layouts/ChatLayout/ChatLayout.tsx index f1e35da04..bb02dc202 100644 --- a/web/src/app/app/_layouts/ChatLayout/ChatLayout.tsx +++ b/web/src/app/app/_layouts/ChatLayout/ChatLayout.tsx @@ -7,7 +7,8 @@ import { FileContainer } from './FileContainer'; import { ChatSplitterContextProvider } from './ChatLayoutContext'; import { useChatLayout } from './ChatLayoutContext'; import { SelectedFile } from './interfaces'; -import { useAutoSetLayout, useDefaultSplitterLayout } from './hooks'; +import { useDefaultSplitterLayout } from './hooks'; +import { ChatContextProvider, useChatContext } from './ChatContext/ChatContext'; export interface ChatSplitterProps { showChatCollapse?: boolean; @@ -30,20 +31,25 @@ export const ChatLayout: React.FC = React.memo( chatId }); - const { hasFile, isPureChat, isPureFile } = useChatSplitterProps; + const useChatContextValue = useChatContext({ chatId, defaultSelectedFile }); + + const { isPureChat, isPureFile } = useChatSplitterProps; + const { hasFile } = useChatContextValue; return ( - } - rightChildren={} - autoSaveId="chat-splitter" - defaultLayout={defaultSplitterLayout} - rightHidden={isPureChat} - preserveSide="left" - leftPanelMinSize={hasFile ? 225 : undefined} - /> + + } + rightChildren={} + autoSaveId="chat-splitter" + defaultLayout={defaultSplitterLayout} + rightHidden={isPureChat} + preserveSide="left" + leftPanelMinSize={hasFile ? 225 : undefined} + /> + ); } diff --git a/web/src/app/app/_layouts/ChatLayout/ChatLayoutContext/ChatLayoutContext.tsx b/web/src/app/app/_layouts/ChatLayout/ChatLayoutContext/ChatLayoutContext.tsx index 8de75a94b..883a5422c 100644 --- a/web/src/app/app/_layouts/ChatLayout/ChatLayoutContext/ChatLayoutContext.tsx +++ b/web/src/app/app/_layouts/ChatLayout/ChatLayoutContext/ChatLayoutContext.tsx @@ -3,7 +3,7 @@ import { createContext, useContextSelector } from '@fluentui/react-context-selector'; -import React, { PropsWithChildren, useMemo, useState, useTransition } from 'react'; +import React, { PropsWithChildren, useMemo, useTransition } from 'react'; import type { SelectedFile } from '../interfaces'; import type { ChatSplitterProps } from '../ChatLayout'; import { useMemoizedFn } from 'ahooks'; @@ -29,14 +29,6 @@ export const useChatLayout = ({ const [isPending, startTransition] = useTransition(); const onChangePage = useAppLayoutContextSelector((state) => state.onChangePage); const selectedLayout = defaultSelectedLayout; - const selectedFileId = defaultSelectedFile?.id; - const selectedFileType = defaultSelectedFile?.type; - const hasFile = !!selectedFileId; - - const selectedFileTitle: string = useMemo(() => { - if (!selectedFileId) return ''; - return 'test'; - }, [selectedFileId]); const animateOpenSplitter = useMemoizedFn((side: 'left' | 'right' | 'both') => { if (appSplitterRef.current) { @@ -84,11 +76,7 @@ export const useChatLayout = ({ }); return { - selectedFileTitle, - selectedFileType, selectedLayout, - selectedFileId, - hasFile, isPureFile, isPureChat, onSetSelectedFile, diff --git a/web/src/app/app/_layouts/ChatLayout/FileContainer/FileContainerHeader/FileContainerHeader.tsx b/web/src/app/app/_layouts/ChatLayout/FileContainer/FileContainerHeader/FileContainerHeader.tsx index 48570ea3a..e0b02b92e 100644 --- a/web/src/app/app/_layouts/ChatLayout/FileContainer/FileContainerHeader/FileContainerHeader.tsx +++ b/web/src/app/app/_layouts/ChatLayout/FileContainer/FileContainerHeader/FileContainerHeader.tsx @@ -6,7 +6,6 @@ import { CollapseFileButton } from './CollapseFileButton'; export const FileContainerHeader: React.FC = React.memo(() => { const { styles, cx } = useStyles(); - const selectedFileType = useChatSplitterContextSelector((state) => state.selectedFileType); const selectedLayout = useChatSplitterContextSelector((state) => state.selectedLayout); const showCollapseButton = true; diff --git a/web/src/app/app/_layouts/ChatLayout/hooks/useDefaultFile.ts b/web/src/app/app/_layouts/ChatLayout/hooks/useDefaultFile.ts index 50c8095d4..f91ffd0e2 100644 --- a/web/src/app/app/_layouts/ChatLayout/hooks/useDefaultFile.ts +++ b/web/src/app/app/_layouts/ChatLayout/hooks/useDefaultFile.ts @@ -3,8 +3,8 @@ import { useMemo } from 'react'; import { SelectedFile } from '../interfaces'; import { useParams } from 'next/navigation'; -import { AppChatMessageFileType } from '@/components/messages/AppChatMessageContainer'; import { ChatSplitterProps } from '../ChatLayout'; +import { FileType } from '@/api/buster_socket/chats'; export const useSelectedFileByParams = () => { const { metricId, collectionId, datasetId, dashboardId, chatId } = useParams() as { @@ -16,10 +16,10 @@ export const useSelectedFileByParams = () => { }; const selectedFile: SelectedFile | undefined = useMemo(() => { - if (metricId) return { id: metricId, type: AppChatMessageFileType.Metric }; - if (collectionId) return { id: collectionId, type: AppChatMessageFileType.Collection }; - if (datasetId) return { id: datasetId, type: AppChatMessageFileType.Dataset }; - if (dashboardId) return { id: dashboardId, type: AppChatMessageFileType.Dashboard }; + if (metricId) return { id: metricId, type: FileType.METRIC }; + if (collectionId) return { id: collectionId, type: FileType.COLLECTION }; + if (datasetId) return { id: datasetId, type: FileType.DATASET }; + if (dashboardId) return { id: dashboardId, type: FileType.DASHBOARD }; }, [metricId, collectionId, datasetId, dashboardId, chatId]); const selectedLayout: ChatSplitterProps['defaultSelectedLayout'] = useMemo(() => { diff --git a/web/src/app/test/splitter/chat/[chatId]/collection/[collectionId]/page.tsx b/web/src/app/test/splitter/chat/[chatId]/collection/[collectionId]/page.tsx deleted file mode 100644 index 7d1e0b717..000000000 --- a/web/src/app/test/splitter/chat/[chatId]/collection/[collectionId]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Page() { - return <>; -} diff --git a/web/src/app/test/splitter/chat/[chatId]/dashboard/[dashboardId]/page.tsx b/web/src/app/test/splitter/chat/[chatId]/dashboard/[dashboardId]/page.tsx deleted file mode 100644 index a01933221..000000000 --- a/web/src/app/test/splitter/chat/[chatId]/dashboard/[dashboardId]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Page() { - return
Dashboard swag
; -} diff --git a/web/src/app/test/splitter/chat/[chatId]/dataset/[datasetId]/page.tsx b/web/src/app/test/splitter/chat/[chatId]/dataset/[datasetId]/page.tsx deleted file mode 100644 index 7d1e0b717..000000000 --- a/web/src/app/test/splitter/chat/[chatId]/dataset/[datasetId]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Page() { - return <>; -} diff --git a/web/src/app/test/splitter/chat/[chatId]/metric/[metricId]/page.tsx b/web/src/app/test/splitter/chat/[chatId]/metric/[metricId]/page.tsx deleted file mode 100644 index 7d1e0b717..000000000 --- a/web/src/app/test/splitter/chat/[chatId]/metric/[metricId]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Page() { - return <>; -} diff --git a/web/src/app/test/splitter/chat/[chatId]/page.tsx b/web/src/app/test/splitter/chat/[chatId]/page.tsx deleted file mode 100644 index 7d1e0b717..000000000 --- a/web/src/app/test/splitter/chat/[chatId]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Page() { - return <>; -} diff --git a/web/src/app/test/splitter/collection/[collectionId]/page.tsx b/web/src/app/test/splitter/collection/[collectionId]/page.tsx deleted file mode 100644 index 7d1e0b717..000000000 --- a/web/src/app/test/splitter/collection/[collectionId]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Page() { - return <>; -} diff --git a/web/src/app/test/splitter/dashboard/[dashboardId]/page.tsx b/web/src/app/test/splitter/dashboard/[dashboardId]/page.tsx deleted file mode 100644 index f99df442a..000000000 --- a/web/src/app/test/splitter/dashboard/[dashboardId]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Page() { - return
Dashboard swag
; -} diff --git a/web/src/app/test/splitter/dataset/[datasetId]/page.tsx b/web/src/app/test/splitter/dataset/[datasetId]/page.tsx deleted file mode 100644 index 7d1e0b717..000000000 --- a/web/src/app/test/splitter/dataset/[datasetId]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Page() { - return <>; -} diff --git a/web/src/app/test/splitter/layout.tsx b/web/src/app/test/splitter/layout.tsx deleted file mode 100644 index 6f1daa448..000000000 --- a/web/src/app/test/splitter/layout.tsx +++ /dev/null @@ -1,43 +0,0 @@ -'use client'; - -import React from 'react'; -import { ChatLayout, useSelectedFileByParams } from '@chatLayout/index'; -import { AppChatMessageFileType } from '@/components/messages/AppChatMessageContainer'; -import { useRouter } from 'next/navigation'; -import { useHotkeys } from 'react-hotkeys-hook'; - -export default function Layout({ children }: { children: React.ReactNode }) { - const { selectedFile, selectedLayout, chatId } = useSelectedFileByParams(); - const router = useRouter(); - - useHotkeys('m', () => { - const randomType: AppChatMessageFileType = ( - ['dataset', 'collection', 'metric', 'dashboard'] as AppChatMessageFileType[] - )[Math.floor(Math.random() * 4)]; - const isPureChat = Math.random() < 0.15; - const isChat = Math.random() < 0.55; - const randomChatId = Math.floor(Math.random() * 1000); - const randomId = Math.floor(Math.random() * 1000000); - - if (isPureChat) { - router.push(`/test/splitter/chat/${randomChatId}`); - return; - } - - const route = isChat - ? `/test/splitter/chat/${randomChatId}/${randomType}/${randomId}` - : `/test/splitter/${randomType}/${randomId}`; - router.push(route); - }); - - return ( -
- - {children} - -
- ); -} diff --git a/web/src/app/test/splitter/metric/[metricId]/page.tsx b/web/src/app/test/splitter/metric/[metricId]/page.tsx deleted file mode 100644 index 7d1e0b717..000000000 --- a/web/src/app/test/splitter/metric/[metricId]/page.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Page() { - return <>; -} diff --git a/web/src/app/test/splitter/page.tsx b/web/src/app/test/splitter/page.tsx deleted file mode 100644 index 7fcbc82fa..000000000 --- a/web/src/app/test/splitter/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -'use client'; - -export default function Page() { - return <>; -} diff --git a/web/src/context/Chats/ChatProvider.tsx b/web/src/context/Chats/ChatProvider.tsx index 7d2c81ce4..dc99d914c 100644 --- a/web/src/context/Chats/ChatProvider.tsx +++ b/web/src/context/Chats/ChatProvider.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useRef, useState, useTransition } from 'react'; +import React, { useEffect, useRef, useTransition } from 'react'; import { createContext, ContextSelector, @@ -6,30 +6,18 @@ import { } from '@fluentui/react-context-selector'; import { useBusterWebSocket } from '../BusterWebSocket'; import type { BusterChatAsset, IBusterChat } from '@/api/buster_socket/chats'; -import { useMemoizedFn, useMount, useUnmount } from 'ahooks'; +import { useMemoizedFn, useUnmount } from 'ahooks'; import type { FileType } from '@/api/buster_socket/chats'; export const useBusterChat = () => { const busterSocket = useBusterWebSocket(); const [isPending, startTransition] = useTransition(); const chatsRef = useRef>({}); - const [seletedAssetId, setSeletedAssetId] = useState>({}); // GETTERS - const getSelectedAssetId = useCallback( - (chatId: string) => { - return seletedAssetId[chatId] || null; - }, - [seletedAssetId] - ); - // SETTERS - const onSetSelectedAssetId = useMemoizedFn((chatId: string, assetId: string | null) => { - setSeletedAssetId((prev) => ({ ...prev, [chatId]: assetId })); - }); - // LISTENERS const _onGetChat = useMemoizedFn((chat: IBusterChat) => { @@ -103,12 +91,10 @@ export const useBusterChat = () => { ); return { - getSelectedAssetId, chats: chatsRef.current, unsubscribeFromChat, subscribeToChat, - getChatAsset, - onSetSelectedAssetId + getChatAsset }; }; @@ -128,25 +114,22 @@ export const useBusterChatContextSelector = ( selector: ContextSelector, T> ) => useContextSelector(BusterChat, selector); -export const useBusterChatIndividual = ({ chatId }: { chatId: string }) => { +export const useBusterChatIndividual = ({ chatId: chatIdProp }: { chatId?: string }) => { + const chatId = chatIdProp || ''; const chat = useBusterChatContextSelector((x) => x.chats[chatId]); const subscribeToChat = useBusterChatContextSelector((x) => x.subscribeToChat); const unsubscribeFromChat = useBusterChatContextSelector((x) => x.unsubscribeFromChat); - const selectedAssetId = useBusterChatContextSelector((x) => x.getSelectedAssetId(chatId)); - const onSetSelectedAssetId = useBusterChatContextSelector((x) => x.onSetSelectedAssetId); - useMount(() => { - subscribeToChat({ chatId }); - }); + useEffect(() => { + if (chatId) subscribeToChat({ chatId }); + }, [chatId]); useUnmount(() => { - unsubscribeFromChat({ chatId }); + if (chatId) unsubscribeFromChat({ chatId }); }); return { - chat, - selectedAssetId, - onSetSelectedAssetId + chat }; }; diff --git a/web/src/routes/busterRoutes/busterAppRoutes.ts b/web/src/routes/busterRoutes/busterAppRoutes.ts index 8d2392523..76779afbd 100644 --- a/web/src/routes/busterRoutes/busterAppRoutes.ts +++ b/web/src/routes/busterRoutes/busterAppRoutes.ts @@ -237,4 +237,6 @@ export type BusterAppRoutesWithArgs = { chatId: string; valueId: string; }; + [BusterAppRoutes.APP_METRIC_ID]: { route: BusterAppRoutes.APP_METRIC_ID; metricId: string }; + [BusterAppRoutes.APP_VALUE_ID]: { route: BusterAppRoutes.APP_VALUE_ID; valueId: string }; };