Skip to content

Commit

Permalink
refactoring QueryTable
Browse files Browse the repository at this point in the history
  • Loading branch information
paolini committed Dec 31, 2023
1 parent 3d1e5fc commit 5e114af
Show file tree
Hide file tree
Showing 8 changed files with 148 additions and 122 deletions.
186 changes: 106 additions & 80 deletions frontend/src/components/QueryTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import React, { useState, createContext, useContext } from 'react'
import { useIndex } from '../modules/engine'
import { Link } from "react-router-dom"
import LoadingMessage from './LoadingMessage'
//import Card from './Card'
import { Card } from 'react-bootstrap'

import {formatDate} from '../modules/dates'
Expand Down Expand Up @@ -36,7 +35,7 @@ export function useQuery() {
return useContext(QueryTableContext)
}

export default function QueryTable<T>({ sort, direction, children }:{
export function QueryTableCard<T>({ sort, direction, children }:{
sort: string,
direction?: number,
children?: any,
Expand All @@ -55,22 +54,65 @@ export default function QueryTable<T>({ sort, direction, children }:{
</Card>
}

export function QueryTableHeaders({ children }) {
export function QueryTableBar({ children }) {
return <div className="d-flex mb-2">
{children}
</div>

}

export function QueryTableBody<T>({ path, headers, getField}:{
export function QueryTable<T>({ path, headers, getField}:{
path: string,
headers: IQueryTableHeader[],
getField?: (item: any, field: string) => JSX.Element | string,
children?: any,
}) {
return <div className="table-responsive-lg">
<Table<T> path={path} headers={headers} getField={getField}/>
<TableItems<T> path={path} headers={headers} getField={getField}>
<thead>
<tr>
<th></th>
{ headers.map( ({ field, label, enable_sort }) =>
<th key={ field }>
{enable_sort
? <SortHeader field={ field } label={ label } />
: label}
</th>
)}
</tr>
</thead>
<TableBody renderCells={renderCells} />
</TableItems>
</div>

function renderCells(item) {
return <>
{headers.map(({ field, enable_link }) =>
<td key={ field }>{ render(item, field, enable_link) }</td>
)}
</>
}

function render(item, field, enable_link) {
const content = getField ? getField(item, field) : defaultGetField(item, field)
if (enable_link) {
return <Link to={ `${item._id}` }>{ content }</Link>
} else {
return content;
}
}

}

export function defaultGetField(item, field:string) {
let value = item
const segments:string[] = field.split('.')
segments.forEach(f => {value = value[f]})
const last = segments[segments.length - 1]
if (["date_submitted", "date_modified", "date_managed"]
.includes(last)) return formatDate(value)
if (last === 'state') return <StateBadge state={value}/>
return value
}

export function FilterBadges() {
Expand Down Expand Up @@ -101,10 +143,33 @@ export function FilterBadges() {
</div>
}

function Table<T>({ path, headers, getField }: {
const ItemsContext = createContext<{
items: any[],
selectedIds: string[],
setSelectedIds: React.Dispatch<React.SetStateAction<string[]>>,
}>({
items: [],
selectedIds: [],
setSelectedIds: () => {}
})

export function useItems<T extends {_id: string}>() {
return useContext(ItemsContext).items as T[]
}

export function useSelectedIds() {
return useContext(ItemsContext).selectedIds
}

export function useSetSelectedIds() {
return useContext(ItemsContext).setSelectedIds
}

function TableItems<T>({ path, children }: {
path:string,
headers: IQueryTableHeader[],
getField?: (item: any, field: string) => JSX.Element | string,
children?: any,
}) {
const ctx = useQuery()
if (!ctx) return null
Expand All @@ -118,27 +183,10 @@ function Table<T>({ path, headers, getField }: {
const data = indexQuery.data
const items = data.items

return <>
return <ItemsContext.Provider value={{items, selectedIds, setSelectedIds}}>
{items.length}/{data.total} elementi mostrati
<table className="table">
<thead>
<tr>
<th></th>
{ headers.map( ({ field, label, enable_sort }) =>
<th key={ field }>
<Header field={ field } label={ label } enable_sort={ enable_sort } />
</th>
)}
</tr>
</thead>
<TableBody
path={path}
headers={headers}
items={ items }
selectedIds={ selectedIds }
setSelectedIds={ setSelectedIds }
getField={ getField }
/>
{ children }
</table>
<p>
{ items.length < data.total
Expand All @@ -148,9 +196,32 @@ function Table<T>({ path, headers, getField }: {
: null
}
</p>
</>
</ItemsContext.Provider>

function increaseLimit(d) {
setQuery(q => {
return {...q,
_limit: q._limit + d
}
})
}
}

function SortHeader({ field, label }) {
const { query, setQuery } = useQuery() || {}

if (query && setQuery) {
return <a href="#" onClick={() => toggleSort(field, setQuery)}>
{ label }&nbsp;{
(query._sort === field)
? (query._direction > 0 ? <></> : <></>)
: ""}
</a>
} else {
return label;
}

function toggleSort(field) {
function toggleSort(field, setQuery) {
setQuery(q => {
if (q._sort == field) {
return {
Expand All @@ -166,48 +237,23 @@ function Table<T>({ path, headers, getField }: {
}
})
}
}

function increaseLimit(d) {
setQuery(q => {
return {...q,
_limit: q._limit + d
}
})
}

function Header({ field, label, enable_sort }) {
if (enable_sort) {
return <a href="#" onClick={() => toggleSort(field)}>
{ label }&nbsp;{
(query._sort === field)
? (query._direction > 0 ? <></> : <></>)
: ""}
</a>
} else {
return label;
}
}
}

function TableBody({ path, headers, getField, items, selectedIds, setSelectedIds }:{
path: string,
headers: IQueryTableHeader[],
getField?: (item: any, field: string) => JSX.Element | string,
items: any[],
selectedIds: string[],
setSelectedIds: React.Dispatch<React.SetStateAction<string[]>>,
function TableBody<T extends {_id:string}>({ renderCells }:{
renderCells: (item: T) => JSX.Element|JSX.Element[],
}) {
const items = useItems<T>()
const selectedIds = useSelectedIds()
const setSelectedIds = useSetSelectedIds()
return <tbody>
{ items.map(item => {
const selected = selectedIds.includes(item._id)
return <tr
key={ item._id }
style={ selected ? {background: "lightgray" } : {}}>
<td><input type="checkbox" checked={ selected } readOnly onClick={ () => onToggle(item) }/></td>
{ headers.map(({ field, enable_link }) =>
<td key={ field }>{ render(item, field, enable_link) }</td>
)}
<td></td>
{ renderCells(item) }
<td />
</tr>})
}
</tbody>
Expand All @@ -221,24 +267,4 @@ function TableBody({ path, headers, getField, items, selectedIds, setSelectedIds
}
})
}

function render(item, field, enable_link) {
const content = getField ? getField(item, field) : defaultGetField(item, field)
if (enable_link) {
return <Link to={ `${item._id}` }>{ content }</Link>
} else {
return content;
}
}
}

export function defaultGetField(item, field:string) {
let value = item
const segments:string[] = field.split('.')
segments.forEach(f => {value = value[f]})
const last = segments[segments.length - 1]
if (["date_submitted", "date_modified", "date_managed"]
.includes(last)) return formatDate(value)
if (last === 'state') return <StateBadge state={value}/>
return value
}
12 changes: 6 additions & 6 deletions frontend/src/pages/CurriculaPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
TableTopRightButtons, FilterButton, FilterInput,
ItemAddButton, CsvDownloadButton, ExcelDownloadButton,
} from '../components/TableElements'
import QueryTable, {QueryTableHeaders, QueryTableBody, FilterBadges} from '../components/QueryTable'
import {QueryTableCard, QueryTableBar, QueryTable, FilterBadges} from '../components/QueryTable'

const path = "/curricula/"
const headers=[
Expand All @@ -26,8 +26,8 @@ const headers=[
export default function CurriculaPage() {
return <>
<h1>Curricula</h1>
<QueryTable sort="name">
<QueryTableHeaders>
<QueryTableCard sort="name">
<QueryTableBar>
<FilterButton>
<FilterInput name="name" label="nome" />
<FilterInput name="academic_year" label="anno" />
Expand All @@ -42,10 +42,10 @@ export default function CurriculaPage() {
<CsvDownloadButton cb={async (query) => []}/>
<ExcelDownloadButton />
</TableTopRightButtons>
</QueryTableHeaders>
</QueryTableBar>
<FilterBadges />
<QueryTableBody path={path} headers={headers}/>
</QueryTable>
<QueryTable path={path} headers={headers}/>
</QueryTableCard>
</>
}

12 changes: 6 additions & 6 deletions frontend/src/pages/DegreesPage.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'

import QueryTable, {QueryTableHeaders, QueryTableBody, FilterBadges} from '../components/QueryTable'
import {QueryTableCard, QueryTableBar, QueryTable, FilterBadges} from '../components/QueryTable'
import {
TableTopRightButtons, FilterButton, FilterInput,
ItemAddButton, CsvDownloadButton, ExcelDownloadButton,
Expand Down Expand Up @@ -33,8 +33,8 @@ const headers=[
export default function DegreesPage() {
return <>
<h1>Corsi di Laurea</h1>
<QueryTable sort="name">
<QueryTableHeaders>
<QueryTableCard sort="name">
<QueryTableBar>
<FilterButton>
<FilterInput name="enabled" label="attivo" />
<FilterInput name="enable_sharing" label="richiesta parere" />
Expand All @@ -51,10 +51,10 @@ export default function DegreesPage() {
<CsvDownloadButton cb={async (query)=>[]}/>
<ExcelDownloadButton />
</TableTopRightButtons>
</QueryTableHeaders>
</QueryTableBar>
<FilterBadges />
<QueryTableBody path={path} headers={headers} getField={getField} />
</QueryTable>
<QueryTable path={path} headers={headers} getField={getField} />
</QueryTableCard>
</>

function getField(item, field) {
Expand Down
12 changes: 6 additions & 6 deletions frontend/src/pages/ExamsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
TableTopRightButtons, FilterButton, FilterInput,
ItemAddButton, CsvDownloadButton, ExcelDownloadButton
} from '../components/TableElements'
import QueryTable, {QueryTableHeaders, QueryTableBody, FilterBadges} from '../components/QueryTable'
import {QueryTableCard, QueryTableBar, QueryTable, FilterBadges} from '../components/QueryTable'

const path="/exams/"
const headers=[
Expand Down Expand Up @@ -35,8 +35,8 @@ const headers=[
export default function ExamsPage() {
return <>
<h1>Esami</h1>
<QueryTable sort="name">
<QueryTableHeaders>
<QueryTableCard sort="name">
<QueryTableBar>
<FilterButton>
<FilterInput name="name" label="nome" />
<FilterInput name="code" label="codice" />
Expand All @@ -52,10 +52,10 @@ export default function ExamsPage() {
<CsvDownloadButton cb={async (query)=>[]}/>
<ExcelDownloadButton />
</TableTopRightButtons>
</QueryTableHeaders>
</QueryTableBar>
<FilterBadges />
<QueryTableBody path={path} headers={headers} getField={getField} />
</QueryTable>
<QueryTable path={path} headers={headers} getField={getField} />
</QueryTableCard>
</>

function getField(item, field) {
Expand Down
Loading

0 comments on commit 5e114af

Please sign in to comment.