diff --git a/packages/backend.ai-ui/src/components/BAIBoardItemTitle.tsx b/packages/backend.ai-ui/src/components/BAIBoardItemTitle.tsx index e4f938dbe5..3c98be63a5 100644 --- a/packages/backend.ai-ui/src/components/BAIBoardItemTitle.tsx +++ b/packages/backend.ai-ui/src/components/BAIBoardItemTitle.tsx @@ -10,6 +10,8 @@ export interface BAIBoardItemTitleProps { style?: React.CSSProperties; } +const Z_INDEX_IN_BAI_BOARD_ITEM_TITLE = 5; + const BAIBoardItemTitle: React.FC = ({ title, tooltip, @@ -28,7 +30,7 @@ const BAIBoardItemTitle: React.FC = ({ position: 'sticky', top: 0, backgroundColor: token.colorBgContainer, - zIndex: 1, + zIndex: Z_INDEX_IN_BAI_BOARD_ITEM_TITLE, ...style, }} gap="xs" diff --git a/react/src/App.tsx b/react/src/App.tsx index 64e1ae067c..e58ca37404 100644 --- a/react/src/App.tsx +++ b/react/src/App.tsx @@ -10,6 +10,7 @@ import MainLayout from './components/MainLayout/MainLayout'; import WebUINavigate from './components/WebUINavigate'; import { useSuspendedBackendaiClient } from './hooks'; import { useBAISettingUserState } from './hooks/useBAISetting'; +import AdminDashboardPage from './pages/AdminDashboardPage'; // High priority to import the component import ComputeSessionListPage from './pages/ComputeSessionListPage'; import ModelStoreListPage from './pages/ModelStoreListPage'; @@ -493,6 +494,19 @@ const router = createBrowserRouter([ handle: { labelKey: 'webui.menu.Settings&Logs' }, Component: UserSettingsPage, }, + { + path: '/admin-dashboard', + handle: { labelKey: 'webui.menu.AdminDashboard' }, + Component: () => { + return ( + + }> + + + + ); + }, + }, { path: '/credential', handle: { labelKey: 'webui.menu.UserCredentials&Policies' }, diff --git a/react/src/components/ActiveAgents.tsx b/react/src/components/ActiveAgents.tsx new file mode 100644 index 0000000000..682d6107fc --- /dev/null +++ b/react/src/components/ActiveAgents.tsx @@ -0,0 +1,84 @@ +import AgentList from './AgentList'; +import BAIFetchKeyButton from './BAIFetchKeyButton'; +import { theme } from 'antd'; +import { BAIFlex, BAIBoardItemTitle } from 'backend.ai-ui'; +import { useTransition } from 'react'; +import { useTranslation } from 'react-i18next'; + +interface ActiveAgentsProps { + fetchKey?: string; + onChangeFetchKey?: (key: string) => void; +} + +// TODO: Refactor this component with agent_nodes. +// ref: https://lablup.atlassian.net/browse/FR-1533 +const ActiveAgents: React.FC = ({ + fetchKey, + onChangeFetchKey, +}) => { + const { t } = useTranslation(); + const { token } = theme.useToken(); + const [isPendingRefetch, startRefetchTransition] = useTransition(); + + return ( + + { + startRefetchTransition(() => { + onChangeFetchKey?.(newFetchKey); + }); + }} + type="text" + style={{ + backgroundColor: 'transparent', + }} + /> + } + /> + + {/* Scrollable Content Section */} + + + + + ); +}; + +export default ActiveAgents; diff --git a/react/src/components/AgentList.tsx b/react/src/components/AgentList.tsx index 0e8b19a386..f742bd8caa 100644 --- a/react/src/components/AgentList.tsx +++ b/react/src/components/AgentList.tsx @@ -10,11 +10,7 @@ import { convertUnitValue, toFixedFloorWithoutTrailingZeros, } from '../helper'; -import { - INITIAL_FETCH_KEY, - useFetchKey, - useSuspendedBackendaiClient, -} from '../hooks'; +import { INITIAL_FETCH_KEY, useSuspendedBackendaiClient } from '../hooks'; import { ResourceSlotName, useResourceSlotsDetails } from '../hooks/backendai'; import { useBAIPaginationOptionStateOnSearchParam } from '../hooks/reactPaginationQueryOptions'; import { useHiddenColumnKeysSetting } from '../hooks/useHiddenColumnKeysSetting'; @@ -34,7 +30,7 @@ import { ReloadOutlined, SettingOutlined, } from '@ant-design/icons'; -import { useToggle } from 'ahooks'; +import { useControllableValue, useToggle } from 'ahooks'; import { Button, TableProps, Tag, theme, Tooltip, Typography } from 'antd'; import { AnyObject } from 'antd/es/_util/type'; import { ColumnsType, ColumnType } from 'antd/es/table'; @@ -43,6 +39,7 @@ import { BAITable, BAIFlex, BAIPropertyFilter, + BAIFlexProps, } from 'backend.ai-ui'; import dayjs from 'dayjs'; import _ from 'lodash'; @@ -55,11 +52,17 @@ type Agent = NonNullable['items'][number]; interface AgentListProps { tableProps?: Omit; + headerProps?: BAIFlexProps; + fetchKey?: string; + onChangeFetchKey?: (key: string) => void; } -const AgentList: React.FC = ({ tableProps }) => { +const AgentList: React.FC = ({ + tableProps, + headerProps, + ...otherProps +}) => { 'use memo'; - const { t } = useTranslation(); const { token } = theme.useToken(); const { isDarkMode } = useThemeMode(); @@ -86,7 +89,11 @@ const AgentList: React.FC = ({ tableProps }) => { pageSize: 10, }); - const [fetchKey, updateFetchKey] = useFetchKey(); + const [fetchKey, setFetchKey] = useControllableValue(otherProps, { + valuePropName: 'fetchKey', + trigger: 'onChangeFetchKey', + defaultValue: INITIAL_FETCH_KEY, + }); const queryVariables = { limit: baiPaginationOption.limit, @@ -99,6 +106,10 @@ const AgentList: React.FC = ({ tableProps }) => { const deferredQueryVariables = useDeferredValue(queryVariables); const deferredFetchKey = useDeferredValue(fetchKey); + const updateFetchKey = () => { + setFetchKey(() => new Date().toISOString()); + }; + const { agent_list } = useLazyLoadQuery( graphql` query AgentListQuery( @@ -762,7 +773,7 @@ const AgentList: React.FC = ({ tableProps }) => { return ( - + = (props) => { ]); const adminMenu: MenuProps['items'] = filterOutEmpty([ + // WARN: Currently only superadmins can access AdminDashboardPage. + // To place the Admin Dashboard menu item at the top of adminMenu, + // add it to adminMenu instead of superAdminMenu: + currentUserRole === 'superadmin' && { + label: ( + + {t('webui.menu.AdminDashboard')} + + ), + icon: , + key: 'admin-dashboard', + }, { label: {t('webui.menu.Users')}, icon: , diff --git a/react/src/components/RecentlyCreatedSession.tsx b/react/src/components/RecentlyCreatedSession.tsx index ed869cf689..7bde63d9f5 100644 --- a/react/src/components/RecentlyCreatedSession.tsx +++ b/react/src/components/RecentlyCreatedSession.tsx @@ -36,14 +36,14 @@ const RecentlyCreatedSession: React.FC = ({ graphql` fragment RecentlyCreatedSessionFragment on Query @argumentDefinitions( - projectId: { type: "UUID!" } + scopeId: { type: "ScopeField" } ) @refetchable(queryName: "RecentlyCreatedSessionRefetchQuery") { compute_session_nodes( first: 5 order: "-created_at" filter: "status == \"running\"" - project_id: $projectId + scope_id: $scopeId ) { edges { node { diff --git a/react/src/components/MySession.tsx b/react/src/components/SessionCountDashboardItem.tsx similarity index 82% rename from react/src/components/MySession.tsx rename to react/src/components/SessionCountDashboardItem.tsx index 902b500cfc..4360a85239 100644 --- a/react/src/components/MySession.tsx +++ b/react/src/components/SessionCountDashboardItem.tsx @@ -1,4 +1,3 @@ -import { MySessionQueryFragment$key } from '../__generated__/MySessionQueryFragment.graphql'; import BAIFetchKeyButton from './BAIFetchKeyButton'; import BAIPanelItem from './BAIPanelItem'; import { theme } from 'antd'; @@ -10,49 +9,55 @@ import { import { useTransition } from 'react'; import { useTranslation } from 'react-i18next'; import { graphql, useRefetchableFragment } from 'react-relay'; +import { SessionCountDashboardItemFragment$key } from 'src/__generated__/SessionCountDashboardItemFragment.graphql'; -interface MySessionProps { - queryRef: MySessionQueryFragment$key; +interface SessionCountDashboardItemProps { + queryRef: SessionCountDashboardItemFragment$key; isRefetching?: boolean; + title?: string; } -const MySession: React.FC = ({ queryRef, isRefetching }) => { +const SessionCountDashboardItem: React.FC = ({ + queryRef, + isRefetching, + title, +}) => { const { t } = useTranslation(); const { token } = theme.useToken(); const [isPendingRefetch, startRefetchTransition] = useTransition(); const [data, refetch] = useRefetchableFragment( graphql` - fragment MySessionQueryFragment on Query + fragment SessionCountDashboardItemFragment on Query @argumentDefinitions( - projectId: { type: "UUID!" } + scopeId: { type: "ScopeField" } ) - @refetchable(queryName: "MySessionQueryFragmentRefetchQuery") { + @refetchable(queryName: "SessionCountDashboardItemRefetchQuery") { myInteractive: compute_session_nodes( first: 0 filter: "status != \"TERMINATED\" & status != \"CANCELLED\" & type == \"interactive\"" - project_id: $projectId + scope_id: $scopeId ) { count } myBatch: compute_session_nodes( first: 0 filter: "status != \"TERMINATED\" & status != \"CANCELLED\" & type == \"batch\"" - project_id: $projectId + scope_id: $scopeId ) { count } myInference: compute_session_nodes( first: 0 filter: "status != \"TERMINATED\" & status != \"CANCELLED\" & type == \"inference\"" - project_id: $projectId + scope_id: $scopeId ) { count } myUpload: compute_session_nodes( first: 0 filter: "status != \"TERMINATED\" & status != \"CANCELLED\" & type == \"system\"" - project_id: $projectId + scope_id: $scopeId ) { count } @@ -79,7 +84,7 @@ const MySession: React.FC = ({ queryRef, isRefetching }) => { > {/* Fixed Title Section */} = ({ queryRef, isRefetching }) => { ); }; -export default MySession; +export default SessionCountDashboardItem; diff --git a/react/src/hooks/useBAISetting.tsx b/react/src/hooks/useBAISetting.tsx index 944bf882b8..b6eae7038e 100644 --- a/react/src/hooks/useBAISetting.tsx +++ b/react/src/hooks/useBAISetting.tsx @@ -24,6 +24,7 @@ interface UserSettings { experimental_dashboard?: boolean; session_metrics_board_items?: Array>; dashboard_board_items?: Array>; + admin_dashboard_board_items?: Array>; resource_panel_type?: | 'MyResource' | 'MyResourceWithinResourceGroup' diff --git a/react/src/pages/AdminDashboardPage.tsx b/react/src/pages/AdminDashboardPage.tsx new file mode 100644 index 0000000000..3d23f4e0b3 --- /dev/null +++ b/react/src/pages/AdminDashboardPage.tsx @@ -0,0 +1,205 @@ +import { AdminDashboardPageQuery } from '../__generated__/AdminDashboardPageQuery.graphql'; +import BAIBoard, { BAIBoardItem } from '../components/BAIBoard'; +import RecentlyCreatedSession from '../components/RecentlyCreatedSession'; +import SessionCountDashboardItem from '../components/SessionCountDashboardItem'; +import TotalResourceWithinResourceGroup, { + useIsAvailableTotalResourceWithinResourceGroup, +} from '../components/TotalResourceWithinResourceGroup'; +import { INITIAL_FETCH_KEY, useFetchKey } from '../hooks'; +import { useBAISettingUserState } from '../hooks/useBAISetting'; +import { + useCurrentProjectValue, + useCurrentResourceGroupValue, +} from '../hooks/useCurrentProject'; +import { useInterval } from '../hooks/useIntervalValue'; +import { Skeleton, theme } from 'antd'; +import { filterOutEmpty } from 'backend.ai-ui'; +import _ from 'lodash'; +import { Suspense, useTransition } from 'react'; +import { useTranslation } from 'react-i18next'; +import { graphql, useLazyLoadQuery } from 'react-relay'; +import ActiveAgents from 'src/components/ActiveAgents'; +import { useCurrentUserRole } from 'src/hooks/backendai'; + +const AdminDashboardPage: React.FC = () => { + const { token } = theme.useToken(); + const { t } = useTranslation(); + + const currentProject = useCurrentProjectValue(); + const currentResourceGroup = useCurrentResourceGroupValue(); + const userRole = useCurrentUserRole(); + + const [fetchKey, updateFetchKey] = useFetchKey(); + const [isPendingIntervalRefetch, startIntervalRefetchTransition] = + useTransition(); + + const [localStorageBoardItems, setLocalStorageBoardItems] = + useBAISettingUserState('admin_dashboard_board_items'); + + const isAvailableTotalResourcePanel = + useIsAvailableTotalResourceWithinResourceGroup(); + + const queryRef = useLazyLoadQuery( + graphql` + query AdminDashboardPageQuery( + $scopeId: ScopeField + $resourceGroup: String + $skipTotalResourceWithinResourceGroup: Boolean! + $isSuperAdmin: Boolean! + $agentNodeFilter: String! + ) { + ...SessionCountDashboardItemFragment @arguments(scopeId: $scopeId) + ...RecentlyCreatedSessionFragment @arguments(scopeId: $scopeId) + ...TotalResourceWithinResourceGroupFragment + @skip(if: $skipTotalResourceWithinResourceGroup) + @alias + @arguments( + resourceGroup: $resourceGroup + isSuperAdmin: $isSuperAdmin + agentNodeFilter: $agentNodeFilter + ) + } + `, + { + scopeId: `project:${currentProject.id}`, + resourceGroup: currentResourceGroup || 'default', + skipTotalResourceWithinResourceGroup: !isAvailableTotalResourcePanel, + isSuperAdmin: _.isEqual(userRole, 'superadmin'), + agentNodeFilter: `schedulable == true & status == "ALIVE" & scaling_group == "${currentResourceGroup}"`, + }, + { + fetchPolicy: + fetchKey === INITIAL_FETCH_KEY ? 'store-and-network' : 'network-only', + fetchKey, + }, + ); + + useInterval(() => { + startIntervalRefetchTransition(() => { + updateFetchKey(); + }); + }, 30_000); + + const initialBoardItems: Array = filterOutEmpty([ + { + id: 'activeSessions', + rowSpan: 2, + columnSpan: 2, + definition: { + minRowSpan: 2, + minColumnSpan: 2, + }, + data: { + content: ( + + } + > + + + ), + }, + }, + isAvailableTotalResourcePanel && { + id: 'totalResourceWithinResourceGroup', + rowSpan: 2, + columnSpan: 2, + definition: { + minRowSpan: 2, + minColumnSpan: 2, + }, + data: { + content: queryRef.TotalResourceWithinResourceGroupFragment && ( + + ), + }, + }, + { + id: 'activeAgents', + rowSpan: 4, + columnSpan: 4, + definition: { + minRowSpan: 3, + minColumnSpan: 4, + }, + data: { + content: ( + + } + > + updateFetchKey()} + /> + + ), + }, + }, + { + id: 'recentlyCreatedSession', + rowSpan: 3, + columnSpan: 4, + definition: { + minRowSpan: 2, + minColumnSpan: 2, + }, + data: { + content: ( + + ), + }, + }, + ]); + + // TODO: Issue occurs when newly added items in new webui version are not saved in localStorage + // and thus not displayed on screen. + // Opted-out items should also be stored separately in localStorage, and newly added items + // should be included in initialBoardItems. + const newlyAddedItems = _.filter( + initialBoardItems, + (item) => + !_.find( + localStorageBoardItems, + (itemInStorage) => itemInStorage.id === item.id, + ), + ); + const localstorageBoardItemsWithData = filterOutEmpty( + _.map(localStorageBoardItems, (item) => { + const matchedData = _.find( + initialBoardItems, + (initialItem) => initialItem.id === item.id, + )?.data; + return matchedData ? { ...item, data: matchedData } : undefined; + }), + ); + const boardItems = [...localstorageBoardItemsWithData, ...newlyAddedItems]; + return ( + { + const changedItems = [...event.detail.items]; + setLocalStorageBoardItems( + _.map(changedItems, (item) => _.omit(item, 'data')), + ); + }} + /> + ); +}; + +export default AdminDashboardPage; diff --git a/react/src/pages/DashboardPage.tsx b/react/src/pages/DashboardPage.tsx index d66d58ede4..e51a5665ff 100644 --- a/react/src/pages/DashboardPage.tsx +++ b/react/src/pages/DashboardPage.tsx @@ -2,8 +2,8 @@ import { DashboardPageQuery } from '../__generated__/DashboardPageQuery.graphql' import BAIBoard, { BAIBoardItem } from '../components/BAIBoard'; import MyResource from '../components/MyResource'; import MyResourceWithinResourceGroup from '../components/MyResourceWithinResourceGroup'; -import MySession from '../components/MySession'; import RecentlyCreatedSession from '../components/RecentlyCreatedSession'; +import SessionCountDashboardItem from '../components/SessionCountDashboardItem'; import TotalResourceWithinResourceGroup, { useIsAvailableTotalResourceWithinResourceGroup, } from '../components/TotalResourceWithinResourceGroup'; @@ -18,11 +18,13 @@ import { Skeleton, theme } from 'antd'; import { filterOutEmpty } from 'backend.ai-ui'; import _ from 'lodash'; import { Suspense, useTransition } from 'react'; +import { useTranslation } from 'react-i18next'; import { graphql, useLazyLoadQuery } from 'react-relay'; import { useCurrentUserRole } from 'src/hooks/backendai'; const DashboardPage: React.FC = () => { const { token } = theme.useToken(); + const { t } = useTranslation(); const currentProject = useCurrentProjectValue(); const currentResourceGroup = useCurrentResourceGroupValue(); @@ -41,14 +43,14 @@ const DashboardPage: React.FC = () => { const queryRef = useLazyLoadQuery( graphql` query DashboardPageQuery( - $projectId: UUID! + $scopeId: ScopeField $resourceGroup: String $skipTotalResourceWithinResourceGroup: Boolean! $isSuperAdmin: Boolean! $agentNodeFilter: String! ) { - ...MySessionQueryFragment @arguments(projectId: $projectId) - ...RecentlyCreatedSessionFragment @arguments(projectId: $projectId) + ...SessionCountDashboardItemFragment @arguments(scopeId: $scopeId) + ...RecentlyCreatedSessionFragment @arguments(scopeId: $scopeId) ...TotalResourceWithinResourceGroupFragment @skip(if: $skipTotalResourceWithinResourceGroup) @alias @@ -60,7 +62,7 @@ const DashboardPage: React.FC = () => { } `, { - projectId: currentProject.id, + scopeId: `project:${currentProject.id}`, resourceGroup: currentResourceGroup || 'default', skipTotalResourceWithinResourceGroup: !isAvailableTotalResourcePanel, isSuperAdmin: _.isEqual(userRole, 'superadmin'), @@ -95,7 +97,8 @@ const DashboardPage: React.FC = () => { } > - diff --git a/resources/i18n/de.json b/resources/i18n/de.json index c5e2ba629c..40128dfd7e 100644 --- a/resources/i18n/de.json +++ b/resources/i18n/de.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Aktive Agenten", + "ActiveAgentsTooltip": "Zeigt einen Überblick auf einen Blick über alle im System ausgeführten Agenten." + }, "agent": { "Agent": "Agent", "AgentSetting": "Agent-Einstellungen", @@ -1253,6 +1257,7 @@ "PendingSessions": "Ausstehende Sitzungen" }, "session": { + "ActiveSessions": "Aktive Sitzungen", "Agent": "Agent", "AgentId": "Agenten -ID", "Agents": "Agenten", @@ -2003,6 +2008,7 @@ "AIAccelerator": "KI-Beschleuniger", "AIAgents": "AI-Agenten", "AboutBackendAI": "Über Backend.AI", + "AdminDashboard": "Admin -Dashboard", "Administration": "Verwaltung", "AgentSummary": "Agent Zusammenfassung", "Architecture": "Architektur", diff --git a/resources/i18n/el.json b/resources/i18n/el.json index 77120418ae..922d914f02 100644 --- a/resources/i18n/el.json +++ b/resources/i18n/el.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Ενεργοί πράκτορες", + "ActiveAgentsTooltip": "Δείχνει μια επισκόπηση όλων των παραγόντων που εκτελούνται σήμερα στο σύστημα." + }, "agent": { "Agent": "Πράκτορας", "AgentSetting": "Ρυθμίσεις πράκτορα", @@ -1252,6 +1256,7 @@ "PendingSessions": "Εκκρεμείς συνεδρίες" }, "session": { + "ActiveSessions": "Ενεργές συνεδρίες", "Agent": "Μέσο", "AgentId": "Αναγνωριστικό πράκτορα", "Agents": "Πράκτορες", @@ -2001,6 +2006,7 @@ "AIAccelerator": "Επιταχυντής AI", "AIAgents": "Πράκτορες AI", "AboutBackendAI": "Σχετικά με το Backend.AI", + "AdminDashboard": "Πίνακας ελέγχου διαχειριστή", "Administration": "Διαχείριση", "AgentSummary": "Περίληψη πράκτορα", "Architecture": "Αρχιτεκτονική", diff --git a/resources/i18n/en.json b/resources/i18n/en.json index 6d50feee90..41f2dd764f 100644 --- a/resources/i18n/en.json +++ b/resources/i18n/en.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Active Agents", + "ActiveAgentsTooltip": "Shows an at-a-glance overview of all agents currently running in the system." + }, "agent": { "Agent": "Agent", "AgentSetting": "Agent Settings", @@ -1260,6 +1264,7 @@ "PendingSessions": "Pending Sessions" }, "session": { + "ActiveSessions": "Active Sessions", "Agent": "Agent", "AgentId": "Agent ID", "Agents": "Agents", @@ -2012,6 +2017,7 @@ "AIAccelerator": "AI Accelerator", "AIAgents": "AI Agents", "AboutBackendAI": "About Backend.AI", + "AdminDashboard": "Admin Dashboard", "Administration": "Administration", "AgentSummary": "Agent Summary", "Architecture": "Architecture", diff --git a/resources/i18n/es.json b/resources/i18n/es.json index 6eff7ba8f1..44b411ca12 100644 --- a/resources/i18n/es.json +++ b/resources/i18n/es.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Agentes activos", + "ActiveAgentsTooltip": "Muestra una descripción general de un vistazo de todos los agentes que actualmente se ejecutan en el sistema." + }, "agent": { "Agent": "Agente", "AgentSetting": "Configuración del agente", @@ -1255,6 +1259,7 @@ "PendingSessions": "Sesiones pendientes" }, "session": { + "ActiveSessions": "Sesiones activas", "Agent": "Agente", "AgentId": "ID de agente", "Agents": "Agentes", @@ -2005,6 +2010,7 @@ "AIAccelerator": "Acelerador de IA", "AIAgents": "Agentes de IA", "AboutBackendAI": "Acerca de Backend.AI", + "AdminDashboard": "Tablero de administración", "Administration": "Administración", "AgentSummary": "Resumen del agente", "Architecture": "Arquitectura", diff --git a/resources/i18n/fi.json b/resources/i18n/fi.json index f6eecd1043..940f48ae8e 100644 --- a/resources/i18n/fi.json +++ b/resources/i18n/fi.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Aktiiviset edustajat", + "ActiveAgentsTooltip": "Näyttää yleiskatsauksen kaikista järjestelmässä tällä hetkellä toimivista edustajista." + }, "agent": { "Agent": "Agentti", "AgentSetting": "Agentin asetukset", @@ -1254,6 +1258,7 @@ "PendingSessions": "Vireillä olevat istunnot" }, "session": { + "ActiveSessions": "Aktiiviset istunnot", "Agent": "Agentti", "AgentId": "Agenttitunnus", "Agents": "Agentit", @@ -2004,6 +2009,7 @@ "AIAccelerator": "AI-kiihdyttämö", "AIAgents": "AI-agentit", "AboutBackendAI": "Tietoja Backend.AI:sta", + "AdminDashboard": "Järjestelmänvalvojan kojelauta", "Administration": "Hallinto", "AgentSummary": "Agentin yhteenveto", "Architecture": "Arkkitehtuuri", diff --git a/resources/i18n/fr.json b/resources/i18n/fr.json index 3b964c4180..9f90a9b8d0 100644 --- a/resources/i18n/fr.json +++ b/resources/i18n/fr.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Agents actifs", + "ActiveAgentsTooltip": "Affiche un aperçu de tous les agents qui s'exécute actuellement dans le système." + }, "agent": { "Agent": "Agent", "AgentSetting": "Paramètres de l'agent", @@ -1255,6 +1259,7 @@ "PendingSessions": "Séances en attente" }, "session": { + "ActiveSessions": "Sessions actives", "Agent": "Agent", "AgentId": "ID d'agent", "Agents": "Agents", @@ -2005,6 +2010,7 @@ "AIAccelerator": "Accélérateur d'IA", "AIAgents": "Agents d'intelligence artificielle", "AboutBackendAI": "À propos de Backend.AI", + "AdminDashboard": "Tableau de bord administratif", "Administration": "Administration", "AgentSummary": "Résumé de l'agent", "Architecture": "Architecture", diff --git a/resources/i18n/id.json b/resources/i18n/id.json index 9e09cb1d58..4df4b2edf1 100644 --- a/resources/i18n/id.json +++ b/resources/i18n/id.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Agen aktif", + "ActiveAgentsTooltip": "Menunjukkan ikhtisar sekilas dari semua agen yang saat ini berjalan dalam sistem." + }, "agent": { "Agent": "Agen", "AgentSetting": "Pengaturan Agen", @@ -1253,6 +1257,7 @@ "PendingSessions": "Sesi yang tertunda" }, "session": { + "ActiveSessions": "Sesi aktif", "Agent": "Agen", "AgentId": "ID Agen", "Agents": "Agen", @@ -2004,6 +2009,7 @@ "AIAccelerator": "Akselerator AI", "AIAgents": "Agen AI", "AboutBackendAI": "Tentang Backend.AI", + "AdminDashboard": "Dasbor Admin", "Administration": "Administrasi", "AgentSummary": "Ringkasan Agen", "Architecture": "Arsitektur", diff --git a/resources/i18n/it.json b/resources/i18n/it.json index 38c13b2a9b..2a02c0f04c 100644 --- a/resources/i18n/it.json +++ b/resources/i18n/it.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Agenti attivi", + "ActiveAgentsTooltip": "Mostra una panoramica a colpo d'occhio di tutti gli agenti attualmente in esecuzione nel sistema." + }, "agent": { "Agent": "Agente", "AgentSetting": "Impostazioni dell'agente", @@ -1252,6 +1256,7 @@ "PendingSessions": "Sessioni in sospeso" }, "session": { + "ActiveSessions": "Sessioni attive", "Agent": "Agente", "AgentId": "ID agente", "Agents": "Agenti", @@ -2002,6 +2007,7 @@ "AIAccelerator": "Acceleratore AI", "AIAgents": "Agenti AI", "AboutBackendAI": "Informazioni su Backend.AI", + "AdminDashboard": "Dashboard di amministrazione", "Administration": "Amministrazione", "AgentSummary": "Riepilogo dell'agente", "Architecture": "Architettura", diff --git a/resources/i18n/ja.json b/resources/i18n/ja.json index 24b1940c8a..31a3628022 100644 --- a/resources/i18n/ja.json +++ b/resources/i18n/ja.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "アクティブなエージェント", + "ActiveAgentsTooltip": "現在システムで実行されているすべてのエージェントの概要を示しています。" + }, "agent": { "Agent": "代理店", "AgentSetting": "エージェント設定", @@ -1254,6 +1258,7 @@ "PendingSessions": "保留中のセッション" }, "session": { + "ActiveSessions": "アクティブセッション", "Agent": "エージェント", "AgentId": "エージェントID", "Agents": "エージェント", @@ -2004,6 +2009,7 @@ "AIAccelerator": "AIアクセラレータ", "AIAgents": "AIエージェント", "AboutBackendAI": "Backend.AIについて", + "AdminDashboard": "管理ダッシュボード", "Administration": "管理", "AgentSummary": "リソース概要", "Architecture": "建築", diff --git a/resources/i18n/ko.json b/resources/i18n/ko.json index 259514a071..c0c4db7e9b 100644 --- a/resources/i18n/ko.json +++ b/resources/i18n/ko.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "활성 에이전트", + "ActiveAgentsTooltip": "현재 시스템에서 실행 중인 모든 에이전트 개요를 표시합니다." + }, "agent": { "Agent": "에이전트", "AgentSetting": "에이전트 설정", @@ -1259,6 +1263,7 @@ "PendingSessions": "대기 세션" }, "session": { + "ActiveSessions": "활성 세션", "Agent": "실행 노드", "AgentId": "에이전트 ID", "Agents": "실행 노드", @@ -2010,6 +2015,7 @@ "AIAccelerator": "AI 가속기", "AIAgents": "AI 에이전트", "AboutBackendAI": "Backend.AI에 대하여", + "AdminDashboard": "관리자 대시보드", "Administration": "관리", "AgentSummary": "자원 요약", "Architecture": "운영체제", diff --git a/resources/i18n/mn.json b/resources/i18n/mn.json index c31588a753..065d4cc515 100644 --- a/resources/i18n/mn.json +++ b/resources/i18n/mn.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Идэвхтэй агентууд", + "ActiveAgentsTooltip": "Системд ажиллаж байгаа бүх агентуудын тоймыг нэг дор харуулна." + }, "agent": { "Agent": "Агент", "AgentSetting": "Агент тохиргоо", @@ -1252,6 +1256,7 @@ "PendingSessions": "Хүлээгдэж буй хуралдаанууд" }, "session": { + "ActiveSessions": "Идэвхтэй сешнүүд", "Agent": "Агент", "AgentId": "Хийнний ID", "Agents": "Агентууд", @@ -2002,6 +2007,7 @@ "AIAccelerator": "AI хурдасгуур", "AIAgents": "AI агентууд", "AboutBackendAI": "Backend.AI-ийн тухай", + "AdminDashboard": "Админ хяналтын самбар", "Administration": "Админ", "AgentSummary": "Агентын хураангуй", "Architecture": "Уран барилгач", diff --git a/resources/i18n/ms.json b/resources/i18n/ms.json index 204c1e342d..08bc1ea7cc 100644 --- a/resources/i18n/ms.json +++ b/resources/i18n/ms.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Ejen aktif", + "ActiveAgentsTooltip": "Menunjukkan gambaran keseluruhan sepintas lalu semua ejen yang sedang berjalan dalam sistem." + }, "agent": { "Agent": "Ejen", "AgentSetting": "Tetapan Agen", @@ -1254,6 +1258,7 @@ "PendingSessions": "Sesi yang belum selesai" }, "session": { + "ActiveSessions": "Sesi aktif", "Agent": "Ejen", "AgentId": "Id ejen", "Agents": "ejen", @@ -2003,6 +2008,7 @@ "AIAccelerator": "Pemecut AI", "AIAgents": "Ejen AI", "AboutBackendAI": "Mengenai Backend.AI", + "AdminDashboard": "Papan pemuka admin", "Administration": "Pentadbiran", "AgentSummary": "Ringkasan Agen", "Architecture": "Seni bina", diff --git a/resources/i18n/pl.json b/resources/i18n/pl.json index c066063710..d4fc10c346 100644 --- a/resources/i18n/pl.json +++ b/resources/i18n/pl.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Aktywni agenci", + "ActiveAgentsTooltip": "Pokazuje przegląd w skrócie wszystkich agentów obecnie działających w systemie." + }, "agent": { "Agent": "Agent", "AgentSetting": "Ustawienia agenta", @@ -1254,6 +1258,7 @@ "PendingSessions": "Oczekujące sesje" }, "session": { + "ActiveSessions": "Aktywne sesje", "Agent": "Agent", "AgentId": "Identyfikator agenta", "Agents": "Agenci", @@ -2004,6 +2009,7 @@ "AIAccelerator": "Akcelerator AI", "AIAgents": "Agenci AI", "AboutBackendAI": "O Backend.AI", + "AdminDashboard": "Pulpit nawigacyjny administratora", "Administration": "Administracja", "AgentSummary": "Podsumowanie agenta", "Architecture": "Architektura", diff --git a/resources/i18n/pt-BR.json b/resources/i18n/pt-BR.json index 400ff49631..e200f2a56a 100644 --- a/resources/i18n/pt-BR.json +++ b/resources/i18n/pt-BR.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Agentes ativos", + "ActiveAgentsTooltip": "Mostra uma visão geral de todos os agentes atualmente em execução no sistema." + }, "agent": { "Agent": "Agente", "AgentSetting": "Definições do agente", @@ -1255,6 +1259,7 @@ "PendingSessions": "Sessões pendentes" }, "session": { + "ActiveSessions": "Sessões ativas", "Agent": "Agente", "AgentId": "ID do agente", "Agents": "Agentes", @@ -2005,6 +2010,7 @@ "AIAccelerator": "Acelerador de IA", "AIAgents": "Agentes de IA", "AboutBackendAI": "Sobre Backend.AI", + "AdminDashboard": "Painel de administrador", "Administration": "Administração", "AgentSummary": "Resumo do agente", "Architecture": "Arquitetura", diff --git a/resources/i18n/pt.json b/resources/i18n/pt.json index 09629d02ff..aa42ecc5de 100644 --- a/resources/i18n/pt.json +++ b/resources/i18n/pt.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Agentes ativos", + "ActiveAgentsTooltip": "Mostra uma visão geral de todos os agentes atualmente em execução no sistema." + }, "agent": { "Agent": "Agente", "AgentSetting": "Definições do agente", @@ -1254,6 +1258,7 @@ "PendingSessions": "Sessões pendentes" }, "session": { + "ActiveSessions": "Sessões ativas", "Agent": "Agente", "AgentId": "ID do agente", "Agents": "Agentes", @@ -2004,6 +2009,7 @@ "AIAccelerator": "Acelerador de IA", "AIAgents": "Agentes de IA", "AboutBackendAI": "Sobre Backend.AI", + "AdminDashboard": "Painel de administrador", "Administration": "Administração", "AgentSummary": "Resumo do agente", "Architecture": "Arquitetura", diff --git a/resources/i18n/ru.json b/resources/i18n/ru.json index c2e4ae40cb..030f9cfb4f 100644 --- a/resources/i18n/ru.json +++ b/resources/i18n/ru.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Активные агенты", + "ActiveAgentsTooltip": "Показывает обзор всех агентов, работающих в настоящее время в системе." + }, "agent": { "Agent": "Агент", "AgentSetting": "Настройки агента", @@ -1254,6 +1258,7 @@ "PendingSessions": "В ожидании сессий" }, "session": { + "ActiveSessions": "Активные сеансы", "Agent": "Агент", "AgentId": "Идентификатор агента", "Agents": "Агенты", @@ -2004,6 +2009,7 @@ "AIAccelerator": "Ускоритель искусственного интеллекта", "AIAgents": "Агенты искусственного интеллекта", "AboutBackendAI": "О Backend.AI", + "AdminDashboard": "Административная панель", "Administration": "Администрация", "AgentSummary": "Резюме агента", "Architecture": "Архитектура", diff --git a/resources/i18n/th.json b/resources/i18n/th.json index 6492dceb99..536f21d854 100644 --- a/resources/i18n/th.json +++ b/resources/i18n/th.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "ตัวแทนที่ใช้งานอยู่", + "ActiveAgentsTooltip": "แสดงภาพรวม at-a-glance ของตัวแทนทั้งหมดที่ทำงานอยู่ในระบบ" + }, "agent": { "Agent": "ตัวแทน", "AgentSetting": "การตั้งค่าตัวแทน", @@ -1244,6 +1248,7 @@ "PendingSessions": "รอการประชุม" }, "session": { + "ActiveSessions": "เซสชันที่ใช้งานอยู่", "Agent": "ตัวแทน", "AgentId": "ID ตัวแทน", "Agents": "ตัวแทน", @@ -1988,6 +1993,7 @@ "AIAccelerator": "ตัวเร่ง AI", "AIAgents": "ตัวแทน AI", "AboutBackendAI": "เกี่ยวกับ Backend.AI", + "AdminDashboard": "แดชบอร์ดผู้ดูแลระบบ", "Administration": "การจัดการระบบ", "AgentSummary": "สรุปตัวแทน", "Architecture": "สถาปัตยกรรม", diff --git a/resources/i18n/tr.json b/resources/i18n/tr.json index 9b68f596af..bd37128b04 100644 --- a/resources/i18n/tr.json +++ b/resources/i18n/tr.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Aktif ajanlar", + "ActiveAgentsTooltip": "Şu anda sistemde çalışan tüm ajanlara bir bakışta genel bir bakış gösterir." + }, "agent": { "Agent": "Ajan", "AgentSetting": "Temsilci Ayarları", @@ -1255,6 +1259,7 @@ "PendingSessions": "Bekleyen seanslar" }, "session": { + "ActiveSessions": "Aktif oturumlar", "Agent": "Temsilci", "AgentId": "Ajan kimliği", "Agents": "Temsilciler", @@ -2005,6 +2010,7 @@ "AIAccelerator": "Yapay Zeka Hızlandırıcı", "AIAgents": "Yapay Zeka Ajanları", "AboutBackendAI": "Backend.AI hakkında", + "AdminDashboard": "Yönetici Gösterge Tablosu", "Administration": "yönetim", "AgentSummary": "Temsilci Özeti", "Architecture": "Mimarlık", diff --git a/resources/i18n/vi.json b/resources/i18n/vi.json index 5c2ce67f96..00093b6164 100644 --- a/resources/i18n/vi.json +++ b/resources/i18n/vi.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "Đại lý tích cực", + "ActiveAgentsTooltip": "Hiển thị tổng quan at-a-glance của tất cả các tác nhân hiện đang chạy trong hệ thống." + }, "agent": { "Agent": "Đại lý", "AgentSetting": "Cài đặt đại lý", @@ -1255,6 +1259,7 @@ "PendingSessions": "Phiên đang chờ xử lý" }, "session": { + "ActiveSessions": "Phiên hoạt động", "Agent": "Đặc vụ", "AgentId": "Id đại lý", "Agents": "Đại lý", @@ -2005,6 +2010,7 @@ "AIAccelerator": "Máy gia tốc AI", "AIAgents": "Đại lý AI", "AboutBackendAI": "Giới thiệu về Backend.AI", + "AdminDashboard": "Bảng điều khiển quản trị viên", "Administration": "Hành chính", "AgentSummary": "Tóm tắt đại lý", "Architecture": "Ngành kiến ​​​​trúc", diff --git a/resources/i18n/zh-CN.json b/resources/i18n/zh-CN.json index 4dba80a3cb..009f8e3b67 100644 --- a/resources/i18n/zh-CN.json +++ b/resources/i18n/zh-CN.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "活性代理", + "ActiveAgentsTooltip": "显示了当前在系统中运行的所有代理的一目了然概述。" + }, "agent": { "Agent": "代理", "AgentSetting": "代理设置", @@ -1255,6 +1259,7 @@ "PendingSessions": "等待会议" }, "session": { + "ActiveSessions": "活动会话", "Agent": "代理人", "AgentId": "代理ID", "Agents": "代理商", @@ -2005,6 +2010,7 @@ "AIAccelerator": "人工智能加速器", "AIAgents": "人工智能代理", "AboutBackendAI": "关于后端.AI", + "AdminDashboard": "管理仪表板", "Administration": "行政", "AgentSummary": "代理摘要", "Architecture": "建筑学", diff --git a/resources/i18n/zh-TW.json b/resources/i18n/zh-TW.json index cc02375d03..3ebd4827be 100644 --- a/resources/i18n/zh-TW.json +++ b/resources/i18n/zh-TW.json @@ -1,5 +1,9 @@ { "$schema": "../i18n.schema.json", + "activeAgent": { + "ActiveAgents": "活性代理", + "ActiveAgentsTooltip": "顯示了當前在系統中運行的所有代理的一目了然的概述。" + }, "agent": { "Agent": "代理", "AgentSetting": "代理设置", @@ -1254,6 +1258,7 @@ "PendingSessions": "等待會議" }, "session": { + "ActiveSessions": "活動會話", "Agent": "代理人", "AgentId": "代理ID", "Agents": "代理商", @@ -2003,6 +2008,7 @@ "AIAccelerator": "人工智能加速器", "AIAgents": "人工智能代理", "AboutBackendAI": "關於後端.AI", + "AdminDashboard": "管理儀表板", "Administration": "行政", "AgentSummary": "代理摘要", "Architecture": "建築學", diff --git a/src/components/backend-ai-webui.ts b/src/components/backend-ai-webui.ts index e039046124..f63a949626 100644 --- a/src/components/backend-ai-webui.ts +++ b/src/components/backend-ai-webui.ts @@ -3,7 +3,6 @@ Copyright (c) 2015-2025 Lablup Inc. All rights reserved. */ import { navigate } from '../backend-ai-app'; -// import '../lib/backend.ai-client-esm'; import { default as TabCount } from '../lib/TabCounter'; import { IronFlex, @@ -142,6 +141,7 @@ export default class BackendAIWebUI extends connect(store)(LitElement) { @property({ type: Array }) availablePages = [ 'start', 'dashboard', + 'admin-dashboard', 'summary', 'verify-email', 'change-password', @@ -174,7 +174,7 @@ export default class BackendAIWebUI extends connect(store)(LitElement) { 'model-store', 'scheduler', 'reservoir', - ]; // temporally block pipeline from available pages 'pipeline', 'pipeline-job' + ]; @property({ type: Array }) adminOnlyPages = [ 'experiment', 'credential', @@ -183,6 +183,7 @@ export default class BackendAIWebUI extends connect(store)(LitElement) { 'resource-policy', ]; @property({ type: Array }) superAdminOnlyPages = [ + 'admin-dashboard', 'agent', 'storage-settings', 'settings',