Skip to content

Commit a832aee

Browse files
committed
fix: use parseAsString for tab param in DeploymentDetailPage to prevent reset loop on invalid values
1 parent 0aac15f commit a832aee

27 files changed

Lines changed: 265 additions & 115 deletions

react/src/components/AutoScalingRuleList.tsx

Lines changed: 31 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
PlusOutlined,
1818
SettingOutlined,
1919
} from '@ant-design/icons';
20-
import { App, Button, Card, Tag, Tooltip, Typography, theme } from 'antd';
20+
import { App, Button, Tag, Tooltip, Typography } from 'antd';
2121
import {
2222
BAIFlex,
2323
BAIGraphQLPropertyFilter,
@@ -335,7 +335,6 @@ const AutoScalingRuleList: React.FC<AutoScalingRuleListProps> = ({
335335
}) => {
336336
'use memo';
337337
const { t } = useTranslation();
338-
const { token } = theme.useToken();
339338
const { message, modal } = App.useApp();
340339
const [isPendingRefetch, startRefetchTransition] = useTransition();
341340
const [fetchKey, updateFetchKey] = useFetchKey();
@@ -504,9 +503,34 @@ const AutoScalingRuleList: React.FC<AutoScalingRuleListProps> = ({
504503

505504
return (
506505
<>
507-
<Card
508-
title={t('modelService.AutoScalingRules')}
509-
extra={
506+
<BAIFlex direction="column" align="stretch" gap="sm">
507+
<BAIFlex align="center" gap="sm">
508+
<BAIGraphQLPropertyFilter
509+
style={{ flex: 1 }}
510+
filterProperties={[
511+
{
512+
key: 'createdAt',
513+
propertyLabel: t('autoScalingRule.CreatedAt'),
514+
type: 'datetime',
515+
operators: ['after', 'before'],
516+
defaultOperator: 'after',
517+
},
518+
{
519+
key: 'lastTriggeredAt',
520+
propertyLabel: t('autoScalingRule.LastTriggered'),
521+
type: 'datetime',
522+
operators: ['after', 'before'],
523+
defaultOperator: 'after',
524+
},
525+
]}
526+
value={graphQLFilter}
527+
onChange={(filter) => {
528+
startRefetchTransition(() => {
529+
setQueryParams({ filter: filter ?? null });
530+
setTablePaginationOption({ current: 1 });
531+
});
532+
}}
533+
/>
510534
<Button
511535
type="primary"
512536
icon={<PlusOutlined />}
@@ -518,34 +542,7 @@ const AutoScalingRuleList: React.FC<AutoScalingRuleListProps> = ({
518542
>
519543
{t('modelService.AddRules')}
520544
</Button>
521-
}
522-
>
523-
<BAIGraphQLPropertyFilter
524-
style={{ marginBottom: token.marginMD }}
525-
filterProperties={[
526-
{
527-
key: 'createdAt',
528-
propertyLabel: t('autoScalingRule.CreatedAt'),
529-
type: 'datetime',
530-
operators: ['after', 'before'],
531-
defaultOperator: 'after',
532-
},
533-
{
534-
key: 'lastTriggeredAt',
535-
propertyLabel: t('autoScalingRule.LastTriggered'),
536-
type: 'datetime',
537-
operators: ['after', 'before'],
538-
defaultOperator: 'after',
539-
},
540-
]}
541-
value={graphQLFilter}
542-
onChange={(filter) => {
543-
startRefetchTransition(() => {
544-
setQueryParams({ filter: filter ?? null });
545-
setTablePaginationOption({ current: 1 });
546-
});
547-
}}
548-
/>
545+
</BAIFlex>
549546
<AutoScalingRuleListNodes
550547
autoScalingRulesFrgmt={autoScalingRuleNodes}
551548
presetMap={presetMap}
@@ -580,7 +577,7 @@ const AutoScalingRuleList: React.FC<AutoScalingRuleListProps> = ({
580577
}}
581578
onDeleteRule={handleDeleteRule}
582579
/>
583-
</Card>
580+
</BAIFlex>
584581
<BAIUnmountAfterClose>
585582
<AutoScalingRuleEditorModal
586583
open={isOpenEditorModal}

react/src/components/DeploymentAccessTokensTab.tsx

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,7 @@ import {
88
DeploymentAccessTokensTab_deployment$key,
99
} from '../__generated__/DeploymentAccessTokensTab_deployment.graphql';
1010
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
11-
import {
12-
Alert,
13-
App,
14-
Button,
15-
Card,
16-
DatePicker,
17-
Form,
18-
Typography,
19-
theme,
20-
} from 'antd';
11+
import { Alert, App, Button, DatePicker, Form, Typography, theme } from 'antd';
2112
import {
2213
BAIFlex,
2314
BAIModal,
@@ -142,9 +133,8 @@ const DeploymentAccessTokensTab: React.FC<DeploymentAccessTokensTabProps> = ({
142133

143134
return (
144135
<>
145-
<Card
146-
title={t('deployment.AccessTokens')}
147-
extra={
136+
<BAIFlex direction="column" align="stretch" gap="sm">
137+
<BAIFlex justify="end">
148138
<Button
149139
type="primary"
150140
icon={<PlusOutlined />}
@@ -153,8 +143,7 @@ const DeploymentAccessTokensTab: React.FC<DeploymentAccessTokensTabProps> = ({
153143
>
154144
{t('deployment.accessToken.Create')}
155145
</Button>
156-
}
157-
>
146+
</BAIFlex>
158147
<BAITable<AccessTokenNode>
159148
scroll={{ x: 'max-content' }}
160149
rowKey="id"
@@ -193,7 +182,7 @@ const DeploymentAccessTokensTab: React.FC<DeploymentAccessTokensTabProps> = ({
193182
},
194183
{
195184
key: 'createdAt',
196-
title: t('deployment.accessToken.Created'),
185+
title: t('deployment.CreatedAt'),
197186
dataIndex: 'createdAt',
198187
render: (_text, row) =>
199188
row?.createdAt ? dayjs(row.createdAt).format('ll LT') : '-',
@@ -209,7 +198,7 @@ const DeploymentAccessTokensTab: React.FC<DeploymentAccessTokensTabProps> = ({
209198
},
210199
]}
211200
/>
212-
</Card>
201+
</BAIFlex>
213202

214203
<BAIUnmountAfterClose>
215204
<CreateAccessTokenModal

react/src/components/DeploymentConfigurationSection.tsx

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
@license
33
Copyright (c) 2015-2026 Lablup Inc. All rights reserved.
44
*/
5+
import { DeploymentConfigurationSectionRefetchQuery } from '../__generated__/DeploymentConfigurationSectionRefetchQuery.graphql';
56
import { DeploymentConfigurationSection_deployment$key } from '../__generated__/DeploymentConfigurationSection_deployment.graphql';
67
import { useWebUINavigate } from '../hooks';
78
import { CheckOutlined, CloseOutlined, EditOutlined } from '@ant-design/icons';
@@ -11,28 +12,36 @@ import {
1112
filterOutEmpty,
1213
BAIButton,
1314
BAICard,
15+
BAIFetchKeyButton,
1416
BAIFlex,
1517
toLocalId,
1618
} from 'backend.ai-ui';
19+
import { useTransition } from 'react';
1720
import { useTranslation } from 'react-i18next';
18-
import { graphql, useFragment } from 'react-relay';
21+
import { graphql, useRefetchableFragment } from 'react-relay';
1922

2023
interface DeploymentConfigurationSectionProps {
2124
deploymentFrgmt: DeploymentConfigurationSection_deployment$key;
25+
isDeploymentDestroying?: boolean;
2226
}
2327

2428
const DeploymentConfigurationSection: React.FC<
2529
DeploymentConfigurationSectionProps
26-
> = ({ deploymentFrgmt }) => {
30+
> = ({ deploymentFrgmt, isDeploymentDestroying = false }) => {
2731
'use memo';
2832

2933
const { t } = useTranslation();
3034
const { token } = theme.useToken();
3135
const webuiNavigate = useWebUINavigate();
36+
const [isPendingRefetch, startRefetchTransition] = useTransition();
3237

33-
const deployment = useFragment(
38+
const [deployment, refetch] = useRefetchableFragment<
39+
DeploymentConfigurationSectionRefetchQuery,
40+
DeploymentConfigurationSection_deployment$key
41+
>(
3442
graphql`
35-
fragment DeploymentConfigurationSection_deployment on ModelDeployment {
43+
fragment DeploymentConfigurationSection_deployment on ModelDeployment
44+
@refetchable(queryName: "DeploymentConfigurationSectionRefetchQuery") {
3645
id
3746
metadata {
3847
name
@@ -215,7 +224,7 @@ const DeploymentConfigurationSection: React.FC<
215224
},
216225
{
217226
key: 'environ',
218-
label: t('deployment.EnvironmentVariables'),
227+
label: t('deployment.Environ'),
219228
children:
220229
environEntries.length > 0 ? (
221230
<BAIFlex direction="column" align="start">
@@ -232,19 +241,33 @@ const DeploymentConfigurationSection: React.FC<
232241
},
233242
]);
234243

244+
const handleRefetch = () => {
245+
startRefetchTransition(() => {
246+
refetch({}, { fetchPolicy: 'network-only' });
247+
});
248+
};
249+
235250
return (
236251
<BAICard
237252
title={t('deployment.Configuration')}
238253
extra={
239-
<BAIButton
240-
type="primary"
241-
icon={<EditOutlined />}
242-
onClick={() => {
243-
webuiNavigate(`/deployments/${deploymentId}/edit`);
244-
}}
245-
>
246-
{t('deployment.EditConfiguration')}
247-
</BAIButton>
254+
<BAIFlex gap="xs" align="center">
255+
<BAIFetchKeyButton
256+
loading={isPendingRefetch}
257+
value=""
258+
onChange={handleRefetch}
259+
/>
260+
<BAIButton
261+
type="primary"
262+
icon={<EditOutlined />}
263+
disabled={isDeploymentDestroying}
264+
onClick={() => {
265+
webuiNavigate(`/deployments/${deploymentId}/edit`);
266+
}}
267+
>
268+
{t('deployment.EditConfiguration')}
269+
</BAIButton>
270+
</BAIFlex>
248271
}
249272
>
250273
<Descriptions

react/src/components/DeploymentReplicasTab.tsx

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,10 @@ import {
1313
BAIFetchKeyButton,
1414
BAIFlex,
1515
BAIId,
16+
BAINameActionCell,
1617
BAITable,
1718
filterOutEmpty,
19+
toLocalId,
1820
} from 'backend.ai-ui';
1921
import dayjs from 'dayjs';
2022
import {
@@ -163,7 +165,14 @@ const DeploymentReplicasTab: React.FC<DeploymentReplicasTabProps> = ({
163165
key: 'id',
164166
title: t('deployment.ReplicaId'),
165167
dataIndex: 'id',
166-
render: (value: string) => <BAIId globalId={value} />,
168+
fixed: 'left',
169+
render: (value: string, record: ReplicaNode) => (
170+
<BAINameActionCell
171+
title={toLocalId(value)}
172+
showActions="always"
173+
onTitleClick={() => setQueryParams({ selected: record.id })}
174+
/>
175+
),
167176
},
168177
{
169178
key: 'livenessStatus',
@@ -325,10 +334,6 @@ const DeploymentReplicasTab: React.FC<DeploymentReplicasTabProps> = ({
325334
loading={isPendingRefetch}
326335
size="small"
327336
scroll={{ x: 'max-content' }}
328-
onRow={(record) => ({
329-
onClick: () => setQueryParams({ selected: record.id }),
330-
style: { cursor: 'pointer' },
331-
})}
332337
pagination={{
333338
pageSize: queryParams.pageSize,
334339
current: queryParams.current,

react/src/components/DeploymentRevisionHistoryTab.tsx

Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@ import { convertToOrderBy } from '../helper';
1212
import { UndoOutlined } from '@ant-design/icons';
1313
import { App, Tag, Typography, theme } from 'antd';
1414
import {
15-
BAIButton,
1615
BAIFetchKeyButton,
1716
BAIFlex,
17+
BAINameActionCell,
1818
BAITable,
1919
filterOutNullAndUndefined,
2020
toLocalId,
@@ -45,6 +45,7 @@ type RevisionNode = NonNullable<
4545

4646
export interface DeploymentRevisionHistoryTabProps {
4747
deploymentFrgmt: DeploymentRevisionHistoryTab_deployment$key;
48+
isDeploymentDestroying?: boolean;
4849
}
4950

5051
/**
@@ -59,7 +60,7 @@ export interface DeploymentRevisionHistoryTabProps {
5960
*/
6061
const DeploymentRevisionHistoryTab: React.FC<
6162
DeploymentRevisionHistoryTabProps
62-
> = ({ deploymentFrgmt }) => {
63+
> = ({ deploymentFrgmt, isDeploymentDestroying = false }) => {
6364
'use memo';
6465
const { t } = useTranslation();
6566
const { token } = theme.useToken();
@@ -249,17 +250,37 @@ const DeploymentRevisionHistoryTab: React.FC<
249250
title: t('deployment.RevisionNumber'),
250251
dataIndex: 'name',
251252
key: 'name',
253+
fixed: 'left',
252254
render: (_value, record) => {
253255
const isCurrent = record.id === deployment.currentRevisionId;
254256
return (
255-
<BAIFlex gap="xs" align="center">
256-
<Typography.Text strong={isCurrent}>
257-
{record.name ?? '-'}
258-
</Typography.Text>
259-
{isCurrent ? (
260-
<Tag color={token.colorPrimary}>{t('deployment.Current')}</Tag>
261-
) : null}
262-
</BAIFlex>
257+
<BAINameActionCell
258+
title={
259+
<BAIFlex gap="xs" align="center">
260+
<Typography.Text strong={isCurrent}>
261+
{record.name ?? '-'}
262+
</Typography.Text>
263+
{isCurrent ? (
264+
<Tag color={token.colorPrimary}>
265+
{t('deployment.Current')}
266+
</Tag>
267+
) : null}
268+
</BAIFlex>
269+
}
270+
showActions="always"
271+
actions={[
272+
{
273+
key: 'rollback',
274+
title: t('deployment.Rollback'),
275+
icon: <UndoOutlined />,
276+
disabled:
277+
isCurrent ||
278+
isDeploymentDestroying ||
279+
rollingBackRevisionId === record.id,
280+
onClick: () => handleRollback(record),
281+
},
282+
]}
283+
/>
263284
);
264285
},
265286
},
@@ -321,25 +342,6 @@ const DeploymentRevisionHistoryTab: React.FC<
321342
return mode ? `${size} (${mode})` : `${size}`;
322343
},
323344
},
324-
{
325-
title: t('general.Actions'),
326-
key: 'actions',
327-
fixed: 'right',
328-
render: (_value, record) => {
329-
const isCurrent = record.id === deployment.currentRevisionId;
330-
return (
331-
<BAIButton
332-
size="small"
333-
icon={<UndoOutlined />}
334-
disabled={isCurrent}
335-
loading={rollingBackRevisionId === record.id}
336-
onClick={() => handleRollback(record)}
337-
>
338-
{t('deployment.Rollback')}
339-
</BAIButton>
340-
);
341-
},
342-
},
343345
];
344346

345347
return (

0 commit comments

Comments
 (0)