Skip to content

Commit

Permalink
Add Table component and add result table to query interface
Browse files Browse the repository at this point in the history
  • Loading branch information
jochenklar committed Jun 25, 2024
1 parent b12e84d commit bede9fa
Show file tree
Hide file tree
Showing 10 changed files with 339 additions and 4 deletions.
28 changes: 28 additions & 0 deletions daiquiri/core/assets/js/components/Table.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react'
import PropTypes from 'prop-types'

import TableHeader from './TableHeader'
import TablePane from './TablePane'

const Table = ({ columns, rows, params, setParams }) => {
return (
<div className="dq-table">
<TableHeader count={rows.count} params={params} setParams={setParams} />
<TablePane columns={columns} rows={rows} params={params} setParams={setParams} />
</div>
)
}

Table.defaultProps = {
columns: [],
rows: {},
}

Table.propTypes = {
columns: PropTypes.array,
rows: PropTypes.object,
params: PropTypes.object.isRequired,
setParams: PropTypes.func.isRequired
}

export default Table
31 changes: 31 additions & 0 deletions daiquiri/core/assets/js/components/TableBody.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React from 'react'
import PropTypes from 'prop-types'

const TableBody = ({ columns, rows }) => {
return (
<tbody>
{
rows.results && rows.results.map((row, rowIndex) => (
<tr key={rowIndex}>
{
columns.map((column, columnIndex) => (
<td key={columnIndex}>
<div className="dq-table-cell">
{row[columnIndex]}
</div>
</td>
))
}
</tr>
))
}
</tbody>
)
}

TableBody.propTypes = {
columns: PropTypes.array.isRequired,
rows: PropTypes.object.isRequired
}

export default TableBody
69 changes: 69 additions & 0 deletions daiquiri/core/assets/js/components/TableHead.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import React from 'react'
import PropTypes from 'prop-types'
import classNames from 'classnames'

const TableHead = ({ columns, params, setParams }) => {
const tooltips = true
const ordering = params.ordering || ''

const handleOrdering = (column) => {
setParams({...params, ordering: (ordering == column.name) ? '-' + column.name : column.name})
}

return (
<thead>
<tr>
{
columns.map((column, columnIndex) => (
<th key={columnIndex}>
<div className="dq-table-cell">
<div className="name">
{
column.label ? (
<span>{column.label}</span>
) : (
<span>{column.name}</span>
)
}
</div>
{
tooltips && (
<div className="info">
<span className="material-symbols-rounded align-middle text-body-tertiary">help</span>
</div>
)
}
<div className="order" onClick={() => handleOrdering(column)}>
<span className={classNames('material-symbols-rounded align-middle', {
'text-body-tertiary': ![column.name, '-' + column.name].includes(ordering)
})}>
{
ordering == '-' + column.name ? (
'expand_less'
) : (
'expand_more'
)
}
</span>
</div>
{
columnIndex < columns.length -1 && (
<div className="handle"></div>
)
}
</div>
</th>
))
}
</tr>
</thead>
)
}

TableHead.propTypes = {
columns: PropTypes.array.isRequired,
params: PropTypes.object.isRequired,
setParams: PropTypes.func.isRequired
}

export default TableHead
47 changes: 47 additions & 0 deletions daiquiri/core/assets/js/components/TableHeader.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import React from 'react'
import PropTypes from 'prop-types'
import classnames from 'classnames'

const TableHeader = ({ count, params, setParams }) => {

const lastPage = count / params.page_size
const isFirstPage = params.page == 1
const isLastPage = params.page == lastPage

const handleFirst = () => setParams({...params, page: 1})
const handlePrevious = () => setParams({...params, page: params.page - 1})
const handleNext = () => setParams({...params, page: params.page + 1})
const handleLast = () => setParams({...params, page: lastPage })
const handleReset = () => setParams({page: 1, page_size: 10})

// eslint-disable-next-line react/prop-types
const PageItem = ({label, disabled, onClick}) => (
<li className={classnames('page-item', { disabled })}>
<span className="page-link" onClick={onClick}>
{label}
</span>
</li>
)

return (
<div className="dq-table-header">
<ul className="pagination">
<PageItem label={gettext('First')} onClick={handleFirst} disabled={isFirstPage} />
<PageItem label={gettext('Previous')} onClick={handlePrevious} disabled={isFirstPage} />
<PageItem label={gettext('Next')} onClick={handleNext} disabled={isLastPage} />
<PageItem label={gettext('Last')} onClick={handleLast} disabled={isLastPage} />
</ul>
<ul className="pagination">
<PageItem label={gettext('Reset')} onClick={handleReset} />
</ul>
</div>
)
}

TableHeader.propTypes = {
count: PropTypes.number,
params: PropTypes.object.isRequired,
setParams: PropTypes.func.isRequired
}

