diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index 75c2d97..958c299 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -20,8 +20,14 @@ import { UmbrellasQuery, } from "@/lib/gql/generated/graphql"; import { client } from "@/lib/graphClient"; -import { useEffect, useState } from "react"; -import { Check, ChevronsUpDown } from "lucide-react"; +import React, { useEffect, useState } from "react"; +import { + Check, + ChevronsUpDown, + Mail, + Building2, + ArrowDownToDot, +} from "lucide-react"; import { cn } from "@/lib/utils"; import { Button } from "@/components/ui/button"; @@ -48,8 +54,12 @@ import { DialogTrigger, } from "@/components/ui/dialog"; import { Badge } from "@/components/ui/badge"; -import { Table, TableBody, TableCaption, TableCell, TableRow } from "@/components/ui/table"; -import {Progress} from "@/components/ui/progress"; +import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table"; +import { + HoverCard, + HoverCardTrigger, + HoverCardContent, +} from "@/components/ui/hover-card"; type GroupedEvents = { [week: number]: { @@ -149,14 +159,6 @@ export default function Home() { fetchData(); }, [filter, umbrella]); - const handleFilterChange = (f: string) => { - setFilter((prevSelected) => - prevSelected.includes(f) - ? prevSelected.filter((t) => t !== f) - : [...prevSelected, f] - ); - }; - const groupedEvents = groupEvents(events); const calculateEventDurationInHours = (from: string, to: string) => { @@ -219,45 +221,22 @@ export default function Home() { - - - - - - - - - - - - -
Thema - {topics.map((topic) => ( - - ))} -
Veranstaltungsart - {types.map((type) => ( - - ))} -
+ {events.length > 0 && ( +
+ t.name)} + filter={filter} + setFilter={setFilter} + /> + t.name)} + filter={filter} + setFilter={setFilter} + /> +
+ )} {loading ? (
@@ -388,6 +367,43 @@ export default function Home() { ); } +function Filter({ + title, + options, + filter, + setFilter, +}: { + title: string; + options: string[]; + filter: string[]; + setFilter: React.Dispatch>; +}) { + const handleFilterChange = (f: string) => { + setFilter((prevSelected) => + prevSelected.includes(f) + ? prevSelected.filter((t) => t !== f) + : [...prevSelected, f] + ); + }; + + return ( +
+

{title}

+
+ {options.map((o) => ( + + ))} +
+
+ ); +} + function EventDialog({ id }: { id: number }) { const [loading, setLoading] = useState(true); const [event, setEvent] = useState( @@ -434,39 +450,131 @@ function EventDialog({ id }: { id: number }) { {event?.description}
- {event?.topic.name} - {event?.type.name} + + {event?.topic.name} + + + {event?.type.name} +
- - - {event?.tutorsAssigned?.map((e) => ( - - - {e.tutors?.map((t) => ( -
-

{t.fn}

-

{t.sn}

-
- ))} -
- - -
- {e.registrations == null ? 0 : e.registrations} - / - {e.room?.capacity} -
-
- - - -
- ))} -
-
+ + + {event?.tutorsAssigned?.map((e) => { + const registrations = e.registrations ?? 0; + const capacity = e.room?.capacity ?? 1; + const utilization = (registrations / capacity) * 100; + + return ( + +
+ + {e.tutors?.map((t) => ( + + +

+ {t.fn + " " + t.sn[0] + "."} +

+
+ +

+ {t.fn + " " + t.sn} +

+ +
+
+ ))} +
+ + + +
+

+ {e.room?.building.name} +

+

+ {e.room?.name ? e.room.name : e.room?.number} +

+
+
+ +
+
+ +
+

+ {e.room?.building.name} +

+

+ {e.room?.building.street + + " " + + e.room?.building.number} +

+
+

{e.room?.building.zip},

+

{e.room?.building.city}

+
+
+
+
+ +
+

+ {e.room?.name + ? e.room.name + : e.room?.number} +

+

+ Ebene {e.room?.floor} +

+
+
+
+ +
+
+
+ +
+ {registrations} + / + {e.room?.capacity} +
+
+ + + + + ); + })} + +
)} diff --git a/frontend/components/ui/badge.tsx b/frontend/components/ui/badge.tsx index 66cba29..e4ea960 100644 --- a/frontend/components/ui/badge.tsx +++ b/frontend/components/ui/badge.tsx @@ -2,6 +2,7 @@ import * as React from "react" import { cva, type VariantProps } from "class-variance-authority" import { cn } from "@/lib/utils/tailwindUtils" +import {calculateFontColor} from "@/lib/utils/colorUtils" const badgeVariants = cva( "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", @@ -26,18 +27,17 @@ const badgeVariants = cva( export interface BadgeProps extends React.HTMLAttributes, - VariantProps {} + VariantProps { + color?: string + } -function Badge({ className, variant, ...props }: BadgeProps) { +function Badge({ color, className, variant, ...props }: BadgeProps) { return ( -
+
) } -function EventLabelBadge({ className, ...props }: BadgeProps) { - return ( -
- ) -} - -export { EventLabelBadge, Badge, badgeVariants } +export { Badge, badgeVariants } diff --git a/frontend/components/ui/hover-card.tsx b/frontend/components/ui/hover-card.tsx new file mode 100644 index 0000000..e54d91c --- /dev/null +++ b/frontend/components/ui/hover-card.tsx @@ -0,0 +1,29 @@ +"use client" + +import * as React from "react" +import * as HoverCardPrimitive from "@radix-ui/react-hover-card" + +import { cn } from "@/lib/utils" + +const HoverCard = HoverCardPrimitive.Root + +const HoverCardTrigger = HoverCardPrimitive.Trigger + +const HoverCardContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + +)) +HoverCardContent.displayName = HoverCardPrimitive.Content.displayName + +export { HoverCard, HoverCardTrigger, HoverCardContent } diff --git a/frontend/lib/gql/generated/gql.ts b/frontend/lib/gql/generated/gql.ts index 623fcb9..0e223b6 100644 --- a/frontend/lib/gql/generated/gql.ts +++ b/frontend/lib/gql/generated/gql.ts @@ -15,7 +15,7 @@ import { TypedDocumentNode as DocumentNode } from '@graphql-typed-document-node/ const documents = { "mutation addStudentApplicationForEvent($application: NewUserToEventApplication!) {\n addStudentApplicationForEvent(application: $application) {\n fn\n }\n}": types.AddStudentApplicationForEventDocument, "mutation addTutor($firstName: String!, $lastName: String!, $email: String!, $eventsAvailable: [Int!]!) {\n addTutor(\n tutor: {fn: $firstName, sn: $lastName, mail: $email}\n availability: {userMail: $email, eventID: $eventsAvailable}\n ) {\n fn\n }\n}": types.AddTutorDocument, - "query tutorFormEvents {\n events(needsTutors: true, onlyFuture: true) {\n ID\n title\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n }\n}\n\nquery plannerEvents($umbrellaID: Int!, $filter: [String!]) {\n umbrellas(id: [$umbrellaID]) {\n title\n }\n typeLabels: labels(kind: EVENT_TYPE, umbrellaID: [$umbrellaID]) {\n name\n }\n topicLabels: labels(kind: TOPIC, umbrellaID: [$umbrellaID]) {\n name\n }\n events(umbrellaID: [$umbrellaID], label: $filter) {\n ID\n title\n from\n to\n topic {\n color\n }\n }\n}\n\nquery eventCloseup($id: Int!) {\n events(id: [$id]) {\n ID\n title\n description\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n tutorsAssigned {\n tutors {\n fn\n sn\n mail\n }\n room {\n capacity\n floor\n name\n number\n building {\n name\n street\n number\n osm\n }\n }\n registrations\n }\n }\n}": types.TutorFormEventsDocument, + "query tutorFormEvents {\n events(needsTutors: true, onlyFuture: true) {\n ID\n title\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n }\n}\n\nquery plannerEvents($umbrellaID: Int!, $filter: [String!]) {\n umbrellas(id: [$umbrellaID]) {\n title\n }\n typeLabels: labels(kind: EVENT_TYPE, umbrellaID: [$umbrellaID]) {\n name\n }\n topicLabels: labels(kind: TOPIC, umbrellaID: [$umbrellaID]) {\n name\n }\n events(umbrellaID: [$umbrellaID], label: $filter) {\n ID\n title\n from\n to\n topic {\n color\n }\n }\n}\n\nquery eventCloseup($id: Int!) {\n events(id: [$id]) {\n ID\n title\n description\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n tutorsAssigned {\n tutors {\n fn\n sn\n mail\n }\n room {\n capacity\n floor\n name\n number\n building {\n name\n street\n number\n city\n zip\n osm\n }\n }\n registrations\n }\n }\n}": types.TutorFormEventsDocument, "query registrationForm($eventID: Int!) {\n forms(id: [$eventID]) {\n title\n description\n questions {\n ID\n title\n type\n required\n answers {\n ID\n title\n points\n }\n }\n }\n}": types.RegistrationFormDocument, "query umbrellas {\n umbrellas {\n ID\n title\n }\n}": types.UmbrellasDocument, }; @@ -45,7 +45,7 @@ export function graphql(source: "mutation addTutor($firstName: String!, $lastNam /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ -export function graphql(source: "query tutorFormEvents {\n events(needsTutors: true, onlyFuture: true) {\n ID\n title\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n }\n}\n\nquery plannerEvents($umbrellaID: Int!, $filter: [String!]) {\n umbrellas(id: [$umbrellaID]) {\n title\n }\n typeLabels: labels(kind: EVENT_TYPE, umbrellaID: [$umbrellaID]) {\n name\n }\n topicLabels: labels(kind: TOPIC, umbrellaID: [$umbrellaID]) {\n name\n }\n events(umbrellaID: [$umbrellaID], label: $filter) {\n ID\n title\n from\n to\n topic {\n color\n }\n }\n}\n\nquery eventCloseup($id: Int!) {\n events(id: [$id]) {\n ID\n title\n description\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n tutorsAssigned {\n tutors {\n fn\n sn\n mail\n }\n room {\n capacity\n floor\n name\n number\n building {\n name\n street\n number\n osm\n }\n }\n registrations\n }\n }\n}"): (typeof documents)["query tutorFormEvents {\n events(needsTutors: true, onlyFuture: true) {\n ID\n title\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n }\n}\n\nquery plannerEvents($umbrellaID: Int!, $filter: [String!]) {\n umbrellas(id: [$umbrellaID]) {\n title\n }\n typeLabels: labels(kind: EVENT_TYPE, umbrellaID: [$umbrellaID]) {\n name\n }\n topicLabels: labels(kind: TOPIC, umbrellaID: [$umbrellaID]) {\n name\n }\n events(umbrellaID: [$umbrellaID], label: $filter) {\n ID\n title\n from\n to\n topic {\n color\n }\n }\n}\n\nquery eventCloseup($id: Int!) {\n events(id: [$id]) {\n ID\n title\n description\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n tutorsAssigned {\n tutors {\n fn\n sn\n mail\n }\n room {\n capacity\n floor\n name\n number\n building {\n name\n street\n number\n osm\n }\n }\n registrations\n }\n }\n}"]; +export function graphql(source: "query tutorFormEvents {\n events(needsTutors: true, onlyFuture: true) {\n ID\n title\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n }\n}\n\nquery plannerEvents($umbrellaID: Int!, $filter: [String!]) {\n umbrellas(id: [$umbrellaID]) {\n title\n }\n typeLabels: labels(kind: EVENT_TYPE, umbrellaID: [$umbrellaID]) {\n name\n }\n topicLabels: labels(kind: TOPIC, umbrellaID: [$umbrellaID]) {\n name\n }\n events(umbrellaID: [$umbrellaID], label: $filter) {\n ID\n title\n from\n to\n topic {\n color\n }\n }\n}\n\nquery eventCloseup($id: Int!) {\n events(id: [$id]) {\n ID\n title\n description\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n tutorsAssigned {\n tutors {\n fn\n sn\n mail\n }\n room {\n capacity\n floor\n name\n number\n building {\n name\n street\n number\n city\n zip\n osm\n }\n }\n registrations\n }\n }\n}"): (typeof documents)["query tutorFormEvents {\n events(needsTutors: true, onlyFuture: true) {\n ID\n title\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n }\n}\n\nquery plannerEvents($umbrellaID: Int!, $filter: [String!]) {\n umbrellas(id: [$umbrellaID]) {\n title\n }\n typeLabels: labels(kind: EVENT_TYPE, umbrellaID: [$umbrellaID]) {\n name\n }\n topicLabels: labels(kind: TOPIC, umbrellaID: [$umbrellaID]) {\n name\n }\n events(umbrellaID: [$umbrellaID], label: $filter) {\n ID\n title\n from\n to\n topic {\n color\n }\n }\n}\n\nquery eventCloseup($id: Int!) {\n events(id: [$id]) {\n ID\n title\n description\n from\n to\n topic {\n name\n color\n }\n type {\n name\n color\n }\n tutorsAssigned {\n tutors {\n fn\n sn\n mail\n }\n room {\n capacity\n floor\n name\n number\n building {\n name\n street\n number\n city\n zip\n osm\n }\n }\n registrations\n }\n }\n}"]; /** * The graphql function is used to parse GraphQL queries into a document that can be used by GraphQL clients. */ diff --git a/frontend/lib/gql/generated/graphql.ts b/frontend/lib/gql/generated/graphql.ts index c3a5900..c745c11 100644 --- a/frontend/lib/gql/generated/graphql.ts +++ b/frontend/lib/gql/generated/graphql.ts @@ -569,7 +569,7 @@ export type EventCloseupQueryVariables = Exact<{ }>; -export type EventCloseupQuery = { __typename?: 'Query', events: Array<{ __typename?: 'Event', ID: number, title: string, description?: string | null, from: any, to: any, topic: { __typename?: 'Label', name: string, color?: any | null }, type: { __typename?: 'Label', name: string, color?: any | null }, tutorsAssigned?: Array<{ __typename?: 'EventTutorRoomPair', registrations?: number | null, tutors?: Array<{ __typename?: 'User', fn: string, sn: string, mail: string }> | null, room?: { __typename?: 'Room', capacity?: number | null, floor?: number | null, name?: string | null, number: string, building: { __typename?: 'Building', name: string, street: string, number: string, osm: string } } | null }> | null }> }; +export type EventCloseupQuery = { __typename?: 'Query', events: Array<{ __typename?: 'Event', ID: number, title: string, description?: string | null, from: any, to: any, topic: { __typename?: 'Label', name: string, color?: any | null }, type: { __typename?: 'Label', name: string, color?: any | null }, tutorsAssigned?: Array<{ __typename?: 'EventTutorRoomPair', registrations?: number | null, tutors?: Array<{ __typename?: 'User', fn: string, sn: string, mail: string }> | null, room?: { __typename?: 'Room', capacity?: number | null, floor?: number | null, name?: string | null, number: string, building: { __typename?: 'Building', name: string, street: string, number: string, city: string, zip: number, osm: string } } | null }> | null }> }; export type RegistrationFormQueryVariables = Exact<{ eventID: Scalars['Int']['input']; @@ -588,6 +588,6 @@ export const AddStudentApplicationForEventDocument = {"kind":"Document","definit export const AddTutorDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"mutation","name":{"kind":"Name","value":"addTutor"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"firstName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"lastName"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"email"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"eventsAvailable"}},"type":{"kind":"NonNullType","type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"addTutor"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"tutor"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"fn"},"value":{"kind":"Variable","name":{"kind":"Name","value":"firstName"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"sn"},"value":{"kind":"Variable","name":{"kind":"Name","value":"lastName"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"mail"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}}]}},{"kind":"Argument","name":{"kind":"Name","value":"availability"},"value":{"kind":"ObjectValue","fields":[{"kind":"ObjectField","name":{"kind":"Name","value":"userMail"},"value":{"kind":"Variable","name":{"kind":"Name","value":"email"}}},{"kind":"ObjectField","name":{"kind":"Name","value":"eventID"},"value":{"kind":"Variable","name":{"kind":"Name","value":"eventsAvailable"}}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fn"}}]}}]}}]} as unknown as DocumentNode; export const TutorFormEventsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"tutorFormEvents"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"events"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"needsTutors"},"value":{"kind":"BooleanValue","value":true}},{"kind":"Argument","name":{"kind":"Name","value":"onlyFuture"},"value":{"kind":"BooleanValue","value":true}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"from"}},{"kind":"Field","name":{"kind":"Name","value":"to"}},{"kind":"Field","name":{"kind":"Name","value":"topic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"type"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}}]}}]}}]} as unknown as DocumentNode; export const PlannerEventsDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"plannerEvents"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"umbrellaID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}},{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"filter"}},"type":{"kind":"ListType","type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"String"}}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"umbrellas"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"umbrellaID"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"typeLabels"},"name":{"kind":"Name","value":"labels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"kind"},"value":{"kind":"EnumValue","value":"EVENT_TYPE"}},{"kind":"Argument","name":{"kind":"Name","value":"umbrellaID"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"umbrellaID"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","alias":{"kind":"Name","value":"topicLabels"},"name":{"kind":"Name","value":"labels"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"kind"},"value":{"kind":"EnumValue","value":"TOPIC"}},{"kind":"Argument","name":{"kind":"Name","value":"umbrellaID"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"umbrellaID"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}}]}},{"kind":"Field","name":{"kind":"Name","value":"events"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"umbrellaID"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"umbrellaID"}}]}},{"kind":"Argument","name":{"kind":"Name","value":"label"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"from"}},{"kind":"Field","name":{"kind":"Name","value":"to"}},{"kind":"Field","name":{"kind":"Name","value":"topic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"color"}}]}}]}}]}}]} as unknown as DocumentNode; -export const EventCloseupDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"eventCloseup"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"events"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"id"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"from"}},{"kind":"Field","name":{"kind":"Name","value":"to"}},{"kind":"Field","name":{"kind":"Name","value":"topic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"type"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tutorsAssigned"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tutors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fn"}},{"kind":"Field","name":{"kind":"Name","value":"sn"}},{"kind":"Field","name":{"kind":"Name","value":"mail"}}]}},{"kind":"Field","name":{"kind":"Name","value":"room"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"capacity"}},{"kind":"Field","name":{"kind":"Name","value":"floor"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"building"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"street"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"osm"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"registrations"}}]}}]}}]}}]} as unknown as DocumentNode; +export const EventCloseupDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"eventCloseup"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"id"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"events"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"id"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"from"}},{"kind":"Field","name":{"kind":"Name","value":"to"}},{"kind":"Field","name":{"kind":"Name","value":"topic"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"type"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"color"}}]}},{"kind":"Field","name":{"kind":"Name","value":"tutorsAssigned"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"tutors"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"fn"}},{"kind":"Field","name":{"kind":"Name","value":"sn"}},{"kind":"Field","name":{"kind":"Name","value":"mail"}}]}},{"kind":"Field","name":{"kind":"Name","value":"room"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"capacity"}},{"kind":"Field","name":{"kind":"Name","value":"floor"}},{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"building"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"name"}},{"kind":"Field","name":{"kind":"Name","value":"street"}},{"kind":"Field","name":{"kind":"Name","value":"number"}},{"kind":"Field","name":{"kind":"Name","value":"city"}},{"kind":"Field","name":{"kind":"Name","value":"zip"}},{"kind":"Field","name":{"kind":"Name","value":"osm"}}]}}]}},{"kind":"Field","name":{"kind":"Name","value":"registrations"}}]}}]}}]}}]} as unknown as DocumentNode; export const RegistrationFormDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"registrationForm"},"variableDefinitions":[{"kind":"VariableDefinition","variable":{"kind":"Variable","name":{"kind":"Name","value":"eventID"}},"type":{"kind":"NonNullType","type":{"kind":"NamedType","name":{"kind":"Name","value":"Int"}}}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"forms"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"id"},"value":{"kind":"ListValue","values":[{"kind":"Variable","name":{"kind":"Name","value":"eventID"}}]}}],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"description"}},{"kind":"Field","name":{"kind":"Name","value":"questions"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"type"}},{"kind":"Field","name":{"kind":"Name","value":"required"}},{"kind":"Field","name":{"kind":"Name","value":"answers"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"title"}},{"kind":"Field","name":{"kind":"Name","value":"points"}}]}}]}}]}}]}}]} as unknown as DocumentNode; export const UmbrellasDocument = {"kind":"Document","definitions":[{"kind":"OperationDefinition","operation":"query","name":{"kind":"Name","value":"umbrellas"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"umbrellas"},"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"ID"}},{"kind":"Field","name":{"kind":"Name","value":"title"}}]}}]}}]} as unknown as DocumentNode; \ No newline at end of file diff --git a/frontend/lib/gql/queries/events.graphql b/frontend/lib/gql/queries/events.graphql index 005aac5..97dd852 100644 --- a/frontend/lib/gql/queries/events.graphql +++ b/frontend/lib/gql/queries/events.graphql @@ -66,6 +66,8 @@ query eventCloseup($id: Int!) { name street number + city + zip osm } } diff --git a/frontend/package-lock.json b/frontend/package-lock.json index a531be7..70aa41e 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,7 @@ "@hookform/resolvers": "^3.9.0", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-hover-card": "^1.1.2", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.1", "@radix-ui/react-progress": "^1.1.0", @@ -30,6 +31,7 @@ "react": "^18", "react-dom": "^18", "react-hook-form": "^7.52.2", + "react-leaflet": "^4.2.1", "shadcn-ui": "^0.9.2", "sharp": "^0.33.4", "sonner": "^1.5.0", @@ -3704,6 +3706,127 @@ } } }, + "node_modules/@radix-ui/react-hover-card": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.2.tgz", + "integrity": "sha512-Y5w0qGhysvmqsIy6nQxaPa6mXNKznfoGjOfBgzOjocLxr2XlSjqBMYQQL+FfyogsMuX+m8cZyQGYhJxvxUzO4w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-popper": "1.2.0", + "@radix-ui/react-portal": "1.1.2", + "@radix-ui/react-presence": "1.1.1", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-controllable-state": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-context": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", + "integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.0", + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-escape-keydown": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-portal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz", + "integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-presence": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz", + "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-id": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", @@ -4235,6 +4358,17 @@ "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", "license": "MIT" }, + "node_modules/@react-leaflet/core": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@react-leaflet/core/-/core-2.1.0.tgz", + "integrity": "sha512-Qk7Pfu8BSarKGqILj4x7bCSZ1pjuAPZ+qmRwH5S7mDS91VSbVVsJSrW4qA+GPrro8t69gFYVMWb1Zc4yFmPiVg==", + "license": "Hippocratic-2.1", + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/@repeaterjs/repeater": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/@repeaterjs/repeater/-/repeater-3.0.6.tgz", @@ -8980,6 +9114,13 @@ "node": ">=0.10" } }, + "node_modules/leaflet": { + "version": "1.9.4", + "resolved": "https://registry.npmjs.org/leaflet/-/leaflet-1.9.4.tgz", + "integrity": "sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==", + "license": "BSD-2-Clause", + "peer": true + }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -10390,6 +10531,20 @@ "dev": true, "license": "MIT" }, + "node_modules/react-leaflet": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/react-leaflet/-/react-leaflet-4.2.1.tgz", + "integrity": "sha512-p9chkvhcKrWn/H/1FFeVSqLdReGwn2qmiobOQGO3BifX+/vV/39qhY8dGqbdcPh1e6jxh/QHriLXr7a4eLFK4Q==", + "license": "Hippocratic-2.1", + "dependencies": { + "@react-leaflet/core": "^2.1.0" + }, + "peerDependencies": { + "leaflet": "^1.9.0", + "react": "^18.0.0", + "react-dom": "^18.0.0" + } + }, "node_modules/react-remove-scroll": { "version": "2.5.7", "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz", diff --git a/frontend/package.json b/frontend/package.json index 1c20f75..1057acd 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -13,6 +13,7 @@ "@hookform/resolvers": "^3.9.0", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-hover-card": "^1.1.2", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-popover": "^1.1.1", "@radix-ui/react-progress": "^1.1.0", @@ -32,6 +33,7 @@ "react": "^18", "react-dom": "^18", "react-hook-form": "^7.52.2", + "react-leaflet": "^4.2.1", "shadcn-ui": "^0.9.2", "sharp": "^0.33.4", "sonner": "^1.5.0",