Skip to content

Commit

Permalink
feat: dashboard layout (DHIS2-3600) (#1803)
Browse files Browse the repository at this point in the history
* don't want to lose the items

* don't want to lose the items

* don't want to lose the items

* fns and tests done, first attempt

* update snapshot

* should be close

* fix: sorting alg bug

* change the tests

* random number of columns

* remount the grid on auto layout

* snapshot

* rename dashboard layout reducer/action

* add layout to edit dashboard

* moved logic to reducer

* add use cases

* share logic in a thunk

* add item to start works

* minor

* minor

* adding vis and textbox works

* adding reports works

* add default value for layout columns

* new layout format works

* remove diff file

* tmp style

* ui for 'add to'

* ui for 'add to'

* add item to end WIP

* add item to end WIP

* add item to end WIP

* layout + add to end works

* all four combos of layout and addTo work

* addItemsTo default

* add to start works

* delete item with layout works

* delete item with layout works

* avoiding reload on add/remove works

* fix: proper html and css for the titlebar

* chore: snapshot update

* feat: add layout modal to edit mode

* avoid freeflow reload

* fix: i18n plural

* fix: prop name count

* connect columns in dialog to store

* resize handles

* resize handles

* resize handles

* keep height

* save columns works

* save insert position works

* layout bar style

* revert preserving height

* disable dragging and resizing in layout mode

* layout bar style

* fn refactor

* add important rules DONT DO THIS AT HOME

* remove comments

* d2 style

* upgrade

* update snapshots

* conflict

* update tests

* import order

Co-authored-by: Martin Ohlson <[email protected]>
  • Loading branch information
janhenrikoverland and martinkrulltott authored Aug 31, 2021
1 parent b313a39 commit 75da018
Show file tree
Hide file tree
Showing 29 changed files with 3,113 additions and 1,347 deletions.
66 changes: 61 additions & 5 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2021-08-23T09:48:29.014Z\n"
"PO-Revision-Date: 2021-08-23T09:48:29.014Z\n"
"POT-Creation-Date: 2021-08-31T11:12:36.355Z\n"
"PO-Revision-Date: 2021-08-31T11:12:36.355Z\n"

msgid "Untitled dashboard"
msgstr "Untitled dashboard"
Expand Down Expand Up @@ -252,12 +252,53 @@ msgstr "Insert"
msgid "Search for items to add to this dashboard"
msgstr "Search for items to add to this dashboard"

msgid "Search for visualizations, reports and more"
msgstr "Search for visualizations, reports and more"

msgid "Additional items"
msgstr "Additional items"

msgid "Text box"
msgstr "Text box"

msgid "Dashboard layout"
msgstr "Dashboard layout"

msgid "Freeflow"
msgstr "Freeflow"

msgid "Dashboard items can be placed anywhere, at any size."
msgstr "Dashboard items can be placed anywhere, at any size."

msgid "Fixed columns"
msgstr "Fixed columns"

msgid ""
"Dashboard items are automatically placed within fixed, horizontal columns. "
"The number of columns can be adjusted."
msgstr ""
"Dashboard items are automatically placed within fixed, horizontal columns. "
"The number of columns can be adjusted."

msgid "Number of columns"
msgstr "Number of columns"

msgid "Gap size between columns (px)"
msgstr "Gap size between columns (px)"

msgid ""
"Default height only applies to items added to a dashboard, this setting "
"will not change existing items"
msgstr ""
"Default height only applies to items added to a dashboard, this setting "
"will not change existing items"

msgid "Default height for items added to dashboard (rows)"
msgstr "Default height for items added to dashboard (rows)"

msgid "Save layout"
msgstr "Save layout"

msgid ""
"Creating dashboards on small screens is not supported. Resize your screen "
"to return to create mode."
Expand All @@ -271,6 +312,24 @@ msgstr "Dashboard title"
msgid "Dashboard description"
msgstr "Dashboard description"

msgid "Layout"
msgstr "Layout"

msgid "{{numberOfColumns}} columns"
msgstr "{{numberOfColumns}} columns"

msgid "Change layout"
msgstr "Change layout"

msgid "Add new items to"
msgstr "Add new items to"

msgid "End of dashboard"
msgstr "End of dashboard"

msgid "Start of dashboard"
msgstr "Start of dashboard"

msgid "Exit print preview"
msgstr "Exit print preview"

Expand Down Expand Up @@ -349,9 +408,6 @@ msgstr "Hide description"
msgid "Show description"
msgstr "Show description"

msgid "Dashboard layout"
msgstr "Dashboard layout"

msgid "One item per page"
msgstr "One item per page"

Expand Down
132 changes: 91 additions & 41 deletions src/actions/editDashboard.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,33 @@
import i18n from '@dhis2/d2-i18n'
import { generateUid } from 'd2/uid'
import { updateDashboard, postDashboard } from '../api/editDashboard'
import {
NEW_ITEM_SHAPE,
getGridItemProperties,
getPageBreakItemShape,
getPrintTitlePageItemShape,
addToItemsEnd,
addToItemsStart,
getAutoItemShapes,
getDashboardItem,
updateItems,
} from '../modules/gridUtil'
import { itemTypeMap, PAGEBREAK, PRINT_TITLE_PAGE } from '../modules/itemTypes'
import { convertUiItemsToBackend } from '../modules/uiBackendItemConverter'
import {
RECEIVED_EDIT_DASHBOARD,
START_NEW_DASHBOARD,
RECEIVED_NOT_EDITING,
RECEIVED_TITLE,
RECEIVED_DESCRIPTION,
RECEIVED_DASHBOARD_LAYOUT,
RECEIVED_DASHBOARD_ITEM_SHAPES,
ADD_DASHBOARD_ITEM,
UPDATE_DASHBOARD_ITEM,
REMOVE_DASHBOARD_ITEM,
SET_PRINT_PREVIEW_VIEW,
CLEAR_PRINT_PREVIEW_VIEW,
RECEIVED_FILTER_SETTINGS,
sGetEditDashboardRoot,
RECEIVED_HIDE_GRID,
RECEIVED_LAYOUT_COLUMNS,
RECEIVED_ITEM_CONFIG_INSERT_POSITION,
sGetEditDashboardItems,
sGetLayoutColumns,
sGetItemConfigInsertPosition,
} from '../reducers/editDashboard'
import { tFetchDashboards } from './dashboards'

Expand Down Expand Up @@ -59,42 +64,15 @@ export const acSetDashboardDescription = value => ({
value,
})

export const acUpdateDashboardLayout = value => ({
type: RECEIVED_DASHBOARD_LAYOUT,
export const acUpdateDashboardItemShapes = value => ({
type: RECEIVED_DASHBOARD_ITEM_SHAPES,
value,
})

export const acAddDashboardItem = item => {
const type = item.type
delete item.type
const itemPropName = itemTypeMap[type].propName

const id = generateUid()
const gridItemProperties = getGridItemProperties(id)

let shape
if (type === PAGEBREAK) {
const yPos = item.yPos || 0
shape = getPageBreakItemShape(yPos, item.isStatic)
} else if (type === PRINT_TITLE_PAGE) {
shape = getPrintTitlePageItemShape()
} else {
shape = NEW_ITEM_SHAPE
}

return {
type: ADD_DASHBOARD_ITEM,
value: {
id,
type,
position: item.position || null,
[itemPropName]: item.content,
...NEW_ITEM_SHAPE,
...gridItemProperties,
...shape,
},
}
}
export const acAddDashboardItem = item => ({
type: ADD_DASHBOARD_ITEM,
value: item,
})

export const acUpdateDashboardItem = item => ({
type: UPDATE_DASHBOARD_ITEM,
Expand All @@ -111,11 +89,83 @@ export const acSetFilterSettings = value => ({
value,
})

export const acSetHideGrid = value => ({
type: RECEIVED_HIDE_GRID,
value,
})

export const acSetLayoutColumns = value => ({
type: RECEIVED_LAYOUT_COLUMNS,
value,
})

export const acSetItemConfigInsertPosition = value => ({
type: RECEIVED_ITEM_CONFIG_INSERT_POSITION,
value,
})

// thunks

// no layout + end: add to new row at the end, default size
// no layout + start: add to 0,0, default size
// layout + end: calculate and add to "next shape in layout"
// layout + start: add to 0,0,0,0, sort, remount

export const tSetDashboardItems =
(itemToAdd, itemIdToRemove) => (dispatch, getState) => {
const insertPosition = sGetItemConfigInsertPosition(getState())
const columns = sGetLayoutColumns(getState())

let items = [...sGetEditDashboardItems(getState())]
let dashboardItemsWithShapes

if (!itemToAdd && !itemIdToRemove) {
// changing columns

if (!columns.length) {
// freeflow
updateItems(items, dispatch)
} else {
dashboardItemsWithShapes = getAutoItemShapes(items, columns)
updateItems(dashboardItemsWithShapes, dispatch, {
reload: true,
})
}
} else {
if (itemIdToRemove) {
items = items.filter(item => item.id !== itemIdToRemove)
}

if (!itemToAdd) {
dashboardItemsWithShapes = getAutoItemShapes(items, columns)
updateItems(dashboardItemsWithShapes, dispatch)
} else {
const newDashboardItem = getDashboardItem(itemToAdd)

switch (insertPosition) {
case 'START':
dashboardItemsWithShapes = addToItemsStart(
items,
columns,
newDashboardItem
)
break
case 'END':
default:
dashboardItemsWithShapes = addToItemsEnd(
items,
columns,
newDashboardItem
)
}

updateItems(dashboardItemsWithShapes, dispatch)
}
}
}

export const tSaveDashboard = () => async (dispatch, getState, dataEngine) => {
const dashboard = sGetEditDashboardRoot(getState())

const dashboardToSave = {
...dashboard,
dashboardItems: convertUiItemsToBackend(dashboard.dashboardItems),
Expand Down
2 changes: 2 additions & 0 deletions src/api/editDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ const generatePayload = (dashboard = {}, data) => {
})),
allowedFilters: data.allowedFilters,
restrictFilters: data.restrictFilters,
layout: data.layout,
itemConfig: data.itemConfig,
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/api/fetchDashboard.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ const baseDashboardFields = arrayClean([
'access',
'restrictFilters',
'allowedFilters',
'layout',
'itemConfig',
`dashboardItems[${getDashboardItemsFields().join(',')}]`,
])

Expand Down Expand Up @@ -68,6 +70,7 @@ export const apiFetchDashboard = async (
{ mode = null, forSave = false } = {}
) => {
const query = isViewMode(mode) ? viewDashboardQuery : editDashboardQuery

try {
const dashboardData = await dataEngine.query(
{ dashboard: query },
Expand Down
28 changes: 21 additions & 7 deletions src/components/Item/ItemHeader/EditItemActions.js
Original file line number Diff line number Diff line change
@@ -1,27 +1,41 @@
import PropTypes from 'prop-types'
import React from 'react'
import { connect } from 'react-redux'
import { acRemoveDashboardItem } from '../../../actions/editDashboard'
import {
acRemoveDashboardItem,
tSetDashboardItems,
} from '../../../actions/editDashboard'
import {
sGetEditDashboardItems,
sGetLayoutColumns,
} from '../../../reducers/editDashboard'
import DeleteItemButton from './DeleteItemButton'
import classes from './styles/ItemHeader.module.css'

const EditItemActions = ({ itemId, acRemoveDashboardItem }) => {
const handleDeleteItem = () => acRemoveDashboardItem(itemId)

const EditItemActions = ({ itemId, onDeleteItem }) => {
return (
<div className={classes.itemActionsWrap}>
<DeleteItemButton onClick={handleDeleteItem} />
<DeleteItemButton onClick={() => onDeleteItem(itemId)} />
</div>
)
}

EditItemActions.propTypes = {
acRemoveDashboardItem: PropTypes.func,
itemId: PropTypes.string,
onDeleteItem: PropTypes.func,
}

const mapDispatchToProps = {
acRemoveDashboardItem,
onDeleteItem: itemId => (dispatch, getState) => {
const columns = sGetLayoutColumns(getState())
const dashboardItems = sGetEditDashboardItems(getState())

if (!columns.length || dashboardItems.length === 1) {
dispatch(acRemoveDashboardItem(itemId))
} else {
dispatch(tSetDashboardItems(null, itemId))
}
},
}

export default connect(null, mapDispatchToProps)(EditItemActions)
4 changes: 4 additions & 0 deletions src/components/styles/ItemGrid.css
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
cursor: move;
}

.react-grid-item.edit.react-resizable-hide {
cursor: auto;
}

.react-grid-item.SPACER.view {
visibility: hidden;
}
Expand Down
Loading

0 comments on commit 75da018

Please sign in to comment.