Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Session detail panel basic layout #2775

Merged
merged 1 commit into from
Nov 5, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 4 additions & 6 deletions .cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"FGPU",
"filebrowser",
"Frgmt",
"Frgmts",
"Gaudi",
"keypair",
"Lablup",
Expand All @@ -19,17 +20,14 @@
"RNGD",
"shmem",
"superadmin",
"textbox",
"vaadin",
"vfolder",
"vfolders",
"Warboy",
"webcomponent",
"webui",
"wsproxy",
"vfolders",
"vfolder",
"filebrowser",
"vaadin",
"textbox"
"wsproxy"
],
"flagWords": [
"데이터레이크",
Expand Down
17 changes: 13 additions & 4 deletions react/data/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,18 @@ type Queries {
group_node(id: String!): GroupNode

"""Added in 24.03.0."""
group_nodes(filter: String, order: String, offset: Int, before: String, after: String, first: Int, last: Int): GroupConnection
group_nodes(
"""Added in 24.09.0."""
filter: String

"""Added in 24.09.0."""
order: String
offset: Int
before: String
after: String
first: Int
last: Int
): GroupConnection
group(
id: UUID!
domain_name: String
Expand Down Expand Up @@ -1258,9 +1269,7 @@ type ContainerRegistryNode implements Node {
"""The ID of the object"""
id: ID!

"""
Added in 24.09.0. The undecoded UUID type id of DB container_registries row.
"""
"""Added in 24.09.0. The UUID type id of DB container_registries row."""
row_id: UUID
name: String

Expand Down
1 change: 1 addition & 0 deletions react/relay.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ module.exports = {
// https://www.typescriptlang.org/docs/handbook/declaration-files/do-s-and-don-ts.html#number-string-boolean-symbol-and-object
DateTime: 'string',
UUID: 'string',
JSONString: 'string',
},
};
9 changes: 9 additions & 0 deletions react/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ const InteractiveLoginPage = React.lazy(
);
const ImportAndRunPage = React.lazy(() => import('./pages/ImportAndRunPage'));

const ComputeSessionList = React.lazy(
() => import('./components/ComputeSessionList'),
);

const RedirectToSummary = () => {
useSuspendedBackendaiClient();
const pathName = '/summary';
Expand Down Expand Up @@ -139,6 +143,11 @@ const router = createBrowserRouter([
{
path: '/job',
handle: { labelKey: 'webui.menu.Sessions' },
element: (
<BAIErrorBoundary>
<ComputeSessionList />
</BAIErrorBoundary>
),
},
{
path: '/serving',
Expand Down
2 changes: 0 additions & 2 deletions react/src/components/BAIModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ import Draggable from 'react-draggable';

export const DEFAULT_BAI_MODAL_Z_INDEX = 1001;
export interface BAIModalProps extends ModalProps {
okText?: string; // customize text of ok button with adequate content
draggable?: boolean; // modal can be draggle
className?: string;
}
const BAIModal: React.FC<BAIModalProps> = ({
className,
Expand Down
18 changes: 18 additions & 0 deletions react/src/components/ComputeSessionList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import SessionDetailDrawer from './SessionDetailDrawer';
import React from 'react';
import { StringParam, useQueryParam } from 'use-query-params';

const ComputeSessionList = () => {
const [sessionId, setSessionId] = useQueryParam('sessionDetail', StringParam);
return (
<SessionDetailDrawer
open={!sessionId}
sessionId={sessionId || undefined}
onClose={() => {
setSessionId(null, 'replaceIn');
}}
/>
);
};

export default ComputeSessionList;
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { EditableSessionNameFragment$key } from './__generated__/EditableSessionNameFragment.graphql';
import { EditableSessionNameMutation } from './__generated__/EditableSessionNameMutation.graphql';
import { theme } from 'antd';
import Text, { TextProps } from 'antd/es/typography/Text';
import Title, { TitleProps } from 'antd/es/typography/Title';
import graphql from 'babel-plugin-relay/macro';
import React, { useState } from 'react';
import { useFragment, useMutation } from 'react-relay';

type EditableSessionNameProps = {
sessionFrgmt: EditableSessionNameFragment$key;
} & (
| ({ component?: typeof Text } & Omit<TextProps, 'children'>)
| ({ component: typeof Title } & Omit<TitleProps, 'children'>)
);

const EditableSessionName: React.FC<EditableSessionNameProps> = ({
component: Component = Text,
sessionFrgmt,
style,
...otherProps
}) => {
const session = useFragment(
graphql`
fragment EditableSessionNameFragment on ComputeSessionNode {
id
name
priority
}
`,
sessionFrgmt,
);
const [optimisticName, setOptimisticName] = useState(session.name);
const { token } = theme.useToken();
const [commitEditMutation, isPendingEditMutation] =
useMutation<EditableSessionNameMutation>(graphql`
mutation EditableSessionNameMutation($input: ModifyComputeSessionInput!) {
modify_compute_session(input: $input) {
item {
id
name
}
}
}
`);
return (
session && (
<Component
editable={
isPendingEditMutation
? undefined
: {
onChange: (newName) => {
setOptimisticName(newName);
commitEditMutation({
variables: {
input: {
id: session.id,
name: newName,
// TODO: Setting the priority is not needed here. However, due to an API bug, we will keep it.
priority: session.priority,
},
},
onCompleted(response, errors) {},
onError(error) {},
});
},
triggerType: ['icon', 'text'],
}
}
copyable
style={{
...style,
color: isPendingEditMutation ? token.colorTextTertiary : style?.color,
}}
{...otherProps}
>
{isPendingEditMutation ? optimisticName : session.name}
</Component>
)
);
};

export default EditableSessionName;
122 changes: 122 additions & 0 deletions react/src/components/ComputeSessionNodeItems/SessionActionButtons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { useBackendAIAppLauncher } from '../../hooks/useBackendAIAppLauncher';
import TerminateSessionModal from './TerminateSessionModal';
import {
SessionActionButtonsFragment$data,
SessionActionButtonsFragment$key,
} from './__generated__/SessionActionButtonsFragment.graphql';
import { Tooltip, Button, theme } from 'antd';
import graphql from 'babel-plugin-relay/macro';
import { TerminalIcon, PowerOffIcon } from 'lucide-react';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useFragment } from 'react-relay';

interface SessionActionButtonsProps {
sessionFrgmt: SessionActionButtonsFragment$key | null;
}
// const isRunning = (session:SessionActionButtonsFragment$data) => {
// return [
// 'batch',
// 'interactive',
// 'inference',
// 'system',
// 'running',
// 'others',
// ].includes(session);
// }

const isActive = (session: SessionActionButtonsFragment$data) => {
return !['TERMINATED', 'CANCELLED'].includes(session?.status || '');
};
// const isTransitional = (session: SessionActionButtonsFragment$data) => {
// return [
// 'RESTARTING',
// 'TERMINATING',
// 'PENDING',
// 'PREPARING',
// 'PULLING',
// ].includes(session?.status || '');
// };

const SessionActionButtons: React.FC<SessionActionButtonsProps> = ({
sessionFrgmt,
}) => {
const { token } = theme.useToken();
const appLauncher = useBackendAIAppLauncher();

const { t } = useTranslation();

const session = useFragment(
graphql`
fragment SessionActionButtonsFragment on ComputeSessionNode {
id
row_id @required(action: NONE)
status
access_key
service_ports
commit_status

...TerminateSessionModalFragment
}
`,
sessionFrgmt,
);
const [openTerminateModal, setOpenTerminateModal] = useState(false);

// const isDisabledTermination = !['PENDING'].includes(session?.status || '') && session?.commit_status === 'ongoing'
// ${(this._isRunning && !this._isPreparing(rowData.item.status)) ||
// this._isError(rowData.item.status)
return (
session && (
<>
{/* <Tooltip title={t('session.SeeAppDialog')}>
<Button icon={<LayoutGridIcon />} onClick={()=>{
appLauncher.showLauncher({
"access-key": session?.access_key || '',
"service-ports": session?.service_ports || '',
})
}} />
</Tooltip> */}
<Tooltip title={t('session.ExecuteTerminalApp')}>
<Button
disabled={!isActive(session)}
icon={<TerminalIcon />}
onClick={() => {
appLauncher.runTerminal(session?.row_id);
}}
/>
</Tooltip>
{/* Don't put this modal to end of the return array(<></>). */}
<TerminateSessionModal
sessionFrgmts={[session]}
open={openTerminateModal}
onRequestClose={() => {
setOpenTerminateModal(false);
}}
/>
{/*
<Tooltip title={t('session.SeeContainerLogs')}>
<Button icon={<ScrollTextIcon />} />
</Tooltip>
<Tooltip title={t('session.RequestContainerCommit')}>
<Button icon={<ContainerIcon />} />
</Tooltip> */}
<Tooltip title={t('session.TerminateSession')}>
<Button
disabled={!isActive(session)}
icon={
<PowerOffIcon
color={isActive(session) ? token.colorError : undefined}
/>
}
onClick={() => {
setOpenTerminateModal(true);
}}
/>
</Tooltip>
</>
)
);
};

export default SessionActionButtons;
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useSuspendedBackendaiClient } from '../../hooks';
import BAIIntervalText from '../BAIIntervalText';
import DoubleTag from '../DoubleTag';
import { SessionReservationFragment$key } from './__generated__/SessionReservationFragment.graphql';
import graphql from 'babel-plugin-relay/macro';
import dayjs from 'dayjs';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { useFragment } from 'react-relay';

const SessionReservation: React.FC<{
sessionFrgmt: SessionReservationFragment$key;
}> = ({ sessionFrgmt }) => {
const baiClient = useSuspendedBackendaiClient();
const { t } = useTranslation();
const session = useFragment(
graphql`
fragment SessionReservationFragment on ComputeSessionNode {
id
created_at
terminated_at
}
`,
sessionFrgmt,
);
return (
<>
{dayjs(session.created_at).format('lll')}
<DoubleTag
values={[
t('session.ElapsedTime'),
<BAIIntervalText
callback={() => {
return session?.created_at
? baiClient.utils.elapsedTime(
session.created_at,
session?.terminated_at,
)
: '-';
}}
delay={1000}
/>,
]}
/>
</>
);
};

export default SessionReservation;
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from 'react';
ironAiken2 marked this conversation as resolved.
Show resolved Hide resolved

const SessionResourceNumbers = () => {
return <div>SessionResourceNumbers</div>;
};

export default SessionResourceNumbers;
Loading
Loading