type DataExplorerProps<T> = DataExplorerDataProps<T> & DataExplorerActionProps<T> & WithStyles<CssRules> & MPVPanelProps;
+type DataExplorerState = {
+ prevRefresh: string;
+ prevRoute: string;
+ notFound: boolean;
+};
+
export const DataExplorer = withStyles(styles)(
class DataExplorerGeneric<T> extends React.Component<DataExplorerProps<T>> {
- state = {
- showLoading: false,
+ state: DataExplorerState = {
prevRefresh: "",
prevRoute: "",
+ notFound: false,
};
multiSelectToolbarInTitle = !this.props.title && !this.props.progressBar;
// Component already mounted, but the user comes from a route change,
// like browsing through a project hierarchy.
this.setState({
- showLoading: this.props.working,
prevRoute: currentRoute,
});
}
// Component already mounted, but the user just clicked the
// refresh button.
this.setState({
- showLoading: this.props.working,
prevRefresh: currentRefresh,
});
}
- if (this.state.showLoading && !this.props.working) {
+
+ if(this.state.notFound === true && this.props.itemsAvailable > 0) {
+ this.setState({
+ notFound: false,
+ });
+ }
+
+ if (prevProps.working === true && this.props.working === false) {
this.setState({
- showLoading: false,
+ notFound: this.props.itemsAvailable === 0,
});
}
}
}
// Component just mounted, so we need to show the loading indicator.
this.setState({
- showLoading: this.props.working,
prevRefresh: this.props.currentRefresh || "",
prevRoute: this.props.currentRoute || "",
});
toggleMSToolbar,
setCheckedListOnStore,
checkedList,
+ working,
} = this.props;
return (
<Paper
onFiltersChange={onFiltersChange}
onSortToggle={onSortToggle}
extractKey={extractKey}
- working={this.state.showLoading}
defaultViewIcon={defaultViewIcon}
defaultViewMessages={defaultViewMessages}
currentItemUuid={currentItemUuid}
toggleMSToolbar={toggleMSToolbar}
setCheckedListOnStore={setCheckedListOnStore}
checkedList={checkedList}
+ working={working}
+ notFound={this.state.notFound}
/>
</Grid>
<Grid
toggleMSToolbar: (isVisible: boolean) => void;
setCheckedListOnStore: (checkedList: TCheckedList) => void;
checkedList: TCheckedList;
+ notFound?: boolean;
}
type CssRules =
};
render() {
- const { items, classes, working, columns } = this.props;
+ const { items, classes, working, columns, notFound } = this.props;
if (columns[0].name === this.checkBoxColumn.name) columns.shift();
columns.unshift(this.checkBoxColumn);
return (
<TableHead>
<TableRow>{this.mapVisibleColumns(this.renderHeadCell)}</TableRow>
</TableHead>
- <TableBody className={classes.tableBody}>{!working && items.map(this.renderBodyRow)}</TableBody>
+ <TableBody className={classes.tableBody}>{(!working && !notFound) && items.map(this.renderBodyRow)}</TableBody>
</Table>
- {items.length === 0 && !working && this.renderNoItemsPlaceholder(this.props.columns)}
+ {(working || notFound) && this.renderNoItemsPlaceholder(this.props.columns)}
</div>
</div>
);
}
renderNoItemsPlaceholder = (columns: DataColumns<T, any>) => {
+ const { working, notFound } = this.props;
const dirty = columns.some(column => getTreeDirty("")(column.filters));
- return (
- <DataTableDefaultView
- icon={this.props.defaultViewIcon}
- messages={this.props.defaultViewMessages}
- filtersApplied={dirty}
- />
- );
+ if (working) {
+ return (
+ <DataTableDefaultView
+ icon={this.props.defaultViewIcon}
+ messages={["Loading data, please wait"]}
+ />
+ );
+ } else if (notFound) {
+ return (
+ <DataTableDefaultView
+ icon={this.props.defaultViewIcon}
+ messages={["Project not found"]}
+ />
+ );
+ } else {
+ return (
+ <DataTableDefaultView
+ icon={this.props.defaultViewIcon}
+ messages={this.props.defaultViewMessages}
+ filtersApplied={dirty}
+ />
+ );
+ }
};
renderHeadCell = (column: DataColumn<T, any>, index: number) => {
savedQueries: SearchBarAdvancedFormData[];
recentQueries: string[];
selectedItem: SearchBarSelectedItem;
- isSearching: boolean;
}
export enum SearchView {
id: '',
query: ''
},
- isSearching: false,
};
const makeSelectedItem = (id: string, query?: string): SearchBarSelectedItem => ({ id, query: query ? query : id });
selectedItem
};
},
- SET_IS_SEARCHING: isSearching => ({ ...state, isSearching }),
default: () => state
});
getSearchSessions,
queryToFilters,
getAdvancedDataFromQuery,
- searchBarActions
} from 'store/search-bar/search-bar-actions';
import { getSortColumn } from "store/data-explorer/data-explorer-reducer";
import { FilterBuilder, joinFilters } from 'services/api/filter-builder';
import { ProjectPanelColumnNames } from 'views/project-panel/project-panel';
import { ResourceKind } from 'models/resource';
import { ContainerRequestResource } from 'models/container-request';
+import { progressIndicatorActions } from 'store/progress-indicator/progress-indicator-actions';
export class SearchResultsMiddlewareService extends DataExplorerMiddlewareService {
constructor(private services: ServiceRepository, id: string) {
const numberOfSessions = sessions.length;
let numberOfResolvedResponses = 0;
- api.dispatch(searchBarActions.SET_IS_SEARCHING(true));
+ api.dispatch(progressIndicatorActions.START_WORKING(this.id))
sessions.forEach(session => {
const params = getParams(dataExplorer, searchValue, session.apiRevision);
api.dispatch(updateResources(response.items));
api.dispatch(appendItems(response));
numberOfResolvedResponses++;
- if (numberOfResolvedResponses === numberOfSessions) {
- api.dispatch(searchBarActions.SET_IS_SEARCHING(false));
- }
+ if (numberOfResolvedResponses === numberOfSessions) {
+ api.dispatch(progressIndicatorActions.STOP_WORKING(this.id))
+ }
// Request all containers for process status to be available
const containerRequests = response.items.filter((item) => item.kind === ResourceKind.CONTAINER_REQUEST) as ContainerRequestResource[];
const containerUuids = containerRequests.map(container => container.containerUuid).filter(uuid => uuid !== null) as string[];
});
}).catch(() => {
api.dispatch(couldNotFetchSearchResults(session.clusterId));
- api.dispatch(searchBarActions.SET_IS_SEARCHING(false));
+ api.dispatch(progressIndicatorActions.STOP_WORKING(this.id))
});
}
);
onContextMenu?: (event: React.MouseEvent<HTMLElement>, item: any, isAdmin?: boolean) => void;
onRowDoubleClick: (item: any) => void;
extractKey?: (item: any) => React.Key;
+ working?: boolean;
}
const mapStateToProps = ({ progressIndicator, dataExplorer, router, multiselect, detailsPanel, properties}: RootState, { id }: Props) => {
- const progress = progressIndicator.find(p => p.id === id);
const dataExplorerState = getDataExplorer(dataExplorer, id);
const currentRoute = router.location ? router.location.pathname : "";
const currentRefresh = localStorage.getItem(LAST_REFRESH_TIMESTAMP) || "";
const isMSToolbarVisible = multiselect.isVisible;
return {
...dataExplorerState,
- working: !!progress?.working,
currentRefresh: currentRefresh,
currentRoute: currentRoute,
paperKey: currentRoute,
import { CollectionResource } from 'models/collection';
import { resourceIsFrozen } from 'common/frozen-resources';
import { ProjectResource } from 'models/project';
-import { NotFoundView } from 'views/not-found-panel/not-found-panel';
import { deselectAllOthers, toggleOne } from 'store/multiselect/multiselect-actions';
-import { PendingIcon } from 'components/icon/icon';
-import { DataTableDefaultView } from 'components/data-table-default-view/data-table-default-view';
type CssRules = 'root' | 'button' | 'loader' | 'notFoundView';
};
}
-type ProjectPanelState = {
- isLoaded: boolean;
-};
-
export const ProjectPanel = withStyles(styles)(
connect(mapStateToProps)(
class extends React.Component<ProjectPanelProps> {
- state: ProjectPanelState ={
- isLoaded: false,
- }
-
- componentDidMount(): void {
- this.setState({ isLoaded: false });
- }
-
- componentDidUpdate( prevProps: Readonly<ProjectPanelProps>, prevState: Readonly<{}>, snapshot?: any ): void {
- if(prevProps.working === true && this.props.working === false) {
- this.setState({ isLoaded: true });
- }
- }
-
render() {
const { classes } = this.props;
-
- return this.props.project ?
- <div data-cy='project-panel' className={classes.root}>
- <DataExplorer
- id={PROJECT_PANEL_ID}
- onRowClick={this.handleRowClick}
- onRowDoubleClick={this.handleRowDoubleClick}
- onContextMenu={this.handleContextMenu}
- contextMenuColumn={true}
- defaultViewIcon={ProjectIcon}
- defaultViewMessages={DEFAULT_VIEW_MESSAGES}
- />
- </div>
- : this.state.isLoaded ?
- <div className={classes.notFoundView}>
- <NotFoundView
- icon={ProjectIcon}
- messages={["Project not found"]}
- />
- </div>
- :
- <div className={classes.loader}>
- <DataTableDefaultView
- icon={PendingIcon}
- messages={["Loading data, please wait."]}
- />
- </div>
+ return <div data-cy='project-panel' className={classes.root}>
+ <DataExplorer
+ id={PROJECT_PANEL_ID}
+ onRowClick={this.handleRowClick}
+ onRowDoubleClick={this.handleRowDoubleClick}
+ onContextMenu={this.handleContextMenu}
+ contextMenuColumn={true}
+ defaultViewIcon={ProjectIcon}
+ defaultViewMessages={DEFAULT_VIEW_MESSAGES}
+ working={this.props.working}
+ />
+ </div>
}
isCurrentItemChild = (resource: Resource) => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [selectedItem]);
- const prevIsSearching = React.useRef<boolean>(props.isSearching);
const [isLoaded, setIsLoaded] = React.useState<boolean>(false);
- useEffect(() => {
- if(prevIsSearching.current === true && props.isSearching === false) {
- setIsLoaded(true);
- props.stopSpinner();
- }
- if(props.isSearching === true) {
- setIsLoaded(false);
- props.startSpinner();
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [props.isSearching]);
-
const onItemClick = useCallback((uuid) => {
setSelectedItem(uuid);
props.onItemClick(uuid);
remoteHostsConfig: { [key: string]: Config };
localCluster: string;
numberOfItems: number;
- isSearching: boolean;
}
export interface SearchResultsPanelActionProps {
export type SearchResultsPanelProps = SearchResultsPanelDataProps & SearchResultsPanelActionProps;
const mapStateToProps = (rootState: RootState) => {
- const { dataExplorer, searchBar } = rootState;
+ const { dataExplorer } = rootState;
const numberOfItems = dataExplorer[SEARCH_RESULTS_PANEL_ID].items.length;
return {
user: rootState.auth.user,
remoteHostsConfig: rootState.auth.remoteHostsConfig,
localCluster: rootState.auth.localCluster,
numberOfItems,
- isSearching: searchBar.isSearching,
};
};