From b8eedb478c9199a2d68eeba609d15c5fc540c033 Mon Sep 17 00:00:00 2001 From: Oliver Studer Date: Wed, 14 Jun 2023 15:30:21 +0200 Subject: [PATCH] rounding start / stop time, mark zero duration bookings --- frontend/next.config.js | 16 ++++++------ frontend/package.json | 3 ++- frontend/public/locales/de/common.json | 1 + frontend/public/locales/en/common.json | 1 + .../contextMenuBar/hooks/useContextMenu.tsx | 25 ++++++++++++++++++- .../pages/user/index/bookingAddUpdateForm.tsx | 18 +++++-------- .../user/index/bookingDayStatsProgressBar.tsx | 5 ++-- .../pages/user/index/bookingDuration.tsx | 20 +++++++++++++-- .../user/index/bookingDurationCounter.tsx | 4 +-- .../layout/pages/user/index/bookingFromTo.tsx | 18 ++++++++----- .../pages/user/index/bookingFromToMobile.tsx | 15 ++++++++--- .../layout/pages/user/index/bookingStart.tsx | 3 +++ .../index/current/bookingCurrentEntry.tsx | 3 ++- .../pages/user/index/homeLayoutDesktop.tsx | 2 +- .../user/index/list/bookingListWrapper.tsx | 2 +- frontend/src/lib/dates.ts | 5 +--- 16 files changed, 94 insertions(+), 47 deletions(-) diff --git a/frontend/next.config.js b/frontend/next.config.js index 89a316f..679e6d2 100644 --- a/frontend/next.config.js +++ b/frontend/next.config.js @@ -38,7 +38,7 @@ const nextConfiguration = { poweredByHeader: false, compiler: { emotion: { - sourceMap: true, + sourceMap: process.env.NODE_ENV === 'development', autoLabel: 'always', labelFormat: '[filename]', }, @@ -51,7 +51,7 @@ const nextConfiguration = { transform: 'date-fns/{{member}}', }, }, - productionBrowserSourceMaps: true, + productionBrowserSourceMaps: process.env.NODE_ENV === 'development', reactStrictMode: true, publicRuntimeConfig: { BUILD_ID: generateBuildIdSync(), @@ -79,10 +79,8 @@ const nextConfiguration = { }; // module.exports = withPWA(withBundleAnalyzer(nextConfiguration)); -module.exports = withPWA( - withPlausibleProxy({ - subdirectory: 's', - scriptName: 'p.js', - customDomain: LASIUS_TELEMETRY_PLAUSIBLE_HOST, - })(nextConfiguration) -); +module.exports = withPlausibleProxy({ + subdirectory: 's', + scriptName: 'p.js', + customDomain: LASIUS_TELEMETRY_PLAUSIBLE_HOST, +})(withPWA(nextConfiguration)); diff --git a/frontend/package.json b/frontend/package.json index b9b2201..f7ceef1 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "lasius-frontend", "private": true, - "version": "1.0.1", + "version": "1.0.2", "description": "Lasius NextJS Frontend Application Server", "author": "tegonal.com ", "license": "AGPL 3.0", @@ -11,6 +11,7 @@ "scripts": { "build": "yarn run svgstore && next build", "start": "next start", + "start-local": "next start -p 3001", "dev": "yarn run svgstore && next dev -p 3001", "svgstore": "node ./scripts/iconNames.js && prettier --check --write './src/types/iconNames.d.ts' && svgstore -o ./public/symbols.svg ./public/icons/**/*.svg", "lint": "next lint", diff --git a/frontend/public/locales/de/common.json b/frontend/public/locales/de/common.json index 67bf440..fc3dbed 100644 --- a/frontend/public/locales/de/common.json +++ b/frontend/public/locales/de/common.json @@ -171,6 +171,7 @@ "The amount of time you expect to book per day, by organisation, during a typical working week. This data is used to calculate your daily and weekly progress.": "Die Zeit, die Du pro Tag und Organisation während einer typischen Arbeitswoche buchen möchtest. Diese Daten werden verwendet, um deinen täglichen und wöchentlichen Fortschritt zu berechnen.", "There is a gap between these two bookings. Click to add a booking in between.": "Zwischen diesen beiden Buchungen besteht eine Lücke. Klicke hier, um eine Buchung dazwischen hinzuzufügen.", "These two bookings overlap. Click to edit the top one and adjust the time.": "Diese beiden Buchungen überschneiden sich. Klicke hier, um die obere zu bearbeiten und die Zeit anzupassen.", + "This booking's duration is zero": "Die Dauer dieser Buchung beträgt null.", "This invitation has been created for someone else. Either log out and refresh, or forward the invitation link to the user {{email}}": "Diese Einladung wurde für einen anderen Benutzer erstellt. Melde dich entweder ab und aktualisiere die Seite oder leite den Einladungslink an den Benutzer {{email}} weiter", "This invitation is no longer valid. It is best to contact the person who sent you the invitation link to get a new one.": "Diese Einladung ist nicht mehr gültig. Am besten wendest du dich an die Person, die dir den Einladungslink geschickt hat, um einen neuen zu erhalten.", "This month": "Dieser Monat", diff --git a/frontend/public/locales/en/common.json b/frontend/public/locales/en/common.json index 001a622..fdcf9e0 100644 --- a/frontend/public/locales/en/common.json +++ b/frontend/public/locales/en/common.json @@ -171,6 +171,7 @@ "The amount of time you expect to book per day, by organisation, during a typical working week. This data is used to calculate your daily and weekly progress.": "The amount of time you expect to book per day, by organisation, during a typical working week. This data is used to calculate your daily and weekly progress.", "There is a gap between these two bookings. Click to add a booking in between.": "There is a gap between these two bookings. Click to add a booking in between.", "These two bookings overlap. Click to edit the top one and adjust the time.": "These two bookings overlap. Click to edit the top one and adjust the time.", + "This booking's duration is zero": "This booking's duration is zero", "This invitation has been created for someone else. Either log out and refresh, or forward the invitation link to the user {{email}}": "This invitation has been created for someone else. Either log out and refresh, or forward the invitation link to the user {{email}}", "This invitation is no longer valid. It is best to contact the person who sent you the invitation link to get a new one.": "This invitation is no longer valid. It is best to contact the person who sent you the invitation link to get a new one.", "This month": "This month", diff --git a/frontend/src/components/contextMenuBar/hooks/useContextMenu.tsx b/frontend/src/components/contextMenuBar/hooks/useContextMenu.tsx index 9616b39..7ece1bc 100644 --- a/frontend/src/components/contextMenuBar/hooks/useContextMenu.tsx +++ b/frontend/src/components/contextMenuBar/hooks/useContextMenu.tsx @@ -26,18 +26,29 @@ import { } from 'lib/api/lasius'; import { deleteUserBooking, + getGetUserBookingCurrentKey, startUserBookingCurrent, + stopUserBookingCurrent, + useGetUserBookingCurrent, } from 'lib/api/lasius/user-bookings/user-bookings'; import { useStore } from 'storeContext/store'; import useModal from 'components/modal/hooks/useModal'; import { useToast } from 'components/toasts/hooks/useToast'; import { useTranslation } from 'next-i18next'; +import { formatISOLocale } from 'lib/dates'; +import { roundToNearestMinutes } from 'date-fns'; +import { useIsClient } from 'usehooks-ts'; +import { mutate } from 'swr'; export const useContextMenu = () => { const { dispatch, state } = useStore(); const { closeModal } = useModal('BookingAddMobileModal'); const { addToast } = useToast(); const { t } = useTranslation('common'); + const isClient = useIsClient(); + const store = useStore(); + + const { data: currentBooking } = useGetUserBookingCurrent({ swr: { enabled: isClient } }); const handleOpenContextMenu = (hash: string) => { dispatch({ type: 'context.open', payload: hash }); @@ -70,7 +81,19 @@ export const useContextMenu = () => { tags = item.booking.tags; } - await startUserBookingCurrent(selectedOrganisationId, { projectId, tags }); + if (currentBooking?.booking?.id) { + await stopUserBookingCurrent(selectedOrganisationId, currentBooking.booking.id, { + end: formatISOLocale(roundToNearestMinutes(new Date(), { roundingMethod: 'floor' })), + }); + await mutate(getGetUserBookingCurrentKey()); + store.dispatch({ type: 'calendar.setSelectedDate', payload: formatISOLocale(new Date()) }); + } + + await startUserBookingCurrent(selectedOrganisationId, { + projectId, + tags, + start: formatISOLocale(roundToNearestMinutes(new Date(), { roundingMethod: 'floor' })), + }); handleCloseAll(); closeModal(); }; diff --git a/frontend/src/layout/pages/user/index/bookingAddUpdateForm.tsx b/frontend/src/layout/pages/user/index/bookingAddUpdateForm.tsx index 968f965..8f904ad 100644 --- a/frontend/src/layout/pages/user/index/bookingAddUpdateForm.tsx +++ b/frontend/src/layout/pages/user/index/bookingAddUpdateForm.tsx @@ -28,7 +28,6 @@ import { InputSelectAutocomplete } from 'components/forms/input/inputSelectAutoc import { InputTagsAutocomplete } from 'components/forms/input/inputTagsAutocomplete'; import { addHours, - addSeconds, getHours, getMinutes, isAfter, @@ -134,7 +133,7 @@ export const BookingAddUpdateForm: React.FC = ({ if (mode === 'add' && itemReference) { const reference = new Date(itemReference.end?.dateTime || ''); - hookForm.setValue('start', formatISOLocale(addSeconds(reference, 1))); + hookForm.setValue('start', formatISOLocale(reference)); hookForm.setValue('end', formatISOLocale(addHours(reference, 1))); hookForm.setValue('projectId', ''); @@ -145,12 +144,9 @@ export const BookingAddUpdateForm: React.FC = ({ logger.info('addBetween'); hookForm.setValue( 'start', - formatISOLocale(addSeconds(new Date(bookingBeforeCurrent?.end?.dateTime || ''), 1)) - ); - hookForm.setValue( - 'end', - formatISOLocale(addSeconds(new Date(itemReference?.start?.dateTime || ''), -1)) + formatISOLocale(new Date(bookingBeforeCurrent?.end?.dateTime || '')) ); + hookForm.setValue('end', formatISOLocale(new Date(itemReference?.start?.dateTime || ''))); hookForm.setValue('projectId', ''); hookForm.setValue('tags', []); @@ -249,8 +245,8 @@ export const BookingAddUpdateForm: React.FC = ({ : t('Use end time of previous booking as start time for this one'), presetDate: mode === 'add' - ? formatISOLocale(addSeconds(new Date(latestBooking?.end?.dateTime || ''), 1)) - : formatISOLocale(addSeconds(new Date(bookingBeforeCurrent?.end?.dateTime || ''), 1)), + ? formatISOLocale(new Date(latestBooking?.end?.dateTime || '')) + : formatISOLocale(new Date(bookingBeforeCurrent?.end?.dateTime || '')), presetIcon: 'move-left-1' as IconNames, }; @@ -261,9 +257,7 @@ export const BookingAddUpdateForm: React.FC = ({ ? {} : { presetLabel: t('Use start time of next booking as end time for this one'), - presetDate: formatISOLocale( - addSeconds(new Date(bookingAfterCurrent?.start?.dateTime || ''), -1) - ), + presetDate: formatISOLocale(new Date(bookingAfterCurrent?.start?.dateTime || '')), presetIcon: 'move-right-1' as IconNames, }; diff --git a/frontend/src/layout/pages/user/index/bookingDayStatsProgressBar.tsx b/frontend/src/layout/pages/user/index/bookingDayStatsProgressBar.tsx index 297eca2..7c2cf1d 100644 --- a/frontend/src/layout/pages/user/index/bookingDayStatsProgressBar.tsx +++ b/frontend/src/layout/pages/user/index/bookingDayStatsProgressBar.tsx @@ -38,10 +38,9 @@ const ProgressBar: React.FC<{ percentage: number; label: string }> = memo( @@ -83,7 +82,7 @@ export const BookingDayStatsProgressBar: React.FC = () => { if (!isClient) return null; return ( - + ); diff --git a/frontend/src/layout/pages/user/index/bookingDuration.tsx b/frontend/src/layout/pages/user/index/bookingDuration.tsx index 60c40f0..805e3f2 100644 --- a/frontend/src/layout/pages/user/index/bookingDuration.tsx +++ b/frontend/src/layout/pages/user/index/bookingDuration.tsx @@ -23,14 +23,30 @@ import { Icon } from 'components/shared/icon'; import { flexRowJustifyStartAlignCenter } from 'styles/shortcuts'; import { durationAsString } from 'lib/dates'; import { ModelsBooking } from 'lib/api/lasius'; +import { ToolTip } from 'components/shared/toolTip'; +import { useTranslation } from 'next-i18next'; type Props = { item: ModelsBooking }; export const BookingDuration: React.FC = ({ item }) => { + const duration = durationAsString(item.start.dateTime, item.end?.dateTime || ''); + const durationIsZero = duration === '00:00'; + const { t } = useTranslation(); return ( - + - {durationAsString(item.start.dateTime, item.end?.dateTime || '')} + {duration} + {durationIsZero && ( + + + + )} ); }; diff --git a/frontend/src/layout/pages/user/index/bookingDurationCounter.tsx b/frontend/src/layout/pages/user/index/bookingDurationCounter.tsx index 82f2acd..343bdf0 100644 --- a/frontend/src/layout/pages/user/index/bookingDurationCounter.tsx +++ b/frontend/src/layout/pages/user/index/bookingDurationCounter.tsx @@ -27,11 +27,11 @@ import { durationAsString, formatISOLocale } from 'lib/dates'; type Props = { startDate: string }; export const BookingDurationCounter: React.FC = ({ startDate }) => { - const [duration, setDuration] = useState(`00:00.00`); + const [duration, setDuration] = useState(`00:00`); useInterval(() => { setDuration(durationAsString(startDate, formatISOLocale(new Date()))); - }, 1000); + }, 25000); return ( diff --git a/frontend/src/layout/pages/user/index/bookingFromTo.tsx b/frontend/src/layout/pages/user/index/bookingFromTo.tsx index af80df8..81a9ddb 100644 --- a/frontend/src/layout/pages/user/index/bookingFromTo.tsx +++ b/frontend/src/layout/pages/user/index/bookingFromTo.tsx @@ -18,7 +18,7 @@ */ import React from 'react'; -import { Box, Flex } from 'theme-ui'; +import { Box, Flex, Text } from 'theme-ui'; import { FormatDate } from 'components/shared/formatDate'; import { ModelsBooking } from 'lib/api/lasius'; import { Icon } from 'components/shared/icon'; @@ -30,15 +30,21 @@ type Props = { export const BookingFromTo: React.FC = ({ item }) => { const { start, end } = item; return ( - + - + + + - - + + + + - + + + ); diff --git a/frontend/src/layout/pages/user/index/bookingFromToMobile.tsx b/frontend/src/layout/pages/user/index/bookingFromToMobile.tsx index b3815a7..d0b192b 100644 --- a/frontend/src/layout/pages/user/index/bookingFromToMobile.tsx +++ b/frontend/src/layout/pages/user/index/bookingFromToMobile.tsx @@ -22,6 +22,7 @@ import { Flex } from 'theme-ui'; import { FormatDate } from 'components/shared/formatDate'; import { ModelsBooking } from 'lib/api/lasius'; import { Icon } from 'components/shared/icon'; +import { Text } from '@theme-ui/components'; type Props = { item: ModelsBooking; @@ -30,15 +31,21 @@ type Props = { export const BookingFromToMobile: React.FC = ({ item }) => { const { start, end } = item; return ( - + - + + + - + + + - + + + ); diff --git a/frontend/src/layout/pages/user/index/bookingStart.tsx b/frontend/src/layout/pages/user/index/bookingStart.tsx index 0f12446..774db97 100644 --- a/frontend/src/layout/pages/user/index/bookingStart.tsx +++ b/frontend/src/layout/pages/user/index/bookingStart.tsx @@ -33,6 +33,8 @@ import { useOrganisation } from 'lib/api/hooks/useOrganisation'; import { useProjects } from 'lib/api/hooks/useProjects'; import { useGetTagsByProject } from 'lib/api/lasius/user-organisations/user-organisations'; import useModal from 'components/modal/hooks/useModal'; +import { formatISOLocale } from 'lib/dates'; +import { roundToNearestMinutes } from 'date-fns'; type FormValues = { projectId: string; @@ -67,6 +69,7 @@ export const BookingStart: React.FC = () => { await startUserBookingCurrent(selectedOrganisationId, { projectId, tags, + start: formatISOLocale(roundToNearestMinutes(new Date(), { roundingMethod: 'floor' })), }); closeModal(); resetComponent(); diff --git a/frontend/src/layout/pages/user/index/current/bookingCurrentEntry.tsx b/frontend/src/layout/pages/user/index/current/bookingCurrentEntry.tsx index bac1559..4d71bea 100644 --- a/frontend/src/layout/pages/user/index/current/bookingCurrentEntry.tsx +++ b/frontend/src/layout/pages/user/index/current/bookingCurrentEntry.tsx @@ -49,6 +49,7 @@ import { useIsClient } from 'usehooks-ts'; import { useOrganisation } from 'lib/api/hooks/useOrganisation'; import { AnimateChange } from 'components/shared/motion/animateChange'; import { useStore } from 'storeContext/store'; +import { roundToNearestMinutes } from 'date-fns'; type Props = { inContainer?: boolean; @@ -66,7 +67,7 @@ export const BookingCurrentEntry: React.FC = ({ inContainer = false }) => const stop = async () => { if (data?.booking?.id) { await stopUserBookingCurrent(selectedOrganisationId, data.booking.id, { - end: formatISOLocale(new Date()), + end: formatISOLocale(roundToNearestMinutes(new Date(), { roundingMethod: 'floor' })), }); await mutate(getGetUserBookingCurrentKey()); store.dispatch({ type: 'calendar.setSelectedDate', payload: formatISOLocale(new Date()) }); diff --git a/frontend/src/layout/pages/user/index/homeLayoutDesktop.tsx b/frontend/src/layout/pages/user/index/homeLayoutDesktop.tsx index 744fb67..0b10909 100644 --- a/frontend/src/layout/pages/user/index/homeLayoutDesktop.tsx +++ b/frontend/src/layout/pages/user/index/homeLayoutDesktop.tsx @@ -47,8 +47,8 @@ export const HomeLayoutDesktop: React.FC = () => { gap: 1, }} > - + diff --git a/frontend/src/layout/pages/user/index/list/bookingListWrapper.tsx b/frontend/src/layout/pages/user/index/list/bookingListWrapper.tsx index 8ab7e15..5025e86 100644 --- a/frontend/src/layout/pages/user/index/list/bookingListWrapper.tsx +++ b/frontend/src/layout/pages/user/index/list/bookingListWrapper.tsx @@ -30,7 +30,7 @@ export const BookingListWrapper: React.FC = ({ children }) => { sx={{ position: 'relative', label: 'BookingListWrapper', - pt: 24, + pt: 2, pb: [96, 0], userSelect: 'none', }} diff --git a/frontend/src/lib/dates.ts b/frontend/src/lib/dates.ts index a496e25..efca062 100644 --- a/frontend/src/lib/dates.ts +++ b/frontend/src/lib/dates.ts @@ -113,14 +113,11 @@ export const durationAsString = (start: IsoDateString, end: IsoDateString) => { days = 0, hours = 0, minutes = 0, - seconds = 0, } = intervalToDuration({ start: new Date(start), end: new Date(end), }); - return `${padTimeSegment(days * 24 + hours)}:${padTimeSegment(minutes)}.${padTimeSegment( - seconds - )}`; + return `${padTimeSegment(days * 24 + hours)}:${padTimeSegment(minutes)}`; }; /**