diff --git a/CHANGELOG.md b/CHANGELOG.md index f1a928d0d..995d9b438 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ - [#1064](https://github.com/City-of-Helsinki/varaamo/pull/1064) Replaced ? with & in Feedback URL. - [#1065](https://github.com/City-of-Helsinki/varaamo/pull/1065) Replaced some frontpage icons. - [#1066](https://github.com/City-of-Helsinki/varaamo/pull/1066) Minor change in ReservationInformationModal reservation data structure. + - [#1067](https://github.com/City-of-Helsinki/varaamo/pull/1067) FullCalendar mobile improvements and styling changes. # 0.6.1 **HOTFIX** diff --git a/app/i18n/messages/en.json b/app/i18n/messages/en.json index 14f1d8aa3..8b317d4e3 100644 --- a/app/i18n/messages/en.json +++ b/app/i18n/messages/en.json @@ -220,6 +220,8 @@ "ReservationInfo.loginMessage": "You must log in to reserve this premise.", "ReservationInfo.loginText": "You must log in to reserve this premise.", "ReservationInfo.maxNumberOfReservations": "Maximum number of reservations per user:", + "ReservationInfo.selectionStartDirections": "Touch and hold the screen to make a selection", + "ReservationInfo.selectionEditDirections": "Press selected period again to resize", "ReservationInfo.reservationMaxLength": "Maximum duration of the reservation:", "ReservationInfo.reservationMinLength": "Minimum duration of the reservation:", "ReservationInfo.reservationEarliestDays": "Reservation at the earliest in {time}", diff --git a/app/i18n/messages/fi.json b/app/i18n/messages/fi.json index ec45608d1..60d06de60 100644 --- a/app/i18n/messages/fi.json +++ b/app/i18n/messages/fi.json @@ -220,6 +220,8 @@ "ReservationInfo.loginMessage": "Sinun täytyy kirjautua sisään, jotta voit tehdä varauksen tähän tilaan.", "ReservationInfo.loginText": "Sinun täytyy kirjautua sisään, jotta voit tehdä varauksen tähän tilaan.", "ReservationInfo.maxNumberOfReservations": "Maksimimäärä varauksia per käyttäjä:", + "ReservationInfo.selectionStartDirections": "Kosketa näyttöä pitkään tehdäksesi valinnan.", + "ReservationInfo.selectionEditDirections": "Paina valintaasi uudelleen muuttaaksesi varauksen pituutta", "ReservationInfo.reservationMaxLength": "Varauksen maksimipituus:", "ReservationInfo.reservationMinLength": "Varauksen vähimmäispituus:", "ReservationInfo.reservationEarliestDays": "Varattavissa vähintään {time} etukäteen", diff --git a/app/i18n/messages/sv.json b/app/i18n/messages/sv.json index 70639418a..294f6292c 100644 --- a/app/i18n/messages/sv.json +++ b/app/i18n/messages/sv.json @@ -221,6 +221,8 @@ "ReservationForm.markAsClosedDescription": "Markera kryssrutan om du stänger resursen genom att reservera.", "ReservationInfo.loginMessage": "För att kunna boka detta utrymme måste du logga in.", "ReservationInfo.loginText": "För att kunna boka detta utrymme måste du logga in.", + "ReservationInfo.selectionStartDirections": "Tryck och håll på skärmen för att välja.", + "ReservationInfo.selectionEditDirections": "Tryck på ditt val igen för att ändra dess längd", "ReservationInfo.maxNumberOfReservations": "Maximalt antal bokningar per användare:", "ReservationInfo.reservationMaxLength": "Bokningens maximala längd:", "ReservationInfo.reservationMinLength": "Minsta längd av bokningen:", diff --git a/app/pages/resource/ResourcePage.js b/app/pages/resource/ResourcePage.js index 5bc59d672..33f857287 100644 --- a/app/pages/resource/ResourcePage.js +++ b/app/pages/resource/ResourcePage.js @@ -234,6 +234,18 @@ class UnconnectedResourcePage extends Component { )} {!resource.externalReservationUrl && (
+ {window.innerWidth < 768 && ( + +
+ {t('ReservationInfo.selectionStartDirections')} +
+
+ {t('ReservationInfo.selectionEditDirections')} +
+
+ ) + } + {/* Show reservation max period text */} {resource.maxPeriod && (
diff --git a/src/common/calendar/TimePickerCalendar.js b/src/common/calendar/TimePickerCalendar.js index 70159d2c1..9f77c0edc 100644 --- a/src/common/calendar/TimePickerCalendar.js +++ b/src/common/calendar/TimePickerCalendar.js @@ -84,17 +84,28 @@ class TimePickerCalendar extends Component { onEventRender = (info) => { // add cancel button for new selected event + let duration; + if (info.event.id === NEW_RESERVATION) { const cancelBtn = document.createElement('span'); cancelBtn.classList.add('app-TimePickerCalendar__cancelEvent'); cancelBtn.addEventListener('click', () => this.onCancel(), { once: true }); info.el.append(cancelBtn); + duration = this.getDurationText(info.event); + } else if (info.event.id === '') { + duration = this.getDurationText(info.event); } - } + + if (duration) { + const eventDuration = document.createElement('span'); + eventDuration.textContent = duration; + eventDuration.classList.add('app-TimePickerCalendar__maxDuration'); + info.el.append(eventDuration); + } + }; onSelect = (selectionInfo) => { const { t } = this.props; - const calendarApi = this.calendarRef.current.getApi(); calendarApi.unselect(); // Clear FullCalendar select tooltip @@ -116,7 +127,6 @@ class TimePickerCalendar extends Component { onEventResize = (selectionInfo) => { const { event } = selectionInfo; const selectable = this.getSelectableTimeRange(event, selectionInfo); - this.onChange(selectable); } @@ -199,29 +209,21 @@ class TimePickerCalendar extends Component { return selectable; } - getDurationText = () => { - const { selected } = this.state; + getDurationText = (selected) => { + const { resource } = this.props; const start = moment(selected.start); const end = moment(selected.end); const duration = moment.duration(end.diff(start)); - const days = duration.days(); - const hours = duration.hours(); - const minutes = duration.minutes(); - let text = ''; - if (days) { - text = `${days}d`; - } - - if (hours) { - text += `${hours}h`; - } + let maxDurationText = ''; - if (minutes) { - text += `${minutes}min`; + if (resource.max_period) { + const maxDuration = get(resource, 'max_period', null); + const maxDurationSeconds = moment.duration(maxDuration).asSeconds(); + maxDurationText = `(${maxDurationSeconds / 3600}h max)`; } - return text; + return `${duration / 3600000}h ${maxDurationText}`; }; getSelectedDateText = () => { @@ -321,7 +323,9 @@ class TimePickerCalendar extends Component { meridiem: 'short' }, unselectAuto: false, - longPressDelay: '500', + longPressDelay: 250, + eventLongPressDelay: 20, + selectLongPressDelay: 200, // Almost invoke click event on mobile immediatelly without any delay }; }; @@ -334,10 +338,12 @@ class TimePickerCalendar extends Component { const events = this.getReservedEvents(); if (selected) { + const webEventSelected = window.innerWidth > 768 ? 'fc-selected' : ''; events.push({ classNames: [ 'app-TimePickerCalendar__event', 'app-TimePickerCalendar__newReservation', + webEventSelected ], editable: true, durationEditable: !calendarUtils.isTimeRangeOverMaxPeriod( diff --git a/src/common/calendar/__tests__/__snapshots__/TimePickerCalendar.test.js.snap b/src/common/calendar/__tests__/__snapshots__/TimePickerCalendar.test.js.snap index f333ced72..dab73ed53 100644 --- a/src/common/calendar/__tests__/__snapshots__/TimePickerCalendar.test.js.snap +++ b/src/common/calendar/__tests__/__snapshots__/TimePickerCalendar.test.js.snap @@ -81,6 +81,7 @@ exports[`TimePickerCalendar FullCalendar render normally 1`] = ` editable={true} eventConstraint="businessHours" eventDrop={[Function]} + eventLongPressDelay={20} eventOverlap={false} eventRender={[Function]} eventResize={[Function]} @@ -146,7 +147,7 @@ exports[`TimePickerCalendar FullCalendar render normally 1`] = ` }, ] } - longPressDelay="500" + longPressDelay={250} maxTime="17:00:00" minTime="07:00:00" nowIndicator={true} @@ -270,6 +271,7 @@ exports[`TimePickerCalendar FullCalendar render normally 1`] = ` } select={[Function]} selectConstraint="businessHours" + selectLongPressDelay={200} selectMirror={true} selectOverlap={false} selectable={true} diff --git a/src/common/calendar/_timePickerCalendar.scss b/src/common/calendar/_timePickerCalendar.scss index 9b2b0e1fb..e10685d51 100644 --- a/src/common/calendar/_timePickerCalendar.scss +++ b/src/common/calendar/_timePickerCalendar.scss @@ -1,9 +1,10 @@ .app-TimePickerCalendar { &__event { - color: $white !important; + color: $hel-coat !important; border-radius: 0 !important; &--reserved { + color: $white !important; background-color: $red !important; border-color: $red !important; } @@ -20,7 +21,7 @@ &__cancelEvent { position: absolute; right: 0.25em; - top: 0; + bottom: 0; z-index: 2; &::after { @@ -32,6 +33,47 @@ } } + &__maxDuration { + font-size: 1.25rem; + } + + .fc-event { + font-size: 1.75rem; + font-weight: bold; + background-color: #bddbf0; + border: 1px solid $hel-coat; + } + + .fc-event.fc-selected:after { + opacity: 0.35; + background-color: $hel-coat; + } + + + .fc-time-grid-event.fc-selected { + .fc-resizer { + width: 12px; + height: 12px; + bottom: -7px; + left: 49%; + z-index: 50 !important; + border-radius: 50%; + + &::before { + width: 80px; + height: 80px; + top: 0; + left: 0; + margin-left: -40px; + } + + &::after { + color: #FFF !important; + } + } + } + + .fc-header-toolbar { .fc-left { width: 30%; @@ -49,7 +91,7 @@ .fc-myToday-button { border: 1px solid $black; border-radius: 0; - padding: 0.2rem 0.3rem; + padding: 0.5rem 0.5rem; color: $black; background: $white; font-weight: $font-weight-bold; @@ -77,7 +119,7 @@ .fc-timeGridDay-button { border: 1px solid $black; border-radius: 0; - padding: 0.3rem 0.3rem; + padding: 0.5rem 0.5rem; background: $white; color: $black; @@ -87,6 +129,7 @@ } } + .fc-myNext-button { @include icon-angle-right($black); }