export default TableHeader
26 changes: 26 additions & 0 deletions daiquiri/core/assets/js/components/TablePane.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React from 'react'
import PropTypes from 'prop-types'

import TableHead from './TableHead'
import TableBody from './TableBody'

const TablePane = ({ columns, rows, params, setParams }) => {

return (
<div className="dq-table-pane">
<table className="table">
<TableHead columns={columns} params={params} setParams={setParams} />
<TableBody columns={columns} rows={rows} />
</table>
</div>
)
}

TablePane.propTypes = {
columns: PropTypes.array.isRequired,
rows: PropTypes.object.isRequired,
params: PropTypes.object.isRequired,
setParams: PropTypes.func.isRequired
}

export default TablePane
1 change: 1 addition & 0 deletions daiquiri/core/assets/scss/base.scss
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,6 @@ $material-symbols-font-path: '~material-symbols/';
@import 'fonts';
@import 'footer';
@import 'layout';
@import 'table';
@import 'typography';
@import 'variables';
91 changes: 91 additions & 0 deletions daiquiri/core/assets/scss/table.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
.dq-table {
.dq-table-header {
display: flex;
gap: 5px;

.page-link {
cursor: pointer;
}
}

.dq-table-pane {
border: 1px solid var(--bs-border-color);
border-radius: 4px;
width: auto;
overflow-x: auto;
table-layout: fixed;
width: 100%;

table.table {
margin: 0;
table-layout: fixed !important;

th,
td {
width: 300px;
padding: 0;
border-right: 1px solid var(--bs-border-color);

&:last-child,
&:last-child {
border-right: none;
}
}
tr {
&.selected {
background-color: var(--dq-table-active-bg);
}
}
th.dq-table-check {
width: 32px;

@include media-breakpoint-up(md) {
width: 40px;
}
}
.dq-table-cell,
.dq-table-check {
white-space: nowrap;
padding: 8px;
position: relative;
overflow: hidden;
}
.dq-table-check {
white-space: nowrap;
padding: 9px 4px 7px 6px;
text-align: center;
}

th {
.dq-table-cell {
display: flex;
padding-right: 0;

.name {
overflow: hidden;
flex-grow: 1;
}
.info {
.material-symbols-rounded {
font-size: 20px !important;
}
}
.order {
cursor: pointer;

.material-symbols-rounded {
line-height: 20px !important;
}
}
.handle {
width: 10px;

&:hover {
cursor: move;
}
}
}
}
}
}
}
8 changes: 8 additions & 0 deletions daiquiri/query/assets/js/query/api/QueryApi.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@ class QueryApi extends BaseApi {
return this.get(`/query/api/jobs/${id}/`)
}

static fetchJobColumns(id, params) {
return this.get(`/query/api/jobs/${id}/columns/?${encodeParams(params)}`)
}

static fetchJobRows(id, params) {
return this.get(`/query/api/jobs/${id}/rows/?${encodeParams(params)}`)
}

static fetchUserSchema(params) {
return this.get(`/query/api/jobs/tables/?${encodeParams(params)}`)
}
Expand Down
23 changes: 21 additions & 2 deletions daiquiri/query/assets/js/query/components/JobResults.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,27 @@
import React from 'react'
import React, { useState } from 'react'
import PropTypes from 'prop-types'

import Table from '../../../../../core/assets/js/components/Table'

import { useJobColumnsQuery, useJobRowsQuery } from '../hooks/query'

const JobResults = ({ job }) => {
return <pre>Results {job.id}</pre>
const [params, setParams] = useState({
page: 1,
page_size: 10
})

const { data: columns } = useJobColumnsQuery(job.id, params)
const { data: rows } = useJobRowsQuery(job.id, params)

return (
<Table
columns={columns}
rows={rows}
params={params}
setParams={setParams}
/>
)
}

JobResults.propTypes = {
Expand Down
19 changes: 17 additions & 2 deletions daiquiri/query/assets/js/query/hooks/query.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'

import { keepPreviousData, useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
import QueryApi from '../api/QueryApi'

const refetchInterval = 4000
Expand Down Expand Up @@ -42,6 +41,22 @@ export const useJobQuery = (jobId) => {
})
}

export const useJobColumnsQuery = (jobId, params) => {
return useQuery({
queryKey: ['jobColumns', jobId, params],
queryFn: () => QueryApi.fetchJobColumns(jobId, params),
placeholderData: keepPreviousData
})
}

export const useJobRowsQuery = (jobId, params) => {
return useQuery({
queryKey: ['jobRows', jobId, params],
queryFn: () => QueryApi.fetchJobRows(jobId, params),
placeholderData: keepPreviousData
})
}

export const useUpdateJobMutation = () => {
const queryClient = useQueryClient()

Expand Down

0 comments on commit bede9fa

Please sign in to comment.