From 10b862baf92141614967396f31eba9256994affc Mon Sep 17 00:00:00 2001 From: Daria Mikhailova Date: Wed, 20 Mar 2024 16:48:37 +1300 Subject: [PATCH] guest invites added --- src/client/components/Header.tsx | 7 +++ src/client/components/OfficeFloorMap.tsx | 13 +++-- src/client/components/ui/ImageWithPanZoom.tsx | 2 +- .../about/client/components/AboutPage.tsx | 11 ++++- .../client/components/GuestInviteDetail.tsx | 2 - .../hub-map/client/components/HubMap.tsx | 3 +- .../client/components/ScheduledItem.tsx | 16 +++--- .../client/components/ScheduledItemsList.tsx | 41 ++++++++++------ src/modules/hub-map/server/helpers/index.ts | 23 +++++++++ src/modules/hub-map/server/router.ts | 49 +++++++++++++++++++ src/modules/hub-map/types.ts | 2 +- 11 files changed, 136 insertions(+), 33 deletions(-) diff --git a/src/client/components/Header.tsx b/src/client/components/Header.tsx index 429bd436..968e1429 100644 --- a/src/client/components/Header.tsx +++ b/src/client/components/Header.tsx @@ -41,6 +41,13 @@ export const Header: React.FC = () => { icon: 'Gear', }) } + if (stores.isAdmin) { + items.push({ + name: 'Admin', + link: '/admin', + icon: 'Person', + }) + } items.push({ name: 'Logout', icon: 'Logout', diff --git a/src/client/components/OfficeFloorMap.tsx b/src/client/components/OfficeFloorMap.tsx index 12197c63..77861f54 100644 --- a/src/client/components/OfficeFloorMap.tsx +++ b/src/client/components/OfficeFloorMap.tsx @@ -20,7 +20,7 @@ type PointComponentFunctionProps = ( ) => Element | JSX.Element const pointCommonStyle = - 'rounded border-2 -translate-y-1/2 -translate-x-1/2 hover:scale-105 transition-all delay-100' + 'border-2 -translate-y-1/2 -translate-x-1/2 hover:scale-105 transition-all delay-100' const PointComponent: Record< VisitType.Visit | VisitType.RoomReservation, @@ -58,11 +58,14 @@ const PointComponent: Record< ? 'border-pink-600 hover:text-white bg-accents-pink hover:bg-accents-pinkDark' : 'text-black bg-green-200 border-green-200 hover:bg-cta-jade hover:border-cta-hover-jadeNoOpacity hover:text-white', 'sm:p-4', - pointCommonStyle + pointCommonStyle, + 'absolute' )} onClick={onClick(item.id, VisitType.RoomReservation)} > -

{item.name}

+

