prettier all the things

This commit is contained in:
Thomas Ruoff
2022-02-19 23:18:49 +01:00
committed by Thomas Ruoff
parent c35d3009c6
commit 36f8719531
28 changed files with 433 additions and 428 deletions

View File

@@ -19,34 +19,34 @@ export default function DateSelect() {
onChange={onChange} onChange={onChange}
className="my-6 max-w-lg" className="my-6 max-w-lg"
/> />
</InputWrapper> </InputWrapper>
<div className="flex flex-wrap -mx-3 mb-2"> <div className="flex flex-wrap -mx-3 mb-2">
<div className="w-full md:w-2/5 px-3 mb-2 md:mb-0"> <div className="w-full md:w-2/5 px-3 mb-2 md:mb-0">
<Input <Input
label="Von" label="Von"
type="date" type="date"
className="" className=""
name="startDate" name="startDate"
value={startDate || ''} value={startDate || ''}
onChange={onChangeEvent} onChange={onChangeEvent}
min={today} min={today}
required required
/> />
</div>
<div className="w-full md:w-2/5 px-3 mb-2 md:mb-0">
<Input
required
label="Bis"
type="date"
className=""
name="endDate"
value={endDate || ''}
placeholder="Von"
onChange={onChangeEvent}
min={startDate || today}
/>
</div>
</div> </div>
<div className="w-full md:w-2/5 px-3 mb-2 md:mb-0">
<Input
required
label="Bis"
type="date"
className=""
name="endDate"
value={endDate || ''}
placeholder="Von"
onChange={onChangeEvent}
min={startDate || today}
/>
</div>
</div>
</> </>
) )
} }

View File

