switch to prisma

This commit is contained in:
Thomas Ruoff
2022-10-11 11:43:32 +02:00
parent 41342475ba
commit 1ef9b14e95
28 changed files with 764 additions and 780 deletions

View File

@@ -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>

View File

@@ -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}

View File

@@ -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
},
})
}
},
});

View File

@@ -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':

View File

@@ -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

View File

@@ -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

View File

@@ -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">

View File

@@ -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'