+ {!isSelected && 'Book'} {item.name} +

), } @@ -184,7 +187,7 @@ export const OfficeFloorMap: React.FC = ({ {`${area.name} {mapObjects(1)} @@ -197,7 +200,7 @@ export const OfficeFloorMap: React.FC = ({ mapObjects(scale)} initialStartPosition={ initialStartPosition ? initialStartPosition.position : undefined diff --git a/src/client/components/ui/ImageWithPanZoom.tsx b/src/client/components/ui/ImageWithPanZoom.tsx index c06c40bc..5bab5a7e 100644 --- a/src/client/components/ui/ImageWithPanZoom.tsx +++ b/src/client/components/ui/ImageWithPanZoom.tsx @@ -61,7 +61,7 @@ export const ImageWithPanZoom = ({ ref={imageRef} src={src} alt={alt} - className={cn('max-w-none h-auto', className)} + className={cn('max-w-auto max-h-[720px] m-auto', className)} />
{/* passing scale so we can do reverse scaling on the mapped points */} diff --git a/src/modules/about/client/components/AboutPage.tsx b/src/modules/about/client/components/AboutPage.tsx index 9da71299..68e324e5 100644 --- a/src/modules/about/client/components/AboutPage.tsx +++ b/src/modules/about/client/components/AboutPage.tsx @@ -14,11 +14,14 @@ import { useDocumentTitle, useOffice } from '#client/utils/hooks' import { useVisitsAreas } from '#modules/visits/client/queries' import { dropMarker } from '#client/components/ui/Map/mapbox' import dayjs from 'dayjs' -import { useMemo } from 'react' +import { useEffect, useMemo, useState } from 'react' +import { openPage } from '#client/stores' export const AboutPage: React.FC = () => { const page = useStore(stores.router) const hubId = page?.route === 'aboutPage' ? page?.params?.hubId : null + const officeId = useStore(stores.officeId) + const [currentOfficeId, setCurrentOfficeId] = useState(officeId) const office = useOffice(hubId ?? '') const { data: areas = [] } = useVisitsAreas(office?.id || '') @@ -33,6 +36,12 @@ export const AboutPage: React.FC = () => { action: onLoad, }, ] + useEffect(() => { + if (currentOfficeId !== officeId) { + openPage(stores.router, 'aboutPage', { hubId: officeId }) + setCurrentOfficeId(officeId) + } + }, [officeId]) const coreHours = useMemo(() => { if (!office || office.workingHours?.length !== 2) { diff --git a/src/modules/guest-invites/client/components/GuestInviteDetail.tsx b/src/modules/guest-invites/client/components/GuestInviteDetail.tsx index 04f88913..ae4daf34 100644 --- a/src/modules/guest-invites/client/components/GuestInviteDetail.tsx +++ b/src/modules/guest-invites/client/components/GuestInviteDetail.tsx @@ -22,8 +22,6 @@ import { DATE_FORMAT_DAY_NAME_FULL } from '#client/constants' import { useVisitsAreas } from '#modules/visits/client/queries' import { OfficeFloorMap } from '#client/components/OfficeFloorMap' import { useUserCompact } from '#modules/users/client/queries' -//@todo better place for this function -import { addParams } from '#modules/hub-map/client/helpers' export const GuestInviteDetail = () => ( { placeholder={'Select area'} containerClassName="w-full sm:w-auto mb-2 block sm:hidden" /> -
+
= { const ColorsBg: Record = { [VisitType.RoomReservation]: 'bg-cta-hover-jade', [VisitType.Visit]: 'bg-cta-hover-purple', - [VisitType.Guest]: 'bg-cta-hover-cerullean', + [VisitType.Guest]: 'bg-cta-hover-cerulean', event: 'bg-gray-50', } const ColorsBorder: Record = { [VisitType.RoomReservation]: 'border-cta-jade', [VisitType.Visit]: 'border-cta-purple', - [VisitType.Guest]: 'border-cta-hover-cerullean', + [VisitType.Guest]: 'border-cta-cerulean', event: 'border-gray-300', } const ColorsHover: Record = { - [VisitType.RoomReservation]: `hover:border-cta-jade`, - [VisitType.Visit]: `hover:border-cta-purple`, - [VisitType.Guest]: `hover:border-cta-hover-cerullean'`, + [VisitType.RoomReservation]: 'hover:border-cta-jade', + [VisitType.Visit]: 'hover:border-cta-purple', + [VisitType.Guest]: 'hover:border-cta-cerulean', event: 'hover:border-gray-300', } @@ -74,7 +74,8 @@ export const ScheduledItem = ({ id: string, type: string, value: string, - date: string + date: string, + dates?: string[] ) => void }) => { const iAmSelected = selected == sheduledItem.id @@ -131,7 +132,8 @@ export const ScheduledItem = ({ sheduledItem.id, sheduledItem.type, sheduledItem.value, - sheduledItem.date + sheduledItem.date, + sheduledItem.dates ) } > diff --git a/src/modules/hub-map/client/components/ScheduledItemsList.tsx b/src/modules/hub-map/client/components/ScheduledItemsList.tsx index 3b38464e..99ea698d 100644 --- a/src/modules/hub-map/client/components/ScheduledItemsList.tsx +++ b/src/modules/hub-map/client/components/ScheduledItemsList.tsx @@ -25,7 +25,6 @@ export const ScheduledItemsList: React.FC<{ className?: string }> = ({ onChooseCard, setDate, date, className }) => { const officeId = useStore(stores.officeId) - const office = useOffice(officeId) const [scheduledItems, setScheduledItems] = React.useState([]) const [selected, setSelected] = React.useState(null) @@ -68,6 +67,7 @@ export const ScheduledItemsList: React.FC<{ const resetView = () => { setSelected(null) + console.log(myUpcomingScheduledItems.upcoming) setScheduledItems(myUpcomingScheduledItems.upcoming) onChooseCard(null, selected?.areaId ?? '', dayjs()) } @@ -100,11 +100,21 @@ export const ScheduledItemsList: React.FC<{ id: string, type: string, value: string, - date: string + date: string, + dates?: string[] ) => { - const confirmMessage = `Are you sure you want to cancel this ${type}: ${value} on ${dayjs( - date - ).format(FRIENDLY_DATE_FORMAT)}?` + let confirmMessage = '' + console.log(dates) + if (type === VisitType.Guest && !!dates && dates?.length > 1) { + const otherDates = dates + ?.map((d) => dayjs(d).format(FRIENDLY_DATE_FORMAT)) + .join('\n\n') + confirmMessage = `By cancelling this guest invite for ${value}, you will cancel the invite for these OTHER DATES as well:\n\n${otherDates}` + } else { + confirmMessage = `Are you sure you want to cancel this ${type}: ${value} on ${dayjs( + date + ).format(FRIENDLY_DATE_FORMAT)}?` + } if (window.confirm(confirmMessage)) { const data: updateData = { id, @@ -136,15 +146,18 @@ export const ScheduledItemsList: React.FC<{
)} {!!scheduledItems?.length && - scheduledItems.map((item: ScheduledItemType, index) => ( - - ))} + scheduledItems.map((item: ScheduledItemType) => { + console.log(item?.id + item.value + item.date) + return ( + + ) + })}
) diff --git a/src/modules/hub-map/server/helpers/index.ts b/src/modules/hub-map/server/helpers/index.ts index 34be8e20..4bee957c 100644 --- a/src/modules/hub-map/server/helpers/index.ts +++ b/src/modules/hub-map/server/helpers/index.ts @@ -1,3 +1,4 @@ +import { GuestInvite } from '#modules/guest-invites/server/models' import { appConfig } from '#server/app-config' import { DATE_FORMAT, FRIENDLY_DATE_FORMAT_SHORT } from '#server/constants' import { @@ -44,6 +45,24 @@ export const formatRoomReservationsResult = ( } } +export const formatGuestInvite = ( + g: GuestInvite & { date: string }, + v: Visit +): ScheduledItemType => { + return { + id: v.id, + value: g.fullName, + type: VisitType.Guest, + date: g.date, + dates: g.dates, + description: `Desk ${v.deskName} - ${v.areaName}`, + dateTime: `Guest visit`, + areaId: v.areaId, + objectId: v.deskId, + status: g.status, + } +} + export const formatVisit = ( v: Visit, user?: User | null @@ -110,6 +129,10 @@ export const getVisits = async ( status: { [Op.in]: ['confirmed', 'pending'], }, + [Op.or]: { + metadata: { [Op.eq]: {} }, + 'metadata.guestInvite': { [Op.ne]: 'true' }, + }, date: { [Op.gte]: dayjs(date).toDate(), }, diff --git a/src/modules/hub-map/server/router.ts b/src/modules/hub-map/server/router.ts index 3349fad7..ccdb7f90 100644 --- a/src/modules/hub-map/server/router.ts +++ b/src/modules/hub-map/server/router.ts @@ -11,6 +11,7 @@ import dayjs from 'dayjs' import { FastifyPluginCallback, FastifyRequest } from 'fastify' import { formatEvent, + formatGuestInvite, formatRoomReservationsResult, formatVisit, getDate, @@ -174,6 +175,53 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) { } } + const today = dayjs().startOf('day') + const lastDay = dayjs().startOf('day').add(14, 'days') + + const guests = await fastify.db.GuestInvite.findAll({ + attributes: ['fullName', 'id', 'dates'], + where: { + creatorUserId: req.user.id, + office: officeId, + status: 'confirmed', + }, + raw: true, + }) + + const dailyGuests = [] + for (const oneGuest of guests) { + const guestInvitations = oneGuest.dates + .map((date) => ({ + ...oneGuest, + date, + })) + .filter( + (invite) => + dayjs(invite.date).isSameOrAfter(today) && + dayjs(invite.date).isSameOrBefore(lastDay) + ) + + for (const oneInvitation of guestInvitations) { + const visit = await fastify.db.Visit.findOne({ + where: { + 'metadata.guestInviteId': oneInvitation.id, + }, + }) + const invite = formatGuestInvite(oneInvitation, visit) + dailyGuests.push(invite) + addToUpcomingByDate( + upcomingByDate, + invite, + dayjs(invite.date).toString(), + VisitType.Guest + ) + } + } + + if (!!dailyGuests.length) { + upcomingItems.push(dailyGuests[0] as ScheduledItemType) + } + return { upcoming: upcomingItems.sort( (a: ScheduledItemType, b: ScheduledItemType) => @@ -183,6 +231,7 @@ const userRouter: FastifyPluginCallback = async function (fastify, opts) { [VisitType.Visit]: dailyEventsVisits, [VisitType.RoomReservation]: dailyEventsReservations, event: myEvents, + [VisitType.Guest]: dailyGuests, }, byDate: upcomingByDate, } diff --git a/src/modules/hub-map/types.ts b/src/modules/hub-map/types.ts index 9184d282..e4fc6bc5 100644 --- a/src/modules/hub-map/types.ts +++ b/src/modules/hub-map/types.ts @@ -5,7 +5,7 @@ export type ScheduledItemType = { value: string type: string date: string - dateTime: string + dateTime?: string description: string areaId?: string objectId?: string