Skip to content

Commit

Permalink
feat: organisation unit table tree
Browse files Browse the repository at this point in the history
  • Loading branch information
Birkbjo committed Apr 29, 2024
1 parent d1eeef9 commit 89650fb
Show file tree
Hide file tree
Showing 8 changed files with 416 additions and 0 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
"dependencies": {
"@dhis2/app-runtime": "^3.9.3",
"@dhis2/ui": "^9.2.0",
"@tanstack/react-table": "^8.16.0",
"@types/lodash": "^4.14.198",
"lodash": "^4.17.21",
"react-color": "^2.19.3",
Expand Down
1 change: 1 addition & 0 deletions src/lib/query/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export * from './useInfiniteDataQuery'
export { useBoundQueryFn } from './useBoundQueryFn'
16 changes: 16 additions & 0 deletions src/lib/query/useBoundQueryFn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { useDataEngine } from '@dhis2/app-runtime'
import { useMemo } from 'react'
import { QueryFunctionContext } from 'react-query'
import type { DataEngine, Query } from '../../types'
// types not exported from app-runtime...

export const createBoundQueryFn =
(engine: DataEngine) =>
<TData>({ queryKey: [query], signal }: QueryFunctionContext<[Query]>) =>
engine.query(query, { signal }) as Promise<TData> // engine.query is not generic...

export const useBoundQueryFn = () => {
const dataEngine = useDataEngine()

return useMemo(() => createBoundQueryFn(dataEngine), [dataEngine])
}
75 changes: 75 additions & 0 deletions src/pages/organisationUnits/List.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import { useDataQuery } from '@dhis2/app-runtime'
import React, { useEffect } from 'react'
import { SectionListWrapper } from '../../components'
import { useModelListView } from '../../components/sectionList/listView'
import {
useSchemaFromHandle,
useParamsForDataQuery,
DEFAULT_FIELD_FILTERS,
DefaultFields,
} from '../../lib/'
import { getFieldFilter } from '../../lib/models/path'
import { Query, WrapQueryResponse } from '../../types'
import {
DataElement,
ModelCollectionResponse,
OrganisationUnit,
} from '../../types/models'
import { OrganisationUnitList } from './list/OrganisationUnitList'

type FilteredOrganisationUnits = Pick<OrganisationUnit, DefaultFields> &
Partial<OrganisationUnit>

type OrganisationUnits = ModelCollectionResponse<
FilteredOrganisationUnits,
'organisationUnits'
>

type OrganisationUnitsResponse = WrapQueryResponse<OrganisationUnits>

const query: Query = {
result: {
resource: 'organisationUnits',
params: (params) => params,
},
}

// export const Component = () => {
// const { columns, query: listViewQuery } = useModelListView()
// const initialParams = useParamsForDataQuery()
// const schema = useSchemaFromHandle()
// const { refetch, error, data } = useDataQuery<OrganisationUnitsResponse>(
// query,
// // refetched on mount by effect below
// { lazy: true }
// )

// useEffect(() => {
// // wait to fetch until selected-columns are loaded
// // so we dont fetch data multiple times
// if (listViewQuery.isLoading) {
// return
// }
// refetch({
// ...initialParams,
// fields: columns
// .map((column) => getFieldFilter(schema, column.path))
// .concat(DEFAULT_FIELD_FILTERS),
// })
// }, [refetch, initialParams, columns, listViewQuery.isLoading, schema])

// return (
// <div>
// <SectionListWrapper
// error={error}
// data={data?.result.dataElements}
// pager={data?.result.pager}
// refetch={refetch}
// />
// </div>
// )
// }

export const Component = () => {
return <OrganisationUnitList />
}
199 changes: 199 additions & 0 deletions src/pages/organisationUnits/list/OrganisationUnitList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
import {
Button,
Checkbox,
DataTable,
DataTableCell,
DataTableRow,
IconChevronDown16,
IconChevronRight16,
} from '@dhis2/ui'
import {
Column,
Table,
ExpandedState,
useReactTable,
getCoreRowModel,
getPaginationRowModel,
getFilteredRowModel,
getExpandedRowModel,
ColumnDef,
flexRender,
} from '@tanstack/react-table'
import React, { useMemo, useState } from 'react'
import { SectionList, SectionListRow } from '../../../components'
import { FilterWrapper } from '../../../components/sectionList/filters/FilterWrapper'
import { DefaultListActions } from '../../../components/sectionList/listActions'
import { SectionListTitle } from '../../../components/sectionList/SectionListTitle'
import { OrganisationUnit } from '../../../types/generated'
import {
UseRootOrganisationUnit,
useExpandedOrgUnits,
useRootOrganisationUnit,
} from './useRootOrganisationUnit'

type PartialChildren = Partial<Pick<OrganisationUnit, 'children'>>
export type OrganisationUnitListItem = Pick<
OrganisationUnit,
'id' | 'displayName' | 'access' | 'children' | 'path' | 'level' | 'parent'
> & {
hasChildren?: boolean
}

const useColumns = () => {
const columns: ColumnDef<OrganisationUnitListItem>[] = [
// {
// id: 'checkbox',
// // accessorKey: 'checkbox',
// header: ({ table }) => (
// <Checkbox
// checked={table.getIsAllRowsSelected()}
// onChange={() => table.getToggleAllRowsSelectedHandler()}
// />
// ),
// },
{
header: 'DisplayName',
accessorKey: 'displayName',
cell: ({ row, getValue }) => getValue<string>(),
},
{
accessorKey: 'id',
header: 'id',
cell: (cell) => cell.getValue(),
},
]

return columns
}

export const OrganisationUnitList = () => {
// const rootQuery = useRootOrganisationUnit()
// rootQuery.data
const columns = useColumns()
//console.log(rootQuery)

const [expanded, setExpanded] = useState<ExpandedState>({
// ImspTQPwCqd: true,
})

console.log({ expanded })
const expandedQueries = useExpandedOrgUnits({ expanded })
const data = useMemo(
() =>
expandedQueries.filter((q) => q.isSuccess).map((q) => q.data!) ??
[],
[expandedQueries]
)

const rootData = useMemo(() => data.filter((d) => d.level === 1), [data])

console.log({ data, rootData })
const table = useReactTable({
columns,
data: rootData ?? [],
getRowId: (row) => row.id,
getCoreRowModel: getCoreRowModel<OrganisationUnitListItem>(),
getRowCanExpand: (row) => {
console.log({ row })
return (
(!!row.original?.children?.length &&
row.original.children.length > 0) ||
!!row.original.hasChildren
)
},
getSubRows: (row) => {
console.log({ data })
const dfilter = data
.filter((d) => d?.id === row.id)
.flatMap((d) => d.children)
console.log({ dfilter })
return dfilter
return data.filter((d) => d.id === row.id)
const expandedRows =
expandedQueries.find((q) => q.data?.result.id === row.id)?.data
?.result.children ?? []

console.log('subrows', row.children, expandedRows)
return row.children?.concat(expandedRows)
},

getExpandedRowModel: getExpandedRowModel(),
onExpandedChange: setExpanded,
state: {
expanded,
},
})

console.log({
table,
data: rootData,
exp: table.getExpandedRowModel(),
})
return (
<div>
<SectionListTitle />
{/* <FilterWrapper /> */}
<SectionList
allSelected={table.getIsAllRowsSelected()}
headerColumns={table.getHeaderGroups()[0].headers.map((h) => ({
label: h.column.columnDef.header as string,
path: h.column.id,
}))}
onSelectAll={() => table.toggleAllRowsSelected()}
>
{table.getRowModel().rows.map((row) => {
return (
<DataTableRow key={row.id}>
<DataTableCell>
<span
style={{
paddingLeft: `${row.depth * 2}rem`,
display: 'flex',
}}
>
{row.getCanExpand() ? (
<Button
secondary
type="button"
icon={
row.getIsExpanded() ? (
<IconChevronDown16 />
) : (
<IconChevronRight16 />
)
}
// onClick={row.getToggleExpandedHandler()}
onClick={() => row.toggleExpanded()}
/>
) : null}{' '}
<Checkbox
checked={row.getIsSelected()}
onChange={({ checked }) =>
row.toggleSelected(checked)
}
/>
</span>
</DataTableCell>
{row.getVisibleCells().map((cell) => {
return (
<DataTableCell key={cell.id}>
{flexRender(
cell.column.columnDef.cell,
cell.getContext()
)}
</DataTableCell>
)
})}
<DataTableCell>
{/* <DefaultListActions
model={row.original}
onShowDetailsClick={() => undefined}
/> */}
</DataTableCell>
</DataTableRow>
)
})}
</SectionList>
</div>
)
}
Loading

0 comments on commit 89650fb

Please sign in to comment.