Dashboard components basic styles (#574)

* Case sensitive renaming

* Update `cvat-js` lib.

* Add `.env` file

* Add basic redux capabilities

* Remove `setTimeout` as it was fixes in `cvat-js`

* Remove redundant state field

* Add header and footer styles

* Remove `Affix`

It does not recalculete width for some reason

* Add basic styles to task cards

* Fix empty component

* Pagination fixes

* Add modal on task delete
main
Artyom Zankevich 7 years ago committed by Nikita Manovich
parent a159826bf7
commit 3ba80d2e77

@ -1,8 +1,10 @@
REACT_APP_VERSION=${npm_package_version} REACT_APP_VERSION=${npm_package_version}
REACT_APP_API_PROTOCOL=http REACT_APP_API_PROTOCOL=http
REACT_APP_API_HOST=localhost REACT_APP_API_HOST=localhost
REACT_APP_API_PORT=7000 REACT_APP_API_PORT=7000
REACT_APP_API_HOST_URL=${REACT_APP_API_PROTOCOL}://${REACT_APP_API_HOST}:${REACT_APP_API_PORT} REACT_APP_API_HOST_URL=${REACT_APP_API_PROTOCOL}://${REACT_APP_API_HOST}:${REACT_APP_API_PORT}
REACT_APP_API_FULL_URL=${REACT_APP_API_PROTOCOL}://${REACT_APP_API_HOST}:${REACT_APP_API_PORT}/api/v1 REACT_APP_API_FULL_URL=${REACT_APP_API_PROTOCOL}://${REACT_APP_API_HOST}:${REACT_APP_API_PORT}/api/v1
REACT_APP_LOGIN=admin REACT_APP_LOGIN=admin
REACT_APP_PASSWORD=admin REACT_APP_PASSWORD=admin

@ -1,23 +1,52 @@
.dashboard-content-сard { .dashboard-content {
&__header { width: 1024px;
min-height: calc(100vh - 64px - 64px);
} margin: 0 auto;
.dashboard-content-сard {
margin: 20px;
padding: 20px;
border: 1px solid #001529;
border-radius: 3px;
background: white;
&__header {
text-align: center;
h2 {
margin-bottom: 20px;
}
}
&__content { &__content {
.card-cover { .card-cover {
img { img {
max-width: 300px; max-width: 300px;
}
} }
}
.card-actions { .card-actions {
display: flex;
flex-flow: column wrap;
} button {
min-width: 200px;
margin-bottom: 1px;
}
}
.card-jobs { .card-jobs {
}
} }
} }
} }
.empty {
display: flex;
flex-direction: column;
justify-content: center;
height: calc(100vh - 64px - 64px);
}

@ -1,10 +1,12 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Layout, Empty, Button, Col, Row } from 'antd'; import { Layout, Empty, Button, Modal, Col, Row } from 'antd';
import Title from 'antd/lib/typography/Title';
import './dashboard-content.scss'; import './dashboard-content.scss';
const { Content } = Layout; const { Content } = Layout;
const { confirm } = Modal;
interface DashboardContentAction { interface DashboardContentAction {
id: number, id: number,
@ -25,26 +27,32 @@ class DashboardContent extends Component<any, any> {
this.apiUrl = process.env.REACT_APP_API_FULL_URL; this.apiUrl = process.env.REACT_APP_API_FULL_URL;
this.actions = [ this.actions = [
// { {
// id: 1, id: 1,
// name: 'Dump annotation', name: 'Dump annotation',
// trigger: () => {}, trigger: () => {
// }, this.onDumpAnnotation();
// { },
// id: 2, },
// name: 'Upload annotation', {
// trigger: () => {}, id: 2,
// }, name: 'Upload annotation',
// { trigger: () => {
// id: 3, this.onUploadAnnotation();
// name: 'Update task', },
// trigger: () => {}, },
// }, {
id: 3,
name: 'Update task',
trigger: (task: any) => {
this.onUpdateTask(task);
},
},
{ {
id: 4, id: 4,
name: 'Delete task', name: 'Delete task',
trigger: (task: any) => { trigger: (task: any) => {
this.props.deleteTask(task); this.onDeleteTask(task);
}, },
}, },
]; ];
@ -58,30 +66,63 @@ class DashboardContent extends Component<any, any> {
); );
} }
private onDumpAnnotation() {
console.log('Dump');
}
private onUploadAnnotation() {
console.log('Upload');
}
private onUpdateTask(task: any) {
console.log('Update');
}
private onDeleteTask(task: any) {
const props = this.props;
confirm({
title: 'Do you want to delete this task?',
okText: 'Yes',
okType: 'danger',
centered: true,
onOk() {
return props.deleteTask(task);
},
cancelText: 'No',
onCancel() {
return;
},
});
}
private renderPlaceholder() { private renderPlaceholder() {
return ( return (
<Empty <Empty
className="empty"
description={ description={
<span> <span>
No tasks in this workspace yet... No tasks found...
</span> </span>
} }
> >
<Button type="primary">Create a new task</Button> <Button type="primary">
Create a new task
</Button>
</Empty> </Empty>
) )
} }
private renderTasks() { private renderTasks() {
return( return(
<Content> <Content className="dashboard-content">
{ {
this.props.tasks.map( this.props.tasks.map(
(task: any) => ( (task: any) => (
<div className="dashboard-content-сard" key={ task.id }> <div className="dashboard-content-сard" key={ task.id }>
<Row className="dashboard-content-сard__header" type="flex"> <Row className="dashboard-content-сard__header" type="flex">
<Col span={24}> <Col span={24}>
<h2>{ `${task.name}: ${task.mode}` }</h2> <Title level={2}>{ `${task.name}: ${task.mode}` }</Title>
</Col> </Col>
</Row> </Row>
@ -90,7 +131,7 @@ class DashboardContent extends Component<any, any> {
<img alt="Task cover" src={ `${this.apiUrl}/tasks/${task.id}/frames/0` } /> <img alt="Task cover" src={ `${this.apiUrl}/tasks/${task.id}/frames/0` } />
</Col> </Col>
<Col className="сard-actions" span={8}> <Col className="card-actions" span={8}>
{ {
this.actions.map( this.actions.map(
(action: DashboardContentAction) => ( (action: DashboardContentAction) => (
@ -105,7 +146,7 @@ class DashboardContent extends Component<any, any> {
</Col> </Col>
<Col className="сard-jobs" span={8}> <Col className="сard-jobs" span={8}>
Jobs <Title level={3}>Jobs</Title>
{ {
task.jobs.map( task.jobs.map(
(job: any) => ( (job: any) => (

@ -25,10 +25,20 @@ class Dashboard extends Component<any, DashboardState> {
render() { render() {
return ( return (
<Layout> <Layout className="layout">
<DashboardHeader onSearch={ this.getTasks } /> <DashboardHeader
<DashboardContent tasks={ this.state.tasks } deleteTask={ this.deleteTask } /> onSearch={ this.getTasks }>
<DashboardFooter tasksCount={ (this.state.tasks as any)['count'] } onPageChange={ this.onPageChange } /> </DashboardHeader>
<DashboardContent
tasks={ this.state.tasks }
deleteTask={ this.deleteTask }>
</DashboardContent>
<DashboardFooter
tasksCount={ (this.state.tasks as any)['count'] }
onPageChange={ this.onPageChange }>
</DashboardFooter>
</Layout> </Layout>
); );
} }

@ -0,0 +1,9 @@
.dashboard-footer {
display: flex;
align-items: center;
justify-content: center;
min-width: 1024px;
padding: 0 50px;
height: 64px;
background: #001529;
}

@ -1,22 +1,29 @@
import React, { Component } from 'react'; import React, { PureComponent } from 'react';
import { Layout, Pagination } from 'antd'; import { Layout, Pagination, Row, Col } from 'antd';
import './dashboard-footer.scss'; import './dashboard-footer.scss';
const { Footer } = Layout; const { Footer } = Layout;
class DashboardFooter extends Component<any, any> { class DashboardFooter extends PureComponent<any, any> {
constructor(props: any) {
super(props);
this.state = {};
}
render() { render() {
const pagination = (
<Col span={24}>
<Pagination
className="dashboard-footer__pagination"
hideOnSinglePage
onChange={ this.props.onPageChange }
total={ this.props.tasksCount }>
</Pagination>
</Col>
);
return( return(
<Footer> <Footer className="dashboard-footer">
<Pagination onChange={ this.props.onPageChange } total={ this.props.tasksCount } /> <Row type="flex" gutter={16}>
{ this.props.tasksCount ? pagination : '' }
</Row>
</Footer> </Footer>
); );
} }

@ -0,0 +1,34 @@
.dashboard-header {
min-width: 1024px;
background: #001529;
&__logo {
height: 64px;
.logo {
color: white;
}
}
&__search {
text-align: center;
.search {
max-width: 300px;
}
}
&__actions {
text-align: right;
.action:not(:nth-child(1)) {
margin-left: 8px;
}
.action {
width: 100px;
}
}
.logo, .search, .action {
vertical-align: middle;
}
}

@ -1,6 +1,7 @@
import React, { Component } from 'react'; import React, { Component } from 'react';
import { Layout, Row, Col, Button, Input } from 'antd'; import { Layout, Row, Col, Button, Input } from 'antd';
import Title from 'antd/lib/typography/Title';
import './dashboard-header.scss'; import './dashboard-header.scss';
@ -10,7 +11,7 @@ const { Search } = Input;
interface DashboardHeaderAction { interface DashboardHeaderAction {
id: number, id: number,
name: string, name: string,
trigger: any, trigger: Function,
} }
class DashboardHeader extends Component<any, any> { class DashboardHeader extends Component<any, any> {
@ -25,11 +26,11 @@ class DashboardHeader extends Component<any, any> {
this.hostUrl = process.env.REACT_APP_API_HOST_URL; this.hostUrl = process.env.REACT_APP_API_HOST_URL;
this.actions = [ this.actions = [
// { {
// id: 1, id: 1,
// name: 'Create task', name: 'Create task',
// trigger: this.props.onSearch, trigger: () => {},
// }, },
{ {
id: 2, id: 2,
name: 'User guide', name: 'User guide',
@ -40,19 +41,28 @@ class DashboardHeader extends Component<any, any> {
render() { render() {
return( return(
<Header> <Header className="dashboard-header">
<Row type="flex"> <Row type="flex" gutter={16}>
<Col span={8}> <Col className="dashboard-header__logo" span={8}>
Tasks <Title className="logo">Tasks</Title>
</Col> </Col>
<Col span={8}> <Col className="dashboard-header__search" span={8}>
<Search placeholder="Search for tasks" onSearch={ query => this.props.onSearch(query) } enterButton /> <Search
className="search"
placeholder="Search for tasks"
onSearch={ query => this.props.onSearch(query) }
enterButton>
</Search>
</Col> </Col>
<Col span={8}> <Col className="dashboard-header__actions" span={8}>
{ {
this.actions.map( this.actions.map(
(action: DashboardHeaderAction) => ( (action: DashboardHeaderAction) => (
<Button type="primary" key={ action.id } onClick={ () => action.trigger() }> <Button
className="action"
type="primary"
key={ action.id }
onClick={ () => action.trigger() }>
{ action.name } { action.name }
</Button> </Button>
) )

Loading…
Cancel
Save