Skip to content

Commit

Permalink
feature: introduce BAIFetchKeyButton
Browse files Browse the repository at this point in the history
  • Loading branch information
yomybaby committed Feb 16, 2025
1 parent 1ac91d3 commit 3b234d0
Show file tree
Hide file tree
Showing 25 changed files with 220 additions and 55 deletions.
100 changes: 100 additions & 0 deletions react/src/components/BAIFetchKeyButton.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import useControllableState from '../hooks/useControllableState';
import { useInterval, useIntervalValue } from '../hooks/useIntervalValue';
import { ReloadOutlined } from '@ant-design/icons';
import { Button, ButtonProps, Tooltip } from 'antd';
import dayjs from 'dayjs';
import React, { useEffect, useLayoutEffect, useState } from 'react';
import { useTranslation } from 'react-i18next';

interface BAIAutoRefetchButtonProps {
value: string;
loading?: boolean;
lastLoadTime?: Date;
showLastLoadTime?: boolean;
autoUpdateDelay?: number | null;
size?: ButtonProps['size'];
onChange: (fetchKey: string) => void;
hidden?: boolean;
}
const BAIFetchKeyButton: React.FC<BAIAutoRefetchButtonProps> = ({
value,
loading,
onChange,
showLastLoadTime,
autoUpdateDelay = null,
size,
hidden,
...props
}) => {
const { t } = useTranslation();
const [lastLoadTime, setLastLoadTime] = useControllableState(
{
value: props.lastLoadTime,
},
{
defaultValue: new Date(),
},
);

// display loading icon for at least "some ms" to avoid flickering
const [displayLoading, setDisplayLoading] = useState(false);
useEffect(() => {
if (loading) {
const startTime = Date.now();
setDisplayLoading(true);

return () => {
const elapsedTime = Date.now() - startTime;
const remainingTime = Math.max(700 - elapsedTime, 0);

setTimeout(() => {
setDisplayLoading(false);
}, remainingTime);
};
}
}, [loading]);

const loadTimeMessage = useIntervalValue(
() => {
if (lastLoadTime) {
return `${t('general.LastUpdated')}: ${dayjs(lastLoadTime).fromNow()}`;
}
return '';
},
showLastLoadTime ? 5_000 : null,
lastLoadTime.toISOString(),
);

// remember when loading is done to display when the last fetch was done
useLayoutEffect(() => {
if (!loading) {
setLastLoadTime(new Date());
}
}, [loading, setLastLoadTime]);

useInterval(
() => {
onChange(new Date().toISOString());
},
// only start auto-updating after the previous loading is false(done).
loading ? null : autoUpdateDelay,
);

return hidden ? null : (
<Tooltip
title={showLastLoadTime ? loadTimeMessage : undefined}
placement="topLeft"
>
<Button
loading={displayLoading}
size={size}
icon={<ReloadOutlined />}
onClick={() => {
onChange(new Date().toISOString());
}}
/>
</Tooltip>
);
};

