diff --git a/package.json b/package.json index 6594f7c6..7302743c 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/lib/query/index.ts b/src/lib/query/index.ts index 572da64c..ccf569a4 100644 --- a/src/lib/query/index.ts +++ b/src/lib/query/index.ts @@ -1 +1,2 @@ export * from './useInfiniteDataQuery' +export { useBoundQueryFn } from './useBoundQueryFn' diff --git a/src/lib/query/useBoundQueryFn.ts b/src/lib/query/useBoundQueryFn.ts new file mode 100644 index 00000000..2c141007 --- /dev/null +++ b/src/lib/query/useBoundQueryFn.ts @@ -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) => + ({ queryKey: [query], signal }: QueryFunctionContext<[Query]>) => + engine.query(query, { signal }) as Promise // engine.query is not generic... + +export const useBoundQueryFn = () => { + const dataEngine = useDataEngine() + + return useMemo(() => createBoundQueryFn(dataEngine), [dataEngine]) +} diff --git a/src/pages/organisationUnits/List.tsx b/src/pages/organisationUnits/List.tsx new file mode 100644 index 00000000..0255208c --- /dev/null +++ b/src/pages/organisationUnits/List.tsx @@ -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 & + Partial + +type OrganisationUnits = ModelCollectionResponse< + FilteredOrganisationUnits, + 'organisationUnits' +> + +type OrganisationUnitsResponse = WrapQueryResponse + +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( +// 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 ( +//
+// +//
+// ) +// } + +export const Component = () => { + return +} diff --git a/src/pages/organisationUnits/list/OrganisationUnitList.tsx b/src/pages/organisationUnits/list/OrganisationUnitList.tsx new file mode 100644 index 00000000..14b7758a --- /dev/null +++ b/src/pages/organisationUnits/list/OrganisationUnitList.tsx @@ -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> +export type OrganisationUnitListItem = Pick< + OrganisationUnit, + 'id' | 'displayName' | 'access' | 'children' | 'path' | 'level' | 'parent' +> & { + hasChildren?: boolean +} + +const useColumns = () => { + const columns: ColumnDef[] = [ + // { + // id: 'checkbox', + // // accessorKey: 'checkbox', + // header: ({ table }) => ( + // table.getToggleAllRowsSelectedHandler()} + // /> + // ), + // }, + { + header: 'DisplayName', + accessorKey: 'displayName', + cell: ({ row, getValue }) => getValue(), + }, + { + 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({ + // 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(), + 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 ( +
+ + {/* */} + ({ + label: h.column.columnDef.header as string, + path: h.column.id, + }))} + onSelectAll={() => table.toggleAllRowsSelected()} + > + {table.getRowModel().rows.map((row) => { + return ( + + + + {row.getCanExpand() ? ( +
+ ) +} diff --git a/src/pages/organisationUnits/list/useRootOrganisationUnit.tsx b/src/pages/organisationUnits/list/useRootOrganisationUnit.tsx new file mode 100644 index 00000000..cfdf541e --- /dev/null +++ b/src/pages/organisationUnits/list/useRootOrganisationUnit.tsx @@ -0,0 +1,110 @@ +import { useDataEngine } from '@dhis2/app-runtime' +import { useCallback, useEffect, useMemo } from 'react' +import { + useQuery, + useQueryClient, + useQueries, + UseQueryOptions, +} from 'react-query' +import { + createBoundQueryFn, + useBoundQueryFn, +} from '../../../lib/query/useBoundQueryFn' +import { WrapQueryResponse } from '../../../types' +import { OrganisationUnitListItem } from './OrganisationUnitList' +import { replace } from 'lodash' +import { ExpandedState } from '@tanstack/react-table' +import { WrapperComponent } from '@testing-library/react-hooks' + +const orgUnitFields = [ + 'id', + 'access', + 'displayName', + 'level', + 'path', + 'parent', + 'children[id,displayName,access,parent,level, path,children~isNotEmpty~rename(hasChildren)]', +] +const rootOrgUnitQuery = { + result: { + resource: 'organisationUnits', + params: { + paging: false, + fields: orgUnitFields, + filter: 'level:eq:1', + }, + }, +} as const + +type OrganisationUnitResponse = WrapQueryResponse<{ + organisationUnits: OrganisationUnitListItem[] +}> + +export const useRootOrganisationUnit = () => { + const dataEngine = useDataEngine() + const queryClient = useQueryClient() + const queryFn = useBoundQueryFn() + const query = useQuery({ + queryKey: [rootOrgUnitQuery], + queryFn: queryFn, + select: (data) => { + return data.result.organisationUnits // || [] + }, + staleTime: Infinity, + cacheTime: Infinity, + }) + return query +} + +export const useExpandedOrgUnits = ({ + expanded, +}: { + expanded: ExpandedState +}) => { + const rootOrgUnits = useRootOrganisationUnit() + + const queryFn = useBoundQueryFn() + const queryObjects = Object.entries(expanded) + .concat(rootOrgUnits.data?.map(({ id }) => [id, true]) ?? []) + .filter( + ([id, expanded], index, arr) => + !!expanded && arr.findIndex(([i]) => i === id) === index + ) + .map(([id, expanded]) => { + const query = { + result: { + resource: 'organisationUnits', + id, + params: { + fields: orgUnitFields, + // filter: `parent.id:eq:${id}`, + // sort: 'displayName:desc', + }, + }, + } as const + const options: UseQueryOptions< + WrapQueryResponse, + unknown, + OrganisationUnitListItem, + [typeof query] + > = { + queryKey: [query], + queryFn: queryFn>, + select: (data) => ({ + ...data.result, + children: data.result.children?.sort((a, b) => + a.displayName.localeCompare(b.displayName) + ), + }), + + // keepPreviousData: true, + staleTime: Infinity, + cacheTime: Infinity, + } + return options + }) + + console.log({ queryObjects }) + const qs = useQueries(queryObjects) + return qs +} diff --git a/src/types/query.ts b/src/types/query.ts index 533dcf2e..b97d1da6 100644 --- a/src/types/query.ts +++ b/src/types/query.ts @@ -1,5 +1,7 @@ import type { useDataQuery } from '@dhis2/app-runtime' +import { useDataEngine } from '@dhis2/app-runtime' +export type DataEngine = ReturnType export type QueryResponse = ReturnType export type Query = Parameters[0] diff --git a/yarn.lock b/yarn.lock index fcd45483..29d2d1a3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2935,6 +2935,18 @@ dependencies: defer-to-connect "^1.0.1" +"@tanstack/react-table@^8.16.0": + version "8.16.0" + resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.16.0.tgz#92151210ff99d6925353d7a2205735d9c31af48c" + integrity sha512-rKRjnt8ostqN2fercRVOIH/dq7MAmOENCMvVlKx6P9Iokhh6woBGnIZEkqsY/vEJf1jN3TqLOb34xQGLVRuhAg== + dependencies: + "@tanstack/table-core" "8.16.0" + +"@tanstack/table-core@8.16.0": + version "8.16.0" + resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.16.0.tgz#7b58018dd3cec8e0015fe22d6bb24d18d33c891f" + integrity sha512-dCG8vQGk4js5v88/k83tTedWOwjGnIyONrKpHpfmSJB8jwFHl8GSu1sBBxbtACVAPtAQgwNxl0rw1d3RqRM1Tg== + "@testing-library/dom@^8.0.0": version "8.20.0" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.0.tgz#914aa862cef0f5e89b98cc48e3445c4c921010f6"