From bcf68bd22508ad1b491f9afcc6b2571cc040fc64 Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Thu, 22 Feb 2024 20:35:48 +1300 Subject: [PATCH 01/10] hub map setup --- config-demo/modules.json | 4 + package.json | 1 + src/client/components/OfficeFloorMap.tsx | 238 +++++++++++++----- src/client/components/ui/Avatar.tsx | 2 +- src/client/components/ui/Button.tsx | 18 +- src/client/components/ui/DaySlider.tsx | 90 +++++++ src/client/components/ui/ImageWithPanZoom.tsx | 55 ++++ src/client/index.css | 15 ++ src/client/utils/hooks.ts | 9 +- .../events/client/components/EventBadge.tsx | 31 ++- .../events/client/components/EventPage.tsx | 6 +- .../events/client/components/EventsPage.tsx | 115 +++++++++ .../events/client/components/GlobalEvents.tsx | 2 +- .../client/components/UpcomingEvents.tsx | 20 +- src/modules/events/client/components/index.ts | 1 + src/modules/events/client/queries.ts | 14 +- src/modules/events/manifest.json | 10 +- .../events/server/models/event-application.ts | 1 + src/modules/events/server/models/event.ts | 1 + src/modules/events/server/models/index.ts | 13 + src/modules/events/server/router.ts | 194 ++++++++++---- src/modules/events/types.ts | 15 ++ .../client/components/AdminGuestInvite.tsx | 14 +- .../client/components/GuestInviteDetail.tsx | 17 +- .../hub-map/client/components/HubMap.tsx | 217 ++++++++++++++++ .../client/components/ScheduledItem.tsx | 144 +++++++++++ .../client/components/ScheduledItemsList.tsx | 149 +++++++++++ .../hub-map/client/components/index.ts | 1 + src/modules/hub-map/client/helpers/index.ts | 49 ++++ src/modules/hub-map/client/queries.ts | 19 ++ src/modules/hub-map/manifest.json | 19 ++ src/modules/hub-map/metadata-schema.ts | 5 + src/modules/hub-map/permissions.ts | 3 + src/modules/hub-map/server/helpers/index.ts | 138 ++++++++++ src/modules/hub-map/server/jobs/index.ts | 13 + src/modules/hub-map/server/models/index.ts | 1 + src/modules/hub-map/server/router.ts | 192 ++++++++++++++ src/modules/hub-map/types.ts | 0 .../news/client/components/LatestNews.tsx | 54 ++-- .../news/client/components/NewsListPage.tsx | 76 ++++++ src/modules/news/client/components/index.ts | 1 + src/modules/news/manifest.json | 7 + .../office-visits/server/helpers/index.ts | 52 +++- src/modules/office-visits/server/router.ts | 73 +++--- src/modules/office-visits/types.ts | 15 ++ .../client/components/RoomListing.tsx | 2 +- .../components/RoomReservationRequest.tsx | 43 +++- src/modules/room-reservation/server/router.ts | 39 +-- .../room-reservation/shared-helpers/index.ts | 11 + .../client/components/DateDeskPicker.tsx | 20 +- .../visits/client/components/DeskPicker.tsx | 14 +- .../visits/client/components/VisitDetail.tsx | 15 +- .../client/components/VisitRequestForm.tsx | 17 ++ src/modules/visits/server/router.ts | 2 + src/modules/visits/types.ts | 2 + src/server/app-config/schemas.ts | 44 ++-- src/server/constants.ts | 2 + yarn.lock | 16 +- 58 files changed, 2068 insertions(+), 273 deletions(-) create mode 100644 src/client/components/ui/DaySlider.tsx create mode 100644 src/client/components/ui/ImageWithPanZoom.tsx create mode 100644 src/modules/events/client/components/EventsPage.tsx create mode 100644 src/modules/hub-map/client/components/HubMap.tsx create mode 100644 src/modules/hub-map/client/components/ScheduledItem.tsx create mode 100644 src/modules/hub-map/client/components/ScheduledItemsList.tsx create mode 100644 src/modules/hub-map/client/components/index.ts create mode 100644 src/modules/hub-map/client/helpers/index.ts create mode 100644 src/modules/hub-map/client/queries.ts create mode 100644 src/modules/hub-map/manifest.json create mode 100644 src/modules/hub-map/metadata-schema.ts create mode 100644 src/modules/hub-map/permissions.ts create mode 100644 src/modules/hub-map/server/helpers/index.ts create mode 100644 src/modules/hub-map/server/jobs/index.ts create mode 100644 src/modules/hub-map/server/models/index.ts create mode 100644 src/modules/hub-map/server/router.ts create mode 100644 src/modules/hub-map/types.ts create mode 100644 src/modules/news/client/components/NewsListPage.tsx diff --git a/config-demo/modules.json b/config-demo/modules.json index 2ac1b89f..52f56c24 100644 --- a/config-demo/modules.json +++ b/config-demo/modules.json @@ -80,6 +80,10 @@ "id": "news", "enabled": true }, + { + "id": "hub-map", + "enabled": true + }, { "id": "profile-questions", "enabled": true, diff --git a/package.json b/package.json index 36d4608f..9a0384ec 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "pino-pretty": "^8.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-easy-panzoom": "^0.4.4", "react-query": "^3.39.1", "react-table": "^7.8.0", "sequelize": "^6.29.0", diff --git a/src/client/components/OfficeFloorMap.tsx b/src/client/components/OfficeFloorMap.tsx index e78addfd..23b3d6ac 100644 --- a/src/client/components/OfficeFloorMap.tsx +++ b/src/client/components/OfficeFloorMap.tsx @@ -1,73 +1,199 @@ -import React from 'react' -import { Button } from '#client/components/ui' -import { OfficeArea } from '#shared/types' +import React, { MouseEventHandler } from 'react' +import { Avatar, Button, P } from '#client/components/ui' +import { + ScheduledItemType, + OfficeArea, + OfficeAreaDesk, + OfficeRoom, + VisitType, +} from '#shared/types' import { cn } from '#client/utils' +import { useStore } from '@nanostores/react' +import * as stores from '#client/stores' +import { ImageWithPanZoom } from './ui/ImageWithPanZoom' + +type PointComponentFunctionProps = ( + item: OfficeAreaDesk | OfficeRoom, + isSelected: boolean, + isAvailable: boolean, + onClick: (id: string, kind: string) => MouseEventHandler +) => Element | JSX.Element + +const pointCommonStyle = + 'rounded-sm border-2 -translate-y-1/2 -translate-x-1/2 hover:scale-105 transition-all delay-100 ' + +const PointComponent: Record< + VisitType.Visit | VisitType.RoomReservation, + PointComponentFunctionProps +> = { + [VisitType.Visit]: (item, isSelected, isAvailable, onClick) => ( + + ), + [VisitType.RoomReservation]: (item, isSelected, isAvailable, onClick) => ( + + ), +} type OfficeFloorMapProps = { area: OfficeArea - availableDeskIds: string[] - selectedDeskId: string | null - onToggleDesk: (deskId: string) => void + mappablePoints?: Array + panZoom?: boolean + officeVisits?: Record> + showUsers?: boolean + selectedPointId: string | null + clickablePoints?: string[] + onToggle: (id: string, kind: string) => void } + export const OfficeFloorMap: React.FC = ({ area, - availableDeskIds, - selectedDeskId, - onToggleDesk, + mappablePoints, + panZoom = false, + officeVisits, + showUsers = false, + selectedPointId, + clickablePoints, + onToggle, }) => { + const me = useStore(stores.me) + const initialStartingPosition = selectedPointId + ? mappablePoints?.find( + (point: ScheduledItemType) => point.id === selectedPointId + ) + : null + const onClick = React.useCallback( - (deskId: string) => (ev: React.MouseEvent) => { + (id: string, kind: string) => (ev: React.MouseEvent) => { ev.preventDefault() - onToggleDesk(deskId) + onToggle(id, kind) }, - [onToggleDesk] + [onToggle] ) + + // @todo fix types here + const mapObjects = (scale: number) => + !mappablePoints + ? [] + : mappablePoints + .filter((x) => x.position) + .map((x) => { + let isSelected = selectedPointId === x.id + let isAvailable = false + let user = null + + if (!!officeVisits && me && showUsers) { + const bookedVisit: ScheduledItemType | undefined = + officeVisits.visit?.find( + (v) => v.areaId === area.id && v.objectId === x.id + ) + if (!!bookedVisit) { + if (bookedVisit?.user?.id === me?.id) { + user = me + } else { + user = bookedVisit.user + } + } + } + isSelected = selectedPointId === x.id + isAvailable = !!clickablePoints?.includes(x.id) + + const style = { + left: `${x.position?.x}%`, + top: `${x.position?.y}%`, + transform: `scale(${1 / scale})`, + transformOrigin: 'top left', + } + + if (!!user && !!me) { + return ( + + + + ) + } + return ( +
+ {/* @ts-ignore */} + {PointComponent[x.kind](x, isSelected, isAvailable, onClick)} +
+ ) + }) + return (
- {`${area.name} - {area.desks - .filter((x) => x.position) - .map((x) => { - const isSelected = selectedDeskId === x.id - const isAvailable = availableDeskIds.includes(x.id) - return ( -
- - -
- ) - })} +
+ {`${area.name} + {mapObjects(1)} +
+
+ mapObjects(scale)} + /> +
) } diff --git a/src/client/components/ui/Avatar.tsx b/src/client/components/ui/Avatar.tsx index 35f53271..e28deed8 100644 --- a/src/client/components/ui/Avatar.tsx +++ b/src/client/components/ui/Avatar.tsx @@ -57,7 +57,7 @@ export const Avatar: React.FC = ({ const [hasError, setHasError] = React.useState(false) const setError = () => setHasError(true) const resultClassName = cn( - 'rounded-full bg-gray-200', + 'block rounded-full bg-gray-200', SIZE_CLASSNAME[size], className ) diff --git a/src/client/components/ui/Button.tsx b/src/client/components/ui/Button.tsx index 837917a4..5ebdb737 100644 --- a/src/client/components/ui/Button.tsx +++ b/src/client/components/ui/Button.tsx @@ -153,11 +153,13 @@ export const RoundButton = ({ icon, className, disabled = false, + size = 'small', }: { onClick: (ev: React.MouseEvent) => void icon: Icon className?: string disabled?: boolean + size?: ButtonSize }) => { let IconComponent = Icons[icon] if (!IconComponent) { @@ -166,11 +168,13 @@ export const RoundButton = ({ } return ( - ) - })} -
- {userIsInOffce - ? `You and ${visitorsNumber - 1}` - : visitorsNumber}{' '} - people in the {office?.name} hub +
+
+ {!!visitors?.length && + visitors.map((v) => { + return ( + + ) + })}
+
+ {userIsInOffce ? `You and ${visitorsNumber - 1}` : visitorsNumber}{' '} + people in the {office?.name} hub +