export default BAIFetchKeyButton;
5 changes: 5 additions & 0 deletions react/src/components/BAITable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ const BAITable = <RecordType extends object = any>({
columns,
components,
neoStyle,
loading,
...tableProps
}: BAITableProps<RecordType>) => {
const { styles } = useStyles();
Expand Down Expand Up @@ -174,6 +175,10 @@ const BAITable = <RecordType extends object = any>({
resizable && styles.resizableTable,
neoStyle && styles.neoHeader,
)}
style={{
opacity: loading ? 0.7 : 1,
transition: 'opacity 0.3s ease',
}}
components={
resizable
? _.merge(components || {}, {
Expand Down
2 changes: 1 addition & 1 deletion react/src/hooks/useDeferredQueryParams.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export function useDeferredQueryParams<QPCMap extends QueryParamConfigMap>(

// Update Jotai state
if (updateType === 'replaceIn' || updateType === 'pushIn') {
setLocalQuery((prev) => ({ ...prev, ...newQuery }));
setLocalQuery({ ...localQuery, ...newQuery });
} else {
setLocalQuery(newQuery as DecodedValueMap<QPCMap>);
}
Expand Down
105 changes: 72 additions & 33 deletions react/src/pages/ComputeSessionListPage.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import BAIFetchKeyButton from '../components/BAIFetchKeyButton';
import BAILink from '../components/BAILink';
import BAIPropertyFilter, {
mergeFilterValues,
Expand All @@ -10,18 +11,33 @@ import { useUpdatableState } from '../hooks';
import { useBAIPaginationOptionState } from '../hooks/reactPaginationQueryOptions';
import { useCurrentProjectValue } from '../hooks/useCurrentProject';
import { useDeferredQueryParams } from '../hooks/useDeferredQueryParams';
import { useInterval } from '../hooks/useIntervalValue';
import {
ComputeSessionListPageQuery,
ComputeSessionListPageQuery$data,
ComputeSessionListPageQuery$variables,
} from './__generated__/ComputeSessionListPageQuery.graphql';
import { LoadingOutlined } from '@ant-design/icons';
import { Badge, Button, Card, Radio, Spin, Tabs, theme, Tooltip } from 'antd';
import {
Badge,
Button,
Card,
Radio,
Spin,
Tabs,
theme,
Tooltip,
Typography,
} from 'antd';
import graphql from 'babel-plugin-relay/macro';
import _ from 'lodash';
import { PowerOffIcon } from 'lucide-react';
import { startTransition, useDeferredValue, useRef, useState } from 'react';
import {
startTransition,
useDeferredValue,
useMemo,
useRef,
useState,
} from 'react';
import { useTranslation } from 'react-i18next';
import { useLazyLoadQuery } from 'react-relay';
import { StringParam, useQueryParam, withDefault } from 'use-query-params';
Expand Down Expand Up @@ -80,16 +96,28 @@ const ComputeSessionListPage = () => {

const [fetchKey, updateFetchKey] = useUpdatableState('first');

const queryVariables: ComputeSessionListPageQuery$variables = {
projectId: currentProject.id,
offset: baiPaginationOption.offset,
first: baiPaginationOption.first,
filter: mergeFilterValues([statusFilter, queryParams.filter, typeFilter]),
order: queryParams.order,
runningTypeFilter: 'status != "TERMINATED" & status != "CANCELLED"',
};
const queryVariables: ComputeSessionListPageQuery$variables = useMemo(
() => ({
projectId: currentProject.id,
offset: baiPaginationOption.offset,
first: baiPaginationOption.first,
filter: mergeFilterValues([statusFilter, queryParams.filter, typeFilter]),
order: queryParams.order,
runningTypeFilter: 'status != "TERMINATED" & status != "CANCELLED"',
}),
[
currentProject.id,
baiPaginationOption.offset,
baiPaginationOption.first,
statusFilter,
queryParams.filter,
typeFilter,
queryParams.order,
],
);

const deferredQueryVariables = useDeferredValue(queryVariables);
const deferredFetchKey = useDeferredValue(fetchKey);

const { compute_session_nodes, allRunningSessionForCount } =
useLazyLoadQuery<ComputeSessionListPageQuery>(
Expand Down Expand Up @@ -131,28 +159,36 @@ const ComputeSessionListPage = () => {
deferredQueryVariables,
{
fetchPolicy: 'network-only',
fetchKey,
fetchKey: deferredFetchKey,
},
);

useInterval(() => {
startTransition(() => {
updateFetchKey();
});
}, 15_000);

return (
<>
{/* TODO: add legacy opener */}
{/* <SessionDetailAndContainerLogOpenerForLegacy /> */}
<Card
bordered={false}
title={t('webui.menu.Sessions')}
extra={[
<BAILink to={'/session/start'} key={'start-session'}>
<Button type="primary">{t('session.launcher.Start')}</Button>
</BAILink>,
]}
extra={
<Flex gap={'xs'}>
<BAIFetchKeyButton
loading={
deferredQueryVariables !== queryVariables ||
deferredFetchKey !== fetchKey
}
autoUpdateDelay={15_000}
// showLastLoadTime
value={fetchKey}
onChange={(newFetchKey) => {
updateFetchKey(newFetchKey);
}}
/>
<BAILink to={'/session/start'}>
<Button type="primary">{t('session.launcher.Start')}</Button>
</BAILink>
</Flex>
}
styles={{
header: {
borderBottom: 'none',
Expand Down Expand Up @@ -193,7 +229,11 @@ const ComputeSessionListPage = () => {
{key === 'all' && (
<Badge
count={allRunningSessionForCount?.count}
color={token.colorPrimary}
color={
queryParams.type === key
? token.colorPrimary
: token.colorTextDisabled
}
size="small"
showZero
style={{
Expand All @@ -210,8 +250,8 @@ const ComputeSessionListPage = () => {
)}
/>
<Flex direction="column" align="stretch" gap={'sm'}>
<Flex justify="between">
<Flex gap={'sm'} align="start">
<Flex justify="between" wrap="wrap">
<Flex gap={'sm'} align="start" wrap="wrap">
<Radio.Group
optionType="button"
value={queryParams.statusCategory}
Expand Down Expand Up @@ -272,6 +312,7 @@ const ComputeSessionListPage = () => {
onClickSessionName={(session) => {
setSessionDetailId(session.row_id);
}}
loading={deferredQueryVariables !== queryVariables}
rowSelection={{
type: 'checkbox',
// Preserve selected rows between pages, but clear when filter changes
Expand Down Expand Up @@ -321,13 +362,11 @@ const ComputeSessionListPage = () => {
pageSize: tablePaginationOption.pageSize,
current: tablePaginationOption.current,
total: compute_session_nodes?.count ?? 0,
// showTotal: (total) => {
// return total;
// },
}}
loading={{
spinning: queryVariables !== deferredQueryVariables,
indicator: <LoadingOutlined />,
showTotal: (total) => (
<Typography.Text type="secondary">
{t('general.TotalItems', { total: total })}
</Typography.Text>
),
}}
onChange={({ current, pageSize }, filters, sorter) => {
if (_.isNumber(current) && _.isNumber(pageSize)) {
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,8 @@
"MaxValueNotification": "{{name}} muss maximal {{max}} sein",
"TotalItems": "Insgesamt {{Gesamt}} Artikel",
"ExtendLoginSession": "Eine Anmeldesitzung verlängern",
"Cores": "Kerne"
"Cores": "Kerne",
"LastUpdated": "Zuletzt aktualisiert"
},
"credential": {
"Permission": "Genehmigung",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/el.json
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,8 @@
"MaxValueNotification": "{{name}} πρέπει να είναι το μέγιστο {{max}}",
"TotalItems": "Σύνολο {{total}} στοιχείων",
"ExtendLoginSession": "Επέκταση μιας περιόδου σύνδεσης",
"Cores": "πυρήνες"
"Cores": "πυρήνες",
"LastUpdated": "Τελευταία ενημέρωση"
},
"credential": {
"Permission": "Αδεια",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,8 @@
"MaxValueNotification": "{{name}} must be maximum {{max}}",
"TotalItems": "Total {{total}} items",
"ExtendLoginSession": "Extend login session",
"Cores": "cores"
"Cores": "cores",
"LastUpdated": "Last Updated"
},
"credential": {
"Permission": "Permission",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,8 @@
"MaxValueNotification": "{{name}} debe ser máximo {{max}}",
"TotalItems": "Total {{total}} artículos",
"ExtendLoginSession": "Prolongar una sesión de inicio de sesión",
"Cores": "núcleos"
"Cores": "núcleos",
"LastUpdated": "Última actualización"
},
"import": {
"CleanUpImportTask": "Tarea de importación de limpieza...",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/fi.json
Original file line number Diff line number Diff line change
Expand Up @@ -584,7 +584,8 @@
"MaxValueNotification": "{{name}} on oltava maksimi {{max}}",
"TotalItems": "Yhteensä {{total}} kohteita",
"ExtendLoginSession": "Sisäänkirjautumisistunnon laajentaminen",
"Cores": "ytimet"
"Cores": "ytimet",
"LastUpdated": "Viimeksi päivitetty"
},
"import": {
"CleanUpImportTask": "Tuontitehtävän siivous...",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,8 @@
"MaxValueNotification": "{{nom}} doit être maximum {{max}}",
"TotalItems": "Total des éléments {{total}}",
"ExtendLoginSession": "Prolonger une session de connexion",
"Cores": "cœurs"
"Cores": "cœurs",
"LastUpdated": "Dernière mise à jour"
},
"credential": {
"Permission": "Autorisation",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/id.json
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,8 @@
"MaxValueNotification": "{{nama}} harus maksimal {{max}}",
"TotalItems": "Total item {{total}}",
"ExtendLoginSession": "Memperpanjang sesi masuk",
"Cores": "core"
"Cores": "core",
"LastUpdated": "Terakhir diperbarui"
},
"credential": {
"Permission": "Izin",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/it.json
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,8 @@
"MaxValueNotification": "{{nome}} deve essere massimo {{max}}",
"TotalItems": "Totale articoli {{totale}}",
"ExtendLoginSession": "Estendere una sessione di login",
"Cores": "core"
"Cores": "core",
"LastUpdated": "Ultimo aggiornamento"
},
"credential": {
"Permission": "Autorizzazione",
Expand Down
3 changes: 2 additions & 1 deletion resources/i18n/ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,8 @@
"MaxValueNotification": "{{ name }}最大値は{{ max }}です",
"TotalItems": "合計{{total}}項目",
"ExtendLoginSession": "ログインセッションの延長",
"Cores": "コア"
"Cores": "コア",
"LastUpdated": "最終更新日"
},
"credential": {
"Permission": "許可",
Expand Down
Loading

0 comments on commit 3b234d0

Please sign in to comment.