mirror of
https://github.com/tomru/pfadi-bussle.git
synced 2026-03-03 06:27:11 +01:00
switch to prisma
This commit is contained in:
@@ -1,17 +1,18 @@
|
||||
import React from 'react'
|
||||
import Link from 'next/link'
|
||||
|
||||
import { daysFormatFrontend } from '../helpers/date'
|
||||
import { BookingDocument } from '../db/booking'
|
||||
import {Booking} from '@prisma/client';
|
||||
import { dateFormatFrontend } from '../helpers/date'
|
||||
|
||||
|
||||
export default function BookingTable({
|
||||
booking,
|
||||
}: {
|
||||
booking: BookingDocument
|
||||
booking: Booking
|
||||
}) {
|
||||
const data = [
|
||||
{ name: 'Status', value: booking.status },
|
||||
{ name: 'Buchungszeitraum', value: daysFormatFrontend(booking.days) },
|
||||
{ name: 'Buchungszeitraum', value: `${dateFormatFrontend(new Date(booking.startDate))}-${dateFormatFrontend(new Date(booking.endDate))}` },
|
||||
{ name: 'Organisation', value: booking.org },
|
||||
{
|
||||
name: 'Addresse',
|
||||
@@ -48,7 +49,7 @@ export default function BookingTable({
|
||||
</h3>
|
||||
<p className="mt-1 max-w-2xl text-sm text-gray-500">
|
||||
Last updated{' '}
|
||||
{new Date(booking.updatedAt as string).toLocaleString(
|
||||
{booking.updatedAt.toLocaleString(
|
||||
new Intl.Locale('de')
|
||||
)}
|
||||
</p>
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
import React, { useEffect, useReducer } from 'react'
|
||||
import { Prisma } from '@prisma/client'
|
||||
import { useRouter } from 'next/router'
|
||||
import { clearBookingData, loadBookingData } from '../helpers/storage'
|
||||
import { log } from '../helpers/log'
|
||||
|
||||
import { createBooking } from '../helpers/booking'
|
||||
import { Booking } from '../db/booking'
|
||||
|
||||
export type BookFormData = Omit<Booking, 'uuid' | 'calendarEventId'>
|
||||
|
||||
type BookingProviderState = {
|
||||
postData?: boolean
|
||||
postDataError?: string
|
||||
postDataSuccess?: boolean
|
||||
formData: BookFormData
|
||||
formData: Prisma.BookingCreateInput,
|
||||
formDataChanged: string[]
|
||||
booking?: Booking
|
||||
booking?: Prisma.BookingCreateInput,
|
||||
dataStored: boolean
|
||||
dataStoredLoaded: boolean
|
||||
}
|
||||
|
||||
91
db/bill.ts
91
db/bill.ts
@@ -1,91 +0,0 @@
|
||||
import * as mongoose from 'mongoose'
|
||||
import { BILL_STATUS, MILAGE_TARIFS } from './enums'
|
||||
import { getBillTotal } from '../helpers/bill'
|
||||
|
||||
export type AdditionalCost = {
|
||||
name: string
|
||||
value: number
|
||||
}
|
||||
|
||||
export type Bill = {
|
||||
milageStart: number
|
||||
milageEnd: number
|
||||
milage?: number
|
||||
tarif: MILAGE_TARIFS
|
||||
status: BILL_STATUS
|
||||
additionalCosts: AdditionalCost[]
|
||||
}
|
||||
|
||||
export type BillDocument = Bill &
|
||||
mongoose.SchemaTimestampsConfig &
|
||||
mongoose.Document
|
||||
|
||||
export type BillModel = mongoose.Model<BillDocument>
|
||||
|
||||
const BillSchema = new mongoose.Schema<BillDocument>(
|
||||
{
|
||||
milageStart: {
|
||||
type: Number,
|
||||
required: true,
|
||||
validate: {
|
||||
validator: function (v: number): boolean {
|
||||
const bill = this as BillDocument
|
||||
|
||||
return v <= bill.milageEnd
|
||||
},
|
||||
message: (props: { value: Number }) =>
|
||||
`${props.value} is bigger than milageEnd!`,
|
||||
},
|
||||
},
|
||||
milageEnd: {
|
||||
type: Number,
|
||||
required: true,
|
||||
|
||||
validate: {
|
||||
validator: function (v: number): boolean {
|
||||
const bill = this as BillDocument
|
||||
|
||||
return v >= bill.milageStart
|
||||
},
|
||||
message: (props: { value: Number }) =>
|
||||
`${props.value} is smaller than milageStart!`,
|
||||
},
|
||||
},
|
||||
tarif: {
|
||||
type: String,
|
||||
enum: Object.values(MILAGE_TARIFS),
|
||||
default: MILAGE_TARIFS.EXTERN,
|
||||
required: true,
|
||||
},
|
||||
additionalCosts: [
|
||||
{
|
||||
name: { type: String, required: true },
|
||||
value: { type: Number, required: true },
|
||||
},
|
||||
],
|
||||
status: {
|
||||
type: String,
|
||||
enum: Object.values(BILL_STATUS),
|
||||
default: BILL_STATUS.UNINVOICED,
|
||||
},
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
toJSON: { virtuals: true, getters: true },
|
||||
toObject: { virtuals: true, getters: true },
|
||||
}
|
||||
)
|
||||
|
||||
BillSchema.virtual('milage').get(function (): number {
|
||||
const bill = this as BillDocument
|
||||
return bill.milageEnd - bill.milageStart
|
||||
})
|
||||
|
||||
BillSchema.virtual('total').get(function (): number {
|
||||
const bill = this as BillDocument
|
||||
|
||||
return getBillTotal(bill)
|
||||
})
|
||||
|
||||
export default (mongoose.models.Bill ||
|
||||
mongoose.model<BillDocument, BillModel>('Bill', BillSchema)) as BillModel
|
||||
168
db/booking.ts
168
db/booking.ts
@@ -1,168 +0,0 @@
|
||||
import * as mongoose from 'mongoose'
|
||||
import { v4 as uuidv4 } from 'uuid'
|
||||
import {
|
||||
dateFormatBackend,
|
||||
getDays,
|
||||
nowInTz,
|
||||
dateParseBackend,
|
||||
} from '../helpers/date'
|
||||
import { createCalendarEvent, deleteCalendarEvent } from '../lib/googlecalendar'
|
||||
|
||||
import { Bill } from './bill'
|
||||
import { BOOKING_STATUS, VALIDATION_ERRORS } from './enums'
|
||||
|
||||
export type Booking = {
|
||||
uuid: string
|
||||
name: string
|
||||
email: string
|
||||
phone: string
|
||||
street: string
|
||||
zip: string
|
||||
city: string
|
||||
bill?: Bill
|
||||
// format YYYY-MM-DD
|
||||
startDate: string
|
||||
// format YYYY-MM-DD
|
||||
endDate: string
|
||||
status?: BOOKING_STATUS
|
||||
purpose?: string
|
||||
org?: string
|
||||
destination?: string
|
||||
days?: string[]
|
||||
calendarEventId: string
|
||||
}
|
||||
|
||||
export type BookingDocument = Booking &
|
||||
mongoose.Document &
|
||||
mongoose.SchemaTimestampsConfig
|
||||
|
||||
export type BookingModel = mongoose.Model<BookingDocument> & {
|
||||
findBookedDays(uuidsToIngore?: string[]): Promise<string[]>
|
||||
}
|
||||
|
||||
const BookingSchema = new mongoose.Schema<BookingDocument>(
|
||||
{
|
||||
// need a seperate uuid to be able to target a booking anonimously
|
||||
uuid: {
|
||||
type: String,
|
||||
default: uuidv4,
|
||||
index: true,
|
||||
},
|
||||
name: { type: String, required: true },
|
||||
email: { type: String, required: true, minlength: 5 },
|
||||
phone: { type: String, required: false },
|
||||
street: { type: String, required: true },
|
||||
zip: { type: String, required: true },
|
||||
city: { type: String, required: true },
|
||||
bill: {
|
||||
type: mongoose.Schema.Types.ObjectId,
|
||||
ref: 'Bill',
|
||||
required: false,
|
||||
},
|
||||
startDate: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: function (value: string): boolean {
|
||||
return !!dateParseBackend(value)
|
||||
},
|
||||
},
|
||||
endDate: {
|
||||
type: String,
|
||||
required: true,
|
||||
validator: function (value: string): boolean {
|
||||
return !!dateParseBackend(value)
|
||||
},
|
||||
},
|
||||
days: {
|
||||
type: [String],
|
||||
required: true,
|
||||
validate: {
|
||||
validator: async function (days: string[]): Promise<boolean> {
|
||||
const booking = this as Booking
|
||||
const uuid = booking.uuid && [booking.uuid]
|
||||
const bookedDays = await BookingModel.findBookedDays(uuid)
|
||||
|
||||
const doubleBookedDays = days.filter((day: string): boolean =>
|
||||
bookedDays.includes(day)
|
||||
)
|
||||
return doubleBookedDays.length === 0
|
||||
},
|
||||
message: (props: { value: string[] }): string =>
|
||||
`At least one day is of ${props.value.join(',')} is already booked`,
|
||||
type: VALIDATION_ERRORS.AT_LEAST_ONE_DAY_BOOKED,
|
||||
},
|
||||
},
|
||||
status: {
|
||||
type: String,
|
||||
enum: Object.values(BOOKING_STATUS),
|
||||
required: true,
|
||||
default: BOOKING_STATUS.REQUESTED,
|
||||
},
|
||||
purpose: { type: String, required: false },
|
||||
org: { type: String, required: false },
|
||||
destination: { type: String, required: false },
|
||||
calendarEventId: { type: String, required: false },
|
||||
},
|
||||
{
|
||||
timestamps: true,
|
||||
toJSON: { virtuals: true, getters: true },
|
||||
toObject: { virtuals: true, getters: true },
|
||||
}
|
||||
)
|
||||
|
||||
BookingSchema.pre('validate', function (next: () => void): void {
|
||||
const booking = this as BookingDocument
|
||||
booking.days = getDays({
|
||||
startDate: new Date(booking.startDate),
|
||||
endDate: new Date(booking.endDate),
|
||||
})
|
||||
next()
|
||||
})
|
||||
|
||||
BookingSchema.pre('save', async function (next: () => void): Promise<void> {
|
||||
const booking = this as BookingDocument
|
||||
|
||||
if (!booking.calendarEventId) {
|
||||
// create calendar event before saving to database
|
||||
await createCalendarEvent(booking)
|
||||
} 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
|
||||
await deleteCalendarEvent(booking)
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
)
|
||||
|
||||
const BookingModel = (mongoose.models.Booking ||
|
||||
mongoose.model<BookingDocument, BookingModel>(
|
||||
'Booking',
|
||||
BookingSchema
|
||||
)) as BookingModel
|
||||
|
||||
export default BookingModel
|
||||
13
db/enums.ts
13
db/enums.ts
@@ -1,16 +1,3 @@
|
||||
export enum BOOKING_STATUS {
|
||||
REQUESTED = 'requested',
|
||||
CONFIRMED = 'confirmed',
|
||||
REJECTED = 'rejected',
|
||||
CANCELED = 'canceled',
|
||||
}
|
||||
|
||||
export enum BILL_STATUS {
|
||||
UNINVOICED = 'uninvoiced',
|
||||
INVOICED = 'invoiced',
|
||||
PAID = 'paid',
|
||||
}
|
||||
|
||||
export enum MILAGE_TARIFS {
|
||||
INTERN = 'intern',
|
||||
EXTERN = 'extern',
|
||||
|
||||
200
db/index.ts
200
db/index.ts
@@ -1,129 +1,121 @@
|
||||
import * as mongoose from 'mongoose'
|
||||
import BookingModel, { Booking, BookingDocument } from './booking'
|
||||
import BillModel, { Bill } from './bill'
|
||||
import { BookingStatus, Booking, PrismaClient, Prisma } from '@prisma/client'
|
||||
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
import { getBookedDays as calendarGetBookedDays } from '../lib/googlecalendar'
|
||||
import { BOOKING_STATUS } from './enums'
|
||||
import { uniqueFilter } from '../helpers/array'
|
||||
|
||||
export const MONGO_URI = process.env.MONGO_URI
|
||||
|
||||
mongoose.connect(process.env.MONGO_URI, {
|
||||
serverSelectionTimeoutMS: 3000,
|
||||
})
|
||||
import { dateFormatBackend, getDays, nowInTz } from '../helpers/date'
|
||||
|
||||
export async function getBookedDays(
|
||||
uuidsToIngore?: string[]
|
||||
): Promise<string[]> {
|
||||
const [bookedInDatabase, bookedInCalendar] = await Promise.all([
|
||||
BookingModel.findBookedDays(uuidsToIngore),
|
||||
) {
|
||||
const [bookingsInDbRaw, bookingsInCalendar] = await Promise.all([
|
||||
prisma.booking.findMany({
|
||||
where: {
|
||||
uuid: { notIn: uuidsToIngore },
|
||||
startDate: { gte: dateFormatBackend(nowInTz()) },
|
||||
status: { notIn: [BookingStatus.REJECTED, BookingStatus.CANCELED] }
|
||||
},
|
||||
select: {
|
||||
startDate: true,
|
||||
endDate: true,
|
||||
},
|
||||
}),
|
||||
calendarGetBookedDays(),
|
||||
])
|
||||
return [...bookedInDatabase, ...bookedInCalendar].filter(uniqueFilter).sort()
|
||||
|
||||
const bookingsInDb = bookingsInDbRaw.map(booking => getDays(booking)).flat();
|
||||
|
||||
return [...bookingsInDb, ...bookingsInCalendar].filter(uniqueFilter).sort()
|
||||
}
|
||||
|
||||
export async function getBookingByUUID(uuid: string): Promise<BookingDocument> {
|
||||
return BookingModel.findOne({ uuid })
|
||||
export function getBookingByUUID(uuid: string) {
|
||||
// TODO: can we ignore canceled and rejected ones ?
|
||||
return prisma.booking.findUniqueOrThrow({
|
||||
where: {
|
||||
uuid,
|
||||
},
|
||||
include: {
|
||||
bill: true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export async function getBookings({
|
||||
status = [BOOKING_STATUS.CONFIRMED, BOOKING_STATUS.REQUESTED],
|
||||
export function getBookings({
|
||||
status = [
|
||||
BookingStatus.REQUESTED,
|
||||
BookingStatus.CONFIRMED,
|
||||
],
|
||||
startDateGreaterThan = '2000-01-01T00:00:00Z',
|
||||
}: { status?: BOOKING_STATUS[]; startDateGreaterThan?: string } = {}): Promise<
|
||||
BookingDocument[]
|
||||
> {
|
||||
return await BookingModel.find({
|
||||
status: { $in: status },
|
||||
startDate: { $gte: startDateGreaterThan },
|
||||
})
|
||||
.sort({ startDate: -1 })
|
||||
.exec()
|
||||
}
|
||||
: { status?: BookingStatus[]; startDateGreaterThan?: string } = {}) {
|
||||
return prisma.booking.findMany({
|
||||
where: {
|
||||
startDate: { gte: startDateGreaterThan },
|
||||
status: { notIn: status }
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export async function createBooking({
|
||||
startDate,
|
||||
endDate,
|
||||
purpose,
|
||||
org,
|
||||
destination,
|
||||
name,
|
||||
email,
|
||||
phone,
|
||||
street,
|
||||
zip,
|
||||
city,
|
||||
}: Booking): Promise<Booking> {
|
||||
const booking = new BookingModel({
|
||||
startDate,
|
||||
endDate,
|
||||
purpose,
|
||||
org,
|
||||
destination,
|
||||
name,
|
||||
email,
|
||||
phone,
|
||||
street,
|
||||
zip,
|
||||
city,
|
||||
})
|
||||
|
||||
await booking.save()
|
||||
return booking.toJSON<Booking>()
|
||||
export function createBooking(data: Booking) {
|
||||
return prisma.booking.create({
|
||||
data
|
||||
});
|
||||
}
|
||||
|
||||
export async function patchBooking(
|
||||
uuid: string,
|
||||
data: Prisma.BookingCreateInput
|
||||
) {
|
||||
const current = await prisma.booking.update({
|
||||
where: { uuid },
|
||||
data
|
||||
});
|
||||
const previous = { ...current, ...data }
|
||||
return { current, previous }
|
||||
|
||||
}
|
||||
|
||||
export function createBill(
|
||||
bookingUUID: string,
|
||||
bookingData: Booking
|
||||
): Promise<{ current: Booking; previous: Booking }> {
|
||||
const booking = await getBookingByUUID(bookingUUID)
|
||||
const oldBooking = booking.toJSON<Booking>()
|
||||
booking.set(bookingData)
|
||||
await booking.save()
|
||||
|
||||
return { current: booking.toJSON<Booking>(), previous: oldBooking }
|
||||
data: Prisma.BillCreateInput,
|
||||
) {
|
||||
return prisma.bill.create({
|
||||
data: {
|
||||
...data,
|
||||
booking: {
|
||||
connect: { uuid: bookingUUID }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function createBill(
|
||||
export function patchBill(
|
||||
bookingUUID: string,
|
||||
billData: Bill
|
||||
): Promise<Bill> {
|
||||
const booking = await getBookingByUUID(bookingUUID)
|
||||
|
||||
const bill = new BillModel()
|
||||
bill.set(billData)
|
||||
|
||||
await bill.save()
|
||||
|
||||
booking.bill = bill._id
|
||||
await booking.save()
|
||||
|
||||
return bill.toJSON<Bill>()
|
||||
data: Prisma.BillUncheckedUpdateInput,
|
||||
) {
|
||||
const { id, ...rest } = data;
|
||||
return prisma.bill.update({
|
||||
where: {
|
||||
},
|
||||
data: {
|
||||
...rest,
|
||||
booking: {
|
||||
connect: { uuid: bookingUUID }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export async function patchBill(
|
||||
bookingUUID: string,
|
||||
billData: Bill
|
||||
): Promise<Bill> {
|
||||
const booking = await getBookingByUUID(bookingUUID)
|
||||
const bill =
|
||||
(booking.bill && (await BillModel.findById(booking.bill))) ||
|
||||
(await BillModel.create({}))
|
||||
export async function getMilageMax() {
|
||||
const { milageEnd } = await prisma.bill.findFirst({
|
||||
select: { milageEnd: true },
|
||||
orderBy: [
|
||||
{
|
||||
milageEnd: 'desc',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
bill.set(billData)
|
||||
await bill.save()
|
||||
|
||||
if (booking.bill !== bill._id) {
|
||||
booking.bill = bill._id
|
||||
await booking.save()
|
||||
}
|
||||
|
||||
return bill.toJSON<Bill>()
|
||||
}
|
||||
|
||||
export async function getMilageMax(): Promise<number> {
|
||||
const billMaxMilageEnd = await BillModel.findOne({})
|
||||
.sort('-milageEnd')
|
||||
.select('milageEnd')
|
||||
.exec()
|
||||
|
||||
return billMaxMilageEnd?.milageEnd || 0
|
||||
return milageEnd || 0;
|
||||
}
|
||||
|
||||
@@ -17,6 +17,6 @@ export function getNextBigger<T>(array: T[], pivot: T): T {
|
||||
return array.sort().find((day) => day > pivot)
|
||||
}
|
||||
|
||||
export function uniqueFilter(value, index, self) {
|
||||
export function uniqueFilter<T>(value: T, index: number, self: T[]) {
|
||||
return self.indexOf(value) === index
|
||||
}
|
||||
|
||||
@@ -1,79 +1,27 @@
|
||||
import { MILAGE_TARIFS } from '../db/enums'
|
||||
import { AdditionalCost, Bill } from '../db/bill'
|
||||
import { Bill, Prisma } from '@prisma/client'
|
||||
import fetch from './fetch'
|
||||
|
||||
function roundToCent(amount: number): number {
|
||||
return Math.round(amount * 100) / 100
|
||||
}
|
||||
|
||||
export function getMilageCosts({
|
||||
tarif,
|
||||
km,
|
||||
}: {
|
||||
tarif: MILAGE_TARIFS
|
||||
km: number
|
||||
}): number {
|
||||
if (tarif === MILAGE_TARIFS.NOCHARGE) {
|
||||
return 0
|
||||
}
|
||||
|
||||
if (km <= 0) {
|
||||
return 0
|
||||
}
|
||||
|
||||
let rate: number
|
||||
|
||||
if (tarif === MILAGE_TARIFS.EXTERN) {
|
||||
if (km <= 200) {
|
||||
rate = 0.42
|
||||
} else if (km <= 1000) {
|
||||
rate = 0.25
|
||||
} else if (km <= 2000) {
|
||||
rate = 0.2
|
||||
} else {
|
||||
rate = 0.18
|
||||
}
|
||||
}
|
||||
|
||||
if (tarif === MILAGE_TARIFS.INTERN) {
|
||||
if (km <= 200) {
|
||||
rate = 0.37
|
||||
} else if (km <= 1000) {
|
||||
rate = 0.22
|
||||
} else if (km <= 2000) {
|
||||
rate = 0.15
|
||||
} else {
|
||||
rate = 0.13
|
||||
}
|
||||
}
|
||||
|
||||
if (rate === undefined) {
|
||||
throw Error('Unable to determine rate')
|
||||
}
|
||||
|
||||
return roundToCent(km * rate)
|
||||
}
|
||||
|
||||
export function getBillTotal({
|
||||
tarif,
|
||||
milage,
|
||||
additionalCosts,
|
||||
}: {
|
||||
tarif: MILAGE_TARIFS
|
||||
tarif: Prisma.Decimal
|
||||
milage?: number
|
||||
additionalCosts: AdditionalCost[]
|
||||
}): number {
|
||||
const milageCosts = getMilageCosts({ tarif, km: milage })
|
||||
additionalCosts: Prisma.AdditionalCostsCreateInput[]
|
||||
}): Prisma.Decimal {
|
||||
const milageCosts = tarif.mul(milage)
|
||||
const additionalCostsSum = additionalCosts
|
||||
.map(({ value }) => value)
|
||||
.reduce((acc: number, value: number) => acc + value, 0)
|
||||
.reduce((acc, {value} ) => (value as Prisma.Decimal).plus(acc), new Prisma.Decimal(0))
|
||||
|
||||
return roundToCent(milageCosts + additionalCostsSum)
|
||||
|
||||
return additionalCostsSum.add(milageCosts).toDecimalPlaces(2);
|
||||
}
|
||||
|
||||
export async function createBill(
|
||||
bookingUuid: string,
|
||||
bill: Bill
|
||||
bill: Prisma.BillCreateInput
|
||||
): Promise<Bill> {
|
||||
return fetch(`/api/bookings/${bookingUuid}/bill`, {
|
||||
method: 'POST',
|
||||
@@ -83,7 +31,7 @@ export async function createBill(
|
||||
|
||||
export async function patchBill(
|
||||
bookingUuid: string,
|
||||
bill: Bill
|
||||
bill: Prisma.BillUpdateInput
|
||||
): Promise<Bill> {
|
||||
return fetch(`/api/bookings/${bookingUuid}/bill`, {
|
||||
method: 'POST',
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
import { BookFormData } from '../context/book'
|
||||
import { BOOKING_STATUS } from '../db/enums'
|
||||
import { Prisma, BookingStatus } from '@prisma/client'
|
||||
import fetch from './fetch'
|
||||
|
||||
export function getBookingStatus(status: BOOKING_STATUS) {
|
||||
export function getBookingStatus(status: BookingStatus) {
|
||||
switch (status) {
|
||||
case BOOKING_STATUS.REQUESTED:
|
||||
case BookingStatus.REQUESTED:
|
||||
return 'Angefragt'
|
||||
case BOOKING_STATUS.CONFIRMED:
|
||||
case BookingStatus.CONFIRMED:
|
||||
return 'Bestätigt'
|
||||
case BOOKING_STATUS.REJECTED:
|
||||
case BookingStatus.REJECTED:
|
||||
return 'Abgewiesen'
|
||||
case BOOKING_STATUS.CANCELED:
|
||||
case BookingStatus.CANCELED:
|
||||
return 'Storniert'
|
||||
default:
|
||||
return 'Unbekannt - bitte kontaktieren Sie uns!'
|
||||
}
|
||||
}
|
||||
|
||||
export async function createBooking(formData: BookFormData) {
|
||||
export async function createBooking(formData: Prisma.BookingCreateInput) {
|
||||
return fetch('/api/bookings', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
@@ -27,13 +26,13 @@ export async function createBooking(formData: BookFormData) {
|
||||
export async function cancelBooking(uuid: string) {
|
||||
return fetch(`/api/bookings/${uuid}`, {
|
||||
method: 'PATCH',
|
||||
body: { status: BOOKING_STATUS.CANCELED },
|
||||
body: { status: BookingStatus.CANCELED },
|
||||
})
|
||||
}
|
||||
|
||||
export async function patchBooking(
|
||||
uuid: string,
|
||||
bookingData: Partial<BookFormData>
|
||||
bookingData: Prisma.BookingUpdateInput
|
||||
) {
|
||||
return fetch(`/api/bookings/${uuid}`, {
|
||||
method: 'PATCH',
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { parse, format, addDays, subDays } from 'date-fns'
|
||||
import { parse, format, addDays, subDays, differenceInDays } from 'date-fns'
|
||||
import { utcToZonedTime } from 'date-fns-tz'
|
||||
|
||||
const FRONTEND_FORMAT = 'dd.MM.yyyy'
|
||||
@@ -23,18 +23,18 @@ export function getDays({
|
||||
endDate,
|
||||
endDateExclusive = false,
|
||||
}: {
|
||||
startDate: Date
|
||||
endDate: Date
|
||||
startDate: string,
|
||||
endDate: string,
|
||||
endDateExclusive?: boolean
|
||||
}): string[] {
|
||||
let currentDay = new Date(startDate.getTime())
|
||||
let currentDay = new Date(startDate);
|
||||
const days = [dateFormatBackend(currentDay)]
|
||||
|
||||
if (!endDate) {
|
||||
return days
|
||||
}
|
||||
|
||||
const inclusiveEndDate = endDateExclusive ? subDays(endDate, 1) : endDate
|
||||
const inclusiveEndDate = endDateExclusive ? subDays(new Date(endDate), 1) : new Date(endDate)
|
||||
|
||||
while (currentDay < inclusiveEndDate) {
|
||||
currentDay = addDays(currentDay, 1)
|
||||
@@ -84,3 +84,8 @@ export function nowInTz(timezone = 'Europe/Berlin'): Date {
|
||||
export function getNextDay(date: Date) {
|
||||
return addDays(date, 1)
|
||||
}
|
||||
|
||||
export function getDayCount({ startDate, endDate }: { startDate: string, endDate: string }) {
|
||||
// TODO: check if this actually works as expected
|
||||
return differenceInDays(new Date(startDate), new Date(endDate)) + 1 // add one as it only counts full days;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import { createEvents, createEvent, EventStatus } from 'ics'
|
||||
import { Booking } from '../db/booking'
|
||||
import { BOOKING_STATUS } from '../db/enums'
|
||||
import { Booking, BookingStatus} from '@prisma/client';
|
||||
import { getBaseURL } from './url'
|
||||
import { daysFormatFrontend } from './date'
|
||||
import { dateFormatFrontend, getDayCount } from './date'
|
||||
|
||||
function convertDay(value: string): [number, number, number] {
|
||||
const parts = value.split('-')
|
||||
@@ -16,9 +15,9 @@ export function generateCalendarEntry(booking: Booking): string {
|
||||
const { error, value } = createEvent({
|
||||
productId: 'app.vercel.pfadi-bussle/ics',
|
||||
title: `Pfadi-Bussle Buchung`,
|
||||
start: convertDay(booking.days[0]),
|
||||
start: convertDay(booking.startDate),
|
||||
startOutputType: 'local',
|
||||
duration: { days: booking.days.length },
|
||||
duration: { days: getDayCount(booking) },
|
||||
location: 'Mömpelgardgasse 25, 72348 Rosenfeld, Deutschland',
|
||||
geo: { lat: 48.287044, lon: 8.726361 },
|
||||
description: `Gebucht auf ${booking.name}
|
||||
@@ -26,7 +25,7 @@ export function generateCalendarEntry(booking: Booking): string {
|
||||
Buchungs-Link: ${getBaseURL()}/bookings/${booking.uuid}
|
||||
`,
|
||||
status:
|
||||
booking.status === BOOKING_STATUS.CONFIRMED
|
||||
booking.status === BookingStatus.CONFIRMED
|
||||
? ('CONFIRMED' as EventStatus)
|
||||
: ('TENTATIVE' as EventStatus),
|
||||
})
|
||||
@@ -54,12 +53,12 @@ export function generateBookedCalendar(bookings: Booking[]): string {
|
||||
} => ({
|
||||
productId: 'app.vercel.pfadi-bussle/ics',
|
||||
calName: 'Pfadi-Bussle Buchungen',
|
||||
start: convertDay(booking.days[0]),
|
||||
start: convertDay(booking.startDate),
|
||||
startOutputType: 'local',
|
||||
duration: { days: booking.days.length },
|
||||
duration: { days: getDayCount(booking) },
|
||||
title: `Buchung ${booking.name}`,
|
||||
description: `Name: ${booking.name}
|
||||
Zeitraum: ${daysFormatFrontend(booking.days)}
|
||||
Zeitraum: ${dateFormatFrontend(new Date(booking.startDate))}-${dateFormatFrontend(new Date(booking.endDate))}
|
||||
|
||||
Email: ${booking.email}
|
||||
Telefon: ${booking.phone}
|
||||
@@ -67,7 +66,7 @@ Telefon: ${booking.phone}
|
||||
Link: ${getBaseURL()}/admin/bookings/${booking.uuid}
|
||||
`,
|
||||
status:
|
||||
booking.status === BOOKING_STATUS.CONFIRMED
|
||||
booking.status === BookingStatus.CONFIRMED
|
||||
? ('CONFIRMED' as EventStatus)
|
||||
: ('TENTATIVE' as EventStatus),
|
||||
})
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Booking } from '../db/booking'
|
||||
import { Booking, Prisma } from '@prisma/client';
|
||||
import { getBaseURL } from '../helpers/url'
|
||||
import { log } from '../helpers/log'
|
||||
import { daysFormatFrontend } from './date'
|
||||
import { dateFormatFrontend } from './date'
|
||||
import { generateCalendarEntry } from './ical'
|
||||
import sgMail from '@sendgrid/mail'
|
||||
|
||||
@@ -34,10 +34,11 @@ Tel. 0151/212 253 62
|
||||
${getBaseURL()}
|
||||
`
|
||||
|
||||
function getReceivedBookingBookerText(booking: Booking): string {
|
||||
function getReceivedBookingBookerText(booking: Prisma.BookingCreateInput): string {
|
||||
return `Hallo liebe/r ${booking.name},
|
||||
|
||||
Vielen Dank für Deine Buchungsanfrage zum ${daysFormatFrontend(booking.days)}!
|
||||
Vielen Dank für Deine Buchungsanfrage vom
|
||||
${dateFormatFrontend(new Date(booking.startDate))} bis ${dateFormatFrontend(new Date(booking.endDate))}
|
||||
|
||||
Nach Prüfung bestätigen wir die Buchung bald per E-Mail!
|
||||
|
||||
@@ -54,9 +55,9 @@ ${footer}
|
||||
function getBookingConfirmedText(booking: Booking): string {
|
||||
return `Hallo liebe/r ${booking.name},
|
||||
|
||||
deine Buchunganfrage zum ${daysFormatFrontend(
|
||||
booking.days
|
||||
)} bestätigen wir gerne!
|
||||
deine Buchunganfrage vom
|
||||
${dateFormatFrontend(new Date(booking.startDate))} bis ${dateFormatFrontend(new Date(booking.endDate))}
|
||||
bestätigen wir gerne!
|
||||
|
||||
Bitte melde dich spätestens 7 Tage vor dem Buchungstermin per E-Mail oder Telefon
|
||||
um eine Schlüsselübergabe zu vereinbaren.
|
||||
@@ -71,9 +72,9 @@ ${footer}
|
||||
function getBookingRejectedText(booking: Booking): string {
|
||||
return `Hallo liebe/r ${booking.name},
|
||||
|
||||
es tut uns leid, aber deine Buchungsanfrage zum ${daysFormatFrontend(
|
||||
booking.days
|
||||
)} konnten wir leider nicht bestätigen.
|
||||
es tut uns leid, aber deine Buchungsanfrage vom
|
||||
${dateFormatFrontend(new Date(booking.startDate))} bis ${dateFormatFrontend(new Date(booking.endDate))}
|
||||
konnten wir leider nicht bestätigen.
|
||||
|
||||
Willst du das Bussle an einem anderen Termin buchen? Dann stelle bitte nochmal
|
||||
eine Buchungsanfrage auf ${getBaseURL()}.
|
||||
@@ -85,7 +86,9 @@ ${footer}
|
||||
function getBookingCanceledText(booking: Booking): string {
|
||||
return `Hallo liebe/r ${booking.name},
|
||||
|
||||
deine Buchungsanfrage zum ${daysFormatFrontend(booking.days)} wurde storniert.
|
||||
deine Buchungsanfrage vom
|
||||
${dateFormatFrontend(new Date(booking.startDate))} bis ${dateFormatFrontend(new Date(booking.endDate))}
|
||||
wurde storniert.
|
||||
|
||||
Willst du das Bussle an einem anderen Termin buchen? Dann stelle bitte nochmal
|
||||
eine Buchungsanfrage auf ${getBaseURL()}.
|
||||
@@ -109,7 +112,7 @@ export async function sendReceivedBookingAdminMail(
|
||||
await sendMail({
|
||||
to: [{ email: ADMIN_EMAIL }],
|
||||
from: { email: FROM_EMAIL, name: 'Pfadi-Bussle Wart' },
|
||||
subject: `Buchung für ${booking.days} eingegangen!`,
|
||||
subject: `Buchung für ${booking.startDate}-${booking.endDate} eingegangen!`,
|
||||
textPlainContent: getReceivedBookingAdminText(booking),
|
||||
})
|
||||
} catch (error) {
|
||||
@@ -118,7 +121,7 @@ export async function sendReceivedBookingAdminMail(
|
||||
}
|
||||
|
||||
export async function sendReceivedBookingBookerMail(
|
||||
booking: Booking
|
||||
booking: Prisma.BookingCreateInput
|
||||
): Promise<void> {
|
||||
try {
|
||||
await sendMail({
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { Booking } from '../db/booking'
|
||||
import { Booking } from '@prisma/client'
|
||||
import { log } from '../helpers/log'
|
||||
|
||||
const BOOKING_DATA_KEY = 'pfadiBussleBookingData'
|
||||
|
||||
@@ -1,39 +1,36 @@
|
||||
import { Booking } from '@prisma/client';
|
||||
import { startOfYear } from 'date-fns'
|
||||
import { nowInTz } from '../helpers/date'
|
||||
import { getBookingByUUID, getBookings } from '../db/index'
|
||||
|
||||
export type ServerSideBooking = {
|
||||
props: {
|
||||
booking: object
|
||||
booking: Booking
|
||||
}
|
||||
}
|
||||
|
||||
export type ServerSideRecentBooking = {
|
||||
props: {
|
||||
bookings: object[]
|
||||
bookings: Booking[]
|
||||
}
|
||||
}
|
||||
|
||||
export const getServerSideRecentBookings =
|
||||
async (): Promise<ServerSideRecentBooking> => {
|
||||
async () => {
|
||||
const bookings = await getBookings({
|
||||
startDateGreaterThan: startOfYear(nowInTz()).toISOString(),
|
||||
})
|
||||
|
||||
// TODO: hack, not sure why _id is not serilizable
|
||||
const bookingsJSON = JSON.parse(
|
||||
JSON.stringify(bookings.map((b) => b.toJSON()))
|
||||
) as object[]
|
||||
return {
|
||||
props: {
|
||||
bookings: bookingsJSON,
|
||||
bookings: bookings,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
export const getServerSideBooking = async (
|
||||
context: any
|
||||
): Promise<ServerSideBooking> => {
|
||||
) => {
|
||||
const {
|
||||
res,
|
||||
params: { uuid: uuids },
|
||||
@@ -47,11 +44,7 @@ export const getServerSideBooking = async (
|
||||
return { props: { booking: null } }
|
||||
}
|
||||
|
||||
await booking.populate('bill')
|
||||
|
||||
// TODO: hack, not sure why _id is not serilizable
|
||||
const bookingJSON = JSON.parse(JSON.stringify(booking.toJSON())) as object
|
||||
return {
|
||||
props: { booking: bookingJSON },
|
||||
props: { booking },
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Booking } from '@prisma/client';
|
||||
import { google } from 'googleapis'
|
||||
import { getBaseURL } from '../helpers/url'
|
||||
import { Booking } from '../db/booking'
|
||||
import { getDays, getNextDay, dateFormatBackend } from '../helpers/date'
|
||||
import { log } from '../helpers/log'
|
||||
|
||||
@@ -39,15 +39,15 @@ export async function getBookedDays() {
|
||||
.filter((event) => !!event.start.date)
|
||||
.flatMap((event) =>
|
||||
getDays({
|
||||
startDate: new Date(event.start.date),
|
||||
endDate: new Date(event.end.date),
|
||||
startDate: event.start.date,
|
||||
endDate: event.end.date,
|
||||
endDateExclusive: true,
|
||||
})
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
function getSummary(booking: Partial<Booking>): string {
|
||||
function getSummary(booking: Booking): string {
|
||||
let summary = ''
|
||||
|
||||
if (booking.org) {
|
||||
@@ -59,13 +59,13 @@ function getSummary(booking: Partial<Booking>): string {
|
||||
return summary
|
||||
}
|
||||
|
||||
function getDescription(booking: Booking): string {
|
||||
function getDescription(booking: Booking) {
|
||||
const bookingUrl = `${getBaseURL()}/admin/booking/${booking.uuid}`
|
||||
|
||||
return `Managelink ${bookingUrl}`
|
||||
}
|
||||
|
||||
export async function createCalendarEvent(booking: Booking): Promise<Booking> {
|
||||
export async function createCalendarEvent(booking: Booking) {
|
||||
const exclusiveEndDate = dateFormatBackend(
|
||||
getNextDay(new Date(booking.endDate))
|
||||
)
|
||||
@@ -85,7 +85,7 @@ export async function createCalendarEvent(booking: Booking): Promise<Booking> {
|
||||
return booking
|
||||
}
|
||||
|
||||
export async function deleteCalendarEvent(booking: Booking) {
|
||||
export async function deleteCalendarEvent(booking: { calendarEventId: string }) {
|
||||
await calendar.events.delete({
|
||||
calendarId,
|
||||
eventId: booking.calendarEventId,
|
||||
@@ -96,11 +96,11 @@ export async function deleteCalendarEvent(booking: Booking) {
|
||||
booking.calendarEventId = null
|
||||
}
|
||||
|
||||
//export async function patchCalendarEvent(booking: { calendarEventId: string } & Partial<Booking> ) : Promise<Booking> {
|
||||
// const response = await calendar.events.patch({
|
||||
// calendarId,
|
||||
// eventId: booking.calendarEventId,
|
||||
// });
|
||||
//
|
||||
// return booking;
|
||||
//}
|
||||
export async function patchCalendarEvent(booking: Booking ) {
|
||||
await calendar.events.patch({
|
||||
calendarId,
|
||||
eventId: booking.calendarEventId,
|
||||
});
|
||||
|
||||
return booking;
|
||||
}
|
||||
|
||||
377
package-lock.json
generated
377
package-lock.json
generated
@@ -8,7 +8,8 @@
|
||||
"name": "pfadi-bussle",
|
||||
"version": "0.1.0",
|
||||
"dependencies": {
|
||||
"@next-auth/mongodb-adapter": "1.1.1",
|
||||
"@next-auth/prisma-adapter": "^1.0.4",
|
||||
"@prisma/client": "^4.4.0",
|
||||
"@sendgrid/mail": "7.7.0",
|
||||
"autoprefixer": "10.4.12",
|
||||
"classnames": "2.3.2",
|
||||
@@ -39,9 +40,10 @@
|
||||
"postcss-flexbugs-fixes": "5.0.2",
|
||||
"postcss-preset-env": "7.8.2",
|
||||
"prettier": "2.7.1",
|
||||
"prisma": "^4.4.0",
|
||||
"swr": "1.3.0",
|
||||
"tailwindcss": "3.1.8",
|
||||
"ts-jest": "29.0.3"
|
||||
"ts-jest": "^29.0.3"
|
||||
}
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
@@ -643,9 +645,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@csstools/postcss-cascade-layers": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.0.tgz",
|
||||
"integrity": "sha512-XpA7g2KViA2ia23A5kZ/EQw+Sy308kLbvMlDPjFZmojwaJ9DYdJuwujFcDGK9v1QhHRmMEHbV2brVSQSLkN/7A==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz",
|
||||
"integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@csstools/selector-specificity": "^2.0.2",
|
||||
@@ -953,9 +955,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@eslint/eslintrc/node_modules/globals": {
|
||||
"version": "13.15.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
|
||||
"integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
|
||||
"version": "13.17.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz",
|
||||
"integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"type-fest": "^0.20.2"
|
||||
@@ -992,9 +994,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/@humanwhocodes/config-array": {
|
||||
"version": "0.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.5.tgz",
|
||||
"integrity": "sha512-XVVDtp+dVvRxMoxSiSfasYaG02VEe1qH5cKgMQJWhol6HwzbcqoCMJi8dAGoYAO57jhUyhI6cWuRiTcRaDaYug==",
|
||||
"version": "0.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz",
|
||||
"integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@humanwhocodes/object-schema": "^1.2.1",
|
||||
@@ -1419,12 +1421,12 @@
|
||||
"react": ">=16"
|
||||
}
|
||||
},
|
||||
"node_modules/@next-auth/mongodb-adapter": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@next-auth/mongodb-adapter/-/mongodb-adapter-1.1.1.tgz",
|
||||
"integrity": "sha512-X5O4U4l2M8nyp/B3qF5GOr/JJw2ShKgWfTZRa80Y5CUzTPPmf09ggL5v5UwCmz9l2RIv2GUxO8hK4qrcaZvDRw==",
|
||||
"node_modules/@next-auth/prisma-adapter": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next-auth/prisma-adapter/-/prisma-adapter-1.0.4.tgz",
|
||||
"integrity": "sha512-jIOM6CzCbl2/Mzbx9kb2IjtHoJOeRN9wtQgLk4EUm5bhneSVGv1rtz5TDskvp2UfCa+EK9nDmug+lje41z80Gg==",
|
||||
"peerDependencies": {
|
||||
"mongodb": "^4.1.1",
|
||||
"@prisma/client": ">=2.26.0 || >=3",
|
||||
"next-auth": "^4"
|
||||
}
|
||||
},
|
||||
@@ -1700,6 +1702,38 @@
|
||||
"url": "https://github.com/sponsors/panva"
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/client": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.4.0.tgz",
|
||||
"integrity": "sha512-ciKOP246x1xwr04G9ajHlJ4pkmtu9Q6esVyqVBO0QJihaKQIUvbPjClp17IsRJyxqNpFm4ScbOc/s9DUzKHINQ==",
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines-version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prisma": "*"
|
||||
},
|
||||
"peerDependenciesMeta": {
|
||||
"prisma": {
|
||||
"optional": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"node_modules/@prisma/engines": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.4.0.tgz",
|
||||
"integrity": "sha512-Fpykccxlt9MHrAs/QpPGpI2nOiRxuLA+LiApgA59ibbf24YICZIMWd3SI2YD+q0IAIso0jCGiHhirAIbxK3RyQ==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true
|
||||
},
|
||||
"node_modules/@prisma/engines-version": {
|
||||
"version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6.tgz",
|
||||
"integrity": "sha512-P5v/PuEIJLYXZUZBvOLPqoyCW+m6StNqHdiR6te++gYVODpPdLakks5HVx3JaZIY+LwR02juJWFlwpc9Eog/ug=="
|
||||
},
|
||||
"node_modules/@rushstack/eslint-patch": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.3.tgz",
|
||||
@@ -2717,9 +2751,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/caniuse-lite": {
|
||||
"version": "1.0.30001407",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001407.tgz",
|
||||
"integrity": "sha512-4ydV+t4P7X3zH83fQWNDX/mQEzYomossfpViCOx9zHBSMV+rIe3LFqglHHtVyvNl1FhTNxPxs3jei82iqOW04w==",
|
||||
"version": "1.0.30001418",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz",
|
||||
"integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -2846,14 +2880,17 @@
|
||||
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
|
||||
},
|
||||
"node_modules/cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/co": {
|
||||
@@ -3225,9 +3262,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/electron-to-chromium": {
|
||||
"version": "1.4.256",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz",
|
||||
"integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw=="
|
||||
"version": "1.4.276",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.276.tgz",
|
||||
"integrity": "sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ=="
|
||||
},
|
||||
"node_modules/emittery": {
|
||||
"version": "0.10.2",
|
||||
@@ -3257,22 +3294,22 @@
|
||||
}
|
||||
},
|
||||
"node_modules/es-abstract": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.2.tgz",
|
||||
"integrity": "sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==",
|
||||
"version": "1.20.4",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
|
||||
"integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"function.prototype.name": "^1.1.5",
|
||||
"get-intrinsic": "^1.1.2",
|
||||
"get-intrinsic": "^1.1.3",
|
||||
"get-symbol-description": "^1.0.0",
|
||||
"has": "^1.0.3",
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"has-symbols": "^1.0.3",
|
||||
"internal-slot": "^1.0.3",
|
||||
"is-callable": "^1.2.4",
|
||||
"is-callable": "^1.2.7",
|
||||
"is-negative-zero": "^2.0.2",
|
||||
"is-regex": "^1.1.4",
|
||||
"is-shared-array-buffer": "^1.0.2",
|
||||
@@ -3282,6 +3319,7 @@
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.4",
|
||||
"regexp.prototype.flags": "^1.4.3",
|
||||
"safe-regex-test": "^1.0.0",
|
||||
"string.prototype.trimend": "^1.0.5",
|
||||
"string.prototype.trimstart": "^1.0.5",
|
||||
"unbox-primitive": "^1.0.2"
|
||||
@@ -3644,9 +3682,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/eslint-plugin-react": {
|
||||
"version": "7.31.8",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz",
|
||||
"integrity": "sha512-5lBTZmgQmARLLSYiwI71tiGVTLUuqXantZM6vlSY39OaDSV0M7+32K5DnLkmFrwTe+Ksz0ffuLUC91RUviVZfw==",
|
||||
"version": "7.31.10",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz",
|
||||
"integrity": "sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"array-includes": "^3.1.5",
|
||||
@@ -3695,34 +3733,18 @@
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react/node_modules/estraverse": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
|
||||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react/node_modules/prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/eslint-plugin-react/node_modules/resolve": {
|
||||
"version": "2.0.0-next.3",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz",
|
||||
"integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==",
|
||||
"version": "2.0.0-next.4",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz",
|
||||
"integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"is-core-module": "^2.2.0",
|
||||
"path-parse": "^1.0.6"
|
||||
"is-core-module": "^2.9.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
},
|
||||
"bin": {
|
||||
"resolve": "bin/resolve"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
@@ -3983,9 +4005,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/estraverse": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
|
||||
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
|
||||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=4.0"
|
||||
@@ -4092,7 +4114,7 @@
|
||||
"node_modules/exit": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
|
||||
"integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
|
||||
"integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.8.0"
|
||||
@@ -4952,9 +4974,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/is-callable": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
|
||||
"integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==",
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">= 0.4"
|
||||
@@ -8453,6 +8475,23 @@
|
||||
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/prisma": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.4.0.tgz",
|
||||
"integrity": "sha512-l/QKLmLcKJQFuc+X02LyICo0NWTUVaNNZ00jKJBqwDyhwMAhboD1FWwYV50rkH4Wls0RviAJSFzkC2ZrfawpfA==",
|
||||
"devOptional": true,
|
||||
"hasInstallScript": true,
|
||||
"dependencies": {
|
||||
"@prisma/engines": "4.4.0"
|
||||
},
|
||||
"bin": {
|
||||
"prisma": "build/index.js",
|
||||
"prisma2": "build/index.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=14.17"
|
||||
}
|
||||
},
|
||||
"node_modules/prompts": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
|
||||
@@ -8467,13 +8506,13 @@
|
||||
}
|
||||
},
|
||||
"node_modules/prop-types": {
|
||||
"version": "15.7.2",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
|
||||
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"dependencies": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.8.1"
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/property-expr": {
|
||||
@@ -8690,7 +8729,7 @@
|
||||
"node_modules/require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"dev": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
@@ -8807,6 +8846,20 @@
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"node_modules/safe-regex-test": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
|
||||
"integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"call-bind": "^1.0.2",
|
||||
"get-intrinsic": "^1.1.3",
|
||||
"is-regex": "^1.1.4"
|
||||
},
|
||||
"funding": {
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/saslprep": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||
@@ -9622,9 +9675,9 @@
|
||||
}
|
||||
},
|
||||
"node_modules/update-browserslist-db": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz",
|
||||
"integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==",
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
|
||||
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
|
||||
"funding": [
|
||||
{
|
||||
"type": "opencollective",
|
||||
@@ -9922,12 +9975,12 @@
|
||||
}
|
||||
},
|
||||
"node_modules/yargs": {
|
||||
"version": "17.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz",
|
||||
"integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==",
|
||||
"version": "17.6.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz",
|
||||
"integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cliui": "^7.0.2",
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
@@ -10439,9 +10492,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"@csstools/postcss-cascade-layers": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.0.tgz",
|
||||
"integrity": "sha512-XpA7g2KViA2ia23A5kZ/EQw+Sy308kLbvMlDPjFZmojwaJ9DYdJuwujFcDGK9v1QhHRmMEHbV2brVSQSLkN/7A==",
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@csstools/postcss-cascade-layers/-/postcss-cascade-layers-1.1.1.tgz",
|
||||
"integrity": "sha512-+KdYrpKC5TgomQr2DlZF4lDEpHcoxnj5IGddYYfBWJAKfj1JtuHUIqMa+E1pJJ+z3kvDViWMqyqPlG4Ja7amQA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@csstools/selector-specificity": "^2.0.2",
|
||||
@@ -10598,9 +10651,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"globals": {
|
||||
"version": "13.15.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.15.0.tgz",
|
||||
"integrity": "sha512-bpzcOlgDhMG070Av0Vy5Owklpv1I6+j96GhUI7Rh7IzDCKLzboflLrrfqMu8NquDbiR4EOQk7XzJwqVJxicxog==",
|
||||
"version": "13.17.0",
|
||||
"resolved": "https://registry.npmjs.org/globals/-/globals-13.17.0.tgz",
|
||||
"integrity": "sha512-1C+6nQRb1GwGMKm2dH/E7enFAMxGTmGI7/dEdhy/DNelv85w9B72t3uc5frtMNXIbzrarJJ/lTCjcaZwbLJmyw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"type-fest": "^0.20.2"
|
||||
@@ -10624,9 +10677,9 @@
|
||||
}
|
||||
},
|
||||
"@humanwhocodes/config-array": {
|
||||
"version": "0.10.5",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.5.tgz",
|
||||
"integrity": "sha512-XVVDtp+dVvRxMoxSiSfasYaG02VEe1qH5cKgMQJWhol6HwzbcqoCMJi8dAGoYAO57jhUyhI6cWuRiTcRaDaYug==",
|
||||
"version": "0.10.7",
|
||||
"resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.7.tgz",
|
||||
"integrity": "sha512-MDl6D6sBsaV452/QSdX+4CXIjZhIcI0PELsxUjk4U828yd58vk3bTIvk/6w5FY+4hIy9sLW0sfrV7K7Kc++j/w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@humanwhocodes/object-schema": "^1.2.1",
|
||||
@@ -10957,10 +11010,10 @@
|
||||
"@types/react": ">=16"
|
||||
}
|
||||
},
|
||||
"@next-auth/mongodb-adapter": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@next-auth/mongodb-adapter/-/mongodb-adapter-1.1.1.tgz",
|
||||
"integrity": "sha512-X5O4U4l2M8nyp/B3qF5GOr/JJw2ShKgWfTZRa80Y5CUzTPPmf09ggL5v5UwCmz9l2RIv2GUxO8hK4qrcaZvDRw==",
|
||||
"@next-auth/prisma-adapter": {
|
||||
"version": "1.0.4",
|
||||
"resolved": "https://registry.npmjs.org/@next-auth/prisma-adapter/-/prisma-adapter-1.0.4.tgz",
|
||||
"integrity": "sha512-jIOM6CzCbl2/Mzbx9kb2IjtHoJOeRN9wtQgLk4EUm5bhneSVGv1rtz5TDskvp2UfCa+EK9nDmug+lje41z80Gg==",
|
||||
"requires": {}
|
||||
},
|
||||
"@next/env": {
|
||||
@@ -11102,6 +11155,25 @@
|
||||
"resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.0.1.tgz",
|
||||
"integrity": "sha512-mMyQ9vjpuFqePkfe5bZVIf/H3Dmk6wA8Kjxff9RcO4kqzJo+Ek9pGKwZHpeMr7Eku0QhLXMCd7fNCSnEnRMubg=="
|
||||
},
|
||||
"@prisma/client": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/client/-/client-4.4.0.tgz",
|
||||
"integrity": "sha512-ciKOP246x1xwr04G9ajHlJ4pkmtu9Q6esVyqVBO0QJihaKQIUvbPjClp17IsRJyxqNpFm4ScbOc/s9DUzKHINQ==",
|
||||
"requires": {
|
||||
"@prisma/engines-version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6"
|
||||
}
|
||||
},
|
||||
"@prisma/engines": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-4.4.0.tgz",
|
||||
"integrity": "sha512-Fpykccxlt9MHrAs/QpPGpI2nOiRxuLA+LiApgA59ibbf24YICZIMWd3SI2YD+q0IAIso0jCGiHhirAIbxK3RyQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"@prisma/engines-version": {
|
||||
"version": "4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6",
|
||||
"resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-4.4.0-66.f352a33b70356f46311da8b00d83386dd9f145d6.tgz",
|
||||
"integrity": "sha512-P5v/PuEIJLYXZUZBvOLPqoyCW+m6StNqHdiR6te++gYVODpPdLakks5HVx3JaZIY+LwR02juJWFlwpc9Eog/ug=="
|
||||
},
|
||||
"@rushstack/eslint-patch": {
|
||||
"version": "1.1.3",
|
||||
"resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.1.3.tgz",
|
||||
@@ -11874,9 +11946,9 @@
|
||||
"dev": true
|
||||
},
|
||||
"caniuse-lite": {
|
||||
"version": "1.0.30001407",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001407.tgz",
|
||||
"integrity": "sha512-4ydV+t4P7X3zH83fQWNDX/mQEzYomossfpViCOx9zHBSMV+rIe3LFqglHHtVyvNl1FhTNxPxs3jei82iqOW04w=="
|
||||
"version": "1.0.30001418",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001418.tgz",
|
||||
"integrity": "sha512-oIs7+JL3K9JRQ3jPZjlH6qyYDp+nBTCais7hjh0s+fuBwufc7uZ7hPYMXrDOJhV360KGMTcczMRObk0/iMqZRg=="
|
||||
},
|
||||
"ccount": {
|
||||
"version": "2.0.1",
|
||||
@@ -11953,13 +12025,13 @@
|
||||
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
|
||||
},
|
||||
"cliui": {
|
||||
"version": "7.0.4",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
|
||||
"integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
|
||||
"integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"string-width": "^4.2.0",
|
||||
"strip-ansi": "^6.0.0",
|
||||
"strip-ansi": "^6.0.1",
|
||||
"wrap-ansi": "^7.0.0"
|
||||
}
|
||||
},
|
||||
@@ -12217,9 +12289,9 @@
|
||||
}
|
||||
},
|
||||
"electron-to-chromium": {
|
||||
"version": "1.4.256",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.256.tgz",
|
||||
"integrity": "sha512-x+JnqyluoJv8I0U9gVe+Sk2st8vF0CzMt78SXxuoWCooLLY2k5VerIBdpvG7ql6GKI4dzNnPjmqgDJ76EdaAKw=="
|
||||
"version": "1.4.276",
|
||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.276.tgz",
|
||||
"integrity": "sha512-EpuHPqu8YhonqLBXHoU6hDJCD98FCe6KDoet3/gY1qsQ6usjJoHqBH2YIVs8FXaAtHwVL8Uqa/fsYao/vq9VWQ=="
|
||||
},
|
||||
"emittery": {
|
||||
"version": "0.10.2",
|
||||
@@ -12243,22 +12315,22 @@
|
||||
}
|
||||
},
|
||||
"es-abstract": {
|
||||
"version": "1.20.2",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.2.tgz",
|
||||
"integrity": "sha512-XxXQuVNrySBNlEkTYJoDNFe5+s2yIOpzq80sUHEdPdQr0S5nTLz4ZPPPswNIpKseDDUS5yghX1gfLIHQZ1iNuQ==",
|
||||
"version": "1.20.4",
|
||||
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.20.4.tgz",
|
||||
"integrity": "sha512-0UtvRN79eMe2L+UNEF1BwRe364sj/DXhQ/k5FmivgoSdpM90b8Jc0mDzKMGo7QS0BVbOP/bTwBKNnDc9rNzaPA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"es-to-primitive": "^1.2.1",
|
||||
"function-bind": "^1.1.1",
|
||||
"function.prototype.name": "^1.1.5",
|
||||
"get-intrinsic": "^1.1.2",
|
||||
"get-intrinsic": "^1.1.3",
|
||||
"get-symbol-description": "^1.0.0",
|
||||
"has": "^1.0.3",
|
||||
"has-property-descriptors": "^1.0.0",
|
||||
"has-symbols": "^1.0.3",
|
||||
"internal-slot": "^1.0.3",
|
||||
"is-callable": "^1.2.4",
|
||||
"is-callable": "^1.2.7",
|
||||
"is-negative-zero": "^2.0.2",
|
||||
"is-regex": "^1.1.4",
|
||||
"is-shared-array-buffer": "^1.0.2",
|
||||
@@ -12268,6 +12340,7 @@
|
||||
"object-keys": "^1.1.1",
|
||||
"object.assign": "^4.1.4",
|
||||
"regexp.prototype.flags": "^1.4.3",
|
||||
"safe-regex-test": "^1.0.0",
|
||||
"string.prototype.trimend": "^1.0.5",
|
||||
"string.prototype.trimstart": "^1.0.5",
|
||||
"unbox-primitive": "^1.0.2"
|
||||
@@ -12638,9 +12711,9 @@
|
||||
}
|
||||
},
|
||||
"eslint-plugin-react": {
|
||||
"version": "7.31.8",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.8.tgz",
|
||||
"integrity": "sha512-5lBTZmgQmARLLSYiwI71tiGVTLUuqXantZM6vlSY39OaDSV0M7+32K5DnLkmFrwTe+Ksz0ffuLUC91RUviVZfw==",
|
||||
"version": "7.31.10",
|
||||
"resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.31.10.tgz",
|
||||
"integrity": "sha512-e4N/nc6AAlg4UKW/mXeYWd3R++qUano5/o+t+wnWxIf+bLsOaH3a4q74kX3nDjYym3VBN4HyO9nEn1GcAqgQOA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"array-includes": "^3.1.5",
|
||||
@@ -12668,31 +12741,15 @@
|
||||
"esutils": "^2.0.2"
|
||||
}
|
||||
},
|
||||
"estraverse": {
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
|
||||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"dev": true
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"resolve": {
|
||||
"version": "2.0.0-next.3",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.3.tgz",
|
||||
"integrity": "sha512-W8LucSynKUIDu9ylraa7ueVZ7hc0uAgJBxVsQSKOXOyle8a93qXhcz+XAXZ8bIq2d6i4Ehddn6Evt+0/UwKk6Q==",
|
||||
"version": "2.0.0-next.4",
|
||||
"resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.4.tgz",
|
||||
"integrity": "sha512-iMDbmAWtfU+MHpxt/I5iWI7cY6YVEZUQ3MBgPQ++XD1PELuJHIl82xBmObyP2KyQmkNB2dsqF7seoQQiAn5yDQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"is-core-module": "^2.2.0",
|
||||
"path-parse": "^1.0.6"
|
||||
"is-core-module": "^2.9.0",
|
||||
"path-parse": "^1.0.7",
|
||||
"supports-preserve-symlinks-flag": "^1.0.0"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
@@ -12787,9 +12844,9 @@
|
||||
}
|
||||
},
|
||||
"estraverse": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
|
||||
"integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
|
||||
"version": "5.3.0",
|
||||
"resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
|
||||
"integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
|
||||
"dev": true
|
||||
},
|
||||
"estree-util-attach-comments": {
|
||||
@@ -12867,7 +12924,7 @@
|
||||
"exit": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
|
||||
"integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
|
||||
"integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==",
|
||||
"dev": true
|
||||
},
|
||||
"expect": {
|
||||
@@ -13486,9 +13543,9 @@
|
||||
"integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ=="
|
||||
},
|
||||
"is-callable": {
|
||||
"version": "1.2.4",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.4.tgz",
|
||||
"integrity": "sha512-nsuwtxZfMX67Oryl9LCQ+upnC0Z0BgpwntpS89m1H/TLF0zNfzfLMV/9Wa/6MZsj0acpEjAO0KF1xT6ZdLl95w==",
|
||||
"version": "1.2.7",
|
||||
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz",
|
||||
"integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==",
|
||||
"dev": true
|
||||
},
|
||||
"is-core-module": {
|
||||
@@ -15831,6 +15888,15 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"prisma": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/prisma/-/prisma-4.4.0.tgz",
|
||||
"integrity": "sha512-l/QKLmLcKJQFuc+X02LyICo0NWTUVaNNZ00jKJBqwDyhwMAhboD1FWwYV50rkH4Wls0RviAJSFzkC2ZrfawpfA==",
|
||||
"devOptional": true,
|
||||
"requires": {
|
||||
"@prisma/engines": "4.4.0"
|
||||
}
|
||||
},
|
||||
"prompts": {
|
||||
"version": "2.4.2",
|
||||
"resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz",
|
||||
@@ -15842,13 +15908,13 @@
|
||||
}
|
||||
},
|
||||
"prop-types": {
|
||||
"version": "15.7.2",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
|
||||
"integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
|
||||
"version": "15.8.1",
|
||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
|
||||
"requires": {
|
||||
"loose-envify": "^1.4.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"react-is": "^16.8.1"
|
||||
"react-is": "^16.13.1"
|
||||
}
|
||||
},
|
||||
"property-expr": {
|
||||
@@ -15992,7 +16058,7 @@
|
||||
"require-directory": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
|
||||
"integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=",
|
||||
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
|
||||
"dev": true
|
||||
},
|
||||
"resolve": {
|
||||
@@ -16064,6 +16130,17 @@
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
|
||||
"integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
|
||||
},
|
||||
"safe-regex-test": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz",
|
||||
"integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"call-bind": "^1.0.2",
|
||||
"get-intrinsic": "^1.1.3",
|
||||
"is-regex": "^1.1.4"
|
||||
}
|
||||
},
|
||||
"saslprep": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/saslprep/-/saslprep-1.0.3.tgz",
|
||||
@@ -16641,9 +16718,9 @@
|
||||
}
|
||||
},
|
||||
"update-browserslist-db": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz",
|
||||
"integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==",
|
||||
"version": "1.0.10",
|
||||
"resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
|
||||
"integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
|
||||
"requires": {
|
||||
"escalade": "^3.1.1",
|
||||
"picocolors": "^1.0.0"
|
||||
@@ -16861,12 +16938,12 @@
|
||||
"dev": true
|
||||
},
|
||||
"yargs": {
|
||||
"version": "17.4.1",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.4.1.tgz",
|
||||
"integrity": "sha512-WSZD9jgobAg3ZKuCQZSa3g9QOJeCCqLoLAykiWgmXnDo9EPnn4RPf5qVTtzgOx66o6/oqhcA5tHtJXpG8pMt3g==",
|
||||
"version": "17.6.0",
|
||||
"resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz",
|
||||
"integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cliui": "^7.0.2",
|
||||
"cliui": "^8.0.1",
|
||||
"escalade": "^3.1.1",
|
||||
"get-caller-file": "^2.0.5",
|
||||
"require-directory": "^2.1.1",
|
||||
|
||||
@@ -11,7 +11,8 @@
|
||||
"test": "jest"
|
||||
},
|
||||
"dependencies": {
|
||||
"@next-auth/mongodb-adapter": "1.1.1",
|
||||
"@next-auth/prisma-adapter": "^1.0.4",
|
||||
"@prisma/client": "^4.4.0",
|
||||
"@sendgrid/mail": "7.7.0",
|
||||
"autoprefixer": "10.4.12",
|
||||
"classnames": "2.3.2",
|
||||
@@ -38,13 +39,14 @@
|
||||
"eslint-config-next": "12.3.1",
|
||||
"eslint-config-prettier": "8.5.0",
|
||||
"jest": "29.1.2",
|
||||
"ts-jest": "29.0.3",
|
||||
"postcss": "8.4.17",
|
||||
"postcss-flexbugs-fixes": "5.0.2",
|
||||
"postcss-preset-env": "7.8.2",
|
||||
"prettier": "2.7.1",
|
||||
"prisma": "^4.4.0",
|
||||
"swr": "1.3.0",
|
||||
"tailwindcss": "3.1.8"
|
||||
"tailwindcss": "3.1.8",
|
||||
"ts-jest": "^29.0.3"
|
||||
},
|
||||
"jest": {
|
||||
"transform": {
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Input from '../../../../components/input'
|
||||
import Select from '../../../../components/select'
|
||||
import { Booking } from '../../../../db/booking'
|
||||
import { BILL_STATUS, MILAGE_TARIFS } from '../../../../db/enums'
|
||||
import { Booking, Bill, BillStatus, AdditionalCosts, Prisma } from '@prisma/client'
|
||||
import { MILAGE_TARIFS } from '../../../../db/enums'
|
||||
import { getMilageMax } from '../../../../db/index'
|
||||
import { daysFormatFrontend } from '../../../../helpers/date'
|
||||
import { dateFormatFrontend } from '../../../../helpers/date'
|
||||
import { log } from '../../../../helpers/log'
|
||||
import { getBillTotal, createBill, patchBill } from '../../../../helpers/bill'
|
||||
import { getBookingStatus } from '../../../../helpers/booking'
|
||||
@@ -29,7 +29,7 @@ const milageTarifOptions = Object.values(MILAGE_TARIFS).map((tarif) => {
|
||||
)
|
||||
})
|
||||
|
||||
const billStatusOptions = Object.values(BILL_STATUS).map((status) => {
|
||||
const billStatusOptions = Object.values(BillStatus).map((status) => {
|
||||
return (
|
||||
<option value={status} key={status}>
|
||||
{getBillStatusLabel(status)}
|
||||
@@ -50,13 +50,13 @@ function getTarifLabel(tarif: MILAGE_TARIFS) {
|
||||
}
|
||||
}
|
||||
|
||||
function getBillStatusLabel(status: BILL_STATUS) {
|
||||
function getBillStatusLabel(status: BillStatus) {
|
||||
switch (status) {
|
||||
case BILL_STATUS.UNINVOICED:
|
||||
case BillStatus.UNINVOICED:
|
||||
return 'Nicht gestellt'
|
||||
case BILL_STATUS.INVOICED:
|
||||
case BillStatus.INVOICED:
|
||||
return 'Gestellt'
|
||||
case BILL_STATUS.PAID:
|
||||
case BillStatus.PAID:
|
||||
return 'Bezahlt'
|
||||
default:
|
||||
return 'Unbekannt!!!'
|
||||
@@ -67,7 +67,7 @@ function BookingBillPage({
|
||||
booking: bookingProp,
|
||||
milageMax,
|
||||
}: {
|
||||
booking: Booking
|
||||
booking: Booking & { bill: Bill }
|
||||
milageMax: number
|
||||
}) {
|
||||
const [booking, setBooking] = useState(bookingProp)
|
||||
@@ -75,11 +75,11 @@ function BookingBillPage({
|
||||
booking?.bill?.milageStart || milageMax
|
||||
)
|
||||
const [milageEnd, setMilageEnd] = useState(booking?.bill?.milageEnd)
|
||||
const [tarif, setTarif] = useState(
|
||||
booking?.bill?.tarif || MILAGE_TARIFS.EXTERN
|
||||
const [tarif, setTarif] = useState<Prisma.Decimal>(
|
||||
booking?.bill?.tarif
|
||||
)
|
||||
const [status, setStatus] = useState(booking?.bill?.status)
|
||||
const [additionalCosts, setAdditionalCosts] = useState([])
|
||||
const [additionalCosts, setAdditionalCosts] = useState<Prisma.AdditionalCostsCreateInput[]>([])
|
||||
const [storingInProgress, setStoringInProgress] = useState(false)
|
||||
const [storingError, setStoringError] = useState(null)
|
||||
const milage =
|
||||
@@ -99,10 +99,9 @@ function BookingBillPage({
|
||||
const bill = await saveBill(booking.uuid, {
|
||||
milageStart,
|
||||
milageEnd,
|
||||
milage,
|
||||
tarif,
|
||||
status,
|
||||
additionalCosts,
|
||||
additionalCosts: { create: additionalCosts },
|
||||
})
|
||||
|
||||
booking.bill = bill
|
||||
@@ -118,7 +117,7 @@ function BookingBillPage({
|
||||
event: React.MouseEvent<HTMLButtonElement>
|
||||
) {
|
||||
event.preventDefault()
|
||||
setAdditionalCosts([...additionalCosts, { name: '', value: 0 }])
|
||||
setAdditionalCosts([...additionalCosts, { name: '', value: new Prisma.Decimal(0) } as AdditionalCosts])
|
||||
}
|
||||
|
||||
const onRemoveAdditionalCost = function(
|
||||
@@ -138,7 +137,7 @@ function BookingBillPage({
|
||||
<form className="w-full" onSubmit={onSubmit}>
|
||||
<div>
|
||||
<strong>Buchungszeitraum:</strong>{' '}
|
||||
{daysFormatFrontend(booking.days)}
|
||||
{dateFormatFrontend(new Date(booking.startDate))}-{dateFormatFrontend(new Date(booking.endDate))}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Bucher:</strong> {booking.name}
|
||||
@@ -171,8 +170,8 @@ function BookingBillPage({
|
||||
<Select
|
||||
label="Rate"
|
||||
name="tarif"
|
||||
value={tarif}
|
||||
onChange={(e) => setTarif(e.target.value as MILAGE_TARIFS)}
|
||||
value={tarif.toString()}
|
||||
onChange={(e) => setTarif(new Prisma.Decimal(e.target.value))}
|
||||
>
|
||||
{milageTarifOptions}
|
||||
</Select>
|
||||
@@ -211,7 +210,7 @@ function BookingBillPage({
|
||||
newAdditonalCosts[index] = {
|
||||
value: newAdditonalCosts[index].value,
|
||||
name: event.target.value,
|
||||
}
|
||||
} as AdditionalCosts
|
||||
setAdditionalCosts(newAdditonalCosts)
|
||||
}}
|
||||
/>
|
||||
@@ -219,13 +218,13 @@ function BookingBillPage({
|
||||
label={`Betrag`}
|
||||
name={`additionalCostValue${index}`}
|
||||
key={`additionalCostValue${index}`}
|
||||
value={additionalCosts[index].value}
|
||||
value={additionalCosts[index].value.toString()}
|
||||
type="number"
|
||||
onChange={(event) => {
|
||||
const newAdditonalCosts = [...additionalCosts]
|
||||
newAdditonalCosts[index] = {
|
||||
name: newAdditonalCosts[index].name,
|
||||
value: Number(event.target.value),
|
||||
value: new Prisma.Decimal(event.target.value),
|
||||
}
|
||||
setAdditionalCosts(newAdditonalCosts)
|
||||
}}
|
||||
@@ -234,13 +233,13 @@ function BookingBillPage({
|
||||
</>
|
||||
)
|
||||
})}
|
||||
<Input label="Summe" name="total" readOnly value={total} />
|
||||
<Input label="Summe" name="total" readOnly value={total.toString()} />
|
||||
</div>
|
||||
<Select
|
||||
label="Status"
|
||||
name={status}
|
||||
value={status}
|
||||
onChange={(e) => setStatus(e.target.value as BILL_STATUS)}
|
||||
onChange={(e) => setStatus(e.target.value as BillStatus)}
|
||||
>
|
||||
{billStatusOptions}
|
||||
</Select>
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { useRouter } from 'next/router'
|
||||
import { BookingStatus, Booking } from '@prisma/client'
|
||||
import Link from 'next/link'
|
||||
import Calendar from '../../../../components/calendar'
|
||||
import { getServerSideBooking } from '../../../../lib/getServerSideProps'
|
||||
import { Booking } from '../../../../db/booking'
|
||||
import { getBookingStatus, patchBooking } from '../../../../helpers/booking'
|
||||
import { daysFormatFrontend } from '../../../../helpers/date'
|
||||
import { log } from '../../../../helpers/log'
|
||||
import { BOOKING_STATUS } from '../../../../db/enums'
|
||||
|
||||
export const getServerSideProps = getServerSideBooking
|
||||
|
||||
@@ -25,7 +24,7 @@ function ShowBookingAdmin({ booking: bookingProp }: { booking: Booking }) {
|
||||
setStoringBookingError(null)
|
||||
setStoringBooking(true)
|
||||
const updatedBooking = await patchBooking(booking.uuid, {
|
||||
status: confirmed ? BOOKING_STATUS.CONFIRMED : BOOKING_STATUS.REJECTED,
|
||||
status: confirmed ? BookingStatus.CONFIRMED : BookingStatus.REJECTED,
|
||||
})
|
||||
setBooking(updatedBooking)
|
||||
} catch (error) {
|
||||
@@ -40,7 +39,7 @@ function ShowBookingAdmin({ booking: bookingProp }: { booking: Booking }) {
|
||||
<h2 className="text-3xl">Buchung {booking.uuid}</h2>
|
||||
<Calendar start={booking.startDate} end={booking.endDate} />
|
||||
<div>
|
||||
<strong>Buchungszeitraum:</strong> {daysFormatFrontend(booking.days)}
|
||||
<strong>Buchungszeitraum:</strong> {daysFormatFrontend([booking.startDate])}-{daysFormatFrontend([booking.endDate])}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Bucher:</strong> {booking.name}
|
||||
|
||||
@@ -1,64 +1,51 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import NextAuth from 'next-auth'
|
||||
import EmailProvider from 'next-auth/providers/email'
|
||||
import GitHubProvider from 'next-auth/providers/github'
|
||||
|
||||
import { MongoDBAdapter } from '@next-auth/mongodb-adapter'
|
||||
import { MONGO_URI } from '../../../db'
|
||||
import { MongoClient } from 'mongodb'
|
||||
import { PrismaAdapter } from "@next-auth/prisma-adapter"
|
||||
import { PrismaClient } from "@prisma/client"
|
||||
|
||||
let client: MongoClient
|
||||
const prisma = new PrismaClient()
|
||||
|
||||
const ADMIN_EMAIL = process.env.ADMIN_EMAIL
|
||||
const GITHUB_USERS_GRANTED = ['111471']
|
||||
|
||||
async function getMongoClient() {
|
||||
if (!client) {
|
||||
client = new MongoClient(MONGO_URI)
|
||||
await client.connect()
|
||||
}
|
||||
|
||||
return client
|
||||
}
|
||||
|
||||
export default async function auth(req: NextApiRequest, res: NextApiResponse) {
|
||||
return await NextAuth(req, res, {
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
adapter: MongoDBAdapter(getMongoClient()),
|
||||
providers: [
|
||||
GitHubProvider({
|
||||
clientId: process.env.GITHUB_CLIENT_ID,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
||||
}),
|
||||
EmailProvider({
|
||||
server: {
|
||||
host: 'smtp.sendgrid.net',
|
||||
port: 587,
|
||||
auth: {
|
||||
user: 'apikey',
|
||||
pass: process.env.SENDGRID_API_KEY,
|
||||
},
|
||||
export default NextAuth({
|
||||
secret: process.env.NEXTAUTH_SECRET,
|
||||
adapter: PrismaAdapter(prisma),
|
||||
providers: [
|
||||
GitHubProvider({
|
||||
clientId: process.env.GITHUB_CLIENT_ID,
|
||||
clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
||||
}),
|
||||
EmailProvider({
|
||||
server: {
|
||||
host: 'smtp.sendgrid.net',
|
||||
port: 587,
|
||||
auth: {
|
||||
user: 'apikey',
|
||||
pass: process.env.SENDGRID_API_KEY,
|
||||
},
|
||||
from: process.env.FROM_EMAIL,
|
||||
}),
|
||||
],
|
||||
callbacks: {
|
||||
async signIn({ account, email }) {
|
||||
// if user sigin requested magic link via EmailProvider
|
||||
if (account.provider === 'email') {
|
||||
if (email.verificationRequest) {
|
||||
// only allow admins by email entered
|
||||
return account.providerAccountId === ADMIN_EMAIL
|
||||
}
|
||||
|
||||
// if user accesses with magic link, also only allow admin
|
||||
return account.providerAccountId === ADMIN_EMAIL
|
||||
} else if (account.provider === 'github') {
|
||||
// only one and only one user
|
||||
return GITHUB_USERS_GRANTED.includes(account.providerAccountId)
|
||||
}
|
||||
return false
|
||||
},
|
||||
from: process.env.FROM_EMAIL,
|
||||
}),
|
||||
],
|
||||
callbacks: {
|
||||
async signIn({ account, email }) {
|
||||
// if user sigin requested magic link via EmailProvider
|
||||
if (account.provider === 'email') {
|
||||
if (email.verificationRequest) {
|
||||
// only allow admins by email entered
|
||||
return account.providerAccountId === ADMIN_EMAIL
|
||||
}
|
||||
|
||||
// if user accesses with magic link, also only allow admin
|
||||
return account.providerAccountId === ADMIN_EMAIL
|
||||
} else if (account.provider === 'github') {
|
||||
// only one and only one user
|
||||
return GITHUB_USERS_GRANTED.includes(account.providerAccountId)
|
||||
}
|
||||
return false
|
||||
},
|
||||
})
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { Bill } from '../../../../db/bill'
|
||||
import { Prisma } from '@prisma/client';
|
||||
import { createBill, patchBill } from '../../../../db/index'
|
||||
import { log } from '../../../../helpers/log'
|
||||
|
||||
@@ -13,7 +13,7 @@ export default async function billHandler(
|
||||
} = req
|
||||
const bookingUUID = Array.isArray(uuids) ? uuids[0] : uuids
|
||||
|
||||
let bill: Bill
|
||||
let bill: Prisma.BillUpdateInput
|
||||
|
||||
switch (method) {
|
||||
case 'POST':
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { Booking } from '../../../../db/booking'
|
||||
import { BOOKING_STATUS } from '../../../../db/enums'
|
||||
import { Prisma, BookingStatus } from '@prisma/client';
|
||||
import { patchBooking } from '../../../../db/index'
|
||||
import {
|
||||
sendBookingConfirmed,
|
||||
@@ -10,28 +9,27 @@ import {
|
||||
import { log } from '../../../../helpers/log'
|
||||
|
||||
function changedStatus(
|
||||
previous: Booking,
|
||||
current: Partial<Booking>,
|
||||
status: BOOKING_STATUS
|
||||
previous: Prisma.BookingUpdateInput,
|
||||
current: Prisma.BookingUpdateInput,
|
||||
status: BookingStatus
|
||||
): boolean {
|
||||
return (
|
||||
[BOOKING_STATUS.REQUESTED].includes(previous.status) &&
|
||||
BookingStatus.REQUESTED === previous.status &&
|
||||
current.status === status
|
||||
)
|
||||
}
|
||||
function wasRejected(previous: Booking, current: Partial<Booking>): boolean {
|
||||
return changedStatus(previous, current, BOOKING_STATUS.REJECTED)
|
||||
function wasRejected(previous: Prisma.BookingUpdateInput, current: Prisma.BookingUpdateInput): boolean {
|
||||
return changedStatus(previous, current, BookingStatus.REJECTED)
|
||||
}
|
||||
|
||||
function wasConfirmed(previous: Booking, current: Partial<Booking>): boolean {
|
||||
return changedStatus(previous, current, BOOKING_STATUS.CONFIRMED)
|
||||
function wasConfirmed(previous: Prisma.BookingUpdateInput, current: Prisma.BookingUpdateInput): boolean {
|
||||
return changedStatus(previous, current, BookingStatus.CONFIRMED)
|
||||
}
|
||||
|
||||
function wasCanceled(previous: Booking, current: Partial<Booking>): boolean {
|
||||
function wasCanceled(previous: Prisma.BookingUpdateInput, current: Prisma.BookingUpdateInput): boolean {
|
||||
return (
|
||||
[BOOKING_STATUS.REQUESTED, BOOKING_STATUS.CONFIRMED].includes(
|
||||
previous.status
|
||||
) && current.status === BOOKING_STATUS.CANCELED
|
||||
[BookingStatus.REQUESTED, BookingStatus.CONFIRMED].find(s => s === previous.status)
|
||||
&& current.status === BookingStatus.CANCELED
|
||||
)
|
||||
}
|
||||
|
||||
@@ -48,12 +46,12 @@ export default async function userHandler(
|
||||
|
||||
switch (method) {
|
||||
case 'PATCH':
|
||||
if (!Object.values(BOOKING_STATUS).includes(req.body.status)) {
|
||||
if (!Object.values(BookingStatus).includes(req.body.status)) {
|
||||
res
|
||||
.status(400)
|
||||
.end(
|
||||
`The attribute status can only be: ${Object.values(
|
||||
BOOKING_STATUS
|
||||
BookingStatus
|
||||
).join(', ')}`
|
||||
)
|
||||
break
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { Error } from 'mongoose'
|
||||
import { NextApiRequest, NextApiResponse } from 'next'
|
||||
import { Booking } from '../../../db/booking'
|
||||
import { Booking, Prisma } from '@prisma/client';
|
||||
import { createBooking } from '../../../db/index'
|
||||
import { log } from '../../../helpers/log'
|
||||
import {
|
||||
@@ -14,17 +13,14 @@ export default async function userHandler(
|
||||
): Promise<void> {
|
||||
const { method } = req
|
||||
|
||||
let booking: Booking
|
||||
let booking: Booking;
|
||||
|
||||
switch (method) {
|
||||
case 'POST':
|
||||
try {
|
||||
booking = await createBooking(req.body)
|
||||
} catch (e) {
|
||||
if (e instanceof Error.ValidationError) {
|
||||
res.status(400).json({ message: e.message, errors: e.errors })
|
||||
return
|
||||
}
|
||||
// TODO: add validation for booking on same day
|
||||
log.error('Failed to store booking', e)
|
||||
res.status(500).end(`Internal Server Error...Guru is meditating...`)
|
||||
return
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import { getServerSideBooking } from '../../../lib/getServerSideProps'
|
||||
import { Booking } from '../../../db/booking'
|
||||
import { BOOKING_STATUS } from '../../../db/enums'
|
||||
import { daysFormatFrontend } from '../../../helpers/date'
|
||||
import { Booking, BookingStatus } from '@prisma/client'
|
||||
import { dateFormatFrontend } from '../../../helpers/date'
|
||||
import { log } from '../../../helpers/log'
|
||||
import { getBookingStatus, cancelBooking } from '../../../helpers/booking'
|
||||
|
||||
@@ -45,12 +44,12 @@ export default function ShowBooking({
|
||||
<strong>Buchungsstatus:</strong> {getBookingStatus(booking.status)}
|
||||
</div>
|
||||
<div>
|
||||
<strong>Buchungszeitraum:</strong> {daysFormatFrontend(booking.days)}
|
||||
<strong>Buchungszeitraum:</strong> {dateFormatFrontend(new Date(booking.startDate))}-{dateFormatFrontend(new Date(booking.endDate))}
|
||||
</div>
|
||||
{storingBookingError && (
|
||||
<div className="error-message flex-grow">{storingBookingError}</div>
|
||||
)}
|
||||
{[BOOKING_STATUS.CONFIRMED, BOOKING_STATUS.REQUESTED].includes(
|
||||
{([BookingStatus.CONFIRMED, BookingStatus.REQUESTED] as BookingStatus[]).includes(
|
||||
booking.status
|
||||
) && (
|
||||
<div className="my-6">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React, { useEffect, useState } from 'react'
|
||||
import Link from 'next/link'
|
||||
import { Booking } from '../../../db/booking'
|
||||
import { Booking } from '@prisma/client'
|
||||
import { loadBookingData, storeBookingData } from '../../../helpers/storage'
|
||||
import { getServerSideBooking } from '../../../lib/getServerSideProps'
|
||||
|
||||
|
||||
148
prisma/migrations/20221011094251_initial/migration.sql
Normal file
148
prisma/migrations/20221011094251_initial/migration.sql
Normal file
@@ -0,0 +1,148 @@
|
||||
-- CreateEnum
|
||||
CREATE TYPE "BillStatus" AS ENUM ('UNINVOICED', 'INVOICED', 'PAID');
|
||||
|
||||
-- CreateEnum
|
||||
CREATE TYPE "BookingStatus" AS ENUM ('REQUESTED', 'CONFIRMED', 'REJECTED', 'CANCELED');
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "AdditionalCosts" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"value" DECIMAL(65,30) NOT NULL,
|
||||
|
||||
CONSTRAINT "AdditionalCosts_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Bill" (
|
||||
"id" SERIAL NOT NULL,
|
||||
"createdAt" DATE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATE NOT NULL,
|
||||
"milageEnd" INTEGER NOT NULL,
|
||||
"milageStart" INTEGER NOT NULL,
|
||||
"status" "BillStatus" NOT NULL DEFAULT 'UNINVOICED',
|
||||
"tarif" DECIMAL(65,30) NOT NULL,
|
||||
|
||||
CONSTRAINT "Bill_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Booking" (
|
||||
"id" TEXT NOT NULL,
|
||||
"uuid" TEXT NOT NULL,
|
||||
"createdAt" DATE NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updatedAt" DATE NOT NULL,
|
||||
"billId" INTEGER NOT NULL,
|
||||
"calendarEventId" TEXT,
|
||||
"city" TEXT NOT NULL,
|
||||
"destination" TEXT NOT NULL,
|
||||
"email" TEXT NOT NULL,
|
||||
"endDate" TEXT NOT NULL,
|
||||
"name" TEXT NOT NULL,
|
||||
"org" TEXT NOT NULL,
|
||||
"phone" TEXT NOT NULL,
|
||||
"purpose" TEXT NOT NULL,
|
||||
"startDate" TEXT NOT NULL,
|
||||
"status" "BookingStatus" NOT NULL DEFAULT 'REQUESTED',
|
||||
"street" TEXT NOT NULL,
|
||||
"zip" TEXT NOT NULL,
|
||||
|
||||
CONSTRAINT "Booking_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Account" (
|
||||
"id" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"type" TEXT NOT NULL,
|
||||
"provider" TEXT NOT NULL,
|
||||
"providerAccountId" TEXT NOT NULL,
|
||||
"refresh_token" TEXT,
|
||||
"access_token" TEXT,
|
||||
"expires_at" INTEGER,
|
||||
"token_type" TEXT,
|
||||
"scope" TEXT,
|
||||
"id_token" TEXT,
|
||||
"session_state" TEXT,
|
||||
|
||||
CONSTRAINT "Account_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Session" (
|
||||
"id" TEXT NOT NULL,
|
||||
"sessionToken" TEXT NOT NULL,
|
||||
"userId" TEXT NOT NULL,
|
||||
"expires" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Session_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "User" (
|
||||
"id" TEXT NOT NULL,
|
||||
"name" TEXT,
|
||||
"email" TEXT,
|
||||
"emailVerified" TIMESTAMP(3),
|
||||
"image" TEXT,
|
||||
|
||||
CONSTRAINT "User_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "VerificationToken" (
|
||||
"identifier" TEXT NOT NULL,
|
||||
"token" TEXT NOT NULL,
|
||||
"expires" TIMESTAMP(3) NOT NULL
|
||||
);
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "_AdditionalCostsToBill" (
|
||||
"A" INTEGER NOT NULL,
|
||||
"B" INTEGER NOT NULL
|
||||
);
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Booking_uuid_key" ON "Booking"("uuid");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Booking_billId_key" ON "Booking"("billId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "uuid_1" ON "Booking"("uuid");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Account_provider_providerAccountId_key" ON "Account"("provider", "providerAccountId");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "Session_sessionToken_key" ON "Session"("sessionToken");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "User_email_key" ON "User"("email");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "VerificationToken_token_key" ON "VerificationToken"("token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "VerificationToken_identifier_token_key" ON "VerificationToken"("identifier", "token");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE UNIQUE INDEX "_AdditionalCostsToBill_AB_unique" ON "_AdditionalCostsToBill"("A", "B");
|
||||
|
||||
-- CreateIndex
|
||||
CREATE INDEX "_AdditionalCostsToBill_B_index" ON "_AdditionalCostsToBill"("B");
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Booking" ADD CONSTRAINT "Booking_billId_fkey" FOREIGN KEY ("billId") REFERENCES "Bill"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Account" ADD CONSTRAINT "Account_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Session" ADD CONSTRAINT "Session_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_AdditionalCostsToBill" ADD CONSTRAINT "_AdditionalCostsToBill_A_fkey" FOREIGN KEY ("A") REFERENCES "AdditionalCosts"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "_AdditionalCostsToBill" ADD CONSTRAINT "_AdditionalCostsToBill_B_fkey" FOREIGN KEY ("B") REFERENCES "Bill"("id") ON DELETE CASCADE ON UPDATE CASCADE;
|
||||
3
prisma/migrations/migration_lock.toml
Normal file
3
prisma/migrations/migration_lock.toml
Normal file
@@ -0,0 +1,3 @@
|
||||
# Please do not edit this file manually
|
||||
# It should be added in your version-control system (i.e. Git)
|
||||
provider = "postgresql"
|
||||
110
prisma/schema.prisma
Normal file
110
prisma/schema.prisma
Normal file
@@ -0,0 +1,110 @@
|
||||
generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgres"
|
||||
url = env("POSTGRES_URI")
|
||||
}
|
||||
|
||||
model AdditionalCosts {
|
||||
id Int @id @default(autoincrement())
|
||||
name String
|
||||
value Decimal
|
||||
Bill Bill[]
|
||||
}
|
||||
|
||||
enum BillStatus {
|
||||
UNINVOICED
|
||||
INVOICED
|
||||
PAID
|
||||
}
|
||||
|
||||
model Bill {
|
||||
id Int @id @default(autoincrement())
|
||||
createdAt DateTime @default(now()) @db.Date
|
||||
updatedAt DateTime @updatedAt @db.Date
|
||||
milageEnd Int
|
||||
milageStart Int
|
||||
status BillStatus @default(UNINVOICED)
|
||||
tarif Decimal
|
||||
additionalCosts AdditionalCosts[]
|
||||
booking Booking?
|
||||
}
|
||||
|
||||
enum BookingStatus {
|
||||
REQUESTED
|
||||
CONFIRMED
|
||||
REJECTED
|
||||
CANCELED
|
||||
}
|
||||
|
||||
model Booking {
|
||||
id String @id @default(uuid())
|
||||
uuid String @unique @default(uuid())
|
||||
createdAt DateTime @default(now()) @db.Date
|
||||
updatedAt DateTime @updatedAt @db.Date
|
||||
bill Bill? @relation(fields: [billId], references: [id], onDelete: Cascade)
|
||||
billId Int @unique
|
||||
calendarEventId String?
|
||||
city String
|
||||
destination String
|
||||
email String
|
||||
endDate String
|
||||
name String
|
||||
org String
|
||||
phone String
|
||||
purpose String
|
||||
startDate String
|
||||
status BookingStatus @default(REQUESTED)
|
||||
street String
|
||||
zip String
|
||||
|
||||
@@index([uuid], map: "uuid_1")
|
||||
}
|
||||
|
||||
// next-auth
|
||||
model Account {
|
||||
id String @id @default(cuid())
|
||||
userId String
|
||||
type String
|
||||
provider String
|
||||
providerAccountId String
|
||||
refresh_token String? @db.Text
|
||||
access_token String? @db.Text
|
||||
expires_at Int?
|
||||
token_type String?
|
||||
scope String?
|
||||
id_token String? @db.Text
|
||||
session_state String?
|
||||
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
|
||||
@@unique([provider, providerAccountId])
|
||||
}
|
||||
|
||||
model Session {
|
||||
id String @id @default(cuid())
|
||||
sessionToken String @unique
|
||||
userId String
|
||||
expires DateTime
|
||||
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
|
||||
}
|
||||
|
||||
model User {
|
||||
id String @id @default(cuid())
|
||||
name String?
|
||||
email String? @unique
|
||||
emailVerified DateTime?
|
||||
image String?
|
||||
accounts Account[]
|
||||
sessions Session[]
|
||||
}
|
||||
|
||||
model VerificationToken {
|
||||
identifier String
|
||||
token String @unique
|
||||
expires DateTime
|
||||
|
||||
@@unique([identifier, token])
|
||||
}
|
||||
Reference in New Issue
Block a user