mirror of
https://github.com/tomru/pfadi-bussle.git
synced 2026-03-03 06:27:11 +01:00
replace momentjs (incl. calendar)
This commit is contained in:
committed by
Thomas Ruoff
parent
7f4604f0e6
commit
02c2b45747
@@ -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 (
|
||||
<div className="mb-12">
|
||||
<div className="mb-12 p-6 bg-blue-100 rounded">
|
||||
<h2 className="text-xl">Buchungsübersicht</h2>
|
||||
<Calendar
|
||||
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={() => {}}
|
||||
/>
|
||||
<Calendar minDate={new Date()} tileClassName={tileClassName} />
|
||||
<div className="mt-6 flex justify-center">
|
||||
<div>
|
||||
<div className="inline-block w-5 h-5 bg-green-200 rounded align-text-bottom"></div>
|
||||
|
||||
@@ -65,7 +65,10 @@ const BookingSchema = new mongoose.Schema<BookingDocument>(
|
||||
)
|
||||
|
||||
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[]> {
|
||||
|
||||
@@ -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
|
||||
|
||||
67
package-lock.json
generated
67
package-lock.json
generated
@@ -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": {
|
||||
|
||||
@@ -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",
|
||||
|
||||
102
styles/index.css
102
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... */
|
||||
|
||||
Reference in New Issue
Block a user