대량의 균일 한 데이터를 시각화 하려는 경우 차트는 개별 데이터 항목에 대한 정보를 효과적으로 숨기므로 제대로 작동하지 않습니다. 그러나 데이터 테이블이 유용한 경우입니다! ?
이 튜토리얼에서는 React에서 처음부터 빌드 된 데이터 테이블에 방대한 양의 데이터를 표시하는 방법을 배웁니다. API를 통해 데이터베이스에서 데이터를 가져오고 필터링, 정렬 등과 같은 필수 기능을 사용하여 데이터 테이블에 시각화 하는 방법을 살펴 봅니다.
React에서 가장 많이 사용되는 UI 프레임 워크이기 때문에 Material UI를 사용할 것입니다. Google의 머티리얼 디자인에서 영감을 받아 만들어졌으며 멋진 사용자 인터페이스를 얻는 데 사용할 수 있는 많은 구성 요소를 제공합니다.
https://dev.to/cubejs/react-data-table-with-material-ui-and-a-spark-of-joy-50o1
데이터 테이블 작성 방법 ?
오늘의 계획입니다!
해킹을 시작하기 전에 구축 할 데이터 테이블의 스크린 샷을 확인하세요. 또한 GitHub에서 제공되는 전체 소스 코드를 확인하세요.
데이터베이스에서 데이터 준비 ?
가장 인기 있는 SQL 데이터 저장소 중 하나인 PostgreSQL 데이터베이스를 사용할 것입니다. PostgreSQL이 설치되어 있는지 확인하십시오. (그렇지 않으면 언젠가는 인기가 멈출 수 있습니다 ?.)
이제 PostgreSQL 용으로 신중하게 준비된 샘플 전자 상거래 데이터 세트를 다운로드하고 가져올 수 있습니다. 데이터 세트는 주문 및 상태를 추적하려는 가상의 전자 상거래 회사와 관련이 있습니다.
$ curl <http://cube.dev/downloads/ecom-dump.sql> > ecom-dump.sql
$ createdb ecom
$ psql --dbname ecom -f ecom-dump.sql
이제 데이터베이스가 준비되었습니다! 계속 진행하겠습니다 ...
데이터 작업을 위한 API 시작 ?
API에 Cube.js를 사용할 것입니다. Cube.js는 SQL 데이터 저장 소용 API를 만들고 분석 앱을 빌드하는 데 도움이 되는 오픈 소스 분석 API 플랫폼입니다. API 계층을 구축하고 SQL을 생성하고 데이터베이스를 쿼리하는 번거로움을 모두 제거합니다. 또한 최적의 성능, 다중 테넌시, 보안 등을 위한 다중 레벨 캐싱과 같은 많은 프로덕션 등급 기능을 제공합니다.
이제 몇 가지 간단한 단계로 Cube.js를 사용하여 데이터베이스 위에 API를 시작해 보겠습니다.
먼저 Cube.js 명령 줄 유틸리티 (CLI)를 설치해야 합니다. 편의를 위해 컴퓨터에 전체적으로 설치하겠습니다.
$ npm install -g cubejs-cli
그런 다음 CLI가 설치된 상태에서 단일 명령을 실행하여 기본 백엔드를 만들 수 있습니다. Cube.js는 널리 사용되는 모든 데이터베이스를 지원하므로 PostgreSQL과 함께 작동하도록 백엔드를 미리 구성 할 수 있습니다.
$ cubejs create <project name> -d <database type>
백엔드를 생성하려면 다음 명령을 실행합니다.
$ cubejs create react-data-table -d postgres
이제 데이터베이스에 연결해야 합니다. 이를 위해 Cube.js 프로젝트 폴더 (react-data-table)의 루트에 있는 .env 파일을 통해 몇 가지 옵션을 제공합니다.
CUBEJS_DB_NAME=ecom
CUBEJS_DB_TYPE=postgres
CUBEJS_API_SECRET=secret
이제 백엔드를 실행할 수 있습니다!
개발 모드에서 백엔드는 Cube.js 플레이 그라운드도 실행합니다. 정말 멋지다. ? Cube.js Playground는 데이터 스키마를 생성하고, 쿼리를 테스트하고, React 대시 보드 보일러 플레이트를 생성하는 데 도움이 되는 시간 절약형 웹 애플리케이션입니다. Cube.js 프로젝트 폴더에서 다음 명령을 실행하십시오.
$ node index.js
그런 다음 브라우저에서 http : // localhost : 4000을 엽니다.
Cube.js 플레이 그라운드를 사용하여 데이터 스키마를 생성합니다. 본질적으로 데이터를 선언적으로 설명하고 측정 값 및 차원과 같은 분석 엔터티를 정의하고 이를 SQL 쿼리에 매핑하는 JavaScript 코드입니다. 다음은 제품 데이터를 설명하는 데 사용할 수 있는 스키마의 예입니다.
cube(`Products`, {
sql: `SELECT * FROM public.products`,
measures: {
count: {
type: `count`
}
},
dimensions: {
name: {
sql: `name`,
type: `string`
},
id: {
sql: `id`,
type: `number`,
primaryKey: true,
shown: true
},
description: {
sql: `description`,
type: `string`
},
createdAt: {
sql: `created_at`,
type: `time`
}
}
});
Cube.js는 데이터베이스의 테이블을 기반으로 간단한 데이터 스키마를 생성 할 수 있습니다. 데이터베이스에 사소하지 않은 테이블 세트가 이미 있는 경우 많은 시간을 절약 할 수 있으므로 데이터 스키마 생성 사용을 고려하십시오.
이제 Cube.js Playground의 Schema 탭으로 이동하여 트리 뷰에서 공개 그룹을 선택하고 line_items, orders, products 및 users 테이블을 선택한 다음 "Generate Schema"를 클릭합니다. 결과적으로 스키마 폴더에 4 개의 생성 된 파일이 생깁니다. 테이블 당 정확히 하나의 스키마 파일입니다.
스키마가 생성되면 Cube.js Playground를 통해 데이터를 쿼리 할 수 있습니다. 이렇게 하려면 "Build"탭으로 이동하여 스키마에서 일부 측정 값과 차원을 선택합니다. 마법 같지 않니?
"빌드"탭은 다양한 시각화 라이브러리를 사용하여 샘플 차트를 빌드하고 생성 된 SQL에서 시작하여 차트를 렌더링 하는 JavaScript 코드까지 차트가 생성 된 방법의 모든 측면을 검사 할 수 있는 곳입니다. Cube.js 백엔드로 전송되는 JSON으로 인코딩 된 Cube.js 쿼리를 검사 할 수도 있습니다.
좋아요, 우리는 모두 준비되었습니다. API가 준비되었습니다. 이제 ...
React로 애플리케이션 만들기 ⚛️
Cube.js 플레이 그라운드는 선택한 프런트 엔드 프레임 워크 및 차트 라이브러리에 대한 템플릿을 생성 할 수 있습니다. 애플리케이션 용 템플릿을 만들려면 "Dashboard App"으로 이동하여 다음 옵션을 사용하십시오.
축하합니다! 이제 프로젝트에 dashboard-app 폴더가 있습니다. 이 폴더에는 확장 할 모든 프런트 엔드 코드가 포함되어 있습니다.
계속하기 전에 가장 중요한 변경을 수행해 보겠습니다. 제목에 데이터 테이블을 만들고 있음을 보여줍니다. ?
이렇게 하려면 dashboard-app의 public / index.html 파일에서 다음과 같이 몇 줄을 변경합니다.
// ...
- <title>React App</title>
+ <title>React Data Table</title>
+ <style>
+ body {
+ background-color: #eeeeee;
+ margin: 0;
+ }
+ </style>
// ...
또한 데이터 테이블을 더 쉽게 구축 할 수 있도록 대시 보드 앱의 몇 가지 종속성을 설치해 보겠습니다.
$ npm install --save react-perfect-scrollbar @material-ui/pickers
자, 이제 우리는 ...
기본 데이터 테이블 작성 ?
테이블에 많은 데이터가 있는 것이 좋습니다. 그렇죠? 이제 API를 통해 가져 오겠습니다.
이를 위해 주문 항목의 수량 (크기), 주문 가격 및 사용자의 전체 이름과 같은 여러 가지 새로운 측정 항목을 정의 할 것입니다. Cube.js를 사용하면 매우 쉽습니다.
먼저 schema / Users.js 파일의 "Users"스키마에 전체 이름을 추가하겠습니다. 전체 이름을 생성하기 위해 SQL 함수 CONCAT를 사용하여 이름과 성을 연결합니다.
cube(`Users`, {
sql: `SELECT * FROM public.users`,
// ...
dimensions: {
// ...
id: {
+ shown: true,
sql: `id`,
type: `number`,
primaryKey: true
},
firstName: {
sql: `first_name`,
type: `string`
},
lastName: {
sql: `last_name`,
type: `string`
},
+ fullName: {
+ sql: `CONCAT(${firstName}, ' ', ${lastName})`,
+ type: `string`
+ },
// ...
그런 다음 schema / Orders.js 파일의 "Orders"스키마에 다른 측정 값을 추가해 보겠습니다.
이러한 측정을 위해 Cube.js의 하위 쿼리 기능을 사용할 것입니다. 하위 쿼리 차원을 사용하여 차원 내의 다른 큐브에서 측정 값을 참조 할 수 있습니다. 이러한 차원을 정의하는 방법은 다음과 같습니다.
cube(`Orders`, {
sql: `SELECT * FROM public.orders`,
dimensions: {
// ...
id: {
+ shown: true,
sql: `id`,
type: `number`,
primaryKey: true
},
createdAt: {
sql: `created_at`,
type: `time`
},
+ size: {
+ sql: `${LineItems.count}`,
+ subQuery: true,
+ type: 'number'
+ },
+
+ price: {
+ sql: `${LineItems.price}`,
+ subQuery: true,
+ type: 'number'
+ },
completedAt: {
sql: `completed_at`,
type: `time`
}
}
});
거의 다 왔어! 따라서 데이터 테이블을 표시하기 위해 src / pages / DashboardPage.js 파일을 다음 내용으로 바꿉니다.
import React from "react";
import { makeStyles } from "@material-ui/styles";
import Table from "../components/Table.js";
const useStyles = makeStyles(theme => ({
root: { padding: 15 },
content: { marginTop: 15 },
}));
const Dashboard = () => {
const classes = useStyles();
const query = {
timeDimensions: [
{
dimension: 'Orders.createdAt',
granularity: 'day'
}
],
dimensions: [
'Users.id',
'Orders.id',
'Orders.size',
'Users.fullName',
'Users.city',
'Orders.price',
'Orders.status',
'Orders.createdAt',
]
};
return (
<div className={classes.root}>
<div className={classes.content}>
<Table query={query}/>
</div>
</div>
);
};
export default Dashboard;
이제 이 파일에는 매우 자명 한 Cube.js 쿼리가 포함되어 있습니다. API에 여러 차원을 반환하도록 요청하는 것 뿐입니다.
대시 보드에 대한 변경은 미미하지만 데이터 테이블을 렌더링하는 모든 마법은 <Table /> 구성 요소 내에서 발생하며 쿼리 결과의 변경 사항은 테이블에 반영됩니다.
src / components / Table.js 파일에 다음 내용으로 이 <Table /> 구성 요소를 만들어 보겠습니다.
import React, { useState } from "react";
import clsx from "clsx";
import PropTypes from "prop-types";
import moment from "moment";
import PerfectScrollbar from "react-perfect-scrollbar";
import { makeStyles } from "@material-ui/styles";
import Typography from "@material-ui/core/Typography";
import { useCubeQuery } from "@cubejs-client/react";
import CircularProgress from "@material-ui/core/CircularProgress";
import {
Card,
CardActions,
CardContent,
Table,
TableBody,
TableCell,
TableHead,
TableRow,
TablePagination
} from "@material-ui/core";
const useStyles = makeStyles(theme => ({
root: {
padding: 0
},
content: {
padding: 0
},
inner: {
minWidth: 1050
},
nameContainer: {
display: "flex",
alignItems: "baseline"
},
status: {
marginRight: 15
},
actions: {
justifyContent: "flex-end"
},
}));
const TableComponent = props => {
const { className, query, cubejsApi, ...rest } = props;
const classes = useStyles();
const [rowsPerPage, setRowsPerPage] = useState(10);
const [page, setPage] = useState(0);
const tableHeaders = [
{ text: "Full Name", value: "Users.fullName" },
{ text: "User city", value: "Users.city" },
{ text: "Order price", value: "Orders.price" },
{ text: "Status", value: "Orders.status" },
{ text: "Created at", value: "Orders.createdAt" }
];
const { resultSet, error, isLoading } = useCubeQuery(query, { cubejsApi });
if (isLoading) {
return <div style={{display: 'flex', alignItems: 'center', justifyContent: 'center'}}><CircularProgress color="secondary" /></div>;
}
if (error) {
return <pre>{error.toString()}</pre>;
}
if (resultSet) {
let orders = resultSet.tablePivot();
const handlePageChange = (event, page) => {
setPage(page);
};
const handleRowsPerPageChange = event => {
setRowsPerPage(event.target.value);
};
return (
<Card
{...rest}
padding={"0"}
className={clsx(classes.root, className)}
>
<CardContent className={classes.content}>
<PerfectScrollbar>
<div className={classes.inner}>
<Table>
<TableHead className={classes.head}>
<TableRow>
{tableHeaders.map((item) => (
<TableCell key={item.value + Math.random()}
className={classes.hoverable}
>
<span>{item.text}</span>
</TableCell>
))}
</TableRow>
</TableHead>
<TableBody>
{orders.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map(obj => (
<TableRow
className={classes.tableRow}
hover
key={obj["Orders.id"]}
>
<TableCell>
{obj["Orders.id"]}
</TableCell>
<TableCell>
{obj["Orders.size"]}
</TableCell>
<TableCell>
{obj["Users.fullName"]}
</TableCell>
<TableCell>
{obj["Users.city"]}
</TableCell>
<TableCell>
{"$ " + obj["Orders.price"]}
</TableCell>
<TableCell>
{obj["Orders.status"]}
</TableCell>
<TableCell>
{moment(obj["Orders.createdAt"]).format("DD/MM/YYYY")}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</div>
</PerfectScrollbar>
</CardContent>
<CardActions className={classes.actions}>
<TablePagination
component="div"
count={orders.length}
onChangePage={handlePageChange}
onChangeRowsPerPage={handleRowsPerPageChange}
page={page}
rowsPerPage={rowsPerPage}
rowsPerPageOptions={[5, 10, 25, 50, 100]}
/>
</CardActions>
</Card>
);
} else {
return null
}
};
TableComponent.propTypes = {
className: PropTypes.string,
query: PropTypes.object.isRequired
};
export default TableComponent;
드디어! 우리가 기다리고 있던 데이터 테이블은 다음과 같습니다.
실제로는 그렇게 기본적이지 않습니다! ? 방대한 양의 데이터를 표시하고 탐색 할 수 있는 페이지 매김이 내장되어 있습니다.
그러나 그것은 회색 빛을 띠고 우울해 보입니다. 이제 색상을 추가하고 테이블을 확장 해 보겠습니다.
사용자 지정 셀 형식
테이블에는 이 시점에서 텍스트로 표시되는 주문 상태가 포함되어 있습니다. 커스텀 컴포넌트로 교체합시다!
아이디어는 다채로운 점으로 주문 상태를 시각화 하는 것입니다. 이를 위해 사용자 지정 <StatusBullet /> 구성 요소를 만듭니다. 다음 내용으로 src / components / StatusBullet.js 파일에 이 구성 요소를 생성 해 보겠습니다.
import React from 'react';
import PropTypes from 'prop-types';
import clsx from 'clsx';
import { makeStyles } from '@material-ui/styles';
const useStyles = makeStyles(theme => ({
root: {
display: 'inline-block',
borderRadius: '50%',
flexGrow: 0,
flexShrink: 0
},
sm: {
height: 15,
width: 15
},
md: {
height: 15,
width: 15
},
lg: {
height: 15,
width: 15
},
neutral: { backgroundColor: '#fff' },
primary: { backgroundColor: '#ccc' },
info: { backgroundColor: '#3cc' },
warning: { backgroundColor: '#cc3' },
danger: { backgroundColor: '#c33' },
success: { backgroundColor: '#3c3' }
}));
const StatusBullet = props => {
const { className, size, color, ...rest } = props;
const classes = useStyles();
return (
<span
{...rest}
className={clsx(
{
[classes.root]: true,
[classes[size]]: size,
[classes[color]]: color
},
className
)}
/>
);
};
StatusBullet.propTypes = {
className: PropTypes.string,
color: PropTypes.oneOf([
'neutral',
'primary',
'info',
'success',
'warning',
'danger'
]),
size: PropTypes.oneOf(['sm', 'md', 'lg'])
};
StatusBullet.defaultProps = {
size: 'md',
color: 'default'
};
export default StatusBullet;
작동하려면 데이터 테이블에 최소한의 변경 사항을 적용해야 합니다. src / components / Table.js를 다음과 같이 수정 해 보겠습니다.
// ...
} from "@material-ui/core";
import StatusBullet from "./StatusBullet";
const statusColors = {
completed: "success",
processing: "info",
shipped: "danger"
};
const useStyles = makeStyles(theme => ({
// ...
<TableCell>
+ <StatusBullet
+ className={classes.status}
+ color={statusColors[obj["Orders.status"]]}
+ size="sm"
+ />
{obj["Orders.status"]}
</TableCell>
// ...
좋은! ? 이제 모든 주문에 대한 정보를 표시하고 다채로운 터치가 있는 테이블이 있습니다.
데이터 필터링
그러나 제공된 컨트롤 만 사용하여 이러한 주문을 탐색하는 것은 어렵습니다. 이 문제를 해결하기 위해 필터가 포함 된 포괄적인 도구 모음을 추가하고 테이블을 대화 형으로 만들 것입니다.
먼저 몇 가지 종속성을 추가하겠습니다. dashboard-app 폴더에서 명령을 실행합니다.
npm install --save @date-io/date-fns@1.x date-fns @date-io/moment@1.x moment
그런 다음 src / components / Toolbar.js 파일에 다음 내용으로 <Toolbar /> 구성 요소를 만듭니다.
import "date-fns";
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "@material-ui/styles";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import Tab from "@material-ui/core/Tab";
import Tabs from "@material-ui/core/Tabs";
import withStyles from "@material-ui/core/styles/withStyles";
const AntTabs = withStyles({
indicator: {},
})(Tabs);
const AntTab = withStyles((theme) => ({
root: {
textTransform: 'none',
minWidth: 25,
fontSize: 12,
fontWeight: theme.typography.fontWeightRegular,
marginRight: 0,
opacity: 0.6,
'&:hover': {
opacity: 1,
},
'&$selected': {
fontWeight: theme.typography.fontWeightMedium,
outline: 'none',
},
'&:focus': {
outline: 'none',
},
},
selected: {},
}))((props) => <Tab disableRipple {...props} />);
const useStyles = makeStyles(theme => ({
root: {},
row: {
marginTop: 15
},
spacer: {
flexGrow: 1
},
importButton: {
marginRight: 15
},
exportButton: {
marginRight: 15
},
searchInput: {
marginRight: 15
},
formControl: {
margin: 25,
fullWidth: true,
display: "flex",
wrap: "nowrap"
},
date: {
marginTop: 3
},
range: {
marginTop: 13
}
}));
const Toolbar = props => {
const { className,
statusFilter,
setStatusFilter,
tabs,
...rest } = props;
const [tabValue, setTabValue] = React.useState(statusFilter);
const classes = useStyles();
const handleChangeTab = (e, value) => {
setTabValue(value);
setStatusFilter(value);
};
return (
<div
{...rest}
className={className}
>
<Grid container spacing={4}>
<Grid
item
lg={3}
sm={6}
xl={3}
xs={12}
m={2}
>
<div className={classes}>
<AntTabs value={tabValue} onChange={(e,value) => {handleChangeTab(e,value)}} aria-label="ant example">
{tabs.map((item) => (<AntTab key={item} label={item} />))}
</AntTabs>
<Typography className={classes.padding} />
</div>
</Grid>
</Grid>
</div>
);
};
Toolbar.propTypes = {
className: PropTypes.string
};
export default Toolbar;
src / pages / DashboardPage 파일을 수정 해 보겠습니다.
import React from "react";
import { makeStyles } from "@material-ui/styles";
+ import Toolbar from "../components/Toolbar.js";
import Table from "../components/Table.js";
const useStyles = makeStyles(theme => ({
root: {
padding: 15
},
content: {
marginTop: 15
},
}));
const DashboardPage = () => {
const classes = useStyles();
+ const tabs = ['All', 'Shipped', 'Processing', 'Completed'];
+ const [statusFilter, setStatusFilter] = React.useState(0);
const query = {
"timeDimensions": [
{
"dimension": "Orders.createdAt",
"granularity": "day"
}
],
"dimensions": [
"Users.id",
"Orders.id",
"Orders.size",
"Users.fullName",
"Users.city",
"Orders.price",
"Orders.status",
"Orders.createdAt"
],
+ "filters": [
+ {
+ "dimension": "Orders.status",
+ "operator": tabs[statusFilter] !== 'All' ? "equals" : "set",
+ "values": [
+ `${tabs[statusFilter].toLowerCase()}`
+ ]
+ }
+ ]
};
return (
<div className={classes.root}>
+ <Toolbar
+ statusFilter={statusFilter}
+ setStatusFilter={setStatusFilter}
+ tabs={tabs}
+ />
<div className={classes.content}>
<Table
query={query}/>
</div>
</div>
);
};
export default DashboardPage;
완전한! ? 이제 데이터 테이블에 다양한 주문 유형간에 전환하는 필터가 있습니다.
그러나 주문에는 가격 및 날짜와 같은 다른 매개 변수가 있습니다. 이러한 매개 변수에 대한 필터를 만들어 보겠습니다. 이렇게 하려면 src / components / Toolbar.js 파일을 수정하십시오.
import "date-fns";
import React from "react";
import PropTypes from "prop-types";
import clsx from "clsx";
import { makeStyles } from "@material-ui/styles";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import Tab from "@material-ui/core/Tab";
import Tabs from "@material-ui/core/Tabs";
import withStyles from "@material-ui/core/styles/withStyles";
+ import DateFnsUtils from "@date-io/date-fns";
+ import {
+ MuiPickersUtilsProvider,
+ KeyboardDatePicker
+ } from "@material-ui/pickers";
+ import Slider from "@material-ui/core/Slider";
// ...
const Toolbar = props => {
const { className,
+ startDate,
+ setStartDate,
+ finishDate,
+ setFinishDate,
+ priceFilter,
+ setPriceFilter,
statusFilter,
setStatusFilter,
tabs,
...rest } = props;
const [tabValue, setTabValue] = React.useState(statusFilter);
+ const [rangeValue, rangeSetValue] = React.useState(priceFilter);
const classes = useStyles();
const handleChangeTab = (e, value) => {
setTabValue(value);
setStatusFilter(value);
};
+ const handleDateChange = (date) => {
+ setStartDate(date);
+ };
+ const handleDateChangeFinish = (date) => {
+ setFinishDate(date);
+ };
+ const handleChangeRange = (event, newValue) => {
+ rangeSetValue(newValue);
+ };
+ const setRangeFilter = (event, newValue) => {
+ setPriceFilter(newValue);
+ };
return (
<div
{...rest}
className={clsx(classes.root, className)}
>
<Grid container spacing={4}>
<Grid
item
lg={3}
sm={6}
xl={3}
xs={12}
m={2}
>
<div className={classes}>
<AntTabs value={tabValue} onChange={(e,value) => {handleChangeTab(e,value)}} aria-label="ant example">
{tabs.map((item) => (<AntTab key={item} label={item} />))}
</AntTabs>
<Typography className={classes.padding} />
</div>
</Grid>
+ <Grid
+ className={classes.date}
+ item
+ lg={3}
+ sm={6}
+ xl={3}
+ xs={12}
+ m={2}
+ >
+ <MuiPickersUtilsProvider utils={DateFnsUtils}>
+ <Grid container justify="space-around">
+ <KeyboardDatePicker
+ id="date-picker-dialog"
+ label={<span style={{opacity: 0.6}}>Start Date</span>}
+ format="MM/dd/yyyy"
+ value={startDate}
+ onChange={handleDateChange}
+ KeyboardButtonProps={{
+ "aria-label": "change date"
+ }}
+ />
+ </Grid>
+ </MuiPickersUtilsProvider>
+ </Grid>
+ <Grid
+ className={classes.date}
+ item
+ lg={3}
+ sm={6}
+ xl={3}
+ xs={12}
+ m={2}
+ >
+ <MuiPickersUtilsProvider utils={DateFnsUtils}>
+ <Grid container justify="space-around">
+ <KeyboardDatePicker
+ id="date-picker-dialog-finish"
+ label={<span style={{opacity: 0.6}}>Finish Date</span>}
+ format="MM/dd/yyyy"
+ value={finishDate}
+ onChange={handleDateChangeFinish}
+ KeyboardButtonProps={{
+ "aria-label": "change date"
+ }}
+ />
+ </Grid>
+ </MuiPickersUtilsProvider>
+ </Grid>
+ <Grid
+ className={classes.range}
+ item
+ lg={3}
+ sm={6}
+ xl={3}
+ xs={12}
+ m={2}
+ >
+ <Typography id="range-slider">
+ Order price range
+ </Typography>
+ <Slider
+ value={rangeValue}
+ onChange={handleChangeRange}
+ onChangeCommitted={setRangeFilter}
+ aria-labelledby="range-slider"
+ valueLabelDisplay="auto"
+ min={0}
+ max={2000}
+ />
+ </Grid>
</Grid>
</div>
);
};
Toolbar.propTypes = {
className: PropTypes.string
};
export default Toolbar;
이러한 필터가 작동하도록 하려면 상위 구성 요소에 연결해야 합니다. 상태 추가, 쿼리 수정, <Toolbar /> 구성 요소에 새 소품 추가. 또한 데이터 테이블에 정렬을 추가합니다. 따라서 다음과 같이 src / pages / DashboardPage.js 파일을 수정합니다.
// ...
const DashboardPage = () => {
const classes = useStyles();
const tabs = ['All', 'Shipped', 'Processing', 'Completed'];
const [statusFilter, setStatusFilter] = React.useState(0);
+ const [startDate, setStartDate] = React.useState(new Date("2019-01-01T00:00:00"));
+ const [finishDate, setFinishDate] = React.useState(new Date("2022-01-01T00:00:00"));
+ const [priceFilter, setPriceFilter] = React.useState([0, 200]);
+ const [sorting, setSorting] = React.useState(['Orders.createdAt', 'desc']);
const query = {
timeDimensions: [
{
"dimension": "Orders.createdAt",
+ "dateRange": [startDate, finishDate],
"granularity": "day"
}
],
+ order: {
+ [`${sorting[0]}`]: sorting[1]
+ },
"dimensions": [
"Users.id",
"Orders.id",
"Orders.size",
"Users.fullName",
"Users.city",
"Orders.price",
"Orders.status",
"Orders.createdAt"
],
"filters": [
{
"dimension": "Orders.status",
"operator": tabs[statusFilter] !== 'All' ? "equals" : "set",
"values": [
`${tabs[statusFilter].toLowerCase()}`
]
},
+ {
+ "dimension": "Orders.price",
+ "operator": "gt",
+ "values": [
+ `${priceFilter[0]}`
+ ]
+ },
+ {
+ "dimension": "Orders.price",
+ "operator": "lt",
+ "values": [
+ `${priceFilter[1]}`
+ ]
+ },
]
};
return (
<div className={classes.root}>
<Toolbar
+ startDate={startDate}
+ setStartDate={setStartDate}
+ finishDate={finishDate}
+ setFinishDate={setFinishDate}
+ priceFilter={priceFilter}
+ setPriceFilter={setPriceFilter}
statusFilter={statusFilter}
setStatusFilter={setStatusFilter}
tabs={tabs}
/>
<div className={classes.content}>
<Table
+ sorting={sorting}
+ setSorting={setSorting}
query={query}/>
</div>
</div>
);
};
export default DataTablePage;
환상적입니다! ? 몇 가지 유용한 필터를 추가했습니다. 실제로 사용자 지정 논리를 사용하여 더 많은 필터를 추가 할 수 있습니다. 필터 형식 옵션에 대한 문서를 참조하십시오.
그리고 한 가지 더 있습니다! 툴바에 정렬 소품을 추가했지만 <Table /> 구성 요소에도 전달해야 합니다. 이 문제를 해결하기 위해 src / components / Table.js 파일을 수정 해 보겠습니다.
// ...
+ import KeyboardArrowUpIcon from "@material-ui/icons/KeyboardArrowUp";
+ import KeyboardArrowDownIcon from "@material-ui/icons/KeyboardArrowDown";
import { useCubeQuery } from "@cubejs-client/react";
import CircularProgress from "@material-ui/core/CircularProgress";
// ...
const useStyles = makeStyles(theme => ({
// ...
actions: {
justifyContent: "flex-end"
},
+ tableRow: {
+ padding: '0 5px',
+ cursor: "pointer",
+ '.MuiTableRow-root.MuiTableRow-hover&:hover': {
+ }
+ },
+ hoverable: {
+ "&:hover": {
+ cursor: `pointer`
+ }
+ },
+ arrow: {
+ fontSize: 10,
+ position: "absolute"
+ }
}));
const statusColors = {
completed: "success",
processing: "info",
shipped: "danger"
};
const TableComponent = props => {
- const { className, query, cubejsApi, ...rest } = props;
+ const { className, sorting, setSorting, query, cubejsApi, ...rest } = props;
// ...
if (resultSet) {
//...
+ const handleSetSorting = str => {
+ setSorting([str, sorting[1] === "desc" ? "asc" : "desc"]);
+ };
return (
// ...
<TableHead className={classes.head}>
<TableRow>
{tableHeaders.map((item) => (
<TableCell key={item.value + Math.random()} className={classes.hoverable}
+ onClick={() => {
+ handleSetSorting(`${item.value}`);
+ }}
>
<span>{item.text}</span>
+ <Typography
+ className={classes.arrow}
+ variant="body2"
+ component="span"
+ >
+ {(sorting[0] === item.value) ? (sorting[1] === "desc" ? <KeyboardArrowUpIcon/> :
+ <KeyboardArrowDownIcon/>) : null}
+ </Typography>
</TableCell>
))}
</TableRow>
</TableHead>
// ...
훌륭한! ? 이제 필터링 및 정렬을 완벽하게 지원하는 데이터 테이블이 있습니다.
그리고 그게 전부입니다! ?이 튜토리얼을 완료 한 것을 축하합니다! ?
또한 GitHub에서 제공되는 전체 소스 코드를 확인하세요.
이제 React 및 Material UI를 사용하여 Cube.js로 구동되는 맞춤형 데이터 테이블을 생성하여 말 그대로 애플리케이션에 모든 양의 데이터를 표시 할 수 있습니다.
Real-Time Dashboard Guide 및 Open Source Web Analytics Platform Guide와 같이 Cube.js로 수행 할 수 있는 다른 예제를 자유롭게 탐색하십시오.
등록된 댓글이 없습니다.