From 004fae4a5b1f2b2f5421e671d61ee9baf6f65fbe Mon Sep 17 00:00:00 2001 From: "jakub.jozwiak" Date: Tue, 31 Aug 2021 21:03:59 +0200 Subject: [PATCH] feat(client): add history list for desktop --- .../src/elements/Dialog/Dialog.styled.ts | 2 +- .../CloseButton/CloseButton.stories.tsx | 5 +- .../buttons/CloseButton/CloseButton.styled.ts | 75 ++++++++++--------- .../CloseButton/CloseButton.types.d.ts | 12 ++- .../elements/buttons/CloseButton/index.tsx | 5 +- .../src/elements/fields/DateField/index.tsx | 19 ++++- .../src/elements/fields/Fields.types.d.ts | 3 + .../FunctionField/FunctionField.styled.ts | 6 ++ .../elements/fields/FunctionField/index.tsx | 13 +++- .../fields/InvitationStatusField/index.tsx | 8 +- .../src/elements/fields/TextField/index.tsx | 9 ++- .../lists/DetailedList/DetailedList.styled.ts | 8 +- .../DetailedList/DetailedList.types.d.ts | 7 +- .../DetailedList/NoData/NoData.styled.ts | 25 +++++++ .../DetailedList/NoData/NoData.types.d.ts | 3 + .../lists/DetailedList/NoData/index.tsx | 19 +++++ .../src/elements/lists/DetailedList/index.tsx | 32 ++++++-- .../client/src/enums/historyEvent.enum.ts | 5 ++ packages/client/src/i18n/resources/en.ts | 4 + packages/client/src/i18n/resources/pl.ts | 4 + packages/client/src/icons/Icons.stories.tsx | 4 + packages/client/src/icons/NoDataIcon.tsx | 36 +++++++++ packages/client/src/icons/PowerSupplyIcon.tsx | 23 ++++++ packages/client/src/icons/index.ts | 2 + packages/client/src/interfaces/api.types.d.ts | 6 ++ .../src/pages/authorized/Dashboard/index.tsx | 2 +- .../authorized/History/History.styled.ts | 13 ++++ .../pages/authorized/History/History.utils.ts | 24 ++++++ .../src/pages/authorized/History/index.tsx | 34 ++++++++- packages/client/src/utils/getCssColor.ts | 4 + packages/client/src/utils/index.ts | 3 + .../src/utils/mapHistoryEventToLabel.ts | 16 ++++ 32 files changed, 358 insertions(+), 73 deletions(-) create mode 100644 packages/client/src/elements/fields/FunctionField/FunctionField.styled.ts create mode 100644 packages/client/src/elements/lists/DetailedList/NoData/NoData.styled.ts create mode 100644 packages/client/src/elements/lists/DetailedList/NoData/NoData.types.d.ts create mode 100644 packages/client/src/elements/lists/DetailedList/NoData/index.tsx create mode 100644 packages/client/src/enums/historyEvent.enum.ts create mode 100644 packages/client/src/icons/NoDataIcon.tsx create mode 100644 packages/client/src/icons/PowerSupplyIcon.tsx create mode 100644 packages/client/src/pages/authorized/History/History.styled.ts create mode 100644 packages/client/src/pages/authorized/History/History.utils.ts create mode 100644 packages/client/src/utils/mapHistoryEventToLabel.ts diff --git a/packages/client/src/elements/Dialog/Dialog.styled.ts b/packages/client/src/elements/Dialog/Dialog.styled.ts index e883d86e..a9971ad6 100644 --- a/packages/client/src/elements/Dialog/Dialog.styled.ts +++ b/packages/client/src/elements/Dialog/Dialog.styled.ts @@ -1,6 +1,6 @@ import styled, { keyframes } from 'styled-components'; -import hexToRgba from '../../utils/hexToRgba'; +import { hexToRgba } from '../../utils'; import Card from '../Card'; const fadeIn = keyframes` diff --git a/packages/client/src/elements/buttons/CloseButton/CloseButton.stories.tsx b/packages/client/src/elements/buttons/CloseButton/CloseButton.stories.tsx index 6c00af3b..982daa8a 100644 --- a/packages/client/src/elements/buttons/CloseButton/CloseButton.stories.tsx +++ b/packages/client/src/elements/buttons/CloseButton/CloseButton.stories.tsx @@ -1,6 +1,7 @@ import { Meta, Story } from '@storybook/react/types-6-0'; import React from 'react'; +import { CloseButtonProps } from './CloseButton.types'; import CloseButton from './index'; export default { @@ -11,7 +12,3 @@ export default { const Template: Story = (args) => ; export const Default = Template.bind({}); -Default.args = { - noLabel: false, - label: 'Custom close label', -}; diff --git a/packages/client/src/elements/buttons/CloseButton/CloseButton.styled.ts b/packages/client/src/elements/buttons/CloseButton/CloseButton.styled.ts index 68fef821..e5e65d83 100644 --- a/packages/client/src/elements/buttons/CloseButton/CloseButton.styled.ts +++ b/packages/client/src/elements/buttons/CloseButton/CloseButton.styled.ts @@ -1,50 +1,51 @@ import styled, { css } from 'styled-components'; -const size = '21px'; - -export const baseLineStyle = css` - background: ${({ theme: { palette } }) => palette.text.secondary}; - border-radius: 2px; - height: 3px; - margin-top: 8px; - position: absolute; - transition: all 0.3s ease-in; - width: ${size}; -`; +import { getCssColor } from '../../../utils'; +import { StyledCloseButtonProps } from './CloseButton.types'; export const LeftRight = styled.div` - ${baseLineStyle}; transform: rotate(45deg); `; export const RightLeft = styled.div` - ${baseLineStyle}; transform: rotate(-45deg); `; -export const StyledButton = styled.button` - align-items: center; - background: transparent; - border: none; - cursor: pointer; - display: flex; - flex-direction: column; - height: ${size}; - outline: none; - position: relative; - width: ${size}; +export const StyledButton = styled.button( + ({ theme, size, color }) => css` + align-items: center; + background: transparent; + border: none; + cursor: pointer; + display: flex; + flex-direction: column; + height: ${size}; + outline: none; + position: relative; + width: ${size}; - :focus-visible { - box-shadow: 0 0 0 2px ${({ theme: { palette } }) => palette.primary.main}; - transition: box-shadow 150ms ease-in-out; - } + :focus-visible { + box-shadow: 0 0 0 2px ${theme.palette.primary.main}; + transition: box-shadow 150ms ease-in-out; + } - &:hover ${LeftRight} { - background: ${({ theme: { palette } }) => palette.colors.red}; - transform: rotate(-45deg); - } - &:hover ${RightLeft} { - background: ${({ theme: { palette } }) => palette.colors.red}; - transform: rotate(45deg); - } -`; + ${LeftRight}, ${RightLeft} { + border-radius: 2px; + height: 3px; + margin-top: 8px; + position: absolute; + transition: all 0.3s ease-in; + width: ${size}; + background: ${getCssColor({ theme, color })}; + } + + &:hover ${LeftRight} { + background: ${theme.palette.colors.red}; + transform: rotate(-45deg); + } + &:hover ${RightLeft} { + background: ${theme.palette.colors.red}; + transform: rotate(45deg); + } + `, +); diff --git a/packages/client/src/elements/buttons/CloseButton/CloseButton.types.d.ts b/packages/client/src/elements/buttons/CloseButton/CloseButton.types.d.ts index 3bcaac69..4646542d 100644 --- a/packages/client/src/elements/buttons/CloseButton/CloseButton.types.d.ts +++ b/packages/client/src/elements/buttons/CloseButton/CloseButton.types.d.ts @@ -1 +1,11 @@ -type CloseButtonProps = ButtonHTMLAttributes; +import { ButtonHTMLAttributes } from 'react'; + +interface CloseButtonProps extends ButtonHTMLAttributes { + size?: string; + color?: string; +} + +interface StyledCloseButtonProps { + size: string; + color: string; +} diff --git a/packages/client/src/elements/buttons/CloseButton/index.tsx b/packages/client/src/elements/buttons/CloseButton/index.tsx index 6bd88740..a9e942a4 100644 --- a/packages/client/src/elements/buttons/CloseButton/index.tsx +++ b/packages/client/src/elements/buttons/CloseButton/index.tsx @@ -1,9 +1,10 @@ import React from 'react'; import { LeftRight, RightLeft, StyledButton } from './CloseButton.styled'; +import { CloseButtonProps } from './CloseButton.types'; -const CloseButton = (props: CloseButtonProps) => ( - +const CloseButton = ({ color = 'text-secondary', size = '21px', ...rest }: CloseButtonProps) => ( + diff --git a/packages/client/src/elements/fields/DateField/index.tsx b/packages/client/src/elements/fields/DateField/index.tsx index b594c17f..b7193141 100644 --- a/packages/client/src/elements/fields/DateField/index.tsx +++ b/packages/client/src/elements/fields/DateField/index.tsx @@ -4,11 +4,20 @@ import { useTranslation } from 'react-i18next'; import { BaseRecordField } from '../Fields.types'; import { DateFieldProps } from './DateField.types'; -const DateField = ({ source, record, showTime }: DateFieldProps) => { +const DateField = ({ + source, + record, + showTime, + style, +}: DateFieldProps) => { const { i18n } = useTranslation(); if (!record || !record[source]) { - return

-

; + return ( +

+ - +

+ ); } // eslint-disable-next-line @typescript-eslint/no-unsafe-call const date = new Date(record[source].toString()); @@ -18,7 +27,11 @@ const DateField = ({ source, record, showTime }: Date }); const formattedDate = formatter.format(date); - return

{formattedDate}

; + return ( +

+ {formattedDate} +

+ ); }; DateField.displayName = 'DateField'; diff --git a/packages/client/src/elements/fields/Fields.types.d.ts b/packages/client/src/elements/fields/Fields.types.d.ts index acf4d022..5103c69e 100644 --- a/packages/client/src/elements/fields/Fields.types.d.ts +++ b/packages/client/src/elements/fields/Fields.types.d.ts @@ -1,3 +1,5 @@ +import { CSSProperties } from 'react'; + import { BaseApiResource } from '../../interfaces/api.types'; interface BaseFieldProps { @@ -7,6 +9,7 @@ interface BaseFieldProps { asTitle?: boolean; noLabel?: boolean; noTranslation?: boolean; + style?: CSSProperties; } // eslint-disable-next-line @typescript-eslint/no-explicit-any diff --git a/packages/client/src/elements/fields/FunctionField/FunctionField.styled.ts b/packages/client/src/elements/fields/FunctionField/FunctionField.styled.ts new file mode 100644 index 00000000..a577af60 --- /dev/null +++ b/packages/client/src/elements/fields/FunctionField/FunctionField.styled.ts @@ -0,0 +1,6 @@ +import styled from 'styled-components'; + +export const FunctionFieldParagraph = styled.p` + display: flex; + align-items: center; +`; diff --git a/packages/client/src/elements/fields/FunctionField/index.tsx b/packages/client/src/elements/fields/FunctionField/index.tsx index a4ee9b2d..d648704c 100644 --- a/packages/client/src/elements/fields/FunctionField/index.tsx +++ b/packages/client/src/elements/fields/FunctionField/index.tsx @@ -1,14 +1,23 @@ import React from 'react'; import { BaseRecordField } from '../Fields.types'; +import { FunctionFieldParagraph } from './FunctionField.styled'; import { FunctionFieldProps } from './FunctionField.types'; -const FunctionField = ({ render, record }: FunctionFieldProps) => { +const FunctionField = ({ + render, + record, + style, +}: FunctionFieldProps) => { if (!record) { return null; } - return

{render(record)}

; + return ( + + {render(record)} + + ); }; FunctionField.displayName = 'FunctionField'; diff --git a/packages/client/src/elements/fields/InvitationStatusField/index.tsx b/packages/client/src/elements/fields/InvitationStatusField/index.tsx index 5c9890f7..d1fe8e9f 100644 --- a/packages/client/src/elements/fields/InvitationStatusField/index.tsx +++ b/packages/client/src/elements/fields/InvitationStatusField/index.tsx @@ -16,14 +16,18 @@ const getStatusIcon = (status: InvitationStatus, expirationDate: Date) => { return ; }; -const InvitationStatusField = ({ record }: InvitationStatusFieldProps) => { +const InvitationStatusField = ({ record, style }: InvitationStatusFieldProps) => { if (!record) { return null; } const { status, expirationDate } = record; const statusIcon = getStatusIcon(status, expirationDate); - return {statusIcon}; + return ( + + {statusIcon} + + ); }; InvitationStatusField.displayName = 'InvitationStatusField'; diff --git a/packages/client/src/elements/fields/TextField/index.tsx b/packages/client/src/elements/fields/TextField/index.tsx index 3206e221..6bd6492f 100644 --- a/packages/client/src/elements/fields/TextField/index.tsx +++ b/packages/client/src/elements/fields/TextField/index.tsx @@ -5,7 +5,12 @@ import { BaseRecordField } from '../Fields.types'; import { Label, Wrapper } from './TextField.styled'; import { TextFieldProps } from './TextField.types'; -const TextField = ({ source, record, label }: TextFieldProps) => { +const TextField = ({ + source, + record, + label, + style, +}: TextFieldProps) => { const { t } = useTranslation(); if (!record) { @@ -13,7 +18,7 @@ const TextField = ({ source, record, label }: TextFie } return ( - + {label && }

{record[source] ?? -}

diff --git a/packages/client/src/elements/lists/DetailedList/DetailedList.styled.ts b/packages/client/src/elements/lists/DetailedList/DetailedList.styled.ts index b8aca98c..02e5d575 100644 --- a/packages/client/src/elements/lists/DetailedList/DetailedList.styled.ts +++ b/packages/client/src/elements/lists/DetailedList/DetailedList.styled.ts @@ -129,12 +129,8 @@ export const BulkActionsWrapper = styled.div<{ isOpen: boolean }>` `} `; -export const BulkCancelButton = styled(IconButton)` - color: ${({ theme }) => theme.palette.text.dark}; - margin-right: 9px; - svg { - width: 14px; - } +export const BulkCancelButtonWrapper = styled.div` + margin: 0 10px; `; export const BulkCancelWrapper = styled.div` diff --git a/packages/client/src/elements/lists/DetailedList/DetailedList.types.d.ts b/packages/client/src/elements/lists/DetailedList/DetailedList.types.d.ts index bb677f7c..85ea2092 100644 --- a/packages/client/src/elements/lists/DetailedList/DetailedList.types.d.ts +++ b/packages/client/src/elements/lists/DetailedList/DetailedList.types.d.ts @@ -1,4 +1,4 @@ -import { MouseEvent, ReactNode } from 'react'; +import { CSSProperties, MouseEvent, ReactNode } from 'react'; import { ITheme } from '../../../theme/Theme'; @@ -6,8 +6,11 @@ export interface DetailedListProps { onRowClick?: (event: MouseEvent) => void; resource: string; children: ReactNode; + noDataLabel?: string; // eslint-disable-next-line @typescript-eslint/no-explicit-any - rowStyle?: (record: any, theme: ITheme) => CSSProperties; + rowStyle?: (record: any, theme: ITheme) => CSSProperties | undefined; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + rowCellsStyle?: (record: any, theme: ITheme) => CSSProperties | undefined; } type PerPage = 5 | 10 | 15 | 25; diff --git a/packages/client/src/elements/lists/DetailedList/NoData/NoData.styled.ts b/packages/client/src/elements/lists/DetailedList/NoData/NoData.styled.ts new file mode 100644 index 00000000..66de76d3 --- /dev/null +++ b/packages/client/src/elements/lists/DetailedList/NoData/NoData.styled.ts @@ -0,0 +1,25 @@ +import styled, { css } from 'styled-components'; + +import { NoDataIcon } from '../../../../icons'; +import Card from '../../../Card'; + +export const Wrapper = styled.div` + display: flex; + justify-content: flex-start; +`; + +export const StyledNoDataIcon = styled(NoDataIcon)( + ({ theme: { palette } }) => css` + color: ${palette.primary.mainInvert}; + `, +); + +export const NoDataCard = styled(Card)` + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 40px; + width: 600px; + padding: 40px 0; +`; diff --git a/packages/client/src/elements/lists/DetailedList/NoData/NoData.types.d.ts b/packages/client/src/elements/lists/DetailedList/NoData/NoData.types.d.ts new file mode 100644 index 00000000..e54f60c7 --- /dev/null +++ b/packages/client/src/elements/lists/DetailedList/NoData/NoData.types.d.ts @@ -0,0 +1,3 @@ +interface NoDataProps { + label: string | undefined; +} diff --git a/packages/client/src/elements/lists/DetailedList/NoData/index.tsx b/packages/client/src/elements/lists/DetailedList/NoData/index.tsx new file mode 100644 index 00000000..af80d35a --- /dev/null +++ b/packages/client/src/elements/lists/DetailedList/NoData/index.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import { useTranslation } from 'react-i18next'; + +import { NoDataCard, StyledNoDataIcon, Wrapper } from './NoData.styled'; + +const NoData = ({ label }: NoDataProps) => { + const { t } = useTranslation(); + + return ( + + +

{t(label as never) ?? t('lists.general.noData')}

+ +
+
+ ); +}; + +export default NoData; diff --git a/packages/client/src/elements/lists/DetailedList/index.tsx b/packages/client/src/elements/lists/DetailedList/index.tsx index c383ecf2..797a822f 100644 --- a/packages/client/src/elements/lists/DetailedList/index.tsx +++ b/packages/client/src/elements/lists/DetailedList/index.tsx @@ -11,14 +11,15 @@ import { useMutation, useQuery } from 'react-query'; import { CSSProperties, useTheme } from 'styled-components'; import { useAxios, useSnackbar } from '../../../hooks'; -import { CancelIcon, TrashIcon } from '../../../icons'; +import { TrashIcon } from '../../../icons'; import { ApiList } from '../../../interfaces/api.types'; import { getLabelFromSource } from '../../../utils'; +import { CloseButton } from '../../buttons'; import { BaseFieldProps, BaseRecordField } from '../../fields/Fields.types'; import { Checkbox } from '../../inputs'; import { BulkActionsWrapper, - BulkCancelButton, + BulkCancelButtonWrapper, BulkCancelWrapper, DeleteButton, ListWrapper, @@ -33,9 +34,17 @@ import { TableRow, } from './DetailedList.styled'; import { DetailedListProps, PerPage } from './DetailedList.types'; +import NoData from './NoData'; import Pagination from './Pagination'; -const DetailedList = ({ onRowClick, children, resource, rowStyle }: DetailedListProps) => { +const DetailedList = ({ + onRowClick, + children, + resource, + rowStyle, + rowCellsStyle, + noDataLabel, +}: DetailedListProps) => { const theme = useTheme(); const axios = useAxios(); const { data: queryResult, refetch } = useQuery>(`/${resource}`); @@ -137,14 +146,18 @@ const DetailedList = ({ onRowClick, children, resource, rowStyle }: DetailedList return t(label as never) || t(`baseApiFields.${source}` as never); }; + if (!totalRecords) { + return ; + } + return ( - - - + + + {selectedRows.length}  {selectedRows.length > 1 ? t('lists.detailedList.items') : t('lists.detailedList.item')} @@ -171,6 +184,11 @@ const DetailedList = ({ onRowClick, children, resource, rowStyle }: DetailedList const injectedRowStyle = rowStyle ? (rowStyle(record, theme) as CSSProperties) : undefined; + + const injectedRowCellsStyle = rowCellsStyle + ? (rowCellsStyle(record, theme) as CSSProperties) + : undefined; + return ( @@ -188,7 +206,7 @@ const DetailedList = ({ onRowClick, children, resource, rowStyle }: DetailedList return ( - {cloneElement(child, { record })} + {cloneElement(child, { record, style: injectedRowCellsStyle })} ); })} diff --git a/packages/client/src/enums/historyEvent.enum.ts b/packages/client/src/enums/historyEvent.enum.ts new file mode 100644 index 00000000..6dbea4fa --- /dev/null +++ b/packages/client/src/enums/historyEvent.enum.ts @@ -0,0 +1,5 @@ +export enum HistoryEvent { + Open = 'open', + TurnedOff = 'turnOff', + TurnedOn = 'turnOn', +} diff --git a/packages/client/src/i18n/resources/en.ts b/packages/client/src/i18n/resources/en.ts index cac82f78..4323c832 100644 --- a/packages/client/src/i18n/resources/en.ts +++ b/packages/client/src/i18n/resources/en.ts @@ -32,6 +32,9 @@ const en = { admin: 'Admin', }, lists: { + general: { + noData: 'No data', + }, detailedList: { items: 'items', item: 'item', @@ -91,6 +94,7 @@ const en = { }, history: { title: 'History', + noData: 'History is empty', }, admin: { title: 'Admin panel', diff --git a/packages/client/src/i18n/resources/pl.ts b/packages/client/src/i18n/resources/pl.ts index ba78a7c0..643cb413 100644 --- a/packages/client/src/i18n/resources/pl.ts +++ b/packages/client/src/i18n/resources/pl.ts @@ -34,6 +34,9 @@ const pl: TranslationStructure = { admin: 'Admin', }, lists: { + general: { + noData: 'Brak danych', + }, detailedList: { items: 'wierszy', item: 'wiersz', @@ -93,6 +96,7 @@ const pl: TranslationStructure = { }, history: { title: 'Historia', + noData: 'Brak historii', }, admin: { title: 'Admin panel', diff --git a/packages/client/src/icons/Icons.stories.tsx b/packages/client/src/icons/Icons.stories.tsx index a5959711..4c450ab4 100644 --- a/packages/client/src/icons/Icons.stories.tsx +++ b/packages/client/src/icons/Icons.stories.tsx @@ -29,7 +29,9 @@ import { LogoutAllIcon, LogoutIcon, MoonIcon, + NoDataIcon, PolishFlagIcon, + PowerSupplyIcon, PrivilegesGroupIcon, QuestionMarkIcon, RefreshIcon, @@ -63,6 +65,8 @@ const IconsWrapper = styled.div` // const AllIconsPanel = () => ( + + diff --git a/packages/client/src/icons/NoDataIcon.tsx b/packages/client/src/icons/NoDataIcon.tsx new file mode 100644 index 00000000..23411212 --- /dev/null +++ b/packages/client/src/icons/NoDataIcon.tsx @@ -0,0 +1,36 @@ +import React, { forwardRef, memo, Ref, SVGProps } from 'react'; + +const NoDataIcon = (props: SVGProps, svgRef?: Ref) => ( + + + + + + + +); + +export default memo(forwardRef(NoDataIcon)); diff --git a/packages/client/src/icons/PowerSupplyIcon.tsx b/packages/client/src/icons/PowerSupplyIcon.tsx new file mode 100644 index 00000000..9dadeb70 --- /dev/null +++ b/packages/client/src/icons/PowerSupplyIcon.tsx @@ -0,0 +1,23 @@ +import React, { forwardRef, memo, Ref, SVGProps } from 'react'; + +const PowerSupplyIcon = (props: SVGProps, svgRef?: Ref) => ( + + + +); + +export default memo(forwardRef(PowerSupplyIcon)); diff --git a/packages/client/src/icons/index.ts b/packages/client/src/icons/index.ts index fe5adcc4..4dc8c238 100644 --- a/packages/client/src/icons/index.ts +++ b/packages/client/src/icons/index.ts @@ -24,7 +24,9 @@ export { default as LockIcon } from './LockIcon'; export { default as LogoutAllIcon } from './LogoutAllIcon'; export { default as LogoutIcon } from './LogoutIcon'; export { default as MoonIcon } from './MoonIcon'; +export { default as NoDataIcon } from './NoDataIcon'; export { default as PolishFlagIcon } from './PolishFlagIcon'; +export { default as PowerSupplyIcon } from './PowerSupplyIcon'; export { default as PrivilegesGroupIcon } from './PrivilegesGroupIcon'; export { default as QuestionMarkIcon } from './QuestionMarkIcon'; export { default as RefreshIcon } from './RefreshIcon'; diff --git a/packages/client/src/interfaces/api.types.d.ts b/packages/client/src/interfaces/api.types.d.ts index 659c2807..50c7a0e8 100644 --- a/packages/client/src/interfaces/api.types.d.ts +++ b/packages/client/src/interfaces/api.types.d.ts @@ -1,3 +1,4 @@ +import { HistoryEvent } from '../enums/historyEvent.enum'; import { InvitationStatus } from '../enums/invitationStatus.enum'; import { Role } from '../enums/role.enum'; @@ -15,6 +16,11 @@ interface ApiUser extends BaseApiResource { externalIntegrationsToken?: string; } +interface ApiHistoryRecord extends BaseApiResource { + user?: ApiUser; + event: HistoryEvent; +} + interface ApiInvitation extends BaseApiResource { email: string; expirationDate: Date; diff --git a/packages/client/src/pages/authorized/Dashboard/index.tsx b/packages/client/src/pages/authorized/Dashboard/index.tsx index 7dd3eea6..934b6add 100644 --- a/packages/client/src/pages/authorized/Dashboard/index.tsx +++ b/packages/client/src/pages/authorized/Dashboard/index.tsx @@ -2,7 +2,7 @@ import React, { useEffect } from 'react'; import { useTranslation } from 'react-i18next'; import { useAxios } from '../../../hooks'; -import registerWebPush from '../../../utils/registerWebPush'; +import { registerWebPush } from '../../../utils'; import { Title } from '../AuthorizedPages.styled'; import TogglingSection from './sections/TogglingSection'; diff --git a/packages/client/src/pages/authorized/History/History.styled.ts b/packages/client/src/pages/authorized/History/History.styled.ts new file mode 100644 index 00000000..9d42604c --- /dev/null +++ b/packages/client/src/pages/authorized/History/History.styled.ts @@ -0,0 +1,13 @@ +import styled from 'styled-components'; + +export const Wrapper = styled.div` + display: flex; + flex-direction: column; + height: 100%; +`; + +export const ListContainer = styled.div` + height: 100%; + overflow-x: auto; + padding-top: 60px; +`; diff --git a/packages/client/src/pages/authorized/History/History.utils.ts b/packages/client/src/pages/authorized/History/History.utils.ts new file mode 100644 index 00000000..cdcaffa5 --- /dev/null +++ b/packages/client/src/pages/authorized/History/History.utils.ts @@ -0,0 +1,24 @@ +import { CSSProperties } from 'react'; + +import { HistoryEvent } from '../../../enums/historyEvent.enum'; +import { ApiHistoryRecord } from '../../../interfaces/api.types'; +import { ITheme } from '../../../theme/Theme'; + +export const getRowCellsStyle = ( + { event }: ApiHistoryRecord, + { palette }: ITheme, +): CSSProperties | undefined => { + if (event === HistoryEvent.TurnedOn) { + return { + color: palette.primary.mainInvert, + }; + } + + if (event === HistoryEvent.TurnedOff) { + return { + color: palette.colors.red, + }; + } + + return undefined; +}; diff --git a/packages/client/src/pages/authorized/History/index.tsx b/packages/client/src/pages/authorized/History/index.tsx index bb25a287..58fccbc6 100644 --- a/packages/client/src/pages/authorized/History/index.tsx +++ b/packages/client/src/pages/authorized/History/index.tsx @@ -1,16 +1,44 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; +import { DateField, DetailedList, FunctionField } from '../../../elements'; +import { PowerSupplyIcon } from '../../../icons'; +import { ApiHistoryRecord } from '../../../interfaces/api.types'; +import { mapHistoryEventToLabel } from '../../../utils'; import { Title } from '../AuthorizedPages.styled'; +import { ListContainer, Wrapper } from './History.styled'; +import { getRowCellsStyle } from './History.utils'; const History = () => { const { t } = useTranslation(); return ( - <> + {t('routes.history.title')} -

{t('routes.general.sectionInConstruction')}

- + + + + label="user.name" + render={({ user }) => { + if (!user) { + return ; + } + const { firstName, lastName } = user; + return `${firstName} ${lastName}`; + }} + /> + + + label="Event" + render={({ event }) => mapHistoryEventToLabel(event)} + /> + + +
); }; diff --git a/packages/client/src/utils/getCssColor.ts b/packages/client/src/utils/getCssColor.ts index a9109ec0..5948d9ea 100644 --- a/packages/client/src/utils/getCssColor.ts +++ b/packages/client/src/utils/getCssColor.ts @@ -9,6 +9,10 @@ const getCssColor = ({ color, theme: { palette } }: GetColorProps): string => { switch (color) { case 'text-primary': return palette.text.primary; + case 'text-secondary': + return palette.text.secondary; + case 'text-dark': + return palette.text.dark; case 'primary': return palette.primary.main; case 'red': diff --git a/packages/client/src/utils/index.ts b/packages/client/src/utils/index.ts index 8e43963b..8e2d8106 100644 --- a/packages/client/src/utils/index.ts +++ b/packages/client/src/utils/index.ts @@ -1,4 +1,7 @@ export { default as getCssColor } from './getCssColor'; export { default as getLabelFromSource } from './getLabelFromSource'; export { default as getPlaceholderFromSource } from './getPlaceholderFromSource'; +export { default as hexToRgba } from './hexToRgba'; +export { default as mapHistoryEventToLabel } from './mapHistoryEventToLabel'; export { default as onlyOnDevEnv } from './onlyOnDevEnv'; +export { default as registerWebPush } from './registerWebPush'; diff --git a/packages/client/src/utils/mapHistoryEventToLabel.ts b/packages/client/src/utils/mapHistoryEventToLabel.ts new file mode 100644 index 00000000..05694ad8 --- /dev/null +++ b/packages/client/src/utils/mapHistoryEventToLabel.ts @@ -0,0 +1,16 @@ +import { HistoryEvent } from '../enums/historyEvent.enum'; + +const mapHistoryEventToLabel = (event: HistoryEvent) => { + switch (event) { + case HistoryEvent.Open: + return 'Unlock'; + case HistoryEvent.TurnedOff: + return 'Device turned off'; + case HistoryEvent.TurnedOn: + return 'Device turned on'; + default: + return event; + } +}; + +export default mapHistoryEventToLabel;