diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 3892cdc..12b19f2 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -34,6 +34,7 @@ "clsx": "^2.1.1", "cmdk": "^1.0.0", "date-fns": "^3.6.0", + "date-fns-tz": "^3.1.3", "firebase": "^10.12.3", "geist": "^1.3.1", "lucide-react": "^0.376.0", @@ -3138,6 +3139,14 @@ "url": "https://github.com/sponsors/kossnocorp" } }, + "node_modules/date-fns-tz": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/date-fns-tz/-/date-fns-tz-3.1.3.tgz", + "integrity": "sha512-ZfbMu+nbzW0mEzC8VZrLiSWvUIaI3aRHeq33mTe7Y38UctKukgqPR4nTDwcwS4d64Gf8GghnVsroBuMY3eiTeA==", + "peerDependencies": { + "date-fns": "^3.0.0" + } + }, "node_modules/debug": { "version": "4.3.5", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", diff --git a/frontend/package.json b/frontend/package.json index e237835..0ea2ff4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -35,6 +35,7 @@ "clsx": "^2.1.1", "cmdk": "^1.0.0", "date-fns": "^3.6.0", + "date-fns-tz": "^3.1.3", "firebase": "^10.12.3", "geist": "^1.3.1", "lucide-react": "^0.376.0", diff --git a/frontend/prisma/schema.prisma b/frontend/prisma/schema.prisma index 58bea78..25bb00d 100644 --- a/frontend/prisma/schema.prisma +++ b/frontend/prisma/schema.prisma @@ -8,20 +8,20 @@ datasource db { } model User { - id String @id @default(cuid()) - name String? - email String @unique - emailVerified DateTime? - image String? - password String? - role UserRole @default(USER) - nummer Int? - isActive Boolean @default(true) - username String? @unique - accounts Account[] - paameldinger ArrangementPaamelding[] - bookings Booking[] - Hovedstyret Hovedstyret? + id String @id @default(cuid()) + name String? + email String @unique + emailVerified DateTime? + image String? + password String? + role UserRole @default(USER) + nummer Int? + isActive Boolean @default(true) + username String? @unique + accounts Account[] + paameldinger ArrangementPaamelding[] + bookings Booking[] + Hovedstyret Hovedstyret? lavterskelArrangements LavterskelArrangement[] } @@ -106,27 +106,13 @@ model Arrangement { paameldinger ArrangementPaamelding[] } -model LavterskelArrangement { - id String @id @default(cuid()) - navn String - sted String? - dato DateTime - type String - beskrivelse String? - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - userId String - user User @relation(fields: [userId], references: [id]) - @@index([userId]) -} - model Booking { id String @id @default(cuid()) userID String komiteID String? item BookedItem bookedAt DateTime - duration Int + duration Int? createdAt DateTime @default(now()) updatedAt DateTime @updatedAt komite komite? @relation(fields: [komiteID], references: [id]) @@ -145,6 +131,19 @@ model ArrangementPaamelding { @@unique([userID, arrangementID]) } +model LavterskelArrangement { + id String @id @default(cuid()) + navn String + sted String? + dato DateTime + type String + beskrivelse String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + userId String + user User @relation(fields: [userId], references: [id]) + @@index([userId]) +} enum BookedItem { KONTOR ONE_SOUNDBOX diff --git a/frontend/src/app/api/arrangementer/[id]/paamelding/route.ts b/frontend/src/app/api/arrangementer/[id]/paamelding/route.ts index e88df20..9114869 100644 --- a/frontend/src/app/api/arrangementer/[id]/paamelding/route.ts +++ b/frontend/src/app/api/arrangementer/[id]/paamelding/route.ts @@ -1,3 +1,4 @@ +import { getUserById } from "@/data/user"; import { db } from "@/lib/db"; import { ArrangementPaamelding, @@ -22,47 +23,60 @@ export async function GET( } } -export async function POST( - req: NextRequest, - { params }: { params: { id: string } }, -) { - try { - const arrangement = await db.arrangement.findFirst({ - where: { id: params.id }, - }); +// export async function POST( +// req: NextRequest, +// { params }: { params: { id: string } }, +// ) { +// try { +// const arrangement = await db.arrangement.findUnique({ +// where: { id: params.id }, +// }); - if (!arrangement) { - return NextResponse.json({ error: "Arrangement not found" }); - } - const parsedData = ArrangementPaameldingSchema.parse(await req.json()); - console.log(parsedData); - const arrangementPaamelding = await db.arrangementPaamelding.create({ - data: parsedData, - }); +// if (!arrangement) { +// return NextResponse.json({ error: "Arrangement not found" }); +// } +// const user = await getUserById(params.id); - await db.user.update({ - where: { id: parsedData.userID }, - data: { - paameldinger: { - connect: { id: arrangementPaamelding.id }, - }, - }, - }); +// const signup = await db.arrangementPaamelding.create({ +// data: { +// user: { +// connect: { id: user?.id }, +// }, +// arrangement: { +// connect: { id: params.id }, +// }, +// }, +// }); - await db.arrangement.update({ - where: { id: params.id }, - data: { - paameldinger: { - connect: { id: arrangementPaamelding.id }, - }, - }, - }); +// // const parsedData = ArrangementPaameldingSchema.parse(await req.json()); +// // console.log(parsedData); +// // const arrangementPaamelding = await db.arrangementPaamelding.create({ +// // data: parsedData, +// // }); - return NextResponse.json( - { message: "Paamelding successfull", arrangementPaamelding }, - { status: 201 }, - ); - } catch (error) { - return NextResponse.json({ error: error }, { status: 500 }); - } -} +// // await db.user.update({ +// // where: { id: parsedData.userID }, +// // data: { +// // paameldinger: { +// // connect: { id: arrangementPaamelding.id }, +// // }, +// // }, +// // }); + +// // await db.arrangement.update({ +// // where: { id: params.id }, +// // data: { +// // paameldinger: { +// // connect: { id: arrangementPaamelding.id }, +// // }, +// // }, +// // }); + +// return NextResponse.json( +// { message: "Paamelding successfull", arrangementPaamelding }, +// { status: 201 }, +// ); +// } catch (error) { +// return NextResponse.json({ error: error }, { status: 500 }); +// } +// } diff --git a/frontend/src/app/api/bookings/route.ts b/frontend/src/app/api/bookings/route.ts index 418df00..67829a5 100644 --- a/frontend/src/app/api/bookings/route.ts +++ b/frontend/src/app/api/bookings/route.ts @@ -2,11 +2,40 @@ import { db } from "@/lib/db"; import { NextRequest, NextResponse } from "next/server"; import { BookingSchema } from "@/schemas/booking"; -export async function GET() { - const bookings = await db.booking.findMany(); - return NextResponse.json(bookings, { status: 200 }); +export async function GET(req: NextRequest) { + const { searchParams } = new URL(req.url); + const userID = searchParams.get("userID"); + + if (userID) { + try { + const bookings = await db.booking.findMany({ + where: { + userID: userID, + }, + }); + return NextResponse.json(bookings, { status: 200 }); + } catch (error) { + return NextResponse.json( + { error: "Failed to fetch bookings" }, + { status: 500 }, + ); + } + } else { + try { + const bookings = await db.booking.findMany(); + return NextResponse.json(bookings, { status: 200 }); + } catch (error) { + return NextResponse.json( + { error: "Failed to fetch bookings" }, + { status: 500 }, + ); + } + } } +// const bookings = await db.booking.findMany(); +// return NextResponse.json(bookings, { status: 200 }); + export async function POST(req: NextRequest) { try { const parsedData = BookingSchema.parse(await req.json()); diff --git a/frontend/src/app/for_studenten/arkiv/page.tsx b/frontend/src/app/for_studenten/arkiv/page.tsx new file mode 100644 index 0000000..c9c8ec7 --- /dev/null +++ b/frontend/src/app/for_studenten/arkiv/page.tsx @@ -0,0 +1,69 @@ +"use client"; + +import MenuCard, { + MenuCardProps, +} from "@/components/ForStudenten/ui/menu-card"; +import MenuCardView from "@/components/ForStudenten/ui/menu-card-view"; +import { HandCoins, ScrollText, Archive, NotebookPen } from "lucide-react"; + +const ArkivPage = () => { + const cards: MenuCardProps[] = [ + { + title: "Vedtekter", + logo: , + description: + "Nedenfor finner du vedtektene til EMIL. Disse blir gjennomgått hvert år på budsjett- og vedtektsmøtet.", + buttonLabel: "Se vedtekter", + href: "https://www.youtube.com/watch?v=xvFZjo5PgG0&ab_channel=Duran", + }, + { + title: "Søknader", + logo: , + description: + "Her finner du alle tidligere søknader, både søknader til Leo's minnefond og Blomsterpotten.", + buttonLabel: "Se søknader", + href: "https://www.youtube.com/watch?v=xvFZjo5PgG0&ab_channel=Duran", + }, + { + title: "Rapporter", + logo: , + description: + "Se arkivet for en oversikt over gamle rapporter f. eks. diplomundersøkelsen og trivselsrapporter. ", + buttonLabel: "Se rapporter", + href: "https://www.youtube.com/watch?v=xvFZjo5PgG0&ab_channel=Duran", + }, + { + title: "Hvordan føre bilag", + logo: , + description: + "Lurer du på hvordan du skal føre et bilag? Trykk på knappen nedenfor og les guiden.", + href: "https://www.youtube.com/watch?v=xvFZjo5PgG0&ab_channel=Duran", + buttonLabel: "Hvordan føre bilag", + }, + ]; + + return ( +
+
+

+ Viktige dokumenter +

+

+ Her ligger alle viktige dokumenter som Emil publiserer hvert år. For å + se Diplomundersøkelsen må du trykke på “Se rapporter”.{" "} +

+
+ +
+ ); +}; + +export default ArkivPage; + +// } +// title="Soundbox" +// description="Emil har hele 2 soundboxer som studenter kan låne! Book en soundbox til en valgt dato eller tid. Ventetiden kan være lang så vær obs på å booke i god tid!" +// buttonLabel="Book Soundbox" +// > diff --git a/frontend/src/app/for_studenten/arrangement/page.tsx b/frontend/src/app/for_studenten/arrangement/page.tsx index fd3e61d..7e814a9 100644 --- a/frontend/src/app/for_studenten/arrangement/page.tsx +++ b/frontend/src/app/for_studenten/arrangement/page.tsx @@ -1,11 +1,13 @@ "use client"; -import ListView from "@/components/calendar/listView"; +import ListView from "@/components/event/listView"; +import Hero from "@/components/hero/hero1"; +import { Button } from "@/components/ui/button"; import { Arrangement } from "@/schemas/arrangement"; import { lavTerskelArrangement } from "@/schemas/lavterskelArrangement"; import { useEffect, useState, useRef } from "react"; -import { format } from 'date-fns'; -import { nb } from 'date-fns/locale'; +import { format } from "date-fns"; +import { nb } from "date-fns/locale"; import StickyNavbar from "@/components/navbar/stickyNavbar"; import NyStudentCard from "@/components/cards/nyStudentCard"; import SmallTransissionDarkHighligt from "@/components/hero/transissions/smallTransissionDarkHighlight"; @@ -15,7 +17,7 @@ import AarligArrangementCard from "@/components/cards/aarligArrangementCard"; import SmallTransissionSPCPC from "@/components/hero/transissions/smallTransissionSPCPC"; import SmallTransissionPCSPC from "@/components/hero/transissions/smallTransissionPCSPC"; import LavterskelArrangementForm from "@/components/forms/lavterskelarrangementform"; -import EventCalendarView from "@/components/calendar/eventCalendarview"; +import EventCalendarView from "@/components/event/eventCalendarview"; import { LavterskelArrangement } from "@prisma/client"; const ForStudentenPage = () => { @@ -74,13 +76,13 @@ const ForStudentenPage = () => { // Handle delete request from user const handleDeletion = (success: boolean) => { - if (success){ + if (success) { fetchData(); closeModal(); - } else{ - console.error("Failed to delete instance") + } else { + console.error("Failed to delete instance"); } - } + }; // Close opened date const closeModal = () => { @@ -121,7 +123,7 @@ const ForStudentenPage = () => { } }; // API call to fetch arrangements from DB -useEffect(() => { + useEffect(() => { fetchData(); }, []); @@ -416,18 +418,28 @@ useEffect(() => { {selectedDate && (
-

{ - // Format date in norwegian with capital letter - (format(selectedDate, "EEEE, d MMMM yyyy", { locale: nb })) - .split(',').map(word => word.charAt(0).toUpperCase() + word.slice(1))} +

+ { + // Format date in norwegian with capital letter + format(selectedDate, "EEEE, d MMMM yyyy", { locale: nb }) + .split(",") + .map( + (word) => + word.charAt(0).toUpperCase() + word.slice(1), + ) + }

- + {openForm ? (
) : ( @@ -478,4 +490,4 @@ useEffect(() => { ); }; -export default ForStudentenPage; \ No newline at end of file +export default ForStudentenPage; diff --git a/frontend/src/app/for_studenten/booking/bookings/page.tsx b/frontend/src/app/for_studenten/booking/bookings/page.tsx new file mode 100644 index 0000000..b809734 --- /dev/null +++ b/frontend/src/app/for_studenten/booking/bookings/page.tsx @@ -0,0 +1,21 @@ +"use client"; + +import BookingView from "@/components/ForStudenten/booking/booking-view"; + +const MineBookingerPage = () => { + return ( +
+
+

+ Soundbox +

+

+ Her er din bookingoversikt{" "} +

+
+ +
+ ); +}; + +export default MineBookingerPage; diff --git a/frontend/src/app/for_studenten/booking/page.tsx b/frontend/src/app/for_studenten/booking/page.tsx index 98483a8..64b1627 100644 --- a/frontend/src/app/for_studenten/booking/page.tsx +++ b/frontend/src/app/for_studenten/booking/page.tsx @@ -1,9 +1,10 @@ -import BackgroundMain from "@/components/ForStudenten/ui/background-main"; +"use client"; + import MenuCard, { MenuCardProps, } from "@/components/ForStudenten/ui/menu-card"; import MenuCardView from "@/components/ForStudenten/ui/menu-card-view"; -import { Speaker, Coffee, Home, FileQuestion } from "lucide-react"; +import { Speaker, Coffee, Home, FileQuestion, ListChecks } from "lucide-react"; const BookingPage = () => { const cards: MenuCardProps[] = [ @@ -13,7 +14,7 @@ const BookingPage = () => { description: "Emil har hele 2 soundboxer som studenter kan låne! Book en soundbox til en valgt dato eller tid. Ventetiden kan være lang så vær obs på å booke i god tid!", buttonLabel: "Book Soundbox", - href: "https://youtube.com", + href: "/for_studenten/booking/soundbox", }, { title: "Kontoret", @@ -32,27 +33,29 @@ const BookingPage = () => { href: "http://eshyttekom.no/", }, { - title: "Annet", - logo: , - description: "Hei hei heo", - href: "https://youtube.com", + title: "Mine bookinger", + logo: , + description: + "Her finner du en oversikt over dine aktive og tidligere bookinger.", + buttonLabel: "Se min oversikt", + href: "/for_studenten/booking/bookings", }, ]; return ( -
- -
-

Booking

-

- På Emil kan du som student booke en rekke ting til diverse - anledninger. Vi har blant annet 2 Soundboxer til disposisjon, - Emil-kontoret og nå en hytte som deles med Smørekoppen! Komiteer kan - også booke ting til arrangementer eller liknende.{" "} -

-
- -
+
+
+

+ Booking +

+

+ På Emil kan du som student booke en rekke ting til diverse + anledninger. Vi har blant annet 2 Soundboxer til disposisjon, + Emil-kontoret og nå en hytte som deles med Smørekoppen! Komiteer kan + også booke ting til arrangementer eller liknende.{" "} +

+
+
); }; diff --git a/frontend/src/app/for_studenten/booking/soundbox/page.tsx b/frontend/src/app/for_studenten/booking/soundbox/page.tsx new file mode 100644 index 0000000..98b9fbb --- /dev/null +++ b/frontend/src/app/for_studenten/booking/soundbox/page.tsx @@ -0,0 +1,23 @@ +"use client"; + +import BookingForm from "@/components/ForStudenten/booking/booking-form"; +import BookingWindow from "@/components/ForStudenten/booking/booking-window"; + +const SoundboxPage = () => { + return ( +
+
+

+ Soundbox +

+

+ Emil har 2 soundboxer som kan bookes. Se oversikten nedenfor og finn + en dato.{" "} +

+
+ +
+ ); +}; + +export default SoundboxPage; diff --git a/frontend/src/app/for_studenten/page.tsx b/frontend/src/app/for_studenten/page.tsx index 14536da..9890529 100644 --- a/frontend/src/app/for_studenten/page.tsx +++ b/frontend/src/app/for_studenten/page.tsx @@ -1,7 +1,7 @@ import Hero2 from "@/components/hero/hero2"; import TransissionIn from "@/components/hero/transissions/transissionIn"; -const forStudentenPage = () => { +const ForStudentenPage = () => { return (
Emil Logo @@ -9,4 +9,4 @@ const forStudentenPage = () => { ); }; -export default forStudentenPage; +export default ForStudentenPage; diff --git a/frontend/src/app/for_studenten/rapporter/page.tsx b/frontend/src/app/for_studenten/rapporter/page.tsx deleted file mode 100644 index b4c9c0d..0000000 --- a/frontend/src/app/for_studenten/rapporter/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import Hero from "@/components/hero/hero1"; - -const forStudentenPage = () => { - return
rapportPage
; -}; - -export default forStudentenPage; diff --git a/frontend/src/app/for_studenten/soeknader/page.tsx b/frontend/src/app/for_studenten/soeknader/page.tsx index dadefc7..b4c9c0d 100644 --- a/frontend/src/app/for_studenten/soeknader/page.tsx +++ b/frontend/src/app/for_studenten/soeknader/page.tsx @@ -1,301 +1,7 @@ import Hero from "@/components/hero/hero1"; -import NyStudentCard, { - nyStudentCardProps, -} from "@/components/cards/nyStudentCard"; -import SmallTransissionPCSPC from "@/components/hero/transissions/smallTransissionPCSPC"; -import SmallTransissionSPCPC from "@/components/hero/transissions/smallTransissionSPCPC"; -import { ArrowRight, ArrowUpRight, Coins, Link } from "lucide-react"; -import { Info } from "lucide-react"; -import { FileQuestion } from "lucide-react"; -import { Flower } from "lucide-react"; -import { Notebook } from "lucide-react"; -import { Button } from "@/components/ui/button"; -const soeknaderPage = () => { - const unnagjortCards: nyStudentCardProps[] = [ - { - title: "Leo´s minnefond", - description: ( - <> - Leos minnefond er et fond i EMIL der det blir satt inn penger hvert - år, slik at medlemmer av EMIL kan søke om midler. -
-
- Du kan søke om midler{" "} - opp til 5000 kr til ulike - formål, slik som arrangementer eller investeringer som ikke er - budsjettert. Merk at om det skal søkes om større beløp enn 5000 kr må - du søke hos Blomsterpotten - . -
-
- Du kan søke fra Leos minnefond både før og etter pengene er brukt. Det - anbefales å søke før du - bruker pengene for å ikke gå på en smell om du ikke får godkjent. - - ), - frist: "", - buttonText: "Søk om støtte her", - href: "https://docs.google.com/forms/d/e/1FAIpQLSdlweUahxa6eTMwyYTz6iLzgc7NwFx_NDKIWJ6PKbS4v7jPDQ/viewform", - icon: , - }, - - { - title: "Info", - description: ( -
- Du kan også sende en mail til styret på @styret@emilweb.no hvor du må ha med: -
    -
  • Navn
  • -
  • Komité
  • -
  • Beløp det er snakk om
  • -
  • Begrunnelse for søknad
  • -
-
- ), - frist: "", - buttonText: "", - href: "", - icon: , - }, - - { - title: "Se tidligere søknader", - description: ( - <> - Gjennom tiden har det blitt levert mange søknader til Emil. Klikk på - knappen nedenfor for å se eldre søknader - - ), - frist: "", - buttonText: "Se eldre søknader", - href: "https://www.youtube.com", - icon: , - }, - ]; - - return ( -
-
-

- Søknader -

- -

- På denne siden finner du alt av informasjon om søknader til - linjeforeningen EMIL. -

-
- - -
-
-
-
- -
-
- - -
-
-
- - - -
- -

- Blomsterpotten -

-
- -
-
-
-

Søknadskriterier

-

-

- Linjeforeningsmedlemmer og komiteer som ønsker å søke om bruk av - midler fra EMILs Blomsterpott skal skrive en søknad som inneholder - følgende momenter. Dersom ett eller flere av momentene mangler, - blir søknaden sendt tilbake til søker med forbedringsforslag og - oppfordring til å søke på nytt. -

-

-

- Søknaden skal være sendt inn minst 4 uker før midlene skal være - utbetalt!  -

-
- -
-
-

Søknaden skal bygges opp slik

-
    -
  • - Søknaden skal skrives digitalt og sendes per epost til - Blomsterpottstyret, eventuelt til annenparten dersom - Blomsterpottstyret er søker. Håndskrevne søknader tillates - ikke.  -
  • -

    -
  • - Søknaden skal føres punktvis, som vist under. Alle punktene - skal ha samme tittel som de har i søknadskriterene. -
  • -

    -
  • - Punkt 6 skal kopieres direkte inn i søknaden og skal stå - ordrett slik den står i søknadskriteriene. -
  • -

    -
  • - På slutten av søknaden skal det være en underskriftslinje. - Under denne linjen skal det stå dato og sted for søkers - underskrift. Dette er eksemplifisert i slutten av - søknadskriterene. -
  • -
-
-
-
-
- -
-
-
- -

Eksempelsøknad

-
- -

- -

1. Søkeren/komiteéns navn

- -

"Navn"

-

- -

2. Konkret hva det søkes om

- -
    -
  • - Konkret og utfyllende om hva det søkes midler til. For eksempel: - Er det innkjøp, som for eksempel lydutsyr, hytte, etc.? -
  • -
  • Akkurat hva skal kjøpes inn? Hva er bruksområdet?
  • -
  • Er det et enkeltarrangement?
  • -
  • Hvem er det for? Komité, klassetrinn, jenter/gutter?
  • -
  • Hvor mange deltakere vil det være?
  • -
  • Er det for et faglig opplegg?
  • -
  • Hvem er det for?
  • -
- -
    -
  • Hvordan vil opplegget være?
  • -
- -

- -

- 3. Hvordan prosjektet vil gangne linjeforeningens medlemmer -

- -

- Blomsterpottens midler er ment å komme hele linjeforeningen til - gode. Hvordan vil ditt prosjekt gjøre dette? -

- -

- Skriv utfyllende og svar på følgende spørsmål i søknadsteksten: -

- -
    -
  • - Er dette noe som er til nytte i fremtiden, eller er det et - prosjekt som gagner medlemmene det året pengene blir brukt?{" "} -
  • -
- -
    -
  • - Er det prioritert slik at alle linjeforeningsmedlemmer får nytte - av det, eller kommer det hovedsaklig enkelte klassetrinn eller - komiteer til gode? -
  • -
- -
    -
  • - Hvorfor mener du dette er noe som vil bidra positivt til - linjeforeningen? -
  • -
- -

- -

- 4. Er det sendt i søknad til Sit eller andre relevante - organisasjoner? -

- -

- Linjeforeningskomiteer får ofte innvilget støtte fra andre aktører - enn linjeforeningen. Har du/dere gjort en innsats for å skaffe - midler på andre måter? -

- -

- Er det noe du lurer på angående dette, ta kontakt med Futen. -

- -

- -

5. Budsjett

- -

- Det skal legges ved et budsjett hvor det står konkret hva pengene - skal bli brukt på, hvor mye det vil koste og hvor man har funnet - prisene. Er det uklart hvordan et budsjett lages og hvordan det skal - se ut, ta kontakt med Futen. -

- -

- -

6. Ansvar for bruk av midler

- -

- “Undertegnede har lest, forstått og akseptert retningslinjene til - EMILs Blomsterpott og har ansvaret for at midlene som er innvilget - og utbetalt brukes i henhold til det godkjente budsjettet. Ved - manglende dokumentasjon på dette, eller dersom midlene er brukt til - noe annet, står undertegnede ansvarlig for å tilbakebetale det - misbrukte beløpet.” -

- -

-

- -

Emil Emilsen

- -

--------------------------

- -

Emil Emilsen

- -

17/03/2022, Trondheim

-
-
-
- ); +const forStudentenPage = () => { + return
rapportPage
; }; -export default soeknaderPage; +export default forStudentenPage; diff --git a/frontend/src/app/for_studenten/viser/page.tsx b/frontend/src/app/for_studenten/viser/page.tsx deleted file mode 100644 index 760965a..0000000 --- a/frontend/src/app/for_studenten/viser/page.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import Hero from "@/components/hero/hero1"; - -const forStudentenPage = () => { - return
viserPage
; -}; - -export default forStudentenPage; diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index 7da45d1..cbc85ee 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -10,7 +10,7 @@ import Link from "next/link"; import { ArrowRight } from "lucide-react"; import Image from "next/image"; import { Calendar } from "@/components/ui/calendar"; -import ListView from "@/components/calendar/listView"; +import ListView from "@/components/event/listView"; import BigCard from "@/components/landing/bigCard"; import { cardData } from "@/static/landingInfo"; import VideoPlayer from "@/components/landing/video-player"; @@ -50,7 +50,7 @@ const HomePage = () => {
- @@ -59,30 +59,30 @@ const HomePage = () => {
-
-
- Hva skjer på Emil? -
-
-
- {loading ? ( -
- ) : ( - - )} +
+
+ Hva skjer på Emil?
+
+
+ {loading ? ( +
+ ) : ( + + )}
-
- -
-
-
- Søk Emil da vel! -
- +
+
+ +
+
+
+ Søk Emil da vel!
+
- {/* +
+ {/* */}
diff --git a/frontend/src/components/ForStudenten/booking/booking-card.tsx b/frontend/src/components/ForStudenten/booking/booking-card.tsx new file mode 100644 index 0000000..6cfaf04 --- /dev/null +++ b/frontend/src/components/ForStudenten/booking/booking-card.tsx @@ -0,0 +1,170 @@ +"use client"; + +import * as z from "zod"; + +import { useState, useTransition } from "react"; +import { useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { format } from "date-fns"; +import { nb } from "date-fns/locale"; + +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; + +import { CardWrapper } from "@/components/auth/card-wrapper"; +import { Button } from "@/components/ui/button"; +import { FormError } from "@/components/form-error"; +import { FormSuccess } from "@/components/form-success"; +import { toast } from "@/components/ui/use-toast"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { CalendarIcon } from "lucide-react"; +import { cn } from "@/lib/utils"; +import { Calendar } from "@/components/ui/calendar"; +import { useCurrentUser } from "@/hooks/use-current-user"; +import { checkBooking } from "@/utils/actions/checkBooking"; +import { bookingFormSchema } from "@/schemas/booking"; + +export const BookingCard = () => { + const user = useCurrentUser(); + const [error, setError] = useState(""); + const [success, setSuccess] = useState(""); + + const [isPending, startTransition] = useTransition(); + + const form = useForm>({ + resolver: zodResolver(bookingFormSchema), + defaultValues: { + bookingDate: undefined, + }, + }); + + const onSubmit = (values: z.infer) => { + setError(""); + setSuccess(""); + + startTransition(() => { + checkBooking(values, user?.id || "").then((data) => { + setError(data?.error); + setSuccess(data?.success); + toast({ + title: "Booking gjennomført!:", + description: ( +
+              
+                {JSON.stringify(values, null, 2)}
+              
+            
+ ), + }); + }); + }); + }; + + return ( + +
+ +
+ ( + + Antall + + + + + )} + /> + ( + + Dato + + + + + + + + + date < new Date() || date < new Date("1900-01-01") + } + initialFocus + /> + + + + Velg en dato for din booking. + + + + )} + /> +
+ + + + + +
+ ); +}; diff --git a/frontend/src/components/ForStudenten/booking/booking-form.tsx b/frontend/src/components/ForStudenten/booking/booking-form.tsx new file mode 100644 index 0000000..2529777 --- /dev/null +++ b/frontend/src/components/ForStudenten/booking/booking-form.tsx @@ -0,0 +1,139 @@ +"use client"; + +import * as z from "zod"; +import { useForm, useFormState } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Button } from "@/components/ui/button"; +import { CalendarIcon } from "lucide-react"; +import { format } from "date-fns"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { Calendar } from "@/components/ui/calendar"; +import { cn } from "@/lib/utils"; + +const formSchema = z.object({ + item: z.string({ + required_error: "Ikke et gyldig antall.", + }), + bookingDate: z.date({ + required_error: "En gyldig dato er påkrevd.", + }), +}); + +const BookingForm = () => { + const form = useForm>({ + resolver: zodResolver(formSchema), + defaultValues: { + bookingDate: undefined, + }, + }); + + const { isSubmitting } = useFormState({ control: form.control }); + + const onSubmit = () => {}; + + return ( +
+
+ + ( + + Antall + + + + + )} + /> + ( + + Dato + + + + + + + + + date > new Date() || date < new Date("1900-01-01") + } + initialFocus + /> + + + + Velg en dato for din booking. + + + + )} + /> + + + +
+ ); +}; + +export default BookingForm; diff --git a/frontend/src/components/ForStudenten/booking/booking-view.tsx b/frontend/src/components/ForStudenten/booking/booking-view.tsx new file mode 100644 index 0000000..5e7c688 --- /dev/null +++ b/frontend/src/components/ForStudenten/booking/booking-view.tsx @@ -0,0 +1,183 @@ +"use client"; + +import { nb } from "date-fns/locale"; +import { format } from "date-fns"; +import { useEffect, useState } from "react"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { Trash2 } from "lucide-react"; +import { useCurrentUser } from "@/hooks/use-current-user"; +import { Booking } from "@/schemas/booking"; + +// Function to get a date string for a future date +const getFutureDate = (daysFromNow: number) => { + const date = new Date(); + date.setDate(date.getDate() + daysFromNow); + return date.toISOString().split("T")[0]; +}; + +const getDate = (date: Date) => { + const newDate = new Date(date); + return format(newDate, "PPP", { locale: nb }); +}; + +export default function BookingView() { + const user = useCurrentUser(); + const [bookings, setBookings] = useState([]); + const [isModalOpen, setIsModalOpen] = useState(false); + const [bookingToCancel, setBookingToCancel] = useState(null); + + useEffect(() => { + const fetchBookings = async () => { + try { + const response = await fetch(`/api/bookings?userID=${user?.id}`); + const data = await response.json(); + setBookings(data); + } catch (error) { + console.error("Failed to fetch bookings:", error); + } + }; + + fetchBookings(); + }, [user]); + + const handleCancelClick = (booking: Booking) => { + setBookingToCancel(booking); + setIsModalOpen(true); + }; + + const handleConfirmCancel = async () => { + if (bookingToCancel) { + try { + const response = await fetch(`/api/bookings/${bookingToCancel.id}`, { + method: "DELETE", + }); + + if (response.ok) { + setBookings( + bookings.filter((booking) => booking.id !== bookingToCancel.id), + ); + } else { + console.log("Failed to delete booking"); + } + } catch (error) {} + } + setIsModalOpen(false); + setBookingToCancel(null); + }; + + const isBookingActive = (date: Date) => { + const bookingDate = new Date(date); + const currentDate = new Date(); + return bookingDate >= currentDate; + }; + + return ( + + + Bookinger + Dine tidligere bookinger. + + + + + + Type + Dato + Antall + Action + + + + {bookings.map((booking) => { + const isActive = isBookingActive(booking.bookedAt); + return ( + + Soundbox + {getDate(booking.bookedAt)} + + {booking.item === "ONE_SOUNDBOX" ? 1 : 2} + + + {isActive ? ( + + ) : ( + N/A + )} + + + ); + })} + +
+
+ + + + + Bekreft kansellering + + Er du sikker på om du vil slette denne bookingen? + + + {bookingToCancel && ( +
+

+ Type: Soundbox +

+

+ Dato: {" "} + {getDate(bookingToCancel.bookedAt)} +

+

+ Antall: + {bookingToCancel.item === "ONE_SOUNDBOX" ? 1 : 2} +

+
+ )} + + + + +
+
+
+ ); +} diff --git a/frontend/src/components/ForStudenten/booking/booking-window.tsx b/frontend/src/components/ForStudenten/booking/booking-window.tsx new file mode 100644 index 0000000..d44220a --- /dev/null +++ b/frontend/src/components/ForStudenten/booking/booking-window.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { Label } from "@radix-ui/react-label"; +import { DatePickerForm } from "../ui/date-picker-form"; +import { ReactNode } from "react"; +import BookingForm from "@/components/ForStudenten/booking/booking-form"; +import { BookingCard } from "./booking-card"; + +const BookingWindow = () => { + return ( +
+ {/* */} + +
+ ); +}; + +export default BookingWindow; diff --git a/frontend/src/components/ForStudenten/ui/date-picker-form.tsx b/frontend/src/components/ForStudenten/ui/date-picker-form.tsx new file mode 100644 index 0000000..06b9b37 --- /dev/null +++ b/frontend/src/components/ForStudenten/ui/date-picker-form.tsx @@ -0,0 +1,146 @@ +"use client"; + +import { zodResolver } from "@hookform/resolvers/zod"; +import { format } from "date-fns"; +import { CalendarIcon } from "lucide-react"; +import { useForm } from "react-hook-form"; +import { z } from "zod"; + +import { cn } from "@/lib/utils"; +import { Button } from "@/components/ui/button"; +import { Calendar } from "@/components/ui/calendar"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "@/components/ui/popover"; +import { toast } from "@/components/ui/use-toast"; +import { Label } from "@/components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +const FormSchema = z.object({ + bookingDate: z.date({ + required_error: "En gyldig dato er påkrevd.", + }), + antall: z.string({ + required_error: "Ikke et gyldig antall", + }), +}); + +export function DatePickerForm() { + const form = useForm>({ + resolver: zodResolver(FormSchema), + }); + + function onSubmit(data: z.infer) { + toast({ + title: "Booking gjennomført!", + description: ( +
+          {JSON.stringify(data, null, 2)}
+        
+ ), + }); + } + + return ( + <> +
+ + ( + + Antall + + + Velg antall soundboxer du vil booke{" "} + + + + )} + /> + ( + + Dato + + + + + + + + + date > new Date() || date < new Date("1900-01-01") + } + initialFocus + /> + + + + Velg en dato for bookingen. + + + + )} + /> + + + + + ); +} diff --git a/frontend/src/components/ForStudenten/ui/menu-card.tsx b/frontend/src/components/ForStudenten/ui/menu-card.tsx index dd70db0..3761fd3 100644 --- a/frontend/src/components/ForStudenten/ui/menu-card.tsx +++ b/frontend/src/components/ForStudenten/ui/menu-card.tsx @@ -32,27 +32,40 @@ const MenuCard = ({ href, }: MenuCardProps) => { const router = useRouter(); + const isInternalLink = href.startsWith("/"); return ( - - + + {logo} - + {title} - {description} + + {description} + - {buttonLabel && ( - - )} + {buttonLabel && + (isInternalLink ? ( + + + + ) : ( + + ))} ); diff --git a/frontend/src/components/auth/login-form.tsx b/frontend/src/components/auth/login-form.tsx index f28efd0..95502a5 100644 --- a/frontend/src/components/auth/login-form.tsx +++ b/frontend/src/components/auth/login-form.tsx @@ -22,11 +22,13 @@ import { FormError } from "../form-error"; import { FormSuccess } from "../form-success"; import { login } from "@/utils/login"; import Link from "next/link"; +import { useRouter } from "next/navigation"; +import { DEFAULT_LOGIN_REDIRECT } from "@/routes"; export const LoginForm = () => { + const router = useRouter(); const [error, setError] = useState(""); const [success, setSuccess] = useState(""); - const [isPending, startTransition] = useTransition(); const form = useForm>({ @@ -46,6 +48,7 @@ export const LoginForm = () => { // This checks if 'data' is truthy after the 'login' call setError(data?.error); setSuccess(data?.success); + router.push(DEFAULT_LOGIN_REDIRECT); }); }); }; diff --git a/frontend/src/components/calendar/event.tsx b/frontend/src/components/event/event.tsx similarity index 100% rename from frontend/src/components/calendar/event.tsx rename to frontend/src/components/event/event.tsx diff --git a/frontend/src/components/calendar/eventCalendarview.tsx b/frontend/src/components/event/eventCalendarview.tsx similarity index 100% rename from frontend/src/components/calendar/eventCalendarview.tsx rename to frontend/src/components/event/eventCalendarview.tsx diff --git a/frontend/src/components/calendar/lavterskelkalender.tsx b/frontend/src/components/event/lavterskelkalender.tsx similarity index 100% rename from frontend/src/components/calendar/lavterskelkalender.tsx rename to frontend/src/components/event/lavterskelkalender.tsx diff --git a/frontend/src/components/calendar/listView.tsx b/frontend/src/components/event/listView.tsx similarity index 66% rename from frontend/src/components/calendar/listView.tsx rename to frontend/src/components/event/listView.tsx index 84826f5..81ba648 100644 --- a/frontend/src/components/calendar/listView.tsx +++ b/frontend/src/components/event/listView.tsx @@ -1,5 +1,5 @@ "use client"; -import Event from "@/components/calendar/event"; +import Event from "@/components/event/event"; import { Arrangement } from "@/schemas/arrangement"; interface ListViewProps { @@ -8,7 +8,7 @@ interface ListViewProps { const ListView = ({ events }: ListViewProps) => { return ( -
+
{events.map((event, index) => ( ))} diff --git a/frontend/src/components/hero/for_studenten_banner.tsx b/frontend/src/components/hero/for_studenten_banner.tsx index a6545c1..700f987 100644 --- a/frontend/src/components/hero/for_studenten_banner.tsx +++ b/frontend/src/components/hero/for_studenten_banner.tsx @@ -3,6 +3,7 @@ import { FC } from "react"; import { useState } from "react"; import Link from "next/link"; +import { HandCoins, FileText } from "lucide-react"; const Banner: FC = () => { const [activeLink, setActiveLink] = useState(""); @@ -152,62 +153,13 @@ const Banner: FC = () => {
handleSetSelectedLink("rapporter")} + href="/for_studenten/arkiv" + className={`text-zinc-400 link-hover-effect ${activeLink === "arkiv" ? "selected-state" : ""}`} + onClick={() => handleSetSelectedLink("arkiv")} >
- - - - - - - - - -

Rapporter

+ +

Arkiv

@@ -263,46 +215,13 @@ const Banner: FC = () => {
handleSetSelectedLink("viser")} + href="/for_studenten/soeknader" + className={`text-zinc-400 link-hover-effect ${activeLink === "soeknader" ? "selected-state" : ""}`} + onClick={() => handleSetSelectedLink("soeknader")} >
- - - - - - - -

Viser

+ +

Søknader

{ return ( -
+