diff --git a/apps/console/src/app/[entity]/catalog/[id]/[...tab]/page.tsx b/apps/console/src/app/[entity]/catalog/[id]/[...tab]/page.tsx new file mode 100644 index 0000000000..10233f95ac --- /dev/null +++ b/apps/console/src/app/[entity]/catalog/[id]/[...tab]/page.tsx @@ -0,0 +1,35 @@ +import { Metadata } from "next"; + +import { + CatalogTabNames, + generateNextMetaBase, + getCatalogTabTitle, +} from "@instill-ai/toolkit/server"; + +import { CatalogTabPageRender } from "./render"; + +type Props = { + params: { entity: string; id: string; tab: string[] }; +}; + +export async function generateMetadata({ params }: Props): Promise { + const { id, tab } = params; + const tabName = (tab[0] as CatalogTabNames) || "catalogs"; + + const metadata: Metadata = { + title: `Instill Cloud | ${id} | ${getCatalogTabTitle(tabName)}`, + description: `${getCatalogTabTitle(tabName)} tab of ${id} catalog`, + metadataBase: generateNextMetaBase({ + defaultBase: "http://localhost:3000", + }), + openGraph: { + images: ["/instill-open-graph.png"], + }, + }; + + return Promise.resolve(metadata); +} + +export default async function Page() { + return ; +} diff --git a/apps/console/src/app/[entity]/catalog/[id]/[...tab]/render.tsx b/apps/console/src/app/[entity]/catalog/[id]/[...tab]/render.tsx new file mode 100644 index 0000000000..2043ee95bb --- /dev/null +++ b/apps/console/src/app/[entity]/catalog/[id]/[...tab]/render.tsx @@ -0,0 +1,32 @@ +"use client"; + +import * as React from "react"; +import { useParams } from "next/navigation"; + +import { + AppTopbar, + CatalogMainView, + NamespaceSwitch, + PageBase, +} from "@instill-ai/toolkit"; + +import { useAppAccessToken } from "~/lib/use-app-access-token"; +import { useAppTrackToken } from "~/lib/useAppTrackToken"; + +export const CatalogTabPageRender = () => { + const params = useParams(); + const { id, tab } = params as { id: string; tab: string[] }; + const activeTab = tab?.[0] || "upload"; + + useAppAccessToken(); + useAppTrackToken({ enabled: true }); + + return ( + + } /> + + + + + ); +}; diff --git a/apps/console/src/app/[entity]/catalog/[id]/page.tsx b/apps/console/src/app/[entity]/catalog/[id]/page.tsx new file mode 100644 index 0000000000..1682cd4aa9 --- /dev/null +++ b/apps/console/src/app/[entity]/catalog/[id]/page.tsx @@ -0,0 +1,12 @@ +import { redirect } from "next/navigation"; + +type RedirectionCatalogPageProps = { + params: { id: string; entity: string }; +}; + +export default async function RedirectionCatalogPage({ + params, +}: RedirectionCatalogPageProps) { + const { entity, id } = params; + return redirect(`/${entity}/catalog/${id}/upload`); +} diff --git a/apps/console/src/app/[entity]/catalog/page.tsx b/apps/console/src/app/[entity]/catalog/page.tsx index ac5b4a5952..45632aaa56 100644 --- a/apps/console/src/app/[entity]/catalog/page.tsx +++ b/apps/console/src/app/[entity]/catalog/page.tsx @@ -2,11 +2,11 @@ import { Metadata } from "next"; import { generateNextMetaBase } from "@instill-ai/toolkit/server"; -import { KnowladgeBasePageRender } from "./render"; +import { CatalogPageRender } from "./render"; export async function generateMetadata() { const metadata: Metadata = { - title: `Instill Cloud | Catalog`, + title: `Instill Cloud | Artifacts`, metadataBase: generateNextMetaBase({ defaultBase: "http://localhost:3000", }), @@ -19,5 +19,5 @@ export async function generateMetadata() { } export default async function Page() { - return ; + return ; } diff --git a/apps/console/src/app/[entity]/catalog/render.tsx b/apps/console/src/app/[entity]/catalog/render.tsx index 1523347637..2afcb2a10f 100644 --- a/apps/console/src/app/[entity]/catalog/render.tsx +++ b/apps/console/src/app/[entity]/catalog/render.tsx @@ -1,7 +1,5 @@ "use client"; -import { useRouter } from "next/navigation"; - import { AppTopbar, CatalogMainView, @@ -12,23 +10,16 @@ import { import { useAppAccessToken } from "~/lib/use-app-access-token"; import { useAppTrackToken } from "~/lib/useAppTrackToken"; -export const KnowladgeBasePageRender = () => { - const router = useRouter(); - - const accessToken = useAppAccessToken(); +export const CatalogPageRender = () => { + useAppAccessToken(); useAppTrackToken({ enabled: true }); - // useInitAmplitude(); return ( } /> - + diff --git a/packages/toolkit/package.json b/packages/toolkit/package.json index 8fb339ddb1..f80ebd602d 100644 --- a/packages/toolkit/package.json +++ b/packages/toolkit/package.json @@ -1,6 +1,7 @@ { "name": "@instill-ai/toolkit", - "version": "0.110.0", + "version": "0.110.0-rc.113", + "description": "Instill AI's frontend toolkit", "repository": "https://github.com/instill-ai/design-system.git", "bugs": "https://github.com/instill-ai/design-system/issues", diff --git a/packages/toolkit/src/lib/dashboard/getModelTriggersSummary.ts b/packages/toolkit/src/lib/dashboard/getModelTriggersSummary.ts index 2b2a7db1e9..f95884dff9 100644 --- a/packages/toolkit/src/lib/dashboard/getModelTriggersSummary.ts +++ b/packages/toolkit/src/lib/dashboard/getModelTriggersSummary.ts @@ -1,28 +1,31 @@ -import { ModelTriggerCountRecord, ModelTriggersStatusSummary } from "instill-sdk"; +import { + ModelTriggerCountRecord, + ModelTriggersStatusSummary, +} from "instill-sdk"; export function getModelTriggersSummary( - modelTriggerCounts: ModelTriggerCountRecord[] + modelTriggerCounts: ModelTriggerCountRecord[], ): ModelTriggersStatusSummary { - const completedModel = modelTriggerCounts.find( - (r) => r.status === "STATUS_COMPLETED" - ); + const completedModel = modelTriggerCounts.find( + (r) => r.status === "STATUS_COMPLETED", + ); - const erroredModel = modelTriggerCounts.find( - (r) => r.status === "STATUS_ERRORED" - ); + const erroredModel = modelTriggerCounts.find( + (r) => r.status === "STATUS_ERRORED", + ); - return { - completed: { - statusType: "STATUS_COMPLETED" as const, - type: "model" as const, - amount: completedModel?.triggerCount || 0, - delta: 0 - }, - errored: { - statusType: "STATUS_ERRORED" as const, - type: "model" as const, - amount: erroredModel?.triggerCount || 0, - delta: 0 - } - }; -} \ No newline at end of file + return { + completed: { + statusType: "STATUS_COMPLETED" as const, + type: "model" as const, + amount: completedModel?.triggerCount || 0, + delta: 0, + }, + errored: { + statusType: "STATUS_ERRORED" as const, + type: "model" as const, + amount: erroredModel?.triggerCount || 0, + delta: 0, + }, + }; +} diff --git a/packages/toolkit/src/lib/react-query-service/metric/useModelTriggerCount.ts b/packages/toolkit/src/lib/react-query-service/metric/useModelTriggerCount.ts index a174d6057b..32551042d5 100644 --- a/packages/toolkit/src/lib/react-query-service/metric/useModelTriggerCount.ts +++ b/packages/toolkit/src/lib/react-query-service/metric/useModelTriggerCount.ts @@ -2,39 +2,40 @@ import { useQuery } from "@tanstack/react-query"; import { ListModelTriggerCountRequest, Nullable } from "instill-sdk"; + import { getInstillAPIClient } from "../../sdk-helper"; export function useModelTriggerCount({ - enabled, - accessToken, - requesterId, - start, - stop, + enabled, + accessToken, + requesterId, + start, + stop, }: { - enabled: boolean; - accessToken: Nullable; - requesterId: string; - start?: string; - stop?: string; + enabled: boolean; + accessToken: Nullable; + requesterId: string; + start?: string; + stop?: string; }) { - return useQuery({ - queryKey: ["modelTriggerCount", requesterId, start, stop], - queryFn: async () => { - if (!accessToken) { - throw new Error("accessToken not provided"); - } + return useQuery({ + queryKey: ["modelTriggerCount", requesterId, start, stop], + queryFn: async () => { + if (!accessToken) { + throw new Error("accessToken not provided"); + } - const client = getInstillAPIClient({ accessToken }); + const client = getInstillAPIClient({ accessToken }); - const request: ListModelTriggerCountRequest = { - requesterId, - start, - stop, - }; + const request: ListModelTriggerCountRequest = { + requesterId, + start, + stop, + }; - const data = await client.core.metric.listModelTriggerCount(request); - return data; - }, - enabled: enabled && !!requesterId, - }); + const data = await client.core.metric.listModelTriggerCount(request); + return data; + }, + enabled: enabled && !!requesterId, + }); } diff --git a/packages/toolkit/src/lib/react-query-service/metric/useModelTriggerMetric.ts b/packages/toolkit/src/lib/react-query-service/metric/useModelTriggerMetric.ts index 02a3eabb9b..91554860ea 100644 --- a/packages/toolkit/src/lib/react-query-service/metric/useModelTriggerMetric.ts +++ b/packages/toolkit/src/lib/react-query-service/metric/useModelTriggerMetric.ts @@ -40,4 +40,4 @@ export function useModelTriggerMetric({ }, enabled, }); -} \ No newline at end of file +} diff --git a/packages/toolkit/src/server/utility/getCatalogTabTitle.ts b/packages/toolkit/src/server/utility/getCatalogTabTitle.ts new file mode 100644 index 0000000000..c07f0af403 --- /dev/null +++ b/packages/toolkit/src/server/utility/getCatalogTabTitle.ts @@ -0,0 +1,16 @@ +const CatalogTabsDictionary = { + catalogs: "My Catalogs", + upload: "Upload Documents", + files: "Files", + chunks: "Chunks", + api: "API", + retrieve: "Retrieve Chunk", + ask_question: "Ask Question", + get_catalog: "Get Catalog", +}; + +export type CatalogTabNames = keyof typeof CatalogTabsDictionary; + +export const getCatalogTabTitle = (tabName: CatalogTabNames = "catalogs") => { + return CatalogTabsDictionary[tabName] || CatalogTabsDictionary.catalogs; +}; diff --git a/packages/toolkit/src/server/utility/index.ts b/packages/toolkit/src/server/utility/index.ts index d58947cd84..1198e974b3 100644 --- a/packages/toolkit/src/server/utility/index.ts +++ b/packages/toolkit/src/server/utility/index.ts @@ -11,6 +11,7 @@ export * from "./chunk"; export * from "./removeObjKey"; export * from "./getModelTabTitle"; export * from "./getPipelineTabTitle"; +export * from "./getCatalogTabTitle"; export * from "./formatResourceId"; export * from "./parseResourceId"; export * from "./generateNextMetaBase"; diff --git a/packages/toolkit/src/view/catalog/CatalogMainView.tsx b/packages/toolkit/src/view/catalog/CatalogMainView.tsx index 38728f7948..c0465c9d82 100644 --- a/packages/toolkit/src/view/catalog/CatalogMainView.tsx +++ b/packages/toolkit/src/view/catalog/CatalogMainView.tsx @@ -7,7 +7,6 @@ import { Catalog, Nullable } from "instill-sdk"; import { cn } from "@instill-ai/design-system"; import { - GeneralAppPageProp, InstillStore, useAuthenticatedUserSubscription, useDeleteNamespaceCatalog, @@ -37,7 +36,10 @@ import { UploadExploreTab, } from "./components/tabs"; -export type CatalogViewProps = GeneralAppPageProp; +type CatalogMainViewProps = { + activeTab: string; + catalogId?: string; +}; const selector = (store: InstillStore) => ({ accessToken: store.accessToken, @@ -45,10 +47,8 @@ const selector = (store: InstillStore) => ({ selectedNamespace: store.navigationNamespaceAnchor, }); -export const CatalogMainView = (props: CatalogViewProps) => { - const [selectedCatalog, setSelectedCatalog] = - React.useState>(null); - const [activeTab, setActiveTab] = React.useState("catalogs"); +export const CatalogMainView = (props: CatalogMainViewProps) => { + const { catalogId, activeTab } = props; const [isProcessed, setIsProcessed] = React.useState(false); const [showCreditUsage, setShowCreditUsage] = React.useState(false); const [creditUsageTimer, setCreditUsageTimer] = @@ -78,10 +78,9 @@ export const CatalogMainView = (props: CatalogViewProps) => { const filesData = useListNamespaceCatalogFiles({ namespaceId: selectedNamespace ?? null, - catalogId: selectedCatalog?.catalogId ?? null, + catalogId: catalogId ?? null, accessToken, - enabled: - enabledQuery && Boolean(selectedNamespace) && Boolean(selectedCatalog), + enabled: enabledQuery && Boolean(selectedNamespace) && Boolean(catalogId), }); const userSub = useAuthenticatedUserSubscription({ @@ -119,7 +118,7 @@ export const CatalogMainView = (props: CatalogViewProps) => { React.useEffect(() => { if (catalogs.data) { const totalUsed = catalogs.data.reduce( - (total, kb) => total + parseInt(String(kb.usedStorage)), + (total, kb) => total + parseInt(String(kb.usedStorage), 10), 0, ); setRemainingStorageSpace( @@ -155,6 +154,13 @@ export const CatalogMainView = (props: CatalogViewProps) => { [subscriptionInfo.plan], ); + const selectedCatalog = React.useMemo(() => { + if (catalogId && catalogs.data) { + return catalogs.data.find((c) => c.catalogId === catalogId); + } + return null; + }, [catalogId, catalogs.data]); + const handleTabChangeAttempt = ( tab: string, isAutomatic: boolean = false, @@ -164,20 +170,15 @@ export const CatalogMainView = (props: CatalogViewProps) => { setShowWarnDialog(true); setPendingTabChange(tab); } else { - changeTab(tab); - } - }; - - const changeTab = React.useCallback( - (tab: string) => { - setActiveTab(tab); if (tab === "catalogs") { - setSelectedCatalog(null); + router.push(`/${selectedNamespace}/catalog`, { scroll: false }); + } else { + router.push(`/${selectedNamespace}/catalog/${catalogId}/${tab}`, { + scroll: false, + }); } - router.push(`#${tab}`, { scroll: false }); - }, - [router], - ); + } + }; const handleWarnDialogClose = async (): Promise => { return new Promise((resolve) => { @@ -190,7 +191,14 @@ export const CatalogMainView = (props: CatalogViewProps) => { const handleWarnDialogDiscard = () => { if (pendingTabChange) { - changeTab(pendingTabChange); + if (pendingTabChange === "catalogs") { + router.push(`/${selectedNamespace}/catalog`, { scroll: false }); + } else { + router.push( + `/${selectedNamespace}/catalog/${catalogId}/${pendingTabChange}`, + { scroll: false }, + ); + } } setShowWarnDialog(false); setPendingTabChange(null); @@ -212,8 +220,9 @@ export const CatalogMainView = (props: CatalogViewProps) => { }; const handleCatalogSelect = (catalog: Catalog) => { - handleTabChangeAttempt("upload"); - setSelectedCatalog(catalog); + router.push(`/${selectedNamespace}/catalog/${catalog.catalogId}/upload`, { + scroll: false, + }); }; const handleDeleteCatalog = async (catalog: Catalog) => { @@ -225,9 +234,8 @@ export const CatalogMainView = (props: CatalogViewProps) => { catalogId: catalog.catalogId, accessToken, }); - if (selectedCatalog?.catalogId === catalog.catalogId) { - setSelectedCatalog(null); - changeTab("catalogs"); + if (catalogId === catalog.catalogId) { + handleTabChangeAttempt("catalogs"); } catalogs.refetch(); } catch (error) { @@ -251,12 +259,6 @@ export const CatalogMainView = (props: CatalogViewProps) => { handleTabChangeAttempt("catalogs"); }; - React.useEffect(() => { - setSelectedCatalog(null); - changeTab("catalogs"); - setHasUnsavedChanges(false); - }, [selectedNamespace, changeTab]); - React.useEffect(() => { return () => { if (creditUsageTimer) { @@ -265,35 +267,6 @@ export const CatalogMainView = (props: CatalogViewProps) => { }; }, [creditUsageTimer]); - React.useEffect(() => { - const handleHashChange = () => { - const hash = window.location.hash.slice(1); - if ( - hash && - [ - "catalogs", - "upload", - "files", - "chunks", - "retrieve", - "ask_question", - "get_catalog", - ].includes(hash) - ) { - changeTab(hash); - } else { - changeTab("catalogs"); - } - }; - - handleHashChange(); - window.addEventListener("hashchange", handleHashChange); - - return () => { - window.removeEventListener("hashchange", handleHashChange); - }; - }, [catalogs.data, changeTab]); - return (
{showCreditUsage && !isLocalEnvironment ? ( @@ -325,7 +298,7 @@ export const CatalogMainView = (props: CatalogViewProps) => { { - const router = useRouter(); const [isApiExpanded, setIsApiExpanded] = React.useState(false); const handleTabChange = (tab: string) => { if (tab === "catalogs") { onDeselectCatalog(); + } else if (selectedCatalog) { + onTabChange(tab); } - onTabChange(tab); - router.push(`#${tab}`, { scroll: false }); }; const getTabClassName = (tabName: string) => @@ -51,7 +49,7 @@ export const Sidebar = ({ const handleApiClick = () => { const newExpandedState = !isApiExpanded; setIsApiExpanded(newExpandedState); - if (newExpandedState && !isApiTabActive) { + if (newExpandedState && !isApiTabActive && selectedCatalog) { handleTabChange("retrieve"); } };