From 110aa7b248679b3b88387c6f96c6f2dda9775963 Mon Sep 17 00:00:00 2001 From: Usama Idriss Kakumba <53287480+usamaidrsk@users.noreply.github.com> Date: Wed, 8 May 2024 16:27:49 +0300 Subject: [PATCH] (feat) O3-3014: Add expand appointments calendar view (#1123) * feat: add expand appointments calendar view * fix: fix with service area row * chore: add translation --- .../monthly/monthly-view-workload.scss | 59 ++++++++++--- ...nthly-workload-view-expanded.component.tsx | 42 +++++++++ .../monthly-workload-view.component.tsx | 85 ++++++++++++------- .../esm-appointments-app/translations/en.json | 3 +- 4 files changed, 149 insertions(+), 40 deletions(-) create mode 100644 packages/esm-appointments-app/src/calendar/monthly/monthly-workload-view-expanded.component.tsx diff --git a/packages/esm-appointments-app/src/calendar/monthly/monthly-view-workload.scss b/packages/esm-appointments-app/src/calendar/monthly/monthly-view-workload.scss index b0c37a864..6bd386367 100644 --- a/packages/esm-appointments-app/src/calendar/monthly/monthly-view-workload.scss +++ b/packages/esm-appointments-app/src/calendar/monthly/monthly-view-workload.scss @@ -25,10 +25,12 @@ border-left: 1px solid colors.$gray-20; border-bottom: 1px solid colors.$gray-20; } + &-disabled:last-child { border-right: 1px solid colors.$gray-20; } } + .identifiers { @include type.type-style('body-compact-02'); color: $ui-04; @@ -38,10 +40,12 @@ margin: 0rem 0.75rem; } } + .identifierTag { display: flex; align-items: center; } + .weekly-cell { position: relative; border-right: 1px solid colors.$gray-20; @@ -77,9 +81,10 @@ } .currentData { - margin: 2px; - padding-top: 2px; - background-color: colors.$gray-10; + display: flex; + flex-direction: column; + align-items: start; + margin: 0.1rem 0.1rem 0.5rem 0.1rem; & > span { @include type.type-style('label-02'); @@ -87,19 +92,21 @@ } .serviceArea { - padding: 0.0125rem; + width: 100%; + padding: 0.125rem; + margin-right: 0.5rem; display: grid; grid-template-columns: 4fr 1fr; justify-content: flex-start; - text-align: left; - margin: 0.125rem; - @include type.type-style('label-01'); color: #020f1b; cursor: pointer; + @include type.type-style('label-01'); & > span:first-child { text-align: left; text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; } & > span:nth-child(2) { @@ -108,8 +115,31 @@ } .serviceArea:hover { - opacity: 0.8; - border: 1px solid grey; + background-color: colors.$gray-10; +} + +.totals { + padding: 0.125rem; + display: grid; + grid-template-columns: 4fr 1fr; + justify-content: flex-start; + background-color: colors.$gray-10; + + & > div { + display: flex; + flex-direction: row; + align-items: center; + justify-content: flex-start; + cursor: pointer; + font-weight: lighter; + } + + & > span:nth-child(2) { + color: #020f1b; + font-weight: bold; + text-align: right; + @include type.type-style('heading-compact-01'); + } } .smallDesktop { @@ -144,6 +174,15 @@ font-weight: bold; } -.calendarDate { +.showMoreItems { + margin-top: 0.1rem; + padding: 0; @include type.type-style('heading-compact-01'); + text-decoration: underline; + color: colors.$blue-50; + background-color: colors.$white; + font-weight: lighter; + border: none; + outline: none; + cursor: pointer; } diff --git a/packages/esm-appointments-app/src/calendar/monthly/monthly-workload-view-expanded.component.tsx b/packages/esm-appointments-app/src/calendar/monthly/monthly-workload-view-expanded.component.tsx new file mode 100644 index 000000000..da2bdf95c --- /dev/null +++ b/packages/esm-appointments-app/src/calendar/monthly/monthly-workload-view-expanded.component.tsx @@ -0,0 +1,42 @@ +import React, { useEffect, useRef } from 'react'; +import { Popover, PopoverContent } from '@carbon/react'; +import styles from './monthly-view-workload.scss'; +import MonthlyWorkloadView, { type MonthlyWorkloadViewProps } from './monthly-workload-view.component'; +import { useTranslation } from 'react-i18next'; + +interface MonthlyWorkloadViewExpandedProps extends MonthlyWorkloadViewProps { + count: number; +} + +const MonthlyWorkloadViewExpanded: React.FC = ({ count, events, dateTime }) => { + const { t } = useTranslation(); + const [isOpen, setIsOpen] = React.useState(false); + const popoverRef = useRef(null); + + const handleClickOutside = (event) => { + if (popoverRef.current && !popoverRef.current.contains(event.target)) { + setIsOpen(false); + } + }; + + useEffect(() => { + document.addEventListener('click', handleClickOutside); + + return () => { + document.removeEventListener('click', handleClickOutside); + }; + }, []); + + return ( + + + + + + + ); +}; + +export default MonthlyWorkloadViewExpanded; diff --git a/packages/esm-appointments-app/src/calendar/monthly/monthly-workload-view.component.tsx b/packages/esm-appointments-app/src/calendar/monthly/monthly-workload-view.component.tsx index 3c0283011..eff4b9795 100644 --- a/packages/esm-appointments-app/src/calendar/monthly/monthly-workload-view.component.tsx +++ b/packages/esm-appointments-app/src/calendar/monthly/monthly-workload-view.component.tsx @@ -1,51 +1,80 @@ -import React, { useContext } from 'react'; +import React, { useContext, useMemo } from 'react'; import classNames from 'classnames'; import dayjs, { type Dayjs } from 'dayjs'; -import { useTranslation } from 'react-i18next'; import { navigate, useLayoutType } from '@openmrs/esm-framework'; import { isSameMonth } from '../../helpers'; import { spaHomePage } from '../../constants'; import styles from './monthly-view-workload.scss'; import { type DailyAppointmentsCountByService } from '../../types'; import SelectedDateContext from '../../hooks/selectedDateContext'; +import { User } from '@carbon/react/icons'; +import MonthlyWorkloadViewExpanded from './monthly-workload-view-expanded.component'; -interface MonthlyWorkloadViewProps { +export interface MonthlyWorkloadViewProps { events: Array; dateTime: Dayjs; + showAllServices?: boolean; } -const MonthlyWorkloadView: React.FC = ({ dateTime, events }) => { +const MonthlyWorkloadView: React.FC = ({ dateTime, events, showAllServices = false }) => { const layout = useLayoutType(); - const { t } = useTranslation(); const { selectedDate } = useContext(SelectedDateContext); - const currentData = events?.find( - (event) => dayjs(event.appointmentDate)?.format('YYYY-MM-DD') === dayjs(dateTime)?.format('YYYY-MM-DD'), + const currentData = useMemo( + () => + events?.find( + (event) => dayjs(event.appointmentDate)?.format('YYYY-MM-DD') === dayjs(dateTime)?.format('YYYY-MM-DD'), + ), + [events], ); - const onClick = (serviceUuid) => { + const visibleServices = useMemo(() => { + if (currentData?.services) { + if (showAllServices) return currentData.services; + return currentData.services.slice(0, layout === 'small-desktop' ? 2 : 4); + } + return []; + }, [currentData, showAllServices, layout, currentData]); + + const hasHiddenServices = useMemo(() => { + if (currentData?.services) { + if (showAllServices) return false; + return layout === 'small-desktop' ? currentData.services.length > 2 : currentData.services.length > 4; + } + return false; + }, [layout, currentData, currentData]); + + const onClick = (serviceUuid: string) => { navigate({ to: `${spaHomePage}/appointments/${dayjs(dateTime).format('YYYY-MM-DD')}/${serviceUuid}` }); }; return (
{ - onClick(''); - }} className={classNames( styles[isSameMonth(dateTime, dayjs(selectedDate)) ? 'monthly-cell' : 'monthly-cell-disabled'], - { - [styles.greyBackground]: currentData?.services, - [styles.smallDesktop]: layout === 'small-desktop', - [styles.largeDesktop]: layout !== 'small-desktop', - }, + showAllServices + ? {} + : { + [styles.smallDesktop]: layout === 'small-desktop', + [styles.largeDesktop]: layout !== 'small-desktop', + }, )}> {isSameMonth(dateTime, dayjs(selectedDate)) && (

- {dateTime.format('D')} +

+ {currentData?.services ? ( +
+ + {currentData?.services.reduce((sum, { count = 0 }) => sum + count, 0)} +
+ ) : ( +
+ )} + {dateTime.format('D')} +
{currentData?.services && (
- {currentData.services.map(({ serviceName, serviceUuid, count }, i) => ( + {visibleServices.map(({ serviceName, serviceUuid, count }, i) => (
= ({ dateTime, eve {count}
))} -
{ - e.stopPropagation(); - onClick(''); - }} - className={classNames(styles.serviceArea, styles.green)}> - {t('total', 'Total')} - {currentData?.services.reduce((sum, { count = 0 }) => sum + count, 0)} -
+ {hasHiddenServices ? ( + + ) : ( + '' + )}
)}

diff --git a/packages/esm-appointments-app/translations/en.json b/packages/esm-appointments-app/translations/en.json index f830fa67f..462922aab 100644 --- a/packages/esm-appointments-app/translations/en.json +++ b/packages/esm-appointments-app/translations/en.json @@ -58,6 +58,8 @@ "chooseProvider": "Choose a provider", "chooseService": "Select service", "completed": "Completed", + "countMore_one": "{{count}} more", + "countMore_other": "{{count}} more", "createAppointmentService": "Create appointment services", "createNewAppointment": "Create new appointment", "date": "Date", @@ -153,7 +155,6 @@ "time": "Time", "today": "Today", "todays": "Today's", - "total": "Total", "unscheduled": "Unscheduled", "unscheduledAppointments": "Unscheduled appointments", "unscheduledAppointments_lower": "unscheduled appointments",