@@ -22,7 +22,10 @@ function BookForm() {
{dataStoredLoaded && ( {dataStoredLoaded && (
<p className="mb-6 info-message"> <p className="mb-6 info-message">
Buchungsdaten wurden aus Deinem Browser geladen und vorausgefüllt.{' '} Buchungsdaten wurden aus Deinem Browser geladen und vorausgefüllt.{' '}
<a className="font-medium text-blue-400 underline cursor-pointer hover:text-blue-600" onClick={forgetData}> <a
className="font-medium text-blue-400 underline cursor-pointer hover:text-blue-600"
onClick={forgetData}
>
Daten wieder vergessen Daten wieder vergessen
</a> </a>
</p> </p>

View File

@@ -9,7 +9,10 @@ export default function Denied() {
onClick={(e) => { onClick={(e) => {
e.preventDefault() e.preventDefault()
signIn() signIn()
}}>Melde dich an um diese Seite zu sehen.</a> }}
>
Melde dich an um diese Seite zu sehen.
</a>
</p> </p>
</> </>
) )

View File

@@ -7,10 +7,18 @@ type InputProps = React.InputHTMLAttributes<HTMLInputElement> & {
} }
export default function Input(props: InputProps) { export default function Input(props: InputProps) {
const { label, name, required, type = 'text', className = "", ...rest } = props const {
label,
name,
required,
type = 'text',
className = '',
...rest
} = props
const defaultClasses = "appearance-none bg-gray-50 text-gray-500 border rounded py-2 px-3 mb-3 leading-tight disabled:bg-gray-200 disabled:cursor-not-allowed focus: outline-none focus:bg-white w-full" const defaultClasses =
const classes = `${defaultClasses} ${className}`; 'appearance-none bg-gray-50 text-gray-500 border rounded py-2 px-3 mb-3 leading-tight disabled:bg-gray-200 disabled:cursor-not-allowed focus: outline-none focus:bg-white w-full'
const classes = `${defaultClasses} ${className}`
return ( return (
<InputWrapper label={label} name={name} required={required}> <InputWrapper label={label} name={name} required={required}>

View File

@@ -13,7 +13,10 @@ export default function Input(props: {
<> <>
<div className="flex flex-wrap -mx-3 mb-2"> <div className="flex flex-wrap -mx-3 mb-2">
<div className="w-full px-3 mb-2"> <div className="w-full px-3 mb-2">
<label className="block uppercase tracking-wide text-gray-500 text-xs font-bold mb-2" htmlFor={name}> <label
className="block uppercase tracking-wide text-gray-500 text-xs font-bold mb-2"
htmlFor={name}
>
{label} {required && <Required />} {label} {required && <Required />}
</label> </label>
{children} {children}

View File

@@ -1,32 +1,36 @@
const H1 = (props: any) => ( const H1 = (props: any) => (
<h1 <h1
className="pb-2 mt-6 mb-4 text-4xl font-semibold leading-tight border-b" className="pb-2 mt-6 mb-4 text-4xl font-semibold leading-tight border-b"
{...props} {...props}
/> />
) )
const H2 = (props: any) => ( const H2 = (props: any) => (
<h2 <h2
className="pb-2 mt-6 mb-4 text-2xl font-semibold leading-tight border-b" className="pb-2 mt-6 mb-4 text-2xl font-semibold leading-tight border-b"
{...props} {...props}
/> />
) )
const H3 = (props: any) => ( const H3 = (props: any) => (
<h3 className="mt-6 mb-4 text-lg font-semibold leading-snug" {...props} /> <h3 className="mt-6 mb-4 text-lg font-semibold leading-snug" {...props} />
) )
const H4 = (props: any) => ( const H4 = (props: any) => (
<h4 className="mt-6 mb-4 text-base font-semibold leading-none" {...props} /> <h4 className="mt-6 mb-4 text-base font-semibold leading-none" {...props} />
) )
const H5 = (props: any) => ( const H5 = (props: any) => (
<h5 className="mt-6 mb-4 text-sm font-semibold leading-tight" {...props} /> <h5 className="mt-6 mb-4 text-sm font-semibold leading-tight" {...props} />
) )
const H6 = (props: any) => ( const H6 = (props: any) => (
<h6 <h6
className="mt-6 mb-4 text-sm font-semibold leading-tight text-gray-600" className="mt-6 mb-4 text-sm font-semibold leading-tight text-gray-600"
{...props} {...props}
/> />
) )
const UL = (props: any) => <ul className="pl-8 text-base list-disc" {...props} /> const UL = (props: any) => (
const OL = (props: any) => <ol className="pl-8 text-base list-decimal" {...props} /> <ul className="pl-8 text-base list-disc" {...props} />
)
const OL = (props: any) => (
<ol className="pl-8 text-base list-decimal" {...props} />
)
const MdComponents = { const MdComponents = {
h1: H1, h1: H1,
@@ -39,4 +43,4 @@ const MdComponents = {
ol: OL, ol: OL,
} }
export default MdComponents; export default MdComponents

View File

@@ -5,7 +5,6 @@ import { useSession } from 'next-auth/react'
import User from './user' import User from './user'
const pathNameLabelMap = { const pathNameLabelMap = {
'/login': 'Login', '/login': 'Login',
'/admin': 'Buchungsübersicht', '/admin': 'Buchungsübersicht',
@@ -21,7 +20,7 @@ function getPathNameMap(route: string) {
} }
export default function Navigation() { export default function Navigation() {
const { data, status } = useSession(); const { data, status } = useSession()
const router = useRouter() const router = useRouter()
const pathname = router.pathname const pathname = router.pathname

View File

@@ -1,11 +1,10 @@
import { useSession, signOut } from 'next-auth/react' import { useSession, signOut } from 'next-auth/react'
export default function User() { export default function User() {
const { data, status } = useSession(); const { data, status } = useSession()
if (status === 'loading' || !data?.user?.email) { if (status === 'loading' || !data?.user?.email) {
return null; return null
} }
return ( return (

View File

@@ -34,9 +34,8 @@ type BookAction = {
payload?: any payload?: any
} }
export const BookContext: React.Context<BookProvider> = React.createContext< export const BookContext: React.Context<BookProvider> =
BookProvider React.createContext<BookProvider>(null)
>(null)
export const ACTIONS = { export const ACTIONS = {
SET_FORM_DATA: 'setFormData', SET_FORM_DATA: 'setFormData',

View File

@@ -22,8 +22,8 @@ export type Booking = {
purpose?: string purpose?: string
org?: string org?: string
destination?: string destination?: string
days?: string[], days?: string[]
calendarEventId: string, calendarEventId: string
} }
export type BookingDocument = Booking & export type BookingDocument = Booking &
@@ -128,37 +128,43 @@ BookingSchema.pre('save', async function (next: () => void): Promise<void> {
if (!booking.calendarEventId) { if (!booking.calendarEventId) {
// create calendar event before saving to database // create calendar event before saving to database
await createCalendarEvent(booking); await createCalendarEvent(booking)
} else if ([BOOKING_STATUS.CANCELED, BOOKING_STATUS.REJECTED].includes(booking.status)) { } else if (
[BOOKING_STATUS.CANCELED, BOOKING_STATUS.REJECTED].includes(booking.status)
) {
// event has been canceled or rejected, delete calendar event again to free up the slot // event has been canceled or rejected, delete calendar event again to free up the slot
await deleteCalendarEvent(booking); await deleteCalendarEvent(booking)
} }
next(); next()
});
BookingSchema.static('findBookedDays', async function (
uuidsToIngore: string[] = []
): Promise<string[]> {
const model = this as BookingModel
const now = nowInTz()
const bookedDays = await model
.find(
{
status: { $in: [BOOKING_STATUS.REQUESTED, BOOKING_STATUS.CONFIRMED] },
uuid: { $nin: uuidsToIngore },
// dateFormatBackend uses YYYY-MM-DD, which is startOfDay anyway
endDate: { $gt: dateFormatBackend(now) },
},
'days'
)
.exec()
return bookedDays
.map((b) => b.days)
.flat()
.sort()
}) })
BookingSchema.static(
'findBookedDays',
async function (uuidsToIngore: string[] = []): Promise<string[]> {
const model = this as BookingModel
const now = nowInTz()
const bookedDays = await model
.find(
{
status: { $in: [BOOKING_STATUS.REQUESTED, BOOKING_STATUS.CONFIRMED] },
uuid: { $nin: uuidsToIngore },
// dateFormatBackend uses YYYY-MM-DD, which is startOfDay anyway
endDate: { $gt: dateFormatBackend(now) },
},
'days'
)
.exec()
return bookedDays
.map((b) => b.days)
.flat()
.sort()
}
)
export default (mongoose.models.Booking || export default (mongoose.models.Booking ||
mongoose.model<BookingDocument, BookingModel>('Booking', BookingSchema)) as BookingModel mongoose.model<BookingDocument, BookingModel>(
'Booking',
BookingSchema
)) as BookingModel

View File

@@ -5,7 +5,7 @@ import { BOOKING_STATUS } from './enums'
let connectedPromise: Promise<mongoose.Mongoose> let connectedPromise: Promise<mongoose.Mongoose>
export const MONGO_URI = process.env.MONGO_URI; export const MONGO_URI = process.env.MONGO_URI
export const MONGODB_OPTIONS = { export const MONGODB_OPTIONS = {
useCreateIndex: true, useCreateIndex: true,
@@ -45,7 +45,9 @@ export async function getBookings({
return await BookingModel.find({ return await BookingModel.find({
status: { $in: status }, status: { $in: status },
startDate: { $gte: startDateGreaterThan }, startDate: { $gte: startDateGreaterThan },
}).sort({ startDate: -1 }).exec() })
.sort({ startDate: -1 })
.exec()
} }
export async function createBooking({ export async function createBooking({

View File

@@ -39,23 +39,26 @@ Buchungs-Link: ${getBaseURL()}/bookings/${booking.uuid}
} }
export function generateBookedCalendar(bookings: Booking[]): string { export function generateBookedCalendar(bookings: Booking[]): string {
const events = bookings.map((booking): { const events = bookings.map(
productId: string (
calName: string booking
start: [number, number, number] ): {
startOutputType: 'local' | 'utc' productId: string
duration: { days: number } calName: string
title: string start: [number, number, number]
description: string startOutputType: 'local' | 'utc'
status: EventStatus duration: { days: number }
} => ({ title: string
productId: 'app.vercel.pfadi-bussle/ics', description: string
calName: 'Pfadi-Bussle Buchungen', status: EventStatus
start: convertDay(booking.days[0]), } => ({
startOutputType: 'local', productId: 'app.vercel.pfadi-bussle/ics',
duration: { days: booking.days.length }, calName: 'Pfadi-Bussle Buchungen',
title: `Buchung ${booking.name}`, start: convertDay(booking.days[0]),
description: `Name: ${booking.name} startOutputType: 'local',
duration: { days: booking.days.length },
title: `Buchung ${booking.name}`,
description: `Name: ${booking.name}
Zeitraum: ${daysFormatFrontend(booking.days)} Zeitraum: ${daysFormatFrontend(booking.days)}
Email: ${booking.email} Email: ${booking.email}
@@ -63,11 +66,12 @@ Telefon: ${booking.phone}
Link: ${getBaseURL()}/admin/bookings/${booking.uuid} Link: ${getBaseURL()}/admin/bookings/${booking.uuid}
`, `,
status: status:
booking.status === BOOKING_STATUS.CONFIRMED booking.status === BOOKING_STATUS.CONFIRMED
? ('CONFIRMED' as EventStatus) ? ('CONFIRMED' as EventStatus)
: ('TENTATIVE' as EventStatus), : ('TENTATIVE' as EventStatus),
})) })
)
const { error, value } = createEvents(events) const { error, value } = createEvents(events)

View File

@@ -14,23 +14,22 @@ export type ServerSideRecentBooking = {
} }
} }
export const getServerSideRecentBookings = async (): Promise< export const getServerSideRecentBookings =
ServerSideRecentBooking async (): Promise<ServerSideRecentBooking> => {
> => { const bookings = await getBookings({
const bookings = await getBookings({ startDateGreaterThan: startOfYear(nowInTz()).toISOString(),
startDateGreaterThan: startOfYear(nowInTz()).toISOString(), })
})
// TODO: hack, not sure why _id is not serilizable // TODO: hack, not sure why _id is not serilizable
const bookingsJSON = JSON.parse( const bookingsJSON = JSON.parse(
JSON.stringify(bookings.map((b) => b.toJSON())) JSON.stringify(bookings.map((b) => b.toJSON()))
) as object[] ) as object[]
return { return {
props: { props: {
bookings: bookingsJSON, bookings: bookingsJSON,
}, },
}
} }
}
export const getServerSideBooking = async ( export const getServerSideBooking = async (
context: any context: any

View File

@@ -1,46 +1,44 @@
import { google } from 'googleapis'
import { Booking } from '../db/booking'
import {google} from 'googleapis'; const keyFile = process.env.GOOGLE_SERVICE_ACCOUNT_KEY_FILE
import { Booking } from '../db/booking'; const calendarId = process.env.GOOGLE_CALENDAR_ID
const keyFile = process.env.GOOGLE_SERVICE_ACCOUNT_KEY_FILE;
const calendarId = process.env.GOOGLE_CALENDAR_ID;
const auth = new google.auth.GoogleAuth({ const auth = new google.auth.GoogleAuth({
keyFile, keyFile,
scopes: [ scopes: [
'https://www.googleapis.com/auth/calendar', 'https://www.googleapis.com/auth/calendar',
'https://www.googleapis.com/auth/calendar.readonly' 'https://www.googleapis.com/auth/calendar.readonly',
], ],
}); })
const calendar = google.calendar({ const calendar = google.calendar({
version: 'v3', version: 'v3',
auth, auth,
}); })
export async function getNewEvents() { export async function getNewEvents() {
const { data } = await calendar.events.list({ const { data } = await calendar.events.list({
calendarId, calendarId,
timeMin: new Date().toISOString()} timeMin: new Date().toISOString(),
); })
return data.items; return data.items
} }
function getSummary(booking: Partial<Booking>) : string { function getSummary(booking: Partial<Booking>): string {
let summary = ""; let summary = ''
if (booking.org) { if (booking.org) {
summary += `${booking.org} - `; summary += `${booking.org} - `
} }
summary += booking.name; summary += booking.name
return summary; return summary
} }
export async function createCalendarEvent(booking : Booking) : Promise<Booking> { export async function createCalendarEvent(booking: Booking): Promise<Booking> {
const response = await calendar.events.insert({ const response = await calendar.events.insert({
auth, auth,
calendarId, calendarId,
@@ -49,12 +47,12 @@ export async function createCalendarEvent(booking : Booking) : Promise<Booking>
// description, // description,
start: { date: booking.startDate }, start: { date: booking.startDate },
end: { date: booking.endDate }, end: { date: booking.endDate },
} },
}); })
booking.calendarEventId = response.data.id; booking.calendarEventId = response.data.id
return booking; return booking
} }
export async function deleteCalendarEvent(booking: Booking) { export async function deleteCalendarEvent(booking: Booking) {
@@ -62,9 +60,9 @@ export async function deleteCalendarEvent(booking: Booking) {
auth, auth,
calendarId, calendarId,
eventId: booking.calendarEventId, eventId: booking.calendarEventId,
}); })
booking.calendarEventId = null; booking.calendarEventId = null
} }
//export async function patchCalendarEvent(booking: { calendarEventId: string } & Partial<Booking> ) : Promise<Booking> { //export async function patchCalendarEvent(booking: { calendarEventId: string } & Partial<Booking> ) : Promise<Booking> {
@@ -76,4 +74,3 @@ export async function deleteCalendarEvent(booking: Booking) {
// //
// return booking; // return booking;
//} //}

View File

@@ -1,6 +1,6 @@
import { useEffect } from 'react'; import { useEffect } from 'react'
import { useSession, signIn, SessionProvider } from "next-auth/react" import { useSession, signIn, SessionProvider } from 'next-auth/react'
import '../styles/index.css' import '../styles/index.css'
function Auth({ children }) { function Auth({ children }) {
@@ -21,8 +21,10 @@ function Auth({ children }) {
return <div>Loading...</div> return <div>Loading...</div>
} }
export default function MyApp({ Component, pageProps: { session, ...pageProps } }) { export default function MyApp({
Component,
pageProps: { session, ...pageProps },
}) {
return ( return (
<div className="flex flex-col min-h-screen"> <div className="flex flex-col min-h-screen">
<SessionProvider session={session}> <SessionProvider session={session}>
@@ -37,4 +39,3 @@ export default function MyApp({ Component, pageProps: { session, ...pageProps }
</div> </div>
) )
} }

View File

@@ -18,7 +18,7 @@ export const getServerSideProps = async (context) => {
...serverSideBookingProps.props, ...serverSideBookingProps.props,
milageMax, milageMax,
}, },
}; }
} }
const milageTarifOptions = Object.values(MILAGE_TARIFS).map((tarif) => { const milageTarifOptions = Object.values(MILAGE_TARIFS).map((tarif) => {
@@ -134,135 +134,133 @@ function BookingBillPage({
return ( return (
<Layout> <Layout>
{booking && ( {booking && (
<form className="w-full" onSubmit={onSubmit}> <form className="w-full" onSubmit={onSubmit}>
<div> <div>
<strong>Buchungszeitraum:</strong>{' '} <strong>Buchungszeitraum:</strong>{' '}
{daysFormatFrontend(booking.days)} {daysFormatFrontend(booking.days)}
</div> </div>
<div> <div>
<strong>Bucher:</strong> {booking.name} <strong>Bucher:</strong> {booking.name}
</div> </div>
<div> <div>
<strong>Buchungsstatus:</strong>{' '} <strong>Buchungsstatus:</strong> {getBookingStatus(booking.status)}
{getBookingStatus(booking.status)} </div>
</div> <div>
<div> <Input
<Input label="Anfangskilometer"
label="Anfangskilometer" name="milageStart"
name="milageStart" required
required value={milageStart}
value={milageStart} type="number"
type="number" onChange={(e: React.ChangeEvent<React.ElementRef<'input'>>) =>
onChange={(e: React.ChangeEvent<React.ElementRef<'input'>>) => setMilageStart(Number(e.target.value))
setMilageStart(Number(e.target.value)) }
} />
/> <Input
<Input label="Endkilometer"
label="Endkilometer" name="milageEnd"
name="milageEnd" required
required value={milageEnd}
value={milageEnd} type="number"
type="number" onChange={(e: React.ChangeEvent<React.ElementRef<'input'>>) =>
onChange={(e: React.ChangeEvent<React.ElementRef<'input'>>) => setMilageEnd(Number(e.target.value))
setMilageEnd(Number(e.target.value)) }
} />
/> <Input label="Gefahren" name="milage" readOnly value={milage} />
<Input label="Gefahren" name="milage" readOnly value={milage} />
<Select
label="Rate"
name="tarif"
value={tarif}
onChange={(e) => setTarif(e.target.value as MILAGE_TARIFS)}
>
{milageTarifOptions}
</Select>
<div className="mb-3">
<button
className="ibtn btn-gray mr-3"
onClick={onAddAdditionalCost}
title="Zusätzliche Kosten hinzufügen"
>
+
</button>
<label className="flabel inline">Zusätzliche Kosten</label>
</div>
{additionalCosts.map((_, index) => {
return (
<>
<div className="mb-3" key={`label${index}`}>
<button
className="ibtn btn-gray mr-3"
onClick={(event) =>
onRemoveAdditionalCost(event, index)
}
title="Entfernen"
>
-
</button>
<label className="flabel inline">{`Kostenpunkt ${index + 1
}`}</label>
</div>
<div className="ml-10 mb-3" key={`input{index}`}>
<Input
label={`Name`}
name={`additionalCostName${index}`}
key={`additionalCostName${index}`}
value={additionalCosts[index].name}
onChange={(event) => {
const newAdditonalCosts = [...additionalCosts]
newAdditonalCosts[index] = {
value: newAdditonalCosts[index].value,
name: event.target.value,
}
setAdditionalCosts(newAdditonalCosts)
}}
/>
<Input
label={`Betrag`}
name={`additionalCostValue${index}`}
key={`additionalCostValue${index}`}
value={additionalCosts[index].value}
type="number"
onChange={(event) => {
const newAdditonalCosts = [...additionalCosts]
newAdditonalCosts[index] = {
name: newAdditonalCosts[index].name,
value: Number(event.target.value),
}
setAdditionalCosts(newAdditonalCosts)
}}
/>
</div>
</>
)
})}
<Input label="Summe" name="total" readOnly value={total} />
</div>
<Select <Select
label="Status" label="Rate"
name={status} name="tarif"
value={status} value={tarif}
onChange={(e) => setStatus(e.target.value as BILL_STATUS)} onChange={(e) => setTarif(e.target.value as MILAGE_TARIFS)}
> >
{billStatusOptions} {milageTarifOptions}
</Select> </Select>
{storingError && ( <div className="mb-3">
<div className="error-message flex-grow mt-6">{storingError}</div> <button
)} className="ibtn btn-gray mr-3"
<button onClick={onAddAdditionalCost}
type="submit" title="Zusätzliche Kosten hinzufügen"
className="btn btn-blue mt-3" >
disabled={storingInProgress} +
> </button>
Rechnung {!!booking.bill ? 'Updaten' : 'Erstellen'} <label className="flabel inline">Zusätzliche Kosten</label>
</button> </div>
</form> {additionalCosts.map((_, index) => {
)} return (
<>
<div className="mb-3" key={`label${index}`}>
<button
className="ibtn btn-gray mr-3"
onClick={(event) => onRemoveAdditionalCost(event, index)}
title="Entfernen"
>
-
</button>
<label className="flabel inline">{`Kostenpunkt ${
index + 1
}`}</label>
</div>
<div className="ml-10 mb-3" key={`input{index}`}>
<Input
label={`Name`}
name={`additionalCostName${index}`}
key={`additionalCostName${index}`}
value={additionalCosts[index].name}
onChange={(event) => {
const newAdditonalCosts = [...additionalCosts]
newAdditonalCosts[index] = {
value: newAdditonalCosts[index].value,
name: event.target.value,
}
setAdditionalCosts(newAdditonalCosts)
}}
/>
<Input
label={`Betrag`}
name={`additionalCostValue${index}`}
key={`additionalCostValue${index}`}
value={additionalCosts[index].value}
type="number"
onChange={(event) => {
const newAdditonalCosts = [...additionalCosts]
newAdditonalCosts[index] = {
name: newAdditonalCosts[index].name,
value: Number(event.target.value),
}
setAdditionalCosts(newAdditonalCosts)
}}
/>
</div>
</>
)
})}
<Input label="Summe" name="total" readOnly value={total} />
</div>
<Select
label="Status"
name={status}
value={status}
onChange={(e) => setStatus(e.target.value as BILL_STATUS)}
>
{billStatusOptions}
</Select>
{storingError && (
<div className="error-message flex-grow mt-6">{storingError}</div>
)}
<button
type="submit"
className="btn btn-blue mt-3"
disabled={storingInProgress}
>
Rechnung {!!booking.bill ? 'Updaten' : 'Erstellen'}
</button>
</form>
)}
</Layout> </Layout>
) )
} }
BookingBillPage.auth = true; BookingBillPage.auth = true
export default BookingBillPage; export default BookingBillPage

View File

@@ -9,13 +9,9 @@ import { getBookingStatus, patchBooking } from '../../../../helpers/booking'
import { daysFormatFrontend } from '../../../../helpers/date' import { daysFormatFrontend } from '../../../../helpers/date'
import { BOOKING_STATUS } from '../../../../db/enums' import { BOOKING_STATUS } from '../../../../db/enums'
export const getServerSideProps = getServerSideBooking; export const getServerSideProps = getServerSideBooking
function ShowBookingAdmin({ function ShowBookingAdmin({ booking: bookingProp }: { booking: Booking }) {
booking: bookingProp,
}: {
booking: Booking
}) {
const router = useRouter() const router = useRouter()
const [booking, setBooking] = useState(bookingProp) const [booking, setBooking] = useState(bookingProp)
const [storingBooking, setStoringBooking] = useState(false) const [storingBooking, setStoringBooking] = useState(false)
@@ -41,43 +37,43 @@ function ShowBookingAdmin({
return ( return (
<Layout> <Layout>
<h2 className="text-3xl">Buchung {booking.uuid}</h2> <h2 className="text-3xl">Buchung {booking.uuid}</h2>
<Calendar start={booking.startDate} end={booking.endDate} /> <Calendar start={booking.startDate} end={booking.endDate} />
<div> <div>
<strong>Buchungszeitraum:</strong> {daysFormatFrontend(booking.days)} <strong>Buchungszeitraum:</strong> {daysFormatFrontend(booking.days)}
</div> </div>
<div> <div>
<strong>Bucher:</strong> {booking.name} <strong>Bucher:</strong> {booking.name}
</div> </div>
<div> <div>
<strong>Buchungsstatus:</strong> {getBookingStatus(booking.status)} <strong>Buchungsstatus:</strong> {getBookingStatus(booking.status)}
</div> </div>
{storingBookingError && ( {storingBookingError && (
<div className="error-message flex-grow">{storingBookingError}</div> <div className="error-message flex-grow">{storingBookingError}</div>
)} )}
<div className="my-6"> <div className="my-6">
<button <button
onClick={() => onStoreBooking(true)} onClick={() => onStoreBooking(true)}
className="btn btn-blue" className="btn btn-blue"
disabled={storingBooking} disabled={storingBooking}
> >
Buchung Bestätigen Buchung Bestätigen
</button> </button>
<button <button
onClick={() => onStoreBooking(false)} onClick={() => onStoreBooking(false)}
className="btn btn-red" className="btn btn-red"
disabled={storingBooking} disabled={storingBooking}
> >
Buchung Abweisen Buchung Abweisen
</button> </button>
<Link href={`${router.asPath}/bill`}> <Link href={`${router.asPath}/bill`}>
<a className="btn btn-gray">Rechnung</a> <a className="btn btn-gray">Rechnung</a>
</Link> </Link>
</div> </div>
</Layout> </Layout>
) )
} }
ShowBookingAdmin.auth = true; ShowBookingAdmin.auth = true
export default ShowBookingAdmin; export default ShowBookingAdmin

View File

@@ -1,20 +1,20 @@
import React from 'react' import React from 'react'
import Link from 'next/link' import Link from 'next/link'
import { useSession } from 'next-auth/react' import { useSession } from 'next-auth/react'
import Layout from '../../components/layout'; import Layout from '../../components/layout'
import Denied from '../../components/denied'; import Denied from '../../components/denied'
import { daysFormatFrontend } from '../../helpers/date' import { daysFormatFrontend } from '../../helpers/date'
import { getServerSideRecentBookings } from '../../lib/getServerSideProps' import { getServerSideRecentBookings } from '../../lib/getServerSideProps'
export const getServerSideProps = getServerSideRecentBookings; export const getServerSideProps = getServerSideRecentBookings
function AdminRecentBookings({ bookings }) { function AdminRecentBookings({ bookings }) {
const { data: session, status} = useSession(); const { data: session, status } = useSession()
if (typeof window !== 'undefined' && status === "loading") return null; if (typeof window !== 'undefined' && status === 'loading') return null
if (!bookings) return null; if (!bookings) return null
return ( return (
<Layout> <Layout>
@@ -71,4 +71,4 @@ function AdminRecentBookings({ bookings }) {
AdminRecentBookings.auth = true AdminRecentBookings.auth = true
export default AdminRecentBookings; export default AdminRecentBookings

View File

@@ -1,20 +1,20 @@
import { NextApiRequest, NextApiResponse } from 'next' import { NextApiRequest, NextApiResponse } from 'next'
import NextAuth from "next-auth" import NextAuth from 'next-auth'
import EmailProvider from "next-auth/providers/email" import EmailProvider from 'next-auth/providers/email'
import { MongoDBAdapter } from "@next-auth/mongodb-adapter" import { MongoDBAdapter } from '@next-auth/mongodb-adapter'
import { MONGO_URI, MONGODB_OPTIONS } from "../../../db" import { MONGO_URI, MONGODB_OPTIONS } from '../../../db'
import { MongoClient } from "mongodb"; import { MongoClient } from 'mongodb'
let client: MongoClient; let client: MongoClient
async function getMongoClient() { async function getMongoClient() {
if (!client) { if (!client) {
client = new MongoClient(MONGO_URI, MONGODB_OPTIONS); client = new MongoClient(MONGO_URI, MONGODB_OPTIONS)
await client.connect(); await client.connect()
} }
return client; return client
} }
export default async function auth(req: NextApiRequest, res: NextApiResponse) { export default async function auth(req: NextApiRequest, res: NextApiResponse) {
@@ -24,17 +24,15 @@ export default async function auth(req: NextApiRequest, res: NextApiResponse) {
providers: [ providers: [
EmailProvider({ EmailProvider({
server: { server: {
host: "smtp.sendgrid.net", host: 'smtp.sendgrid.net',
port: 587, port: 587,
auth: { auth: {
user: "apikey", user: 'apikey',
pass: process.env.SENDGRID_API_KEY, pass: process.env.SENDGRID_API_KEY,
}, },
}, },
from: process.env.FROM_EMAIL from: process.env.FROM_EMAIL,
}), }),
], ],
}) })
} }

View File

@@ -2,7 +2,6 @@ import { Bill } from '../../../../db/bill'
import { createBill, patchBill } from '../../../../db/index' import { createBill, patchBill } from '../../../../db/index'
export default async function billHandler(req, res): Promise<void> { export default async function billHandler(req, res): Promise<void> {
const { const {
method, method,
query: { uuid: uuids }, query: { uuid: uuids },

View File

@@ -41,28 +41,28 @@ export default function ShowBooking({
return ( return (
<Layout> <Layout>
<div> <div>
<strong>Buchungsstatus:</strong> {getBookingStatus(booking.status)} <strong>Buchungsstatus:</strong> {getBookingStatus(booking.status)}
</div>
<div>
<strong>Buchungszeitraum:</strong> {daysFormatFrontend(booking.days)}
</div>
{storingBookingError && (
<div className="error-message flex-grow">{storingBookingError}</div>
)}
{[BOOKING_STATUS.CONFIRMED, BOOKING_STATUS.REQUESTED].includes(
booking.status
) && (
<div className="my-6">
<button
onClick={onCancelBooking}
className="btn btn-red"
disabled={storingBooking}
>
Buchung Stornieren
</button>
</div> </div>
<div> )}
<strong>Buchungszeitraum:</strong> {daysFormatFrontend(booking.days)} </Layout>
</div>
{storingBookingError && (
<div className="error-message flex-grow">{storingBookingError}</div>
)}
{[BOOKING_STATUS.CONFIRMED, BOOKING_STATUS.REQUESTED].includes(
booking.status
) && (
<div className="my-6">
<button
onClick={onCancelBooking}
className="btn btn-red"
disabled={storingBooking}
>
Buchung Stornieren
</button>
</div>
)}
</Layout>
) )
} }

View File

@@ -20,37 +20,38 @@ export default function ShowBookingStored({ booking }: { booking: Booking }) {
return ( return (
<Layout> <Layout>
<h3 className="thanks mb-3 mt-6 text-lg">Vielen Dank für die Buchungsanfrage</h3> <h3 className="thanks mb-3 mt-6 text-lg">
<p className="mb-6"> Vielen Dank für die Buchungsanfrage
Nach Prüfung bestätigen wir die Buchung zeitnah per E-Mail. </h3>
</p> <p className="mb-6">
<p> Nach Prüfung bestätigen wir die Buchung zeitnah per E-Mail.
Den{' '} </p>
<Link href={`/bookings/${booking.uuid}`}> <p>
<a className="link underline">Link</a> Den{' '}
</Link>{' '} <Link href={`/bookings/${booking.uuid}`}>
zur Buchung schicken wir Dir auch per E-Mail. Dort kann die Buchung <a className="link underline">Link</a>
auch jederzeit storniert werden. </Link>{' '}
</p> zur Buchung schicken wir Dir auch per E-Mail. Dort kann die Buchung auch
{!storedBookingData && !bookingDataStored && ( jederzeit storniert werden.
<div className="mt-6"> </p>
<p> {!storedBookingData && !bookingDataStored && (
Sollen deine Buchungsdaten für die nächste Buchung in{' '} <div className="mt-6">
<strong>deinem Browser</strong> gespeichert werden? <p>
</p> Sollen deine Buchungsdaten für die nächste Buchung in{' '}
<button onClick={store} className="mt-3 ml-0 btn btn-blue"> <strong>deinem Browser</strong> gespeichert werden?
Ja, bitte speichern </p>
</button> <button onClick={store} className="mt-3 ml-0 btn btn-blue">
</div> Ja, bitte speichern
)} </button>
{bookingDataStored === true && ( </div>
<div className="mt-6"> )}
<p className="info-message"> {bookingDataStored === true && (
Ok, deine Buchungsdaten wurden für die nächste Buchung <div className="mt-6">
gespeichert. <p className="info-message">
</p> Ok, deine Buchungsdaten wurden für die nächste Buchung gespeichert.
</div> </p>
)} </div>
</Layout> )}
</Layout>
) )
} }

View File

@@ -10,7 +10,7 @@ import Layout from '../components/layout'
export default function TermsPage({ source }) { export default function TermsPage({ source }) {
return ( return (
<Layout> <Layout>
<MDXRemote {...source} components={{...mdComponents}} /> <MDXRemote {...source} components={{ ...mdComponents }} />
</Layout> </Layout>
) )
} }

View File

@@ -4,7 +4,6 @@ import Layout from '../components/layout'
export default function Home() { export default function Home() {
const hello = 'fooo' const hello = 'fooo'
return ( return (
<Layout> <Layout>
<div className="mt-6 sm:text-center"> <div className="mt-6 sm:text-center">

View File

@@ -9,7 +9,7 @@ export default function TermsPage({ source }) {
return ( return (
<Layout> <Layout>
<div className="text-gray-700"> <div className="text-gray-700">
<MDXRemote {...source} components={{...mdComponents}}/> <MDXRemote {...source} components={{ ...mdComponents }} />
</div> </div>
</Layout> </Layout>
) )

View File

@@ -10,8 +10,8 @@ import Layout from '../components/layout'
export default function TermsPage({ source }) { export default function TermsPage({ source }) {
return ( return (
<Layout> <Layout>
<div className="text-gray-700" > <div className="text-gray-700">
<MDXRemote {...source} components={{...mdComponents}} /> <MDXRemote {...source} components={{ ...mdComponents }} />
</div> </div>
</Layout> </Layout>
) )

View File

@@ -1,8 +1,5 @@
{ {
"extends": [ "extends": ["config:base", ":dependencyDashboard"],
"config:base",
":dependencyDashboard"
],
"schedule": "every weekend", "schedule": "every weekend",
"packageRules": [ "packageRules": [
{ {

View File

@@ -1,11 +1,7 @@
{ {
"compilerOptions": { "compilerOptions": {
"target": "es5", "target": "es5",
"lib": [ "lib": ["dom", "dom.iterable", "esnext"],
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true, "allowJs": true,
"skipLibCheck": true, "skipLibCheck": true,
"strict": false, "strict": false,
@@ -20,12 +16,6 @@
"jsx": "preserve", "jsx": "preserve",
"incremental": true "incremental": true
}, },
"include": [ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
"next-env.d.ts", "exclude": ["node_modules"]
"**/*.ts",
"**/*.tsx"
],
"exclude": [
"node_modules"
]
} }