From 24b1b4ee13e46f688682ca29a5c276f3e5d027b1 Mon Sep 17 00:00:00 2001 From: Marc Buma Date: Tue, 2 Apr 2024 11:54:49 +0200 Subject: [PATCH 1/2] Add debian binary target for prisma --- prisma/schema.prisma | 66 ++++++++++++++++++++++---------------------- 1 file changed, 33 insertions(+), 33 deletions(-) diff --git a/prisma/schema.prisma b/prisma/schema.prisma index aa3180f..ee4066e 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1,6 +1,6 @@ generator client { provider = "prisma-client-js" - binaryTargets = ["native", "linux-musl-openssl-3.0.x"] + binaryTargets = ["native", "linux-musl-openssl-3.0.x", "debian-openssl-3.0.x"] } datasource db { @@ -2027,46 +2027,46 @@ enum accounts_account_type { // } 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]) + 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) + 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[] + 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 + identifier String + token String @unique + expires DateTime - @@unique([identifier, token]) + @@unique([identifier, token]) } From 8ca3b0b0d04ce44e16950802baef7e0019373d6b Mon Sep 17 00:00:00 2001 From: Marc Buma Date: Thu, 4 Apr 2024 02:47:28 +0200 Subject: [PATCH 2/2] work in progress --- mysql-db/handy-queries.sql | 28 ++++++++++++++++ .../services/fietsenstallingen-service.ts | 32 +++++++++++++++++-- .../fietsenstallingen_services-service.ts | 8 +++-- src/components/parking/ParkingEdit.tsx | 19 +++++++---- .../parking/ParkingEditCapaciteit.tsx | 4 ++- src/components/parking/ParkingView.tsx | 6 ++-- .../parking/ParkingViewBeheerder.tsx | 26 ++++++++++----- .../parking/ParkingViewCapaciteit.tsx | 21 ++++++++---- .../parking/ParkingViewServices.tsx | 8 +++++ src/pages/api/auth/[...nextauth].ts | 2 +- src/pages/index.tsx | 1 - src/utils/parkings.tsx | 26 +++++++++++++++ 12 files changed, 149 insertions(+), 32 deletions(-) create mode 100644 mysql-db/handy-queries.sql diff --git a/mysql-db/handy-queries.sql b/mysql-db/handy-queries.sql new file mode 100644 index 0000000..2bbfe70 --- /dev/null +++ b/mysql-db/handy-queries.sql @@ -0,0 +1,28 @@ +select UserID FROM security_users where UserName="mosbuma@bumos.nl" into @marc; +select ID From fietsenstallingen where Title like "%Marktstraat%" AND Plaats="Apeldoorn" INTO @stalling; +select @marc, @stalling; +INSERT INTO security_users_sites(UserID, SiteID, isContact) VALUES(@marc, @stalling, 0); + +select fs.title +from security_users_sites sus +JOIN fietsenstallingen fs ON (fs.ID=sus.SiteID) +where sus.UserID=@marc; + +Select * from fietsenstallingen_services where FietsenstallingID="0066B68F-6F95-4C42-BACF7B44C50FA061"; +Select * from fietsenstallingen where ID="0066B68F-6F95-4C42-BACF7B44C50FA061"; + +select ID, Title, Beheerder, BeheerderContact, Capacity from fietsenstallingen WHERE IsStationsstalling; +select * from fietsenstallingen WHERE ID="0066B68F-6F95-4C42-BACF7B44C50FA061"; + +select * from security_users_sites where UserID="D4351342-685D-D17A-B3617EEBBF39451C"; +select * from security_users_sites where UserID = @marc; + +select * from fietsenstalling_sectie where fietsenstallingsId=@SiteID; + +select fs.ID, fs.Title, fs.Plaats, fs.Capacity, count(fss.sectieId), sum(fss.capaciteit) +from fietsenstalling_sectie fss +left join fietsenstallingen fs ON fs.id=fss.fietsenstallingsId +WHERE fs.ID="0066B68F-6F95-4C42-BACF7B44C50FA061" +group by fs.ID +having count(fss.sectieId)>1 +order by fs.Plaats, fs.Title diff --git a/src/backend/services/fietsenstallingen-service.ts b/src/backend/services/fietsenstallingen-service.ts index 7ec5506..93ab822 100644 --- a/src/backend/services/fietsenstallingen-service.ts +++ b/src/backend/services/fietsenstallingen-service.ts @@ -2,13 +2,41 @@ import { prisma } from "~/server/db"; import type { fietsenstallingen } from "@prisma/client"; import type { ICrudService } from "~/backend/handlers/crud-service-interface"; +BigInt.prototype.toJSON = function () { + const int = Number.parseInt(this.toString()); + return int ?? this.toString(); +}; + +const include = { + fietsenstalling_secties: { + include: { + secties_fietstype: { + include: { fietstype: true } + } + } + }, + fietsenstallingen_services: { + include: { + services: true + } + } +} + // inspired by https://medium.com/@brandonlostboy/build-it-better-next-js-crud-api-b45d2e923896 const FietsenstallingenService: ICrudService = { getAll: async () => { - return await prisma.fietsenstallingen.findMany(); + return await prisma.fietsenstallingen.findMany({ + include: { + fietsenstalling_secties: true, + } + }); }, getOne: async (id: string) => { - return await prisma.fietsenstallingen.findFirst({ where: { ID: id } }); + return await prisma.fietsenstallingen.findFirst({ + where: { ID: id }, + include + } + ); }, create: async (_data: fietsenstallingen): Promise => { return await prisma.fietsenstallingen.create({ data: _data }); diff --git a/src/backend/services/fietsenstallingen_services-service.ts b/src/backend/services/fietsenstallingen_services-service.ts index 5b1af04..8d687ed 100644 --- a/src/backend/services/fietsenstallingen_services-service.ts +++ b/src/backend/services/fietsenstallingen_services-service.ts @@ -8,9 +8,11 @@ const FietsenstallingenServicesService: ICrudService return await prisma.fietsenstallingen_services.findMany(); }, getOne: async (fietsenstallingId: string) => { - return await prisma.fietsenstallingen_services.findFirst({ where: { - FietsenstallingID: fietsenstallingId - } }); + return await prisma.fietsenstallingen_services.findFirst({ + where: { + FietsenstallingID: fietsenstallingId + } + }); }, create: async (_data: fietsenstallingen_services): Promise => { return await prisma.fietsenstallingen_services.create({ data: _data }); diff --git a/src/components/parking/ParkingEdit.tsx b/src/components/parking/ParkingEdit.tsx index d61f599..36cef13 100644 --- a/src/components/parking/ParkingEdit.tsx +++ b/src/components/parking/ParkingEdit.tsx @@ -18,6 +18,8 @@ import { Tabs, Tab, FormHelperText, Typography } from "@mui/material"; /* Use nicely formatted items for items that can not be changed yet */ import ParkingViewTarief from "~/components/parking/ParkingViewTarief"; +import type { ServiceType } from "~/components/parking/ParkingViewServices"; + import ParkingViewAbonnementen from "~/components/parking/ParkingViewAbonnementen"; import ParkingEditCapaciteit, { type CapaciteitType } from "~/components/parking/ParkingEditCapaciteit"; import ParkingEditLocation from "~/components/parking/ParkingEditLocation"; @@ -36,8 +38,8 @@ export type ParkingEditUpdateStructure = { Postcode?: string; Plaats?: string; Coordinaten?: string; - DateCreated: Date; - DateModified: Date; + DateCreated?: Date; + DateModified?: Date; Type?: string; SiteID?: string; Beheerder?: string, @@ -48,7 +50,6 @@ export type ParkingEditUpdateStructure = { fietsenstalling_secties?: ParkingSections; // Replace with the actual type if different } -type ServiceType = { ID: string, Name: string }; type ChangedType = { ID: string, selected: boolean }; const NoClickOverlay = () => { @@ -237,11 +238,11 @@ const ParkingEdit = ({ parkingdata, onClose, onChange }: { parkingdata: ParkingD let checks: checkInfo[] = [ { type: "string", text: "invoer van de titel", value: parkingdata.Title, newvalue: newTitle }, { type: "string", text: "invoer van de straat en huisnummer", value: parkingdata.Location, newvalue: newLocation }, - { type: "string", text: "invoer van de postcode", value: parkingdata.Postcode, newvalue: newPostcode }, { type: "string", text: "invoer van de plaats", value: parkingdata.Plaats, newvalue: newPlaats }, { type: "string", text: "selectie van de gemeente", value: parkingdata.SiteID, newvalue: newSiteID }, { type: "coordinaten", text: "instellen van de locatie op de kaart", value: parkingdata.Coordinaten, newvalue: newCoordinaten }, ] + // parkingdata.Postcode is optional // FMS & ExploitantID cannot be changed for now, so no need to check those for changes if (parkingdata.FMS !== true && parkingdata.ExploitantID === null) { @@ -311,6 +312,7 @@ const ParkingEdit = ({ parkingdata, onClose, onChange }: { parkingdata: ParkingD if (!parkingdata.DateCreated) { update.DateCreated = today; } + update.DateModified = today; return update; @@ -363,6 +365,7 @@ const ParkingEdit = ({ parkingdata, onClose, onChange }: { parkingdata: ParkingD } const updateCapaciteit = async (parkingdata: ParkingDetailsType, newCapaciteit: ParkingSections) => { + console.log("update capaciteit", newCapaciteit); if (!newCapaciteit || newCapaciteit.length <= 0) return; try { @@ -541,8 +544,10 @@ const ParkingEdit = ({ parkingdata, onClose, onChange }: { parkingdata: ParkingD return; } + const method = isNew ? "POST" : "PUT"; const body = JSON.stringify(isNew ? Object.assign({}, parkingdata, update) : update); + console.log("update %s / %s", isNew, method, update); const result = await fetch( "/api/fietsenstallingen?id=" + parkingdata.ID, @@ -564,9 +569,9 @@ const ParkingEdit = ({ parkingdata, onClose, onChange }: { parkingdata: ParkingD } // If capaciteit is updated: Update capaciteit - if (newCapaciteit && newCapaciteit.length > 0) { - await updateCapaciteit(parkingdata, newCapaciteit); - } + // if (newCapaciteit && newCapaciteit.length > 0) { + // await updateCapaciteit(parkingdata, newCapaciteit); + // } let returnID: string | boolean = parkingdata.ID if (session === null) { diff --git a/src/components/parking/ParkingEditCapaciteit.tsx b/src/components/parking/ParkingEditCapaciteit.tsx index 56337ac..260cf92 100644 --- a/src/components/parking/ParkingEditCapaciteit.tsx +++ b/src/components/parking/ParkingEditCapaciteit.tsx @@ -151,7 +151,9 @@ const toggleActive = (fietsenstalling_secties: ParkingSections, fietstypeName: s const handleCapacityChange = (fietsenstalling_secties: ParkingSections, fietstypeName: any, amountstr: string): ParkingSections => { // It's mandatory to have at least 1 section - if (!fietsenstalling_secties) return fietsenstalling_secties; + if (!fietsenstalling_secties) { + return fietsenstalling_secties; + } if (!fietsenstalling_secties[0]) return fietsenstalling_secties; if (!fietsenstalling_secties[0].secties_fietstype) return fietsenstalling_secties; diff --git a/src/components/parking/ParkingView.tsx b/src/components/parking/ParkingView.tsx index c07c950..4af20ff 100644 --- a/src/components/parking/ParkingView.tsx +++ b/src/components/parking/ParkingView.tsx @@ -115,7 +115,7 @@ const ParkingView = ({ - {parkingdata.fietsenstalling_type?.name || "Onbekend"} + {parkingdata.Type || "Onbekend"} @@ -141,7 +141,7 @@ const ParkingView = ({ sm:absolute sm:bottom-1 " - onClick={(e) => { + onClick={(e: any) => { if (e) e.preventDefault(); openRoute(parkingdata.Coordinaten); }} @@ -158,7 +158,7 @@ const ParkingView = ({ - + ); }; diff --git a/src/components/parking/ParkingViewBeheerder.tsx b/src/components/parking/ParkingViewBeheerder.tsx index 62bfe08..d776fd6 100644 --- a/src/components/parking/ParkingViewBeheerder.tsx +++ b/src/components/parking/ParkingViewBeheerder.tsx @@ -4,21 +4,31 @@ import HorizontalDivider from "~/components/HorizontalDivider"; import SectionBlock from "~/components/SectionBlock"; const ParkingViewBeheerder = ({ parkingdata }: { parkingdata: any }) => { - // console.log("### ParkingViewBeheerder", parkingdata, parkingdata.Exploitant, parkingdata.Beheerder, parkingdata.BeheerderContact); - if (parkingdata.FMS === true) { - return FMS; - } else if(parkingdata?.exploitant) { + console.log("### ParkingViewBeheerder", parkingdata, parkingdata.Exploitant, parkingdata.Beheerder, parkingdata.BeheerderContact); + // if (parkingdata.FMS === true) { + // return FMS; + // } else + if (parkingdata?.exploitant) { return ( - {parkingdata.exploitant.CompanyName} + {parkingdata.exploitant.CompanyName} ) - } else if(parkingdata.BeheerderContact !== null) { + } else if (parkingdata.BeheerderContact !== null) { + let contactlink = ""; + if (parkingdata.BeheerderContact.includes("@")) { + contactlink = 'mailto:' + parkingdata.BeheerderContact + } else if (parkingdata.BeheerderContact.startsWith("http")) { + contactlink = parkingdata.BeheerderContact; + } else if (parkingdata.BeheerderContact.startsWith("www")) { + contactlink = 'https://' + parkingdata.BeheerderContact; + } + return ( - {parkingdata.Beheerder === null ? 'contact' : parkingdata.Beheerder} + {parkingdata.Beheerder === null ? parkingdata.BeheerderContact : parkingdata.Beheerder} - ); + ); } else { return null } diff --git a/src/components/parking/ParkingViewCapaciteit.tsx b/src/components/parking/ParkingViewCapaciteit.tsx index 00fcf2c..831426a 100644 --- a/src/components/parking/ParkingViewCapaciteit.tsx +++ b/src/components/parking/ParkingViewCapaciteit.tsx @@ -23,8 +23,11 @@ const calculateCapacityData = (parking: ParkingDetailsType): capacitydata | null detailed: {}, }; - if (parking === null || parking.Capacity === 0) { - capacity.unknown = true; + if (parking === null) { + capacity.unknown = true + } else if (parking.fietsenstalling_secties.length === 0) { + capacity.unknown = false; + capacity.total = parking.Capacity || 0; } else { // Get parking section (new: 1 per parking, to make it easy) parking.fietsenstalling_secties.forEach((sectie) => { @@ -41,7 +44,7 @@ const calculateCapacityData = (parking: ParkingDetailsType): capacitydata | null let detailed = capacity.detailed[name]; if (detailed !== undefined) { detailed.Toegestaan = detailed.Toegestaan || (data.Toegestaan !== null && data.Toegestaan); - detailed.Capaciteit += detailed.Capaciteit || 0; + detailed.Capaciteit += data.Capaciteit || 0; } // capacity.detailed[name].Toegestaan = capacity.detailed[name].Toegestaan || data.Toegestaan !== null && data.Toegestaan; // capacity.detailed[name].Capaciteit += data.Capaciteit || 0; @@ -63,9 +66,11 @@ const ParkingViewCapaciteit = ({ parkingdata }: { parkingdata: ParkingDetailsTyp const capacitydata = calculateCapacityData(parkingdata); // console.log("#### capacitydata", capacitydata, parkingdata); - if (capacitydata === null || capacitydata?.unknown) { - content = "Onbekend"; - } else if (capacitydata.detailed === null || Object.keys(capacitydata.detailed).length === 0) { + if (capacitydata === null || capacitydata?.unknown || (Object.keys(capacitydata.detailed).length === 0 && capacitydata.total === 0)) { + return null; + } + + if (capacitydata.detailed === null || Object.keys(capacitydata.detailed).length === 0) { content = ( <>
{parkingdata.Capacity}
@@ -98,6 +103,10 @@ const ParkingViewCapaciteit = ({ parkingdata }: { parkingdata: ParkingDetailsTyp }); } + if (content === null) { + return null; + } + return ( <> diff --git a/src/components/parking/ParkingViewServices.tsx b/src/components/parking/ParkingViewServices.tsx index 940d6a6..f4b6266 100644 --- a/src/components/parking/ParkingViewServices.tsx +++ b/src/components/parking/ParkingViewServices.tsx @@ -7,6 +7,8 @@ import { getAllServices } from "~/utils/parkings"; +export type ServiceType = { ID: string, Name: string }; + const ParkingViewServices = ({ parkingdata }: { parkingdata: any }) => { const [allServices, setAllServices] = React.useState([]); @@ -32,6 +34,12 @@ const ParkingViewServices = ({ parkingdata }: { parkingdata: any }) => { return null } + const activeServices = allServices && allServices.filter((service: any) => serviceIsActive(service)) || []; + if (activeServices.length === 0) { + // dont show services header if there are none + return null; + } + return <>
diff --git a/src/pages/api/auth/[...nextauth].ts b/src/pages/api/auth/[...nextauth].ts index 0955a3e..67888f3 100644 --- a/src/pages/api/auth/[...nextauth].ts +++ b/src/pages/api/auth/[...nextauth].ts @@ -4,7 +4,7 @@ import type { Provider } from "next-auth/providers"; import NextAuth from "next-auth"; // import { PrismaAdapter } from "@auth/prisma-adapter" -import type { NextAuthOptions, User } from "next-auth"; +import type { NextAuthOptions, RequestInternal, User } from "next-auth"; // import EmailProvider from "next-auth/providers/email" import CredentialsProvider from "next-auth/providers/credentials"; diff --git a/src/pages/index.tsx b/src/pages/index.tsx index 11ba1cc..69f8e8b 100644 --- a/src/pages/index.tsx +++ b/src/pages/index.tsx @@ -56,7 +56,6 @@ export async function getServerSideProps(context: any) { const fietsenstallingen: fietsenstallingen[] = await getParkingsFromDatabase(sites); // TODO: Don't include: EditorCreated, EditorModified - console.log("###### sites", sites) return { props: { diff --git a/src/utils/parkings.tsx b/src/utils/parkings.tsx index 91d72e3..112d935 100644 --- a/src/utils/parkings.tsx +++ b/src/utils/parkings.tsx @@ -167,6 +167,30 @@ export const getDefaultLocation = (): string => { return '52.09066,5.121317' } +// const getNewStallingSectieDefaultRecord = () => { +// const sectieId = generateRandomId(); + +// return { +// sectieId, +// externalId: "", +// titel: "sectie 1", +// omschrijving: "", +// capaciteit: 0, +// CapaciteitBromfiets: 0, +// kleur: "00FF00", +// // fietsenstallingsId: null, +// isKluis: 0, +// reserveringskostenPerDag: null, +// urlwebservice: 0, +// Reservable: 0, +// NotaVerwijssysteem: null, +// Bezetting: 0, +// isactief: 1, +// qualificatie: "NONE", +// secties_fietstype: [] +// } +// } + export const getNewStallingDefaultRecord = (ID: string, latlong?: string[] | undefined): ParkingDetailsType => { const data: ParkingDetailsType = { ID, @@ -196,6 +220,8 @@ export const getNewStallingDefaultRecord = (ID: string, latlong?: string[] | und FMS: false, Beheerder: "", BeheerderContact: "", + + // fietsenstalling_secties: [getNewStallingSectieDefaultRecord()], } return data