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: project domain default page #352

Merged
merged 3 commits into from
Apr 5, 2022
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
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@
"@commitlint/cli": "^8.3.5",
"@commitlint/config-conventional": "^8.3.4",
"@date-io/moment": "1.3.9",
"@flyteorg/flyteidl": "0.23.1",
"@flyteorg/flyteidl": "0.24.11",
"@material-ui/core": "^4.0.0",
"@material-ui/icons": "^4.0.0",
"@material-ui/pickers": "^3.2.2",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,11 +92,11 @@ export const ExecutionDetailsAppBarContent: React.FC<{
const onCloseRelaunch = () => setShowRelaunchForm(false);
const fromExecutionNav = new URLSearchParams(history.location.search).get('fromExecutionNav');
const backLink = fromExecutionNav
? Routes.ProjectDetails.sections.executions.makeUrl(project, domain)
? Routes.ProjectDetails.sections.dashboard.makeUrl(project, domain)
: originalBackLink;
const {
recoverExecution,
recoverState: { isLoading: recovering, error, data: recoveredId },
recoverState: { isLoading: recovering, data: recoveredId },
} = useRecoverExecutionState();

React.useEffect(() => {
Expand Down
26 changes: 13 additions & 13 deletions src/components/Navigation/ProjectNavigation.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { SvgIconProps } from '@material-ui/core/SvgIcon';
import ChevronRight from '@material-ui/icons/ChevronRight';
import DeviceHub from '@material-ui/icons/DeviceHub';
import LinearScale from '@material-ui/icons/LinearScale';
import TrendingFlat from '@material-ui/icons/TrendingFlat';
import Dashboard from '@material-ui/icons/Dashboard';
import classnames from 'classnames';
import { useCommonStyles } from 'components/common/styles';
import { withRouteParams } from 'components/common/withRouteParams';
Expand Down Expand Up @@ -72,46 +72,46 @@ const ProjectNavigationImpl: React.FC<ProjectNavigationRouteParams> = ({

const routes: ProjectRoute[] = [
{
icon: DeviceHub,
icon: Dashboard,
isActive: (match, location) => {
const finalMatch = match
? match
: matchPath(location.pathname, {
path: Routes.WorkflowDetails.path,
path: Routes.ProjectDashboard.path,
exact: false,
});
return !!finalMatch;
},
path: Routes.ProjectDetails.sections.workflows.makeUrl(project.value.id, domainId),
text: 'Workflows',
path: Routes.ProjectDetails.sections.dashboard.makeUrl(project.value.id, domainId),
text: 'Project Dashboard',
},
{
icon: LinearScale,
icon: DeviceHub,
isActive: (match, location) => {
const finalMatch = match
? match
: matchPath(location.pathname, {
path: Routes.TaskDetails.path,
path: Routes.WorkflowDetails.path,
exact: false,
});
return !!finalMatch;
},
path: Routes.ProjectDetails.sections.tasks.makeUrl(project.value.id, domainId),
text: 'Tasks',
path: Routes.ProjectDetails.sections.workflows.makeUrl(project.value.id, domainId),
text: 'Workflows',
},
{
icon: TrendingFlat,
icon: LinearScale,
isActive: (match, location) => {
const finalMatch = match
? match
: matchPath(location.pathname, {
path: Routes.ProjectExecutions.path,
path: Routes.TaskDetails.path,
exact: false,
});
return !!finalMatch;
},
path: Routes.ProjectDetails.sections.executions.makeUrl(project.value.id, domainId),
text: 'Executions',
path: Routes.ProjectDetails.sections.tasks.makeUrl(project.value.id, domainId),
text: 'Tasks',
},
];

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import Typography from '@material-ui/core/Typography';
import { makeStyles, Theme } from '@material-ui/core/styles';
import * as React from 'react';
import { Typography } from '@material-ui/core';
import { useTaskNameList, useWorkflowNameList } from 'components/hooks/useNamedEntity';
import { useWorkflowExecutions } from 'components/hooks/useWorkflowExecutions';
import { WaitForQuery } from 'components/common/WaitForQuery';
import { useInfiniteQuery, useQuery, useQueryClient } from 'react-query';
import { Admin } from 'flyteidl';
import { DomainSettingsSection } from 'components/common/DomainSettingsSection';
import { getCacheKey } from 'components/Cache/utils';
import { ErrorBoundary } from 'components/common/ErrorBoundary';
import { LargeLoadingSpinner } from 'components/common/LoadingSpinner';
Expand All @@ -11,24 +18,30 @@ import { makeWorkflowExecutionListQuery } from 'components/Executions/workflowEx
import { SortDirection } from 'models/AdminEntity/types';
import { executionSortFields } from 'models/Execution/constants';
import { Execution } from 'models/Execution/types';
import * as React from 'react';
import { useInfiniteQuery } from 'react-query';
import { BarChart } from 'components/common/BarChart';
import {
getExecutionTimeData,
getStartExecutionTime,
} from 'components/Entities/EntityExecutionsBarChart';
import classNames from 'classnames';
import { useWorkflowExecutions } from 'components/hooks/useWorkflowExecutions';
import { useExecutionShowArchivedState } from 'components/Executions/filters/useExecutionArchiveState';
import { useOnlyMyExecutionsFilterState } from 'components/Executions/filters/useOnlyMyExecutionsFilterState';
import { WaitForData } from 'components/common/WaitForData';
import { history } from 'routes/history';
import { Routes } from 'routes/routes';
import { compact } from 'lodash';
import { getProjectDomainAttributes } from 'models/Project/api';
import t from './strings';
import { failedToLoadExecutionsString } from './constants';

const useStyles = makeStyles((theme: Theme) => ({
projectStats: {
paddingTop: theme.spacing(7),
paddingBottom: theme.spacing(7),
display: 'flex',
justifyContent: 'space-evenly',
alignItems: 'center',
},
container: {
display: 'flex',
flex: '1 1 auto',
Expand All @@ -48,7 +61,8 @@ const useStyles = makeStyles((theme: Theme) => ({
paddingTop: theme.spacing(1),
},
}));
export interface ProjectExecutionsProps {

export interface ProjectDashboardProps {
projectId: string;
domainId: string;
}
Expand All @@ -58,8 +72,7 @@ const defaultSort = {
direction: SortDirection.DESCENDING,
};

/** A listing of all executions across a project/domain combination. */
export const ProjectExecutions: React.FC<ProjectExecutionsProps> = ({
export const ProjectDashboard: React.FC<ProjectDashboardProps> = ({
domainId: domain,
projectId: project,
}) => {
Expand Down Expand Up @@ -90,18 +103,18 @@ export const ProjectExecutions: React.FC<ProjectExecutionsProps> = ({
[domain, project, allFilters],
);

const query = useInfiniteQuery({
const executionsQuery = useInfiniteQuery({
...makeWorkflowExecutionListQuery({ domain, project }, config),
});

// useInfiniteQuery returns pages of items, but the table would like a single
// flat list.
const executions = React.useMemo(
() =>
query.data?.pages
? query.data.pages.reduce<Execution[]>((acc, { data }) => acc.concat(data), [])
executionsQuery.data?.pages
? executionsQuery.data.pages.reduce<Execution[]>((acc, { data }) => acc.concat(data), [])
: [],
[query.data?.pages],
[executionsQuery.data?.pages],
);

const handleBarChartItemClick = React.useCallback((item) => {
Expand All @@ -118,50 +131,89 @@ export const ProjectExecutions: React.FC<ProjectExecutionsProps> = ({
},
);

const fetch = React.useCallback(() => query.fetchNextPage(), [query]);
const fetch = React.useCallback(() => executionsQuery.fetchNextPage(), [executionsQuery]);

const { value: workflows } = useWorkflowNameList({ domain, project }, {});
const numberOfWorkflows = workflows.length;
const { value: tasks } = useTaskNameList({ domain, project }, {});
const numberOfTasks = tasks.length;

const content = query.isLoadingError ? (
<DataError error={query.error} errorTitle={failedToLoadExecutionsString} retry={fetch} />
) : query.isLoading ? (
const queryClient = useQueryClient();

const projectDomainAttributesQuery = useQuery<Admin.ProjectDomainAttributesGetResponse, Error>({
queryKey: ['projectDomainAttributes', project, domain],
queryFn: async () => {
const projectDomainAtributes = await getProjectDomainAttributes({ domain, project });
queryClient.setQueryData(
['projectDomainAttributes', project, domain],
projectDomainAtributes,
);
return projectDomainAtributes;
},
staleTime: Infinity,
});

const content = executionsQuery.isLoadingError ? (
<DataError
error={executionsQuery.error}
errorTitle={failedToLoadExecutionsString}
retry={fetch}
/>
) : executionsQuery.isLoading ? (
<LargeLoadingSpinner />
) : (
<WorkflowExecutionsTable
key={tableKey}
fetch={fetch}
value={executions}
lastError={query.error}
moreItemsAvailable={!!query.hasNextPage}
lastError={executionsQuery.error}
moreItemsAvailable={!!executionsQuery.hasNextPage}
showWorkflowName={true}
isFetching={query.isFetching}
isFetching={executionsQuery.isFetching}
data-testid="workflow-table"
/>
);

const configData =
projectDomainAttributesQuery.data?.attributes?.matchingAttributes?.workflowExecutionConfig ??
undefined;

const renderDomainSettingsSection = () => <DomainSettingsSection configData={configData} />;

return (
<div className={styles.container}>
<Typography className={classNames(styles.header, styles.marginTop)} variant="h6">
Last 100 Executions in the Project
</Typography>
<div className={styles.chartContainer}>
<WaitForData {...last100Executions}>
<BarChart
chartIds={[]}
data={getExecutionTimeData(last100Executions.value)}
startDate={getStartExecutionTime(last100Executions.value)}
onClickItem={handleBarChartItemClick}
/>
</WaitForData>
<div className={styles.projectStats}>
<Typography variant="h5">{t('workflowsTotal', numberOfWorkflows)}</Typography>
<Typography variant="h5">{t('tasksTotal', numberOfTasks)}</Typography>
</div>
anrusina marked this conversation as resolved.
Show resolved Hide resolved
<WaitForQuery query={projectDomainAttributesQuery}>
{renderDomainSettingsSection}
</WaitForQuery>
<div className={styles.container}>
<Typography className={classNames(styles.header, styles.marginTop)} variant="h6">
{t('last100ExecutionsTitle')}
</Typography>
anrusina marked this conversation as resolved.
Show resolved Hide resolved
<div className={styles.chartContainer}>
<WaitForData {...last100Executions}>
<BarChart
chartIds={[]}
data={getExecutionTimeData(last100Executions.value)}
startDate={getStartExecutionTime(last100Executions.value)}
onClickItem={handleBarChartItemClick}
/>
</WaitForData>
</div>
<Typography className={styles.header} variant="h6">
{t('allExecutionsTitle')}
</Typography>
<ExecutionFilters
{...filtersState}
showArchived={archivedFilter.showArchived}
onArchiveFilterChange={archivedFilter.setShowArchived}
onlyMyExecutionsFilterState={onlyMyExecutionsFilterState}
/>
<ErrorBoundary>{content}</ErrorBoundary>
</div>
<Typography className={styles.header} variant="h6">
All Executions in the Project
</Typography>
<ExecutionFilters
{...filtersState}
showArchived={archivedFilter.showArchived}
onArchiveFilterChange={archivedFilter.setShowArchived}
onlyMyExecutionsFilterState={onlyMyExecutionsFilterState}
/>
<ErrorBoundary>{content}</ErrorBoundary>
</div>
);
};
12 changes: 6 additions & 6 deletions src/components/Project/ProjectDetails.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { Project } from 'models/Project/types';
import * as React from 'react';
import { Redirect, Route, Switch } from 'react-router';
import { Routes } from 'routes/routes';
import { ProjectExecutions } from './ProjectExecutions';
import { ProjectDashboard } from './ProjectDashboard';
import { ProjectTasks } from './ProjectTasks';
import { ProjectWorkflows } from './ProjectWorkflows';

Expand All @@ -27,7 +27,7 @@ export interface ProjectDetailsRouteParams {
export type ProjectDetailsProps = ProjectDetailsRouteParams;

const entityTypeToComponent = {
executions: ProjectExecutions,
executions: ProjectDashboard,
tasks: ProjectTasks,
workflows: ProjectWorkflows,
};
Expand Down Expand Up @@ -59,7 +59,7 @@ const ProjectEntitiesByDomain: React.FC<{
);
};

const ProjectExecutionsByDomain: React.FC<{ project: Project }> = ({ project }) => (
const ProjectDashboardByDomain: React.FC<{ project: Project }> = ({ project }) => (
<ProjectEntitiesByDomain project={project} entityType="executions" />
);

Expand All @@ -79,15 +79,15 @@ export const ProjectDetailsContainer: React.FC<ProjectDetailsRouteParams> = ({ p
{() => {
return (
<Switch>
<Route path={Routes.ProjectDetails.sections.dashboard.path}>
<ProjectDashboardByDomain project={project.value} />
</Route>
<Route path={Routes.ProjectDetails.sections.workflows.path}>
<ProjectWorkflowsByDomain project={project.value} />
</Route>
<Route path={Routes.ProjectDetails.sections.tasks.path}>
<ProjectTasksByDomain project={project.value} />
</Route>
<Route path={Routes.ProjectDetails.sections.executions.path}>
<ProjectExecutionsByDomain project={project.value} />
</Route>
<Redirect to={Routes.ProjectDetails.sections.workflows.makeUrl(projectId)} />
</Switch>
);
Expand Down
11 changes: 11 additions & 0 deletions src/components/Project/strings.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createLocalizedString } from 'basics/Locale';

const str = {
allExecutionsTitle: 'All Executions in the Project',
last100ExecutionsTitle: 'Last 100 Executions in the Project',
tasksTotal: (n: number) => `${n} Tasks`,
workflowsTotal: (n: number) => `${n} Workflows`,
};

export { patternKey } from 'basics/Locale';
anrusina marked this conversation as resolved.
Show resolved Hide resolved
export default createLocalizedString(str);
Loading