From 02c2b45747d8bcbb1163d3d57eef0791f26bf8e7 Mon Sep 17 00:00:00 2001 From: Thomas Ruoff Date: Tue, 22 Sep 2020 00:21:08 +0200 Subject: [PATCH] replace momentjs (incl. calendar) --- components/wizard/calendar.tsx | 64 ++++++--------------- db/booking.ts | 5 +- helpers/date.ts | 33 ++++++----- package-lock.json | 67 +++++++++++++--------- package.json | 5 +- styles/index.css | 102 ++++++++++++++++++++++++++------- 6 files changed, 161 insertions(+), 115 deletions(-) diff --git a/components/wizard/calendar.tsx b/components/wizard/calendar.tsx index 040043c..c89cb74 100644 --- a/components/wizard/calendar.tsx +++ b/components/wizard/calendar.tsx @@ -1,8 +1,8 @@ -import React, { useState } from 'react' +import React from 'react' import classnames from 'classnames' +import { isPast } from 'date-fns' import useSWR from 'swr' -import moment, { Moment } from 'moment' -import { Calendar } from 'react-calendar-component' +import Calendar from 'react-calendar' import { dateFormatBackend } from '../../helpers/date' const fetcher = (path: string) => fetch(path).then((r) => r.json()) @@ -13,10 +13,17 @@ export default function MyCalendar() { fetcher ) - const [date, setDate] = useState(moment()) - - function dayBooked(day: Date) { - return daysBooked && daysBooked.includes(dateFormatBackend(day)) + function tileClassName({ date, view }) { + const isMonthView = view === 'month' + const isDaysBookedLoaded = !!daysBooked + const isBooked = daysBooked?.includes(dateFormatBackend(date)) + return classnames({ + 'react-calendar__tile--past': isPast(date), + 'react-calendar__tile--booked': + isMonthView && isDaysBookedLoaded && isBooked, + 'react-calendar__tile--free': + isMonthView && isDaysBookedLoaded && !isBooked, + }) } if (fetchBookedOnError) { @@ -29,48 +36,9 @@ export default function MyCalendar() { } return ( -
+

Buchungsübersicht

- ) => setDate(date)} - date={date} - renderDay={({ - day, - classNames, - onPickDate, - }: { - day: Moment - classNames: string - onPickDate: any - }) => ( -
onPickDate(day)} - > - {day.format('D')} -
- )} - renderHeader={({ date, onPrevMonth, onNextMonth }) => ( -
- -
- {date.format('MMMM YYYY')} -
- -
- )} - onPickDate={() => {}} - /> +
diff --git a/db/booking.ts b/db/booking.ts index 954e5c3..943b6db 100644 --- a/db/booking.ts +++ b/db/booking.ts @@ -65,7 +65,10 @@ const BookingSchema = new mongoose.Schema( ) BookingSchema.virtual('days').get(function () { - return getDays({ startDate: this.startDate, endDate: this.endDate }) + return getDays({ + startDate: new Date(this.startDate), + endDate: new Date(this.endDate), + }) }) BookingSchema.static('findBookedDays', async function (): Promise { diff --git a/helpers/date.ts b/helpers/date.ts index 1c56293..8169806 100644 --- a/helpers/date.ts +++ b/helpers/date.ts @@ -1,50 +1,49 @@ -import moment from 'moment' +import { parse, format, addDays } from 'date-fns' -const FRONTEND_FORMAT = 'DD.MM.YYYY' -const BACKEND_FORMAT = 'YYYY-MM-DD' +const FRONTEND_FORMAT = 'dd.MM.yyyy' +const BACKEND_FORMAT = 'yyyy-MM-dd' export function getDays({ startDate, endDate, }: { - startDate: moment.MomentInput - endDate: moment.MomentInput + startDate: Date + endDate: Date }) { - let currentDay = moment(startDate) + let currentDay = new Date(startDate.getTime()) const days = [dateFormatBackend(currentDay)] if (!endDate) { return days } - const end = moment(endDate) - while (currentDay < end) { - currentDay = currentDay.add(1, 'day') + while (currentDay < endDate) { + currentDay = addDays(currentDay, 1) days.push(dateFormatBackend(currentDay)) } return days } -function dateFormat(date: moment.MomentInput, format: string) { +function dateFormat(date: Date, formatString: string) { if (!date) { return null } - return moment(date).format(format) + return format(date, formatString) } -export function dateFormatBackend(date: moment.MomentInput) { +export function dateFormatBackend(date: Date) { return dateFormat(date, BACKEND_FORMAT) } -export function dateFormatFrontend(date: moment.MomentInput) { +export function dateFormatFrontend(date: Date) { return dateFormat(date, FRONTEND_FORMAT) } -function dateParse(input: string, format: string) { - const date = moment(input, format) - if (date.isValid()) { - return date.toDate() +function dateParse(input: string, formatString: string) { + const date = parse(input, formatString, new Date()) + if (date.getTime() !== NaN) { + return date } return null diff --git a/package-lock.json b/package-lock.json index c7a9ed8..edc10de 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1153,19 +1153,6 @@ "esutils": "^2.0.2" } }, - "@babel/preset-react": { - "version": "7.9.4", - "resolved": "https://registry.npmjs.org/@babel/preset-react/-/preset-react-7.9.4.tgz", - "integrity": "sha512-AxylVB3FXeOTQXNXyiuAQJSvss62FEotbX2Pzx3K/7c+MKJMdSg6Ose6QYllkdCFA8EInCJVw7M/o5QbLuA4ZQ==", - "requires": { - "@babel/helper-plugin-utils": "^7.8.3", - "@babel/plugin-transform-react-display-name": "^7.8.3", - "@babel/plugin-transform-react-jsx": "^7.9.4", - "@babel/plugin-transform-react-jsx-development": "^7.9.0", - "@babel/plugin-transform-react-jsx-self": "^7.9.0", - "@babel/plugin-transform-react-jsx-source": "^7.9.0" - } - }, "@babel/preset-typescript": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.10.4.tgz", @@ -2151,6 +2138,11 @@ "csstype": "^3.0.2" } }, + "@types/react-calendar": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/react-calendar/-/react-calendar-3.1.1.tgz", + "integrity": "sha512-tjaxwYm/6R3N/vYq4q0+RJP6p6SlNrMbD4AZf9GQUu8nhN7Rkpu5YlrK0J/q77h1ikAlAyNWor8yqc7tGgfFQg==" + }, "@types/stack-utils": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", @@ -2335,6 +2327,11 @@ "@xtuc/long": "4.2.2" } }, + "@wojtekmaj/date-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-1.0.3.tgz", + "integrity": "sha512-1VPkkTBk07gMR1fjpBtse4G+oJqpmE+0gUFB0dg3VIL7qJmUVaBoD/vlzMm/jNeOPfvlmerl1lpnsZyBUFIRuw==" + }, "@xtuc/ieee754": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", @@ -3900,6 +3897,11 @@ "whatwg-url": "^8.0.0" } }, + "date-fns": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.16.1.tgz", + "integrity": "sha512-sAJVKx/FqrLYHAQeN7VpJrPhagZc9R4ImZIWYRFZaaohR3KzmuK88touwsSwSVT8Qcbd4zoDsnGfX4GFB4imyQ==" + }, "dayjs": { "version": "1.8.36", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz", @@ -4793,6 +4795,14 @@ "pump": "^3.0.0" } }, + "get-user-locale": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-1.4.0.tgz", + "integrity": "sha512-gQo03lP1OArHLKlnoglqrGGl7b04u2EP9Xutmp72cMdtrrSD7ZgIsCsUKZynYWLDkVJW33Cj3pliP7uP0UonHQ==", + "requires": { + "lodash.once": "^4.1.1" + } + }, "get-value": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", @@ -7109,6 +7119,11 @@ "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "dev": true }, + "lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha1-DdOXEhPHxW34gJd9UEyI+0cal6w=" + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -7217,6 +7232,11 @@ "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", "optional": true }, + "merge-class-names": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/merge-class-names/-/merge-class-names-1.3.0.tgz", + "integrity": "sha512-k0Qaj36VBpKgdc8c188LEZvo6v/zzry/FUufwopWbMSp6/knfVFU/KIB55/hJjeIpg18IH2WskXJCRnM/1BrdQ==" + }, "merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -7368,11 +7388,6 @@ "minimist": "^1.2.5" } }, - "moment": { - "version": "2.28.0", - "resolved": "https://registry.npmjs.org/moment/-/moment-2.28.0.tgz", - "integrity": "sha512-Z5KOjYmnHyd/ukynmFd/WwyXHd7L4J9vTI/nn5Ap9AVUgaAE15VvQ9MOGmJJygEUklupqIrFnor/tjTwRU+tQw==" - }, "mongodb": { "version": "3.6.2", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz", @@ -8983,15 +8998,15 @@ "prop-types": "^15.6.2" } }, - "react-calendar-component": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/react-calendar-component/-/react-calendar-component-3.0.0.tgz", - "integrity": "sha512-vua/pu+hTeLD9SSc+NH79IwCLc3REvl4gL9C3dtUo/+cGhPiQo6CGC/Jm8AVgTBhO4CtNr21tEWAdlCVqIxBbA==", + "react-calendar": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/react-calendar/-/react-calendar-3.1.0.tgz", + "integrity": "sha512-xoKdRe6FrnZ30LD9pyr20fhet1uwSbc6srLGm1ib7G4b7tAXniZrwzrJ4YV/Hbmmwf/zAFGyXtBzLAIV1KNvuA==", "requires": { - "@babel/preset-react": "^7.0.0", - "classnames": "^2.2.5", - "moment": "^2.19.1", - "prop-types": "^15.7.2" + "@wojtekmaj/date-utils": "^1.0.2", + "get-user-locale": "^1.2.0", + "merge-class-names": "^1.1.1", + "prop-types": "^15.6.0" } }, "react-dom": { diff --git a/package.json b/package.json index e04a3e6..175019f 100644 --- a/package.json +++ b/package.json @@ -10,12 +10,12 @@ "test:ci": "jest" }, "dependencies": { + "date-fns": "^2.16.1", "ics": "^2.24.0", - "moment": "^2.28.0", "mongoose": "^5.10.5", "next": "^9.5.3", "react": "16.13.1", - "react-calendar-component": "^3.0.0", + "react-calendar": "^3.1.0", "react-dom": "16.13.1", "uuid": "^8.3.0" }, @@ -24,6 +24,7 @@ "@types/jest": "^26.0.14", "@types/mongoose": "^5.7.36", "@types/react": "^16.9.49", + "@types/react-calendar": "^3.1.1", "@types/uuid": "^8.3.0", "babel-jest": "^26.3.0", "jest": "^26.4.2", diff --git a/styles/index.css b/styles/index.css index 6a3c641..a79a933 100644 --- a/styles/index.css +++ b/styles/index.css @@ -120,49 +120,109 @@ content: '\274c'; } -.Calendar-grid { - @apply flex flex-wrap; +.react-calendar { + @apply border border-gray-900; } -.Calendar-header { - @apply flex justify-between bg-gray-700 text-gray-200 text-center; +.react-calendar button { + @apply outline-none; } -.Calendar-header button { - @apply w-10 bg-transparent text-gray-200 cursor-pointer; +.react-calendar button:enabled:hover { + cursor: pointer; } -.Calendar-grid-item { - @apply text-center p-1 border-r border-b border-solid border-gray-500; - flex: 0 calc(100% / 7); +.react-calendar__navigation { + @apply text-gray-100 bg-gray-900; } -.Calendar-grid-item--past { - @apply text-gray-500 bg-gray-200; +.react-calendar__navigation button { + @apply px-3 py-2; + min-width: 2rem; } -.Calendar-grid-item--current { +.react-calendar__navigation button:enabled:hover, +.react-calendar__navigation button:enabled:focus { +} +.react-calendar__navigation button[disabled] { + @apply text-gray-700 cursor-not-allowed; +} +.react-calendar__month-view__weekdays { + @apply text-sm text-center uppercase font-bold; +} +.react-calendar__month-view__weekdays__weekday { + @apply p-1; +} + +.react-calendar__month-view__weekdays__weekday abbr { + @apply no-underline; +} + +.react-calendar__month-view__weekNumbers { @apply font-bold; } +.react-calendar__month-view__weekNumbers .react-calendar__tile { + @apply flex items-center content-center; +} +.react-calendar__month-view__days__day--weekend { + @apply text-red-600; +} +.react-calendar__month-view__days__day--neighboringMonth { + @apply text-gray-400; +} +.react-calendar__year-view .react-calendar__tile, +.react-calendar__decade-view .react-calendar__tile, +.react-calendar__century-view .react-calendar__tile { +} -.Calendar-grid-item--free:not(.Calendar-grid-item--past) { +.react-calendar__tile { + @apply py-1; +} + +.react-calendar__tile--free { @apply bg-green-200; } -.Calendar-grid-item--booked { +.react-calendar__tile--booked { @apply bg-red-200 cursor-not-allowed; } -.Calendar-grid-item.nextMonth, -.Calendar-grid-item.prevMonth { - @apply text-gray-500; +.react-calendar__tile--past { + @apply bg-gray-400 cursor-not-allowed; } -.Calendar-grid-item:nth-child(7n + 1) { - @apply border-l; +.react-calendar__tile:disabled { + @apply bg-gray-300; } -.Calendar-grid-item:nth-child(-n + 7) { - @apply border-t; +.react-calendar__tile:enabled:hover, +.react-calendar__tile:enabled:focus { + @apply bg-gray-500; +} +.react-calendar__tile--now { + @apply text-green-400; +} + +.react-calendar__tile--now:enabled:hover, +.react-calendar__tile--now:enabled:focus { + background: #ffffa9; +} +.react-calendar__tile--hasActive { + background: #76baff; +} +.react-calendar__tile--hasActive:enabled:hover, +.react-calendar__tile--hasActive:enabled:focus { + background: #a9d4ff; +} +.react-calendar__tile--active { + background: #006edc; + color: white; +} +.react-calendar__tile--active:enabled:hover, +.react-calendar__tile--active:enabled:focus { + background: #1087ff; +} +.react-calendar--selectRange .react-calendar__tile--hover { + background-color: #e6e6e6; } /* Start purging... */