mirror of
https://github.com/tomru/pfadi-bussle.git
synced 2026-03-03 06:27:11 +01:00
remover booker, that's overdosed
It also brings the problem of consolidating bookers over multiple bookings. The amount of data is not justifying having it in an own entity
This commit is contained in:
@@ -3,13 +3,11 @@ import { useRouter } from 'next/router'
|
|||||||
import { clearBookingData, loadBookingData } from '../helpers/storage'
|
import { clearBookingData, loadBookingData } from '../helpers/storage'
|
||||||
|
|
||||||
import { createBooking } from '../helpers/booking'
|
import { createBooking } from '../helpers/booking'
|
||||||
import { Booker } from '../db/booker'
|
|
||||||
import { Booking } from '../db/booking'
|
import { Booking } from '../db/booking'
|
||||||
|
|
||||||
export type BookFormData = Omit<Booking, 'uuid'> &
|
export type BookFormData = Omit<Booking, 'uuid'> & {
|
||||||
Booker & {
|
storeData?: boolean
|
||||||
storeData?: boolean
|
}
|
||||||
}
|
|
||||||
|
|
||||||
type BookingProviderState = {
|
type BookingProviderState = {
|
||||||
postData?: boolean
|
postData?: boolean
|
||||||
|
|||||||
31
db/booker.ts
31
db/booker.ts
@@ -1,31 +0,0 @@
|
|||||||
import * as mongoose from 'mongoose'
|
|
||||||
|
|
||||||
export type Booker = {
|
|
||||||
name: string
|
|
||||||
email: string
|
|
||||||
phone: string
|
|
||||||
street: string
|
|
||||||
zip: string
|
|
||||||
city: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export type BookerDocument = Booker &
|
|
||||||
mongoose.SchemaTimestampsConfig &
|
|
||||||
mongoose.Document
|
|
||||||
|
|
||||||
export type BookerModel = mongoose.Model<BookerDocument>
|
|
||||||
|
|
||||||
const BookerSchema = new mongoose.Schema<BookerDocument>(
|
|
||||||
{
|
|
||||||
name: { type: String, required: true },
|
|
||||||
email: { type: String, required: true, unique: true, minlength: 5 },
|
|
||||||
phone: { type: String, required: false },
|
|
||||||
street: { type: String, required: true },
|
|
||||||
zip: { type: String, required: true },
|
|
||||||
city: { type: String, required: true },
|
|
||||||
},
|
|
||||||
{ timestamps: true, collation: { locale: 'de', strength: 1 } }
|
|
||||||
)
|
|
||||||
|
|
||||||
export default <BookerModel>mongoose.models.Booker ||
|
|
||||||
mongoose.model<BookerDocument, BookerModel>('Booker', BookerSchema)
|
|
||||||
@@ -3,13 +3,17 @@ import { v4 as uuidv4 } from 'uuid'
|
|||||||
import { dateFormatBackend, getDays, nowInTz } from '../helpers/date'
|
import { dateFormatBackend, getDays, nowInTz } from '../helpers/date'
|
||||||
|
|
||||||
import { Bill } from './bill'
|
import { Bill } from './bill'
|
||||||
import { Booker } from './booker'
|
|
||||||
import { BOOKING_STATUS, VALIDATION_ERRORS } from './enums'
|
import { BOOKING_STATUS, VALIDATION_ERRORS } from './enums'
|
||||||
import { getBookedDays } from './index'
|
import { getBookedDays } from './index'
|
||||||
|
|
||||||
export type Booking = {
|
export type Booking = {
|
||||||
uuid: string
|
uuid: string
|
||||||
booker?: Booker
|
name: string
|
||||||
|
email: string
|
||||||
|
phone: string
|
||||||
|
street: string
|
||||||
|
zip: string
|
||||||
|
city: string
|
||||||
bill?: Bill
|
bill?: Bill
|
||||||
startDate: string
|
startDate: string
|
||||||
endDate: string
|
endDate: string
|
||||||
@@ -36,11 +40,12 @@ const BookingSchema = new mongoose.Schema<BookingDocument>(
|
|||||||
default: uuidv4,
|
default: uuidv4,
|
||||||
index: true,
|
index: true,
|
||||||
},
|
},
|
||||||
booker: {
|
name: { type: String, required: true },
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
email: { type: String, required: true, minlength: 5 },
|
||||||
ref: 'Booker',
|
phone: { type: String, required: false },
|
||||||
required: true,
|
street: { type: String, required: true },
|
||||||
},
|
zip: { type: String, required: true },
|
||||||
|
city: { type: String, required: true },
|
||||||
bill: {
|
bill: {
|
||||||
type: mongoose.Schema.Types.ObjectId,
|
type: mongoose.Schema.Types.ObjectId,
|
||||||
ref: 'Bill',
|
ref: 'Bill',
|
||||||
|
|||||||
29
db/index.ts
29
db/index.ts
@@ -1,5 +1,4 @@
|
|||||||
import * as mongoose from 'mongoose'
|
import * as mongoose from 'mongoose'
|
||||||
import BookerModel, { Booker } from './booker'
|
|
||||||
import BookingModel, { Booking, BookingDocument } from './booking'
|
import BookingModel, { Booking, BookingDocument } from './booking'
|
||||||
import BillModel, { Bill } from './bill'
|
import BillModel, { Bill } from './bill'
|
||||||
import { BOOKING_STATUS } from './enums'
|
import { BOOKING_STATUS } from './enums'
|
||||||
@@ -20,7 +19,9 @@ function connect(): Promise<typeof mongoose> {
|
|||||||
return connectedPromise
|
return connectedPromise
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getBookedDays(uuidsToIngore?: string[]): Promise<string[]> {
|
export async function getBookedDays(
|
||||||
|
uuidsToIngore?: string[]
|
||||||
|
): Promise<string[]> {
|
||||||
await connect()
|
await connect()
|
||||||
return BookingModel.findBookedDays(uuidsToIngore)
|
return BookingModel.findBookedDays(uuidsToIngore)
|
||||||
}
|
}
|
||||||
@@ -33,14 +34,14 @@ export async function getBookingByUUID(uuid: string): Promise<BookingDocument> {
|
|||||||
export async function getBookings({
|
export async function getBookings({
|
||||||
status = [BOOKING_STATUS.CONFIRMED, BOOKING_STATUS.REQUESTED],
|
status = [BOOKING_STATUS.CONFIRMED, BOOKING_STATUS.REQUESTED],
|
||||||
startDateGreaterThan = '2000-01-01T00:00:00Z',
|
startDateGreaterThan = '2000-01-01T00:00:00Z',
|
||||||
}: { status?: BOOKING_STATUS[]; startDateGreaterThan?: string } = {}): Promise<BookingDocument[]> {
|
}: { status?: BOOKING_STATUS[]; startDateGreaterThan?: string } = {}): Promise<
|
||||||
|
BookingDocument[]
|
||||||
|
> {
|
||||||
await connect()
|
await connect()
|
||||||
return await BookingModel.find({
|
return await BookingModel.find({
|
||||||
status: { $in: status },
|
status: { $in: status },
|
||||||
startDate: { $gte: startDateGreaterThan },
|
startDate: { $gte: startDateGreaterThan },
|
||||||
})
|
}).exec()
|
||||||
.populate('booker')
|
|
||||||
.exec()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function createBooking({
|
export async function createBooking({
|
||||||
@@ -55,7 +56,7 @@ export async function createBooking({
|
|||||||
street,
|
street,
|
||||||
zip,
|
zip,
|
||||||
city,
|
city,
|
||||||
}: Booking & Booker): Promise<Booking> {
|
}: Booking): Promise<Booking> {
|
||||||
await connect()
|
await connect()
|
||||||
const booking = new BookingModel({
|
const booking = new BookingModel({
|
||||||
startDate,
|
startDate,
|
||||||
@@ -63,17 +64,15 @@ export async function createBooking({
|
|||||||
purpose,
|
purpose,
|
||||||
org,
|
org,
|
||||||
destination,
|
destination,
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
phone,
|
||||||
|
street,
|
||||||
|
zip,
|
||||||
|
city,
|
||||||
})
|
})
|
||||||
|
|
||||||
let booker = await BookerModel.findOne({ email }).exec()
|
|
||||||
if (!booker) {
|
|
||||||
booker = new BookerModel({ name, email, phone, street, zip, city })
|
|
||||||
await booker.save()
|
|
||||||
}
|
|
||||||
|
|
||||||
booking.booker = booker._id
|
|
||||||
await booking.save()
|
await booking.save()
|
||||||
await booking.populate('booker').execPopulate()
|
|
||||||
return booking.toJSON()
|
return booking.toJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export function generateCalendarEntry(booking: Booking): string {
|
|||||||
duration: { days: booking.days.length },
|
duration: { days: booking.days.length },
|
||||||
location: 'Mömpelgardgasse 25, 72348 Rosenfeld, Deutschland',
|
location: 'Mömpelgardgasse 25, 72348 Rosenfeld, Deutschland',
|
||||||
geo: { lat: 48.287044, lon: 8.726361 },
|
geo: { lat: 48.287044, lon: 8.726361 },
|
||||||
description: `Gebucht auf ${booking.booker.name}
|
description: `Gebucht auf ${booking.name}
|
||||||
|
|
||||||
Buchungs-Link: ${getBaseURL()}/booking/${booking.uuid}
|
Buchungs-Link: ${getBaseURL()}/booking/${booking.uuid}
|
||||||
`,
|
`,
|
||||||
@@ -39,18 +39,27 @@ Buchungs-Link: ${getBaseURL()}/booking/${booking.uuid}
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function generateBookedCalendar(bookings: Booking[]): string {
|
export function generateBookedCalendar(bookings: Booking[]): string {
|
||||||
const events = bookings.map((booking): { productId: string; calName: string; start: [number, number, number]; startOutputType: 'local' | 'utc'; duration: { days: number }; title: string; description: string; status: EventStatus } => ({
|
const events = bookings.map((booking): {
|
||||||
|
productId: string
|
||||||
|
calName: string
|
||||||
|
start: [number, number, number]
|
||||||
|
startOutputType: 'local' | 'utc'
|
||||||
|
duration: { days: number }
|
||||||
|
title: string
|
||||||
|
description: string
|
||||||
|
status: EventStatus
|
||||||
|
} => ({
|
||||||
productId: 'app.vercel.pfadi-bussle/ics',
|
productId: 'app.vercel.pfadi-bussle/ics',
|
||||||
calName: 'Pfadi-Bussle Buchungen',
|
calName: 'Pfadi-Bussle Buchungen',
|
||||||
start: convertDay(booking.days[0]),
|
start: convertDay(booking.days[0]),
|
||||||
startOutputType: 'local',
|
startOutputType: 'local',
|
||||||
duration: { days: booking.days.length },
|
duration: { days: booking.days.length },
|
||||||
title: `Buchung ${booking.booker.name}`,
|
title: `Buchung ${booking.name}`,
|
||||||
description: `Name: ${booking.booker.name}
|
description: `Name: ${booking.name}
|
||||||
Zeitraum: ${daysFormatFrontend(booking.days)}
|
Zeitraum: ${daysFormatFrontend(booking.days)}
|
||||||
|
|
||||||
Email: ${booking.booker.email}
|
Email: ${booking.email}
|
||||||
Telefon: ${booking.booker.phone}
|
Telefon: ${booking.phone}
|
||||||
|
|
||||||
Link: ${getBaseURL()}/admin/booking/${booking.uuid}
|
Link: ${getBaseURL()}/admin/booking/${booking.uuid}
|
||||||
`,
|
`,
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ Tel. 0151/212 253 62
|
|||||||
`
|
`
|
||||||
|
|
||||||
function getReceivedBookingBookerText(booking: Booking): string {
|
function getReceivedBookingBookerText(booking: Booking): string {
|
||||||
return `Hallo liebe/r ${booking.booker.name},
|
return `Hallo liebe/r ${booking.name},
|
||||||
|
|
||||||
Vielen Dank für Deine Buchungsanfrage zum ${daysFormatFrontend(booking.days)}!
|
Vielen Dank für Deine Buchungsanfrage zum ${daysFormatFrontend(booking.days)}!
|
||||||
|
|
||||||
@@ -49,7 +49,7 @@ ${footer}
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getBookingConfirmedText(booking: Booking): string {
|
function getBookingConfirmedText(booking: Booking): string {
|
||||||
return `Hallo liebe/r ${booking.booker.name},
|
return `Hallo liebe/r ${booking.name},
|
||||||
|
|
||||||
deine Buchunganfrage zum ${daysFormatFrontend(
|
deine Buchunganfrage zum ${daysFormatFrontend(
|
||||||
booking.days
|
booking.days
|
||||||
@@ -65,7 +65,7 @@ ${footer}
|
|||||||
`
|
`
|
||||||
}
|
}
|
||||||
function getBookingRejectedText(booking: Booking): string {
|
function getBookingRejectedText(booking: Booking): string {
|
||||||
return `Hallo liebe/r ${booking.booker.name},
|
return `Hallo liebe/r ${booking.name},
|
||||||
|
|
||||||
es tut uns leid aber deine Buchungsanfrage zum ${daysFormatFrontend(
|
es tut uns leid aber deine Buchungsanfrage zum ${daysFormatFrontend(
|
||||||
booking.days
|
booking.days
|
||||||
@@ -86,7 +86,9 @@ es ging folgende Buchung ein: ${getBaseURL()}/admin/booking/${booking.uuid}
|
|||||||
MfG`
|
MfG`
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendReceivedBookingAdminMail(booking: Booking): Promise<void> {
|
export async function sendReceivedBookingAdminMail(
|
||||||
|
booking: Booking
|
||||||
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await sendMail({
|
await sendMail({
|
||||||
to: [{ email: ADMIN_EMAIL }],
|
to: [{ email: ADMIN_EMAIL }],
|
||||||
@@ -102,10 +104,12 @@ export async function sendReceivedBookingAdminMail(booking: Booking): Promise<vo
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function sendReceivedBookingBookerMail(booking: Booking): Promise<void> {
|
export async function sendReceivedBookingBookerMail(
|
||||||
|
booking: Booking
|
||||||
|
): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await sendMail({
|
await sendMail({
|
||||||
to: [{ email: booking.booker.email, name: booking.booker.name }],
|
to: [{ email: booking.email, name: booking.name }],
|
||||||
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
||||||
subject: `Deine Pfadi-Bussle Buchung ist eingegangen!`,
|
subject: `Deine Pfadi-Bussle Buchung ist eingegangen!`,
|
||||||
textPlainContent: getReceivedBookingBookerText(booking),
|
textPlainContent: getReceivedBookingBookerText(booking),
|
||||||
@@ -121,7 +125,7 @@ export async function sendReceivedBookingBookerMail(booking: Booking): Promise<v
|
|||||||
export async function sendBookingConfirmed(booking: Booking): Promise<void> {
|
export async function sendBookingConfirmed(booking: Booking): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await sendMail({
|
await sendMail({
|
||||||
to: [{ email: booking.booker.email, name: booking.booker.name }],
|
to: [{ email: booking.email, name: booking.name }],
|
||||||
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
||||||
subject: `Deine Pfadi-Bussle Buchung wurde bestätigt!`,
|
subject: `Deine Pfadi-Bussle Buchung wurde bestätigt!`,
|
||||||
textPlainContent: getBookingConfirmedText(booking),
|
textPlainContent: getBookingConfirmedText(booking),
|
||||||
@@ -146,7 +150,7 @@ export async function sendBookingConfirmed(booking: Booking): Promise<void> {
|
|||||||
export async function sendBookingRejected(booking: Booking): Promise<void> {
|
export async function sendBookingRejected(booking: Booking): Promise<void> {
|
||||||
try {
|
try {
|
||||||
await sendMail({
|
await sendMail({
|
||||||
to: [{ email: booking.booker.email, name: booking.booker.name }],
|
to: [{ email: booking.email, name: booking.name }],
|
||||||
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
||||||
subject: `Deine Pfadi-Bussle Buchung wurde abgelehnt!`,
|
subject: `Deine Pfadi-Bussle Buchung wurde abgelehnt!`,
|
||||||
textPlainContent: getBookingRejectedText(booking),
|
textPlainContent: getBookingRejectedText(booking),
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ function getStorage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function storeBookingData(booking: Booking) {
|
export function storeBookingData(booking: Booking) {
|
||||||
const { name, email, street, zip, city } = booking.booker
|
const { name, email, street, zip, city, org } = booking
|
||||||
const { org } = booking
|
|
||||||
|
|
||||||
getStorage().setItem(
|
getStorage().setItem(
|
||||||
BOOKING_DATA_KEY,
|
BOOKING_DATA_KEY,
|
||||||
|
|||||||
@@ -48,7 +48,6 @@ export const getServerSideBooking = async (
|
|||||||
return { props: { booking: null } }
|
return { props: { booking: null } }
|
||||||
}
|
}
|
||||||
|
|
||||||
await booking.populate('booker').execPopulate()
|
|
||||||
await booking.populate('bill').execPopulate()
|
await booking.populate('bill').execPopulate()
|
||||||
|
|
||||||
// TODO: hack, not sure why _id is not serilizable
|
// TODO: hack, not sure why _id is not serilizable
|
||||||
|
|||||||
@@ -158,7 +158,7 @@ export default function BookingBillPage({
|
|||||||
{daysFormatFrontend(booking.days)}
|
{daysFormatFrontend(booking.days)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Bucher:</strong> {booking.booker.name}
|
<strong>Bucher:</strong> {booking.name}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Buchungsstatus:</strong>{' '}
|
<strong>Buchungsstatus:</strong>{' '}
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ export default function ShowBookingAdmin({
|
|||||||
<strong>Buchungszeitraum:</strong> {daysFormatFrontend(booking.days)}
|
<strong>Buchungszeitraum:</strong> {daysFormatFrontend(booking.days)}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Bucher:</strong> {booking.booker.name}
|
<strong>Bucher:</strong> {booking.name}
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<strong>Buchungsstatus:</strong> {getBookingStatus(booking.status)}
|
<strong>Buchungsstatus:</strong> {getBookingStatus(booking.status)}
|
||||||
|
|||||||
@@ -59,13 +59,13 @@ export default function AdminRecentBookings({ bookings }) {
|
|||||||
<div className="bg-white px-2 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
<div className="bg-white px-2 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
<dt className="text-sm font-medium text-gray-500">Bucher</dt>
|
<dt className="text-sm font-medium text-gray-500">Bucher</dt>
|
||||||
<dd className="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
<dd className="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
{booking.booker.name}
|
{booking.name}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-gray-100 px-2 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
<div className="bg-gray-100 px-2 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
<dt className="text-sm font-medium text-gray-500">Email</dt>
|
<dt className="text-sm font-medium text-gray-500">Email</dt>
|
||||||
<dd className="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
<dd className="mt-1 text-sm text-gray-900 sm:mt-0 sm:col-span-2">
|
||||||
{booking.booker.email}
|
{booking.email}
|
||||||
</dd>
|
</dd>
|
||||||
</div>
|
</div>
|
||||||
<div className="bg-white px-2 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
<div className="bg-white px-2 py-3 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-6">
|
||||||
|
|||||||
@@ -51,7 +51,6 @@ export default withSession(async function userHandler(
|
|||||||
booking.set(req.body)
|
booking.set(req.body)
|
||||||
try {
|
try {
|
||||||
await booking.save()
|
await booking.save()
|
||||||
await booking.populate('booker').execPopulate()
|
|
||||||
res.status(200).json(booking.toJSON())
|
res.status(200).json(booking.toJSON())
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
res.status(400).end(`Failed to save booking: ${error.message}`)
|
res.status(400).end(`Failed to save booking: ${error.message}`)
|
||||||
|
|||||||
@@ -30,15 +30,11 @@ export default async function userHandler(
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(
|
console.log(`received booking ${booking.uuid} from {booking.email}`)
|
||||||
`received booking ${booking.uuid} from {booking.booker.email}`
|
|
||||||
)
|
|
||||||
await sendReceivedBookingAdminMail(booking)
|
await sendReceivedBookingAdminMail(booking)
|
||||||
console.log(`send booking ${booking.uuid} received to admin`)
|
console.log(`send booking ${booking.uuid} received to admin`)
|
||||||
await sendReceivedBookingBookerMail(booking)
|
await sendReceivedBookingBookerMail(booking)
|
||||||
console.log(
|
console.log(`send booking ${booking.uuid} received to {booking.email}`)
|
||||||
`send booking ${booking.uuid} received to {booking.booker.email}`
|
|
||||||
)
|
|
||||||
break
|
break
|
||||||
default:
|
default:
|
||||||
res.setHeader('Allow', ['POST'])
|
res.setHeader('Allow', ['POST'])
|
||||||
|
|||||||
Reference in New Issue
Block a user