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

VTAdmin(web): Add refresh compatibility to workflow screen (all tabs) #16865

Merged
merged 1 commit into from
Oct 14, 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
4 changes: 3 additions & 1 deletion web/vtadmin/src/components/inputs/Select.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ interface Props<T> {
size?: 'large';
description?: string;
required?: boolean;
disableClearSelection?: boolean;
}

/**
Expand All @@ -60,6 +61,7 @@ export const Select = <T,>({
size,
description,
required,
disableClearSelection,
}: Props<T>) => {
const _itemToString = React.useCallback(
(item: T | null): string => {
Expand Down Expand Up @@ -146,7 +148,7 @@ export const Select = <T,>({
</button>
<div className={style.dropdown} hidden={!isOpen}>
{content}
{selectedItem && (
{selectedItem && !disableClearSelection && (
<button className={style.clear} onClick={() => selectItem(null as any)} type="button">
Clear selection
</button>
Expand Down
80 changes: 63 additions & 17 deletions web/vtadmin/src/components/routes/workflow/Workflow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Link, Redirect, Route, Switch, useLocation, useParams, useRouteMatch } from 'react-router-dom';
import { Link, Redirect, Route, Switch, useParams, useRouteMatch } from 'react-router-dom';
import { useState } from 'react';

import style from './Workflow.module.scss';

Expand All @@ -31,25 +32,48 @@ import { Tab } from '../../tabs/Tab';
import { getStreams } from '../../../util/workflows';
import { Code } from '../../Code';
import { ShardLink } from '../../links/ShardLink';
import { Select } from '../../inputs/Select';
import { formatDateTimeShort } from '../../../util/time';

interface RouteParams {
clusterID: string;
keyspace: string;
name: string;
}

const REFETCH_OPTIONS = [
{
displayText: '10s',
interval: 10 * 1000,
},
{
displayText: '30s',
interval: 30 * 1000,
},
{
displayText: '1m',
interval: 60 * 1000,
},
{
displayText: '10m',
interval: 600 * 1000,
},
{
displayText: 'Never',
interval: 0,
},
];

export const Workflow = () => {
const { clusterID, keyspace, name } = useParams<RouteParams>();
const { path, url } = useRouteMatch();
const location = useLocation();

useDocumentTitle(`${name} (${keyspace})`);

const { data } = useWorkflow({ clusterID, keyspace, name });
const streams = getStreams(data);
const [refetchInterval, setRefetchInterval] = useState(60 * 1000);

const detailsURL = `${url}/details`;
const detailsTab = location.pathname === detailsURL;
const { data, ...workflowQuery } = useWorkflow({ clusterID, keyspace, name }, { refetchInterval });
const streams = getStreams(data);

let isReshard = false;
if (data && data.workflow) {
Expand All @@ -59,11 +83,33 @@ export const Workflow = () => {
return (
<div>
<WorkspaceHeader>
<NavCrumbs>
<Link to="/workflows">Workflows</Link>
</NavCrumbs>
<div className="w-full flex flex-row justify-between">
<div>
<NavCrumbs>
<Link to="/workflows">Workflows</Link>
</NavCrumbs>
<WorkspaceTitle className="font-mono">{name}</WorkspaceTitle>
</div>

<WorkspaceTitle className="font-mono">{name}</WorkspaceTitle>
<div className="float-right">
<Select
className="block w-full"
inputClassName="block w-full"
itemToString={(option) => option?.displayText || ''}
items={REFETCH_OPTIONS}
label="Refresh Interval"
onChange={(option) => setRefetchInterval(option?.interval || 0)}
renderItem={(option) => option?.displayText || ''}
placeholder={'Select Interval'}
helpText={'Automatically refreshes workflow status after selected intervals'}
selectedItem={REFETCH_OPTIONS.find((option) => option.interval === refetchInterval)}
disableClearSelection
/>
<div className="text-sm mt-2">{`Last updated: ${formatDateTimeShort(
workflowQuery.dataUpdatedAt / 1000
)}`}</div>
</div>
</div>
{isReshard && (
<div className={style.headingMetaContainer}>
<div className={style.headingMeta}>
Expand Down Expand Up @@ -112,18 +158,13 @@ export const Workflow = () => {
</KeyspaceLink>
</span>
</div>
{detailsTab && (
<div style={{ float: 'right' }}>
<a href={`#workflowStreams`}>Scroll To Streams</a>
</div>
)}
</div>
</WorkspaceHeader>

<ContentContainer>
<TabContainer>
<Tab text="Streams" to={`${url}/streams`} count={streams.length} />
<Tab text="Details" to={detailsURL} />
<Tab text="Details" to={`${url}/details`} />
<Tab text="JSON" to={`${url}/json`} />
</TabContainer>

Expand All @@ -133,7 +174,12 @@ export const Workflow = () => {
</Route>

<Route path={`${path}/details`}>
<WorkflowDetails clusterID={clusterID} keyspace={keyspace} name={name} />
<WorkflowDetails
clusterID={clusterID}
keyspace={keyspace}
name={name}
refetchInterval={refetchInterval}
/>
</Route>

<Route path={`${path}/json`}>
Expand Down
20 changes: 13 additions & 7 deletions web/vtadmin/src/components/routes/workflow/WorkflowDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ interface Props {
clusterID: string;
keyspace: string;
name: string;
refetchInterval: number;
}

const SUMMARY_COLUMNS = ['Stream Status', 'Traffic Status', 'Max VReplication Lag', 'Reverse Workflow'];
Expand All @@ -53,16 +54,21 @@ const TABLE_COPY_STATE_COLUMNS = ['Table Name', 'Total Bytes', 'Bytes Copied', '

const STREAM_COLUMNS = ['Stream', 'Source Shard', 'Target Shard', 'Message', 'Transaction Timestamp', 'Database Name'];

export const WorkflowDetails = ({ clusterID, keyspace, name }: Props) => {
export const WorkflowDetails = ({ clusterID, keyspace, name, refetchInterval }: Props) => {
const { data: workflowData } = useWorkflow({ clusterID, keyspace, name });

const { data: workflowsData = [] } = useWorkflows();
const { data: workflowsData = [] } = useWorkflows({ refetchInterval });

const { data: workflowStatus } = useWorkflowStatus({
clusterID,
keyspace,
name,
});
const { data: workflowStatus } = useWorkflowStatus(
{
clusterID,
keyspace,
name,
},
{
refetchInterval,
}
);

const reverseWorkflow = getReverseWorkflow(workflowsData, workflowData);

Expand Down
2 changes: 1 addition & 1 deletion web/vtadmin/src/hooks/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -472,7 +472,7 @@ export const useWorkflowStatus = (
params: Parameters<typeof fetchWorkflowStatus>[0],
options?: UseQueryOptions<vtctldata.WorkflowStatusResponse, Error> | undefined
) => {
return useQuery(['workflow_status', params], () => fetchWorkflowStatus(params));
return useQuery(['workflow_status', params], () => fetchWorkflowStatus(params), options);
};

/**
Expand Down
Loading