replace momentjs (incl. calendar)

This commit is contained in:
Thomas Ruoff
2020-09-22 00:21:08 +02:00
committed by Thomas Ruoff
parent 7f4604f0e6
commit 02c2b45747
6 changed files with 161 additions and 115 deletions

View File

@@ -1,8 +1,8 @@
import React, { useState } from 'react' import React from 'react'
import classnames from 'classnames' import classnames from 'classnames'
import { isPast } from 'date-fns'
import useSWR from 'swr' import useSWR from 'swr'
import moment, { Moment } from 'moment' import Calendar from 'react-calendar'
import { Calendar } from 'react-calendar-component'
import { dateFormatBackend } from '../../helpers/date' import { dateFormatBackend } from '../../helpers/date'
const fetcher = (path: string) => fetch(path).then((r) => r.json()) const fetcher = (path: string) => fetch(path).then((r) => r.json())
@@ -13,10 +13,17 @@ export default function MyCalendar() {
fetcher fetcher
) )
const [date, setDate] = useState(moment()) function tileClassName({ date, view }) {
const isMonthView = view === 'month'
function dayBooked(day: Date) { const isDaysBookedLoaded = !!daysBooked
return daysBooked && daysBooked.includes(dateFormatBackend(day)) 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) { if (fetchBookedOnError) {
@@ -29,48 +36,9 @@ export default function MyCalendar() {
} }
return ( return (
<div className="mb-12"> <div className="mb-12 p-6 bg-blue-100 rounded">
<h2 className="text-xl">Buchungsübersicht</h2> <h2 className="text-xl">Buchungsübersicht</h2>
<Calendar <Calendar minDate={new Date()} tileClassName={tileClassName} />
onChangeMonth={(date: React.SetStateAction<Moment>) => setDate(date)}
date={date}
renderDay={({
day,
classNames,
onPickDate,
}: {
day: Moment
classNames: string
onPickDate: any
}) => (
<div
key={day.format()}
className={classnames(
'Calendar-grid-item',
day.isSame(moment(), 'day') && 'Calendar-grid-item--current',
day.isBefore(moment(), 'day') && 'Calendar-grid-item--past',
!!daysBooked &&
(dayBooked(day.toDate())
? 'Calendar-grid-item--booked'
: 'Calendar-grid-item--free'),
classNames
)}
onClick={(e) => onPickDate(day)}
>
{day.format('D')}
</div>
)}
renderHeader={({ date, onPrevMonth, onNextMonth }) => (
<div className="Calendar-header">
<button onClick={onPrevMonth}>«</button>
<div className="Calendar-header-currentDate">
{date.format('MMMM YYYY')}
</div>
<button onClick={onNextMonth}>»</button>
</div>
)}
onPickDate={() => {}}
/>
<div className="mt-6 flex justify-center"> <div className="mt-6 flex justify-center">
<div> <div>
<div className="inline-block w-5 h-5 bg-green-200 rounded align-text-bottom"></div> <div className="inline-block w-5 h-5 bg-green-200 rounded align-text-bottom"></div>

View File

@@ -65,7 +65,10 @@ const BookingSchema = new mongoose.Schema<BookingDocument>(
) )
BookingSchema.virtual('days').get(function () { 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<string[]> { BookingSchema.static('findBookedDays', async function (): Promise<string[]> {

View File

@@ -1,50 +1,49 @@
import moment from 'moment' import { parse, format, addDays } from 'date-fns'
const FRONTEND_FORMAT = 'DD.MM.YYYY' const FRONTEND_FORMAT = 'dd.MM.yyyy'
const BACKEND_FORMAT = 'YYYY-MM-DD' const BACKEND_FORMAT = 'yyyy-MM-dd'
export function getDays({ export function getDays({
startDate, startDate,
endDate, endDate,
}: { }: {
startDate: moment.MomentInput startDate: Date
endDate: moment.MomentInput endDate: Date
}) { }) {
let currentDay = moment(startDate) let currentDay = new Date(startDate.getTime())
const days = [dateFormatBackend(currentDay)] const days = [dateFormatBackend(currentDay)]
if (!endDate) { if (!endDate) {
return days return days
} }
const end = moment(endDate) while (currentDay < endDate) {
while (currentDay < end) { currentDay = addDays(currentDay, 1)
currentDay = currentDay.add(1, 'day')
days.push(dateFormatBackend(currentDay)) days.push(dateFormatBackend(currentDay))
} }
return days return days
} }
function dateFormat(date: moment.MomentInput, format: string) { function dateFormat(date: Date, formatString: string) {
if (!date) { if (!date) {
return null 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) return dateFormat(date, BACKEND_FORMAT)
} }
export function dateFormatFrontend(date: moment.MomentInput) { export function dateFormatFrontend(date: Date) {
return dateFormat(date, FRONTEND_FORMAT) return dateFormat(date, FRONTEND_FORMAT)
} }
function dateParse(input: string, format: string) { function dateParse(input: string, formatString: string) {
const date = moment(input, format) const date = parse(input, formatString, new Date())
if (date.isValid()) { if (date.getTime() !== NaN) {
return date.toDate() return date
} }
return null return null

67
package-lock.json generated
View File

@@ -1153,19 +1153,6 @@
"esutils": "^2.0.2" "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": { "@babel/preset-typescript": {
"version": "7.10.4", "version": "7.10.4",
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.10.4.tgz", "resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.10.4.tgz",
@@ -2151,6 +2138,11 @@
"csstype": "^3.0.2" "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": { "@types/stack-utils": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
@@ -2335,6 +2327,11 @@
"@xtuc/long": "4.2.2" "@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": { "@xtuc/ieee754": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz", "resolved": "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz",
@@ -3900,6 +3897,11 @@
"whatwg-url": "^8.0.0" "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": { "dayjs": {
"version": "1.8.36", "version": "1.8.36",
"resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.8.36.tgz",
@@ -4793,6 +4795,14 @@
"pump": "^3.0.0" "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": { "get-value": {
"version": "2.0.6", "version": "2.0.6",
"resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
@@ -7109,6 +7119,11 @@
"integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=", "integrity": "sha1-DM8tiRZq8Ds2Y8eWU4t1rG4RTZ0=",
"dev": true "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": { "lodash.sortby": {
"version": "4.7.0", "version": "4.7.0",
"resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz",
@@ -7217,6 +7232,11 @@
"integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==",
"optional": true "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": { "merge-stream": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -7368,11 +7388,6 @@
"minimist": "^1.2.5" "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": { "mongodb": {
"version": "3.6.2", "version": "3.6.2",
"resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz", "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-3.6.2.tgz",
@@ -8983,15 +8998,15 @@
"prop-types": "^15.6.2" "prop-types": "^15.6.2"
} }
}, },
"react-calendar-component": { "react-calendar": {
"version": "3.0.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/react-calendar-component/-/react-calendar-component-3.0.0.tgz", "resolved": "https://registry.npmjs.org/react-calendar/-/react-calendar-3.1.0.tgz",
"integrity": "sha512-vua/pu+hTeLD9SSc+NH79IwCLc3REvl4gL9C3dtUo/+cGhPiQo6CGC/Jm8AVgTBhO4CtNr21tEWAdlCVqIxBbA==", "integrity": "sha512-xoKdRe6FrnZ30LD9pyr20fhet1uwSbc6srLGm1ib7G4b7tAXniZrwzrJ4YV/Hbmmwf/zAFGyXtBzLAIV1KNvuA==",
"requires": { "requires": {
"@babel/preset-react": "^7.0.0", "@wojtekmaj/date-utils": "^1.0.2",
"classnames": "^2.2.5", "get-user-locale": "^1.2.0",
"moment": "^2.19.1", "merge-class-names": "^1.1.1",
"prop-types": "^15.7.2" "prop-types": "^15.6.0"
} }
}, },
"react-dom": { "react-dom": {

View File

@@ -10,12 +10,12 @@
"test:ci": "jest" "test:ci": "jest"
}, },
"dependencies": { "dependencies": {
"date-fns": "^2.16.1",
"ics": "^2.24.0", "ics": "^2.24.0",
"moment": "^2.28.0",
"mongoose": "^5.10.5", "mongoose": "^5.10.5",
"next": "^9.5.3", "next": "^9.5.3",
"react": "16.13.1", "react": "16.13.1",
"react-calendar-component": "^3.0.0", "react-calendar": "^3.1.0",
"react-dom": "16.13.1", "react-dom": "16.13.1",
"uuid": "^8.3.0" "uuid": "^8.3.0"
}, },
@@ -24,6 +24,7 @@
"@types/jest": "^26.0.14", "@types/jest": "^26.0.14",
"@types/mongoose": "^5.7.36", "@types/mongoose": "^5.7.36",
"@types/react": "^16.9.49", "@types/react": "^16.9.49",
"@types/react-calendar": "^3.1.1",
"@types/uuid": "^8.3.0", "@types/uuid": "^8.3.0",
"babel-jest": "^26.3.0", "babel-jest": "^26.3.0",
"jest": "^26.4.2", "jest": "^26.4.2",

View File

@@ -120,49 +120,109 @@
content: '\274c'; content: '\274c';
} }
.Calendar-grid { .react-calendar {
@apply flex flex-wrap; @apply border border-gray-900;
} }
.Calendar-header { .react-calendar button {
@apply flex justify-between bg-gray-700 text-gray-200 text-center; @apply outline-none;
} }
.Calendar-header button { .react-calendar button:enabled:hover {
@apply w-10 bg-transparent text-gray-200 cursor-pointer; cursor: pointer;
} }
.Calendar-grid-item { .react-calendar__navigation {
@apply text-center p-1 border-r border-b border-solid border-gray-500; @apply text-gray-100 bg-gray-900;
flex: 0 calc(100% / 7);
} }
.Calendar-grid-item--past { .react-calendar__navigation button {
@apply text-gray-500 bg-gray-200; @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; @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; @apply bg-green-200;
} }
.Calendar-grid-item--booked { .react-calendar__tile--booked {
@apply bg-red-200 cursor-not-allowed; @apply bg-red-200 cursor-not-allowed;
} }
.Calendar-grid-item.nextMonth, .react-calendar__tile--past {
.Calendar-grid-item.prevMonth { @apply bg-gray-400 cursor-not-allowed;
@apply text-gray-500;
} }
.Calendar-grid-item:nth-child(7n + 1) { .react-calendar__tile:disabled {
@apply border-l; @apply bg-gray-300;
} }
.Calendar-grid-item:nth-child(-n + 7) { .react-calendar__tile:enabled:hover,
@apply border-t; .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... */ /* Start purging... */