Skip to content

Commit

Permalink
4032 - Collaborators Grouped UI
Browse files Browse the repository at this point in the history
  • Loading branch information
minotogna committed Dec 16, 2024
1 parent c4453ba commit 8e7b273
Show file tree
Hide file tree
Showing 17 changed files with 233 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export const useOnChange = (user: User): Returned => {
return userRole
})

const params = { assessmentName, cycleName, roles, userId: user.id }
const params = { assessmentName, cycleName, roles, userUuid: user.uuid }
dispatch(UserManagementActions.updateUserRoles(params))
},
[assessmentName, countryIso, cycle, cycleName, dispatch, user]
Expand Down
6 changes: 6 additions & 0 deletions src/client/components/TablePaginated/Body/Body.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@import 'src/client/style/partials';

.table-paginated__groups {
display: grid;
grid-row-gap: $spacing-xs;
}
64 changes: 26 additions & 38 deletions src/client/components/TablePaginated/Body/Body.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,42 @@
import './Body.scss'
import React from 'react'

import classNames from 'classnames'
import { Objects } from 'utils/objects'

import { CountryIso } from 'meta/area'
import { CountryUserSummary } from 'meta/user'
import { CountryUserSummaries } from 'meta/user/countryUserSummaries'

import { useTablePaginatedData } from 'client/store/ui/tablePaginated'
import { useCountryRouteParams } from 'client/hooks/useRouteParams'
import DataColumn from 'client/components/DataGridDeprecated/DataColumn'
import Rows from 'client/components/TablePaginated/Body/Rows'
import RowsGroup from 'client/components/TablePaginated/Body/RowsGroup'
import RowsSkeleton from 'client/components/TablePaginated/Body/RowsSkeleton'
import { Props as BaseProps } from 'client/components/TablePaginated/types'

import { useTablePaginatedBodyData } from './hooks/useTablePaginatedBodyData'

const Body = <Datum extends object>(props: BaseProps<Datum>) => {
const { columns, compareFn, limit, path, wrapCells, skeleton } = props
const { columns, groups, limit, wrapCells, skeleton } = props

const data = useTablePaginatedBodyData<Datum>(props)

const data = useTablePaginatedData<Datum>(path, compareFn)
const { countryIso } = useCountryRouteParams<CountryIso>()
if (Objects.isNil(data)) {
return <RowsSkeleton columns={columns} limit={limit} skeleton={skeleton} />
}

// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const d2 = Object.groupBy(data ?? [], (d) => CountryUserSummaries.getRoleName(d as CountryUserSummary, countryIso))

// console.log({ d2: Object.entries(d2) })

return (
<>
{data.map((datum, rowIndex) => (
<React.Fragment key={`row_${String(rowIndex)}}`}>
{columns.map((column) => {
const { component: Component, key } = column

if (wrapCells) {
return (
<DataColumn key={key} className={classNames({ withBorder: rowIndex !== 0 })}>
<Component datum={datum} rowIndex={rowIndex} />
</DataColumn>
)
}

return <Component key={key} datum={datum} rowIndex={rowIndex} />
})}
</React.Fragment>
))}
</>
)
if (!Objects.isNil(groups)) {
return (
<div className="table-paginated__groups">
{(data as Array<[PropertyKey, Array<Datum>]>).map(([propertyKey, dataRows]) => (
<RowsGroup
key={propertyKey.toString()}
columns={columns}
data={dataRows}
groups={groups}
propertyKey={propertyKey}
wrapCells={wrapCells}
/>
))}
</div>
)
}

return <Rows columns={columns} data={data as Array<Datum>} wrapCells={wrapCells} />
}

export default Body
34 changes: 34 additions & 0 deletions src/client/components/TablePaginated/Body/Rows/Rows.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React from 'react'

import classNames from 'classnames'

import DataColumn from 'client/components/DataGridDeprecated/DataColumn'
import { Props as BaseProps } from 'client/components/TablePaginated/types'

type Props<Datum extends object> = Pick<BaseProps<Datum>, 'columns' | 'wrapCells'> & {
data: Array<Datum>
}

const Rows = <Datum extends object>(props: Props<Datum>) => {
const { columns, data, wrapCells } = props

return data.map((datum, rowIndex) => (
<React.Fragment key={`row_${String(rowIndex)}}`}>
{columns.map((column) => {
const { component: Component, key } = column

if (wrapCells) {
return (
<DataColumn key={key} className={classNames({ withBorder: rowIndex !== 0 })}>
<Component datum={datum} rowIndex={rowIndex} />
</DataColumn>
)
}

return <Component key={key} datum={datum} rowIndex={rowIndex} />
})}
</React.Fragment>
))
}

export default Rows
1 change: 1 addition & 0 deletions src/client/components/TablePaginated/Body/Rows/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './Rows'
31 changes: 31 additions & 0 deletions src/client/components/TablePaginated/Body/RowsGroup/RowsGroup.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
@import 'src/client/style/partials';

button.rows-group__header.inverse {
background-color: $ui-bg-light;
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
border-bottom: 1px solid transparent;
border-left: 1px solid $ui-border;
border-right: 1px solid $ui-border;
border-top: 1px solid $ui-border;

&.collapsed {
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
border-bottom: 1px solid $ui-border;

svg {
transform: rotate(-90deg);
}
}
}

.rows-group__rows {
background-color: $ui-bg-light;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
border-top-right-radius: 2px;
border: 1px solid $ui-border;
margin-top: -1px;
padding: $spacing-xxs;
}
41 changes: 41 additions & 0 deletions src/client/components/TablePaginated/Body/RowsGroup/RowsGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import './RowsGroup.scss'
import React, { useCallback, useState } from 'react'

import classNames from 'classnames'

import Button, { ButtonSize, ButtonType } from 'client/components/Buttons/Button'
import Rows from 'client/components/TablePaginated/Body/Rows'
import { Props as BaseProps } from 'client/components/TablePaginated/types'

type Props<Datum extends object> = Pick<BaseProps<Datum>, 'columns' | 'groups' | 'wrapCells'> & {
data: Array<Datum>
propertyKey: PropertyKey
}

const RowsGroup = <Datum extends object>(props: Props<Datum>) => {
const { columns, data, groups, propertyKey, wrapCells } = props

const [collapsed, setCollapsed] = useState<boolean>(false)
const toggleView = useCallback(() => setCollapsed((prevState) => !prevState), [])

return (
<div>
<Button
className={classNames('rows-group__header', { collapsed })}
iconName="small-down"
inverse
label={`${groups.headerLabel(propertyKey)} (${data.length})`}
onClick={toggleView}
size={ButtonSize.m}
type={ButtonType.black}
/>
{!collapsed && (
<div className="rows-group__rows">
<Rows columns={columns} data={data} wrapCells={wrapCells} />
</div>
)}
</div>
)
}

export default RowsGroup
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from './RowsGroup'
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Objects } from 'utils/objects'

import { useTablePaginatedData } from 'client/store/ui/tablePaginated'
import { Props as BaseProps } from 'client/components/TablePaginated/types'

type Returned<Datum extends object> = Array<Datum> | Array<[PropertyKey, Array<Datum>]> | undefined

export const useTablePaginatedBodyData = <Datum extends object>(props: BaseProps<Datum>): Returned<Datum> => {
const { compareFn, groups, path } = props

const data = useTablePaginatedData<Datum>(path, compareFn)

if (Objects.isNil(data) || Objects.isNil(groups)) {
return data
}

return Object.entries(Object.groupBy(data ?? [], groups.keySelector))
}
36 changes: 13 additions & 23 deletions src/client/components/TablePaginated/TablePaginated.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,8 @@ import React, { HTMLAttributes, useMemo, useRef } from 'react'
import Skeleton from 'react-loading-skeleton'

import classNames from 'classnames'
import { Objects } from 'utils/objects'

import { useTablePaginatedCount, useTablePaginatedData, useTablePaginatedPage } from 'client/store/ui/tablePaginated'
import { useOnUpdate } from 'client/hooks'
import { useTablePaginatedCount } from 'client/store/ui/tablePaginated'
import DataGrid from 'client/components/DataGridDeprecated'
import { PaginatorProps } from 'client/components/Paginator'
import Filters from 'client/components/TablePaginated/Filters/Filters'
Expand All @@ -15,6 +13,7 @@ import ExportButton from './ExportButton/ExportButton'
import { useFetchData } from './hooks/useFetchData'
import { useInitTablePaginated } from './hooks/useInitTablePaginated'
import { useResetOnUnmount } from './hooks/useResetOnUnmount'
import { useScrollToTopOnPageUpdate } from './hooks/useScrollToTopOnPageUpdate'
import Body from './Body'
import Count from './Count'
import DefaultEmptyList from './DefaultEmptyList'
Expand All @@ -33,31 +32,24 @@ type Props<Datum extends object> = Pick<HTMLAttributes<HTMLDivElement>, 'classNa
}

const TablePaginated = <Datum extends object>(props: Props<Datum>) => {
const { className, gridTemplateColumns } = props // HTMLDivElement Props
const { className, gridTemplateColumns: gridTemplateColumnsProps } = props // HTMLDivElement Props
const { marginPagesDisplayed, pageRangeDisplayed } = props // Paginator Props
const { columns, filters, limit, path } = props // Base Props
const { columns, filters, groups, limit, path } = props // Base Props
const { counter, EmptyListComponent, export: exportTable, header, skeleton, wrapCells, compareFn } = props // Component Props

const divRef = useRef<HTMLDivElement>()

useInitTablePaginated({ filters, path })
useFetchData({ counter, limit, path })
useResetOnUnmount({ path })

useScrollToTopOnPageUpdate({ divRef, path })
const count = useTablePaginatedCount(path)
const data = useTablePaginatedData(path)
const page = useTablePaginatedPage(path)

const gridTemplateColumns = useMemo<string | number>(
() => gridTemplateColumnsProps ?? `repeat(${columns.length}, auto)`,
[columns.length, gridTemplateColumnsProps]
)
const withFilters = useMemo<boolean>(() => filters.filter((filter) => !filter.hidden).length > 0, [filters])
const divRef = useRef<HTMLDivElement>()

// on page update -> scroll on top
useOnUpdate(() => {
if (!Objects.isNil(data)) {
setTimeout(() => {
const opts: ScrollIntoViewOptions = { behavior: 'smooth', block: 'start', inline: 'nearest' }
divRef.current?.parentElement?.parentElement?.scrollIntoView(opts)
})
}
}, [page])

return (
<div ref={divRef} className={classNames('table-paginated', className)}>
Expand All @@ -69,15 +61,13 @@ const TablePaginated = <Datum extends object>(props: Props<Datum>) => {
{withFilters && <Filters filters={filters} path={path} />}
</div>
)}
<DataGrid
className="table-paginated-datagrid"
style={{ gridTemplateColumns: gridTemplateColumns ?? `repeat(${columns.length}, auto)` }}
>
<DataGrid className="table-paginated-datagrid" style={{ gridTemplateColumns }}>
{header && <Header columns={columns} path={path} />}
{count?.total === 0 && <EmptyListComponent />}
<Body
columns={columns}
compareFn={compareFn}
groups={groups}
limit={limit}
path={path}
skeleton={skeleton}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { MutableRefObject } from 'react'

import { Objects } from 'utils/objects'

import { useTablePaginatedData, useTablePaginatedPage } from 'client/store/ui/tablePaginated'
import { useOnUpdate } from 'client/hooks'

type Props = {
divRef: MutableRefObject<HTMLDivElement>
path: string
}

export const useScrollToTopOnPageUpdate = (props: Props): void => {
const { divRef, path } = props

const data = useTablePaginatedData(path)
const page = useTablePaginatedPage(path)

useOnUpdate(() => {
if (!Objects.isNil(data)) {
setTimeout(() => {
const opts: ScrollIntoViewOptions = { behavior: 'smooth', block: 'start', inline: 'nearest' }
divRef.current?.parentElement?.parentElement?.scrollIntoView(opts)
})
}
}, [page])
}
1 change: 1 addition & 0 deletions src/client/components/TablePaginated/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export type Props<Datum extends object> = {
columns: Array<Column<Datum>>
compareFn?: TablePaginatedCompareFn<Datum>
filters?: Array<TablePaginatedFilter<TablePaginatedFilterType>>
groups?: { headerLabel: (key: PropertyKey) => string; keySelector: (datum: Datum) => PropertyKey }
limit?: number
path: string
skeleton?: TablePaginatedSkeleton
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,19 @@

.home-user-info {
&.expired {
.home-user-name {
.home-user-name .name {
text-decoration: line-through;
}
}
}

.home-user-name {
font-weight: 700;
margin-top: -6px;
}

.home-user-role {
align-items: center;
display: flex;
gap: 8px;

.role {
color: lighten($text-mute, 10%);
font-size: $font-xs;
text-transform: uppercase;
.name {
font-weight: 700;
}
}

Expand All @@ -33,9 +26,9 @@
border: 1px solid rgba($ui-alert, 0.8);
color: darken($ui-alert, 20%);
display: grid;
font-size: 9px;
font-size: 8px;
font-weight: 600;
height: 18px;
height: 16px;
justify-content: center;
letter-spacing: 0.4px;
line-height: 0;
Expand Down
Loading

0 comments on commit 8e7b273

Please sign in to comment.