This is an automated email from the ASF dual-hosted git repository. marat pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/camel-karavan.git
commit f1382d702f6ae8b9cd15f5c7478d3bcd3fb1b4a4 Author: Marat Gubaidullin <marat.gubaidul...@gmail.com> AuthorDate: Fri Nov 18 17:05:42 2022 -0500 Align Look and feel --- .../src/main/webui/src/dashboard/DashboardPage.tsx | 35 +++- .../src/main/webui/src/designer/karavan.css | 1 + karavan-app/src/main/webui/src/index.css | 14 +- .../src/main/webui/src/projects/ProjectPage.tsx | 4 +- .../src/main/webui/src/projects/ProjectsPage.tsx | 181 ++++++++++++--------- karavan-designer/src/designer/karavan.css | 1 + 6 files changed, 135 insertions(+), 101 deletions(-) diff --git a/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx b/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx index 8f5fe20..e1e5ced 100644 --- a/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx +++ b/karavan-app/src/main/webui/src/dashboard/DashboardPage.tsx @@ -1,13 +1,13 @@ import React from 'react'; import { - Badge, - Button, + Badge, Bullseye, + Button, EmptyState, EmptyStateIcon, EmptyStateVariant, Flex, FlexItem, HelperText, HelperTextItem, Label, LabelGroup, - PageSection, + PageSection, Spinner, Text, TextContent, - TextInput, ToggleGroup, ToggleGroupItem, + TextInput, Title, ToggleGroup, ToggleGroupItem, Toolbar, ToolbarContent, ToolbarItem, Tooltip @@ -22,6 +22,7 @@ import Icon from "../Logo"; import UpIcon from "@patternfly/react-icons/dist/esm/icons/check-circle-icon"; import DownIcon from "@patternfly/react-icons/dist/esm/icons/error-circle-o-icon"; import RefreshIcon from "@patternfly/react-icons/dist/esm/icons/sync-alt-icon"; +import SearchIcon from "@patternfly/react-icons/dist/esm/icons/search-icon"; interface Props { config: any, @@ -37,6 +38,7 @@ interface State { isCreateModalOpen: boolean, isDeleteModalOpen: boolean, isCopy: boolean, + loading: boolean, projectToCopy?: Project, projectToDelete?: Project, filter: string, @@ -56,6 +58,7 @@ export class DashboardPage extends React.Component<Props, State> { isCreateModalOpen: false, isDeleteModalOpen: false, isCopy: false, + loading: true, filter: '', name: '', description: '', @@ -75,7 +78,7 @@ export class DashboardPage extends React.Component<Props, State> { onGetProjects = () => { KaravanApi.getConfiguration((config: any) => { KaravanApi.getProjects((projects: Project[]) => { - this.setState({projects: projects}) + this.setState({projects: projects, loading: false}) }); KaravanApi.getAllDeploymentStatuses((statuses: DeploymentStatus[]) => { this.setState({deploymentStatuses: statuses}); @@ -223,6 +226,27 @@ export class DashboardPage extends React.Component<Props, State> { } } + getEmptyState() { + const {loading} = this.state; + return ( + <Tr> + <Td colSpan={8}> + <Bullseye> + {loading && <Spinner className="progress-stepper" isSVG diameter="80px" aria-label="Loading..."/>} + {!loading && + <EmptyState variant={EmptyStateVariant.small}> + <EmptyStateIcon icon={SearchIcon}/> + <Title headingLevel="h2" size="lg"> + No results found + </Title> + </EmptyState> + } + </Bullseye> + </Td> + </Tr> + ) + } + render() { const deployments = Array.from(new Set(this.state.deploymentStatuses.filter(d => d.name.toLowerCase().includes(this.state.filter)).map(d => d.name))); return ( @@ -315,6 +339,7 @@ export class DashboardPage extends React.Component<Props, State> { </Td> </Tr> ))} + {deployments.length === 0 && this.getEmptyState()} </Tbody> </TableComposable> </PageSection> diff --git a/karavan-app/src/main/webui/src/designer/karavan.css b/karavan-app/src/main/webui/src/designer/karavan.css index 38e2973..77ff41e 100644 --- a/karavan-app/src/main/webui/src/designer/karavan.css +++ b/karavan-app/src/main/webui/src/designer/karavan.css @@ -1274,6 +1274,7 @@ overflow: auto; flex-shrink: unset; flex-grow: 1; + background-color: var(--pf-global--BackgroundColor--light-300); } .karavan .designer-page { diff --git a/karavan-app/src/main/webui/src/index.css b/karavan-app/src/main/webui/src/index.css index b19e0f5..efa6238 100644 --- a/karavan-app/src/main/webui/src/index.css +++ b/karavan-app/src/main/webui/src/index.css @@ -68,18 +68,6 @@ background-color: var(--pf-global--BackgroundColor--dark-400); } -.karavan .kamelet-section { - display: flex; - flex-direction: column; - height: 100%; -} - -.karavan .kamelets-page { - overflow: auto; - flex-shrink: unset; - flex-grow: 1; -} - .karavan .integration-card .pf-c-card__footer { text-align: end; } @@ -128,7 +116,7 @@ font-size: 14px; } -.karavan .project-page .project-page-section { +.karavan .project-page .project-tabs { background-color: white; } diff --git a/karavan-app/src/main/webui/src/projects/ProjectPage.tsx b/karavan-app/src/main/webui/src/projects/ProjectPage.tsx index e09b981..e1edd56 100644 --- a/karavan-app/src/main/webui/src/projects/ProjectPage.tsx +++ b/karavan-app/src/main/webui/src/projects/ProjectPage.tsx @@ -420,7 +420,7 @@ export class ProjectPage extends React.Component<Props, State> { padding={{default: file !== undefined ? 'noPadding' : 'noPadding'}}> <Flex direction={{default: "column"}} spaceItems={{default: "spaceItemsNone"}}> {!isTemplates && - <FlexItem> + <FlexItem className="project-tabs"> <Tabs activeKey={tab} onSelect={(event, tabIndex) => this.setState({tab: tabIndex})}> <Tab eventKey="development" title="Development"/> <Tab eventKey="operations" title="Operations"/> @@ -431,12 +431,12 @@ export class ProjectPage extends React.Component<Props, State> { <FlexItem> <PageSection padding={{default: "padding"}}> {tab === 'development' && <ProjectInfo project={this.props.project} config={this.props.config} deleteEntity={this.deleteEntity} showLog={this.showLogs}/>} + {tab === 'development' && this.getProjectFiles()} {tab === 'operations' && <ProjectOperations environments={this.state.environments} project={this.props.project} config={this.props.config}/>} </PageSection> </FlexItem> } </Flex> - {tab === 'development' && this.getProjectFiles()} </PageSection>} {showDesigner && this.getDesigner()} {showEditor && this.getEditor()} diff --git a/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx b/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx index 32a7a4d..320be76 100644 --- a/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx +++ b/karavan-app/src/main/webui/src/projects/ProjectsPage.tsx @@ -23,7 +23,7 @@ import { OverflowMenuContent, OverflowMenuGroup, OverflowMenuItem, - Flex, FlexItem, Radio + Flex, FlexItem, Radio, Spinner } from '@patternfly/react-core'; import '../designer/karavan.css'; import {MainToolbar} from "../MainToolbar"; @@ -51,6 +51,7 @@ interface State { isCreateModalOpen: boolean, isDeleteModalOpen: boolean, isCopy: boolean, + loading: boolean, projectToCopy?: Project, projectToDelete?: Project, filter: string, @@ -68,6 +69,7 @@ export class ProjectsPage extends React.Component<Props, State> { isCreateModalOpen: false, isDeleteModalOpen: false, isCopy: false, + loading: true, filter: '', name: '', description: '', @@ -114,8 +116,9 @@ export class ProjectsPage extends React.Component<Props, State> { }; onGetProjects = () => { + this.setState({loading: true}); KaravanApi.getProjects((projects: Project[]) => { - this.setState({projects: projects}) + this.setState({projects: projects, loading: false}) }); KaravanApi.getDeploymentStatuses(this.props.config.environment, (statuses: DeploymentStatus[]) => { this.setState({deploymentStatuses: statuses}); @@ -144,7 +147,7 @@ export class ProjectsPage extends React.Component<Props, State> { </TextContent>); closeModal = () => { - this.setState({isCreateModalOpen: false, isCopy: false, name: this.props.config.groupId, description: '', projectId: '', runtime: this.props.config.runtime }); + this.setState({isCreateModalOpen: false, isCopy: false, name: this.props.config.groupId, description: '', projectId: '', runtime: this.props.config.runtime}); this.onGetProjects(); } @@ -245,6 +248,99 @@ export class ProjectsPage extends React.Component<Props, State> { }); } + getEmptyState() { + const {loading} = this.state; + return ( + <Tr> + <Td colSpan={8}> + <Bullseye> + {loading && <Spinner className="progress-stepper" isSVG diameter="80px" aria-label="Loading..."/>} + {!loading && + <EmptyState variant={EmptyStateVariant.small}> + <EmptyStateIcon icon={SearchIcon}/> + <Title headingLevel="h2" size="lg"> + No results found + </Title> + </EmptyState> + } + </Bullseye> + </Td> + </Tr> + ) + } + + getProjectsTable() { + const {projects, filter} = this.state; + const projs = projects.filter(p => p.name.toLowerCase().includes(filter) || p.description.toLowerCase().includes(filter)); + return ( + <TableComposable aria-label="Projects" variant={"compact"}> + <Thead> + <Tr> + <Th key='type'>Runtime</Th> + <Th key='projectId'>Project ID</Th> + <Th key='name'>Name</Th> + <Th key='description'>Description</Th> + <Th key='commit'>Commit</Th> + <Th key='deployment'>Environment</Th> + <Th key='action'></Th> + </Tr> + </Thead> + <Tbody> + {projs.map(project => ( + <Tr key={project.projectId}> + <Td modifier={"fitContent"}> + <Tooltip content={project.runtime} position={"left"}> + <Badge className="runtime-badge">{project.runtime.substring(0, 1).toUpperCase()}</Badge> + </Tooltip> + </Td> + <Td> + <Button style={{padding: '6px'}} variant={"link"} onClick={e => this.props.onSelect?.call(this, project)}> + {project.projectId} + </Button> + </Td> + <Td>{project.name}</Td> + <Td>{project.description}</Td> + <Td isActionCell> + <Tooltip content={project.lastCommit} position={"bottom"}> + <Badge>{project.lastCommit?.substr(0, 7)}</Badge> + </Tooltip> + </Td> + <Td noPadding style={{width: "180px"}}> + <Flex direction={{default: "row"}}> + {this.getDeploymentByEnvironments(project.projectId).map(value => ( + <FlexItem className="badge-flex-item" key={value[0]}> + <Badge className="badge" isRead={!value[1]}>{value[0]}</Badge> + </FlexItem> + ))} + </Flex> + </Td> + <Td isActionCell> + <OverflowMenu breakpoint="md"> + <OverflowMenuContent> + <OverflowMenuGroup groupType="button"> + <OverflowMenuItem> + <Tooltip content={"Copy project"} position={"bottom"}> + <Button variant={"plain"} icon={<CopyIcon/>} + onClick={e => this.setState({isCreateModalOpen: true, isCopy: true, projectToCopy: project})}></Button> + </Tooltip> + </OverflowMenuItem> + <OverflowMenuItem> + <Tooltip content={"Delete project"} position={"bottom"}> + <Button variant={"plain"} icon={<DeleteIcon/>} onClick={e => this.onProjectDelete(project)}></Button> + </Tooltip> + </OverflowMenuItem> + </OverflowMenuGroup> + </OverflowMenuContent> + </OverflowMenu> + </Td> + </Tr> + ))} + {projs.length === 0 && this.getEmptyState()} + </Tbody> + </TableComposable> + ) + } + render() { const projects = this.state.projects.filter(p => p.name.toLowerCase().includes(this.state.filter) || p.description.toLowerCase().includes(this.state.filter)); return ( @@ -253,84 +349,7 @@ export class ProjectsPage extends React.Component<Props, State> { <MainToolbar title={this.title()} tools={this.tools()}/> </PageSection> <PageSection isFilled className="kamelets-page"> - <TableComposable aria-label="Projects" variant={"compact"}> - <Thead> - <Tr> - <Th key='type'>Runtime</Th> - <Th key='projectId'>Project ID</Th> - <Th key='name'>Name</Th> - <Th key='description'>Description</Th> - <Th key='commit'>Commit</Th> - <Th key='deployment'>Environment</Th> - <Th key='action'></Th> - </Tr> - </Thead> - <Tbody> - {projects.map(project => ( - <Tr key={project.projectId}> - <Td modifier={"fitContent"}> - <Tooltip content={project.runtime} position={"left"}> - <Badge className="runtime-badge">{project.runtime.substring(0, 1).toUpperCase()}</Badge> - </Tooltip> - </Td> - <Td> - <Button style={{padding: '6px'}} variant={"link"} onClick={e => this.props.onSelect?.call(this, project)}> - {project.projectId} - </Button> - </Td> - <Td>{project.name}</Td> - <Td>{project.description}</Td> - <Td isActionCell> - <Tooltip content={project.lastCommit} position={"bottom"}> - <Badge>{project.lastCommit?.substr(0, 7)}</Badge> - </Tooltip> - </Td> - <Td noPadding style={{width: "180px"}}> - <Flex direction={{default: "row"}}> - {this.getDeploymentByEnvironments(project.projectId).map(value => ( - <FlexItem className="badge-flex-item" key={value[0]}> - <Badge className="badge" isRead={!value[1]}>{value[0]}</Badge> - </FlexItem> - ))} - </Flex> - </Td> - <Td isActionCell> - <OverflowMenu breakpoint="md"> - <OverflowMenuContent> - <OverflowMenuGroup groupType="button"> - <OverflowMenuItem> - <Tooltip content={"Copy project"} position={"bottom"}> - <Button variant={"plain"} icon={<CopyIcon/>} - onClick={e => this.setState({isCreateModalOpen: true, isCopy: true, projectToCopy: project})}></Button> - </Tooltip> - </OverflowMenuItem> - <OverflowMenuItem> - <Tooltip content={"Delete project"} position={"bottom"}> - <Button variant={"plain"} icon={<DeleteIcon/>} onClick={e => this.onProjectDelete(project)}></Button> - </Tooltip> - </OverflowMenuItem> - </OverflowMenuGroup> - </OverflowMenuContent> - </OverflowMenu> - </Td> - </Tr> - ))} - {projects.length === 0 && - <Tr> - <Td colSpan={8}> - <Bullseye> - <EmptyState variant={EmptyStateVariant.small}> - <EmptyStateIcon icon={SearchIcon}/> - <Title headingLevel="h2" size="lg"> - No results found - </Title> - </EmptyState> - </Bullseye> - </Td> - </Tr> - } - </Tbody> - </TableComposable> + {this.getProjectsTable()} </PageSection> {this.createModalForm()} {this.deleteModalForm()} diff --git a/karavan-designer/src/designer/karavan.css b/karavan-designer/src/designer/karavan.css index 38e2973..77ff41e 100644 --- a/karavan-designer/src/designer/karavan.css +++ b/karavan-designer/src/designer/karavan.css @@ -1274,6 +1274,7 @@ overflow: auto; flex-shrink: unset; flex-grow: 1; + background-color: var(--pf-global--BackgroundColor--light-300); } .karavan .designer-page {