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);
}