diff --git a/.env.example b/.env.example index 41d13b5..1feabf8 100644 --- a/.env.example +++ b/.env.example @@ -6,23 +6,4 @@ AUTH_AUTHENTIK_ID= AUTH_AUTHENTIK_SECRET= AUTH_AUTHENTIK_ISSUER= -AUTH_DISCORD_ID= -AUTH_DISCORD_SECRET= - -AUTH_FACEBOOK_ID= -AUTH_FACEBOOK_SECRET= - -AUTH_GITHUB_ID= -AUTH_GITHUB_SECRET= - -AUTH_GITLAB_ID= -AUTH_GITLAB_SECRET= - -AUTH_GOOGLE_ID= -AUTH_GOOGLE_SECRET= - -AUTH_KEYCLOAK_ID= -AUTH_KEYCLOAK_SECRET= -AUTH_KEYCLOAK_ISSUER= - NEXT_PUBLIC_APP_URL= diff --git a/README.md b/README.md index 24990d2..56fa41d 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,5 @@ # MeetUP -## Table of contents - -- [Description](#description) -- [Project Status](#project-status) -- [Features](#features) - - [Implemented Features](#implemented-features) - - [Planned Features](#planned-features-roadmap) -- [Technologies Used](#technologies-used) -- [Development environment setup](#development-environment-setup) - - [Without Docker](#without-docker) - - [With Docker](#with-docker) -- [Production Deployment using Docker](#production-deployment-using-docker) -- [Contributing](#contributing) - ## Description MeetUP is a social calendar application designed to make coordinating schedules with friends seamless and intuitive. It was created because it can be a hassle coordinating meetings between multiple friends and across different friend groups. MeetUP aims to simplify the process of finding mutual availability without endless back-and-forth messaging. @@ -26,54 +12,52 @@ MeetUP is a social calendar application designed to make coordinating schedules ### Implemented Features -- Event creation, deletion, and editing -- SSO and credentials login and signup -- Participant invitation and status -- Calendar of your own events -- Calendar of other users' availability (only in event creation form) +- Core infrastructure setup in progress. No user-facing features are implemented yet. ### Planned Features (Roadmap) -- Friendships -- Group calendars -- iCal import and export -- Notifications (in-app and external/mail) +- **Friendships:** Connect with friends to share calendars. +- **Group Calendars:** Create and manage shared calendars for groups. +- **iCal Import:** Import existing calendars from iCalendar (.ics) files. +- **iCal Export:** Export personal or shared calendars in iCalendar (.ics) format. +- **Email Notifications:** Receive email alerts for event bookings, reminders, and updates. +- **View Blocked Slots:** See when friends are busy without revealing event details. +- **Book Timeslots:** Request and confirm meeting times in friends' available slots. +- **SSO Compatibility:** Planning for Single Sign-On integration. ## Technologies Used This project is built with a modern tech stack: - **Package Manager:** [Yarn](https://yarnpkg.com/) -- **Framework:** [Next.js](https://nextjs.org/) -- **Language:** [TypeScript](https://www.typescriptlang.org/) -- **ORM:** [Prisma](https://www.prisma.io/) -- **Authentication:** [Auth.js](https://authjs.dev/) -- **Styling:** [Tailwind CSS](https://tailwindcss.com/) -- **UI Components:** [shadcn/ui](https://ui.shadcn.com/) -- **Containerization:** [Docker](https://www.docker.com/) -- **API Docs:** [Swagger](https://swagger.io/) -- **React hook API client:** [orval](https://orval.dev/) +- **Framework:** [Next.js](https://nextjs.org/) - React framework for server-side rendering and static site generation. +- **Language:** [TypeScript](https://www.typescriptlang.org/) - Superset of JavaScript that adds static typing. +- **ORM:** [Prisma](https://www.prisma.io/) - Next-generation ORM for Node.js and TypeScript. +- **Authentication:** [Auth.js](https://authjs.dev/) (formerly NextAuth.js) - Authentication for Next.js. +- **Styling:** [Tailwind CSS](https://tailwindcss.com/) - A utility-first CSS framework. +- **UI Components:** [shadcn/ui](https://ui.shadcn.com/) - Re-usable components built using Radix UI and Tailwind CSS. +- **Containerization:** [Docker](https://www.docker.com/) (for planned self-hosting option) +- _(You can also list related tools here, e.g., ESLint, Prettier, testing libraries if you plan to use them)_ -## Development environment setup - -### Without Docker +## Getting Started **Prerequisites:** -- **Node.js**: version 22+ -- **corepack**: enable using `corepack enable` +- Node.js: Version is continually upgraded. It's recommended to use the latest LTS or a recent stable version. (Check `.nvmrc` if available). +- Yarn: Version is continually upgraded. (Check `package.json` engines field if specified). +- A database supported by Prisma (e.g., PostgreSQL, MySQL, SQLite). Ensure your database server is running. -**Installation & Running:** +**Installation & Running Locally:** 1. **Clone the repository:** - - Using HTTPS: - ```bash - git clone https://git.dominikstahl.dev/DHBW-WE/MeetUp.git - ``` - - Or using SSH: + - Using SSH: ```bash git clone ssh://git@git.dominikstahl.dev/DHBW-WE/MeetUp.git ``` + - Or using HTTPS (recommended for most users): + ```bash + git clone [https://git.dominikstahl.dev/DHBW-WE/MeetUp.git](https://git.dominikstahl.dev/DHBW-WE/MeetUp.git) + ``` ```bash cd MeetUp ``` @@ -82,44 +66,53 @@ This project is built with a modern tech stack: yarn install ``` 3. **Set up environment variables:** - You will need to create an `AUTH_SECRET`. You can generate one using the following command: + - You will need to create an `AUTH_SECRET`. You can generate one using the following command: + ```bash + npx auth secret + ``` + - Copy the `.env.example` file (if it exists) to `.env.local`. If not, create `.env.local`. + ```bash + # If .env.example exists: + cp .env.example .env.local + # Otherwise, create .env.local and add the following: + ``` + - Ensure the following environment variables are set in your `.env.local` file. Adjust `DATABASE_URL` for your specific database provider and credentials. - ```bash - npx auth secret - ``` + ```env + # Database Connection String (Prisma) + # Example for PostgreSQL: DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE?schema=public" + DATABASE_URL="your_database_connection_string" - Add any additional needed environment variables into the generated `.env.local` file. - Example variables can be found in the `.env.example` file. The following variables are required: + # Generated with npx auth secret + AUTH_SECRET="your_generated_auth_secret" - ```env - # Generated with npx auth secret - AUTH_SECRET="your_generated_auth_secret" + # Authentik SSO Variables (if you are using this provider) + AUTH_AUTHENTIK_ID= + AUTH_AUTHENTIK_SECRET= + AUTH_AUTHENTIK_ISSUER= - DATABASE_URL="file:./dev.db" - ``` + # Base URL of your application + NEXT_PUBLIC_APP_URL="http://localhost:3000" + ``` 4. **Apply database migrations (Prisma):** - Set up/update the database with these commands: + - Ensure your Prisma schema (`prisma/schema.prisma`) is defined. + - Setup/update the database with these commands: + ```bash + yarn prisma:generate + ``` + ```bash + yarn prisma:db:push + ``` + - Run the following command to apply migrations and generate Prisma Client: + ```bash + npx prisma migrate dev + # You might be prompted to name your first migration. + ``` - ```bash - yarn prisma:generate - ``` + Tipp: You can open the prisma database UI with `yarn prisma:studio` - ```bash - yarn prisma:db:push - ``` - - Tip: You can open the Prisma database UI with `yarn prisma:studio` - -5. **Generate needed TypeScript files:** - Generate the `swagger.json` file and the API client using: - - ```bash - yarn swagger:generate - yarn orval:generate - ``` - -6. **Run the development server:** +5. **Run the development server:** ```bash yarn dev @@ -127,64 +120,56 @@ This project is built with a modern tech stack: Open [http://localhost:3000](http://localhost:3000) in your browser to see the application. -### With Docker + The test user for the application is: -**Prerequisites:** + ```bash + email: test@example.com + password: password + ``` -- **Docker** -- **Docker Compose** +**Docker Development Environment:** -**Running:** +- The docker development environment can be started with the following command: ```bash yarn dev_container ``` -## Production Deployment using Docker +**Self-Hosting with Docker (Planned):** -The application can be hosted using the [Docker container](https://git.dominikstahl.dev/DHBW-WE/-/packages/container/meetup/main). - -There is an example Docker Compose file provided [here](https://git.dominikstahl.dev/DHBW-WE/MeetUp/src/branch/main/docker-compose.yml). +- A Docker image and `docker-compose.yml` file will be provided in the future to allow for easy self-hosting of the MeetUP application. This setup will also include database services. Instructions will be updated here once available. ## Contributing -Contributions are welcome! If you'd like to contribute, please follow these steps: +Contributions are welcome! If you'd like to contribute, please: 1. Fork the repository. -2. Create a new branch: - - ```bash - git checkout -b /- - ``` - - - Example: `feat/42-add_login_form` - +2. Create a new branch (`git checkout -b /-action_name`). 3. Make your changes. -4. Commit your changes using [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/): - - The commit message should be structured as follows: - - ``` - (optional scope): - ``` - - - Example: `feat(auth): add login form` - - Example: `fix(events): correct event time calculation` - - Example: `docs: update README with setup instructions` - - - Used types: - - `feat`: Feature added - - `fix`: Bug fix - - `test`: Add or modify tests - - `docs`: Documentation changes - - `chore`: Changes to non-code files (workflows, lock files, etc.) - - `refactor`: Code refactoring without changing functionality - - `style`: Code style changes (formatting, etc.) - - `revert`: Revert a previous commit - -5. Push to your branch: - ```bash - git push origin /- - ``` +4. Commit your changes (`git commit -m ': add some feature'`). +5. Push to the branch (`git push origin /-action_name`). 6. Open a Pull Request against the `main` branch. -Please ensure your code adheres to the project's coding standards (run `yarn format`) and that any database schema changes are accompanied by a Prisma migration. +Possible actions are: + + *feat* -> Feature added + *fix* -> Fixed a bug + *test* -> Modified or added tests + *docs* -> Modified documentation + *chore* -> changes to non code files (workflows, lock files, ...) + *refactor* -> rewritten code without changing functionality + *style* -> code style (yarn format) + *revert* -> reverts a previous commit + +Please ensure your code adheres to the project's coding standards (e.g., run linters/formatters if configured) and that any database schema changes are accompanied by a Prisma migration. + +--- + +**(Optional Sections You Might Want to Add Later):** + +- **Screenshots/Demo:** (Once you have UI to show) +- **API Reference:** (If you plan to expose an API) +- **Detailed Deployment Guides:** (For various platforms beyond Docker) +- **License:** (e.g., MIT, GPL - Important for open source projects) +- **Contact:** (How to get in touch with the maintainers) +- **Acknowledgements:** (Credit to any libraries, inspirations, or contributors) diff --git a/package.json b/package.json index e5e8c9e..9a33a39 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,6 @@ "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", "@hookform/resolvers": "^5.0.1", - "@marko19907/string-to-color": "^1.0.0", "@prisma/client": "^6.9.0", "@radix-ui/react-avatar": "^1.1.10", "@radix-ui/react-collapsible": "^1.1.11", @@ -52,7 +51,7 @@ "clsx": "^2.1.1", "cmdk": "^1.1.1", "date-fns": "^4.1.0", - "lucide-react": "^0.525.0", + "lucide-react": "^0.523.0", "next": "15.3.4", "next-auth": "^5.0.0-beta.25", "next-swagger-doc": "^0.4.1", @@ -73,7 +72,7 @@ "devDependencies": { "@eslint/eslintrc": "3.3.1", "@tailwindcss/postcss": "4.1.11", - "@types/node": "22.15.34", + "@types/node": "22.15.33", "@types/react": "19.1.8", "@types/react-big-calendar": "1.16.2", "@types/react-dom": "19.1.6", @@ -81,7 +80,7 @@ "@types/webpack-env": "1.18.8", "cypress": "14.5.0", "dotenv-cli": "8.0.0", - "eslint": "9.30.0", + "eslint": "9.29.0", "eslint-config-next": "15.3.4", "eslint-config-prettier": "10.1.5", "orval": "7.10.0", diff --git a/src/app/(main)/events/[eventID]/page.tsx b/src/app/(main)/events/[eventID]/page.tsx index c11e028..bfc390d 100644 --- a/src/app/(main)/events/[eventID]/page.tsx +++ b/src/app/(main)/events/[eventID]/page.tsx @@ -40,12 +40,14 @@ export default function ShowEvent() { if (isLoading || userLoading) { return ( -
Loading...
+
+ Loading... +
); } if (error || !eventData?.data?.event) { return ( -
+
Error loading event.
); @@ -68,8 +70,8 @@ export default function ShowEvent() { }; return ( -
- +
+ diff --git a/src/app/(main)/events/edit/[eventID]/page.tsx b/src/app/(main)/events/edit/[eventID]/page.tsx index b099f10..42c6e8b 100644 --- a/src/app/(main)/events/edit/[eventID]/page.tsx +++ b/src/app/(main)/events/edit/[eventID]/page.tsx @@ -9,14 +9,16 @@ export default async function Page({ }) { const eventID = (await params).eventID; return ( - - +
+ + - - - - - - + + + + + + +
); } diff --git a/src/app/(main)/events/new/page.tsx b/src/app/(main)/events/new/page.tsx index 1dc1bde..2db7ae2 100644 --- a/src/app/(main)/events/new/page.tsx +++ b/src/app/(main)/events/new/page.tsx @@ -1,10 +1,12 @@ +import { ThemePicker } from '@/components/misc/theme-picker'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; import EventForm from '@/components/forms/event-form'; import { Suspense } from 'react'; export default function NewEvent() { return ( -
+
+
{}
diff --git a/src/app/(main)/events/page.tsx b/src/app/(main)/events/page.tsx index bcd1e57..f0391dd 100644 --- a/src/app/(main)/events/page.tsx +++ b/src/app/(main)/events/page.tsx @@ -17,7 +17,7 @@ export default function Events() { const events = eventsData?.data?.events || []; return ( -
+
{/* Heading */}

My Events diff --git a/src/app/(main)/home/page.tsx b/src/app/(main)/home/page.tsx index 735f19b..1cf8a90 100644 --- a/src/app/(main)/home/page.tsx +++ b/src/app/(main)/home/page.tsx @@ -4,29 +4,14 @@ import Calendar from '@/components/calendar'; import { useGetApiUserMe } from '@/generated/api/user/user'; export default function Home() { - const { data, isLoading } = useGetApiUserMe(); + const { data } = useGetApiUserMe(); return ( -
-
-

- Welcome, - - {isLoading - ? 'Loading...' - : data?.data.user?.first_name || - data?.data.user?.name || - 'Unknown User'}{' '} - 👋 - -

-
-
- -
+
+
); } diff --git a/src/app/api/search/user/route.ts b/src/app/api/search/user/route.ts index 0bcb6cf..a8b6414 100644 --- a/src/app/api/search/user/route.ts +++ b/src/app/api/search/user/route.ts @@ -19,7 +19,7 @@ export const GET = auth(async function GET(req) { authCheck.metadata, ); - const dataRaw = Object.fromEntries(req.nextUrl.searchParams); + const dataRaw = Object.fromEntries(new URL(req.url).searchParams); const data = await searchUserSchema.safeParseAsync(dataRaw); if (!data.success) return returnZodTypeCheckedResponse( diff --git a/src/app/api/calendar/route.ts b/src/app/api/user/[user]/calendar/route.ts similarity index 72% rename from src/app/api/calendar/route.ts rename to src/app/api/user/[user]/calendar/route.ts index 440bbd7..62142e9 100644 --- a/src/app/api/calendar/route.ts +++ b/src/app/api/user/[user]/calendar/route.ts @@ -15,7 +15,7 @@ import { } from '@/app/api/validation'; import { z } from 'zod/v4'; -export const GET = auth(async function GET(req) { +export const GET = auth(async function GET(req, { params }) { const authCheck = userAuthenticated(req); if (!authCheck.continue) return returnZodTypeCheckedResponse( @@ -24,22 +24,7 @@ export const GET = auth(async function GET(req) { authCheck.metadata, ); - const dataRaw: Record = {}; - for (const [key, value] of req.nextUrl.searchParams.entries()) { - if (key.endsWith('[]')) { - const cleanKey = key.slice(0, -2); - if (!dataRaw[cleanKey]) { - dataRaw[cleanKey] = []; - } - if (Array.isArray(dataRaw[cleanKey])) { - (dataRaw[cleanKey] as string[]).push(value); - } else { - dataRaw[cleanKey] = [dataRaw[cleanKey] as string, value]; - } - } else { - dataRaw[key] = value; - } - } + const dataRaw = Object.fromEntries(new URL(req.url).searchParams); const data = await userCalendarQuerySchema.safeParseAsync(dataRaw); if (!data.success) return returnZodTypeCheckedResponse( @@ -51,15 +36,15 @@ export const GET = auth(async function GET(req) { }, { status: 400 }, ); - const { end, start, userIds } = data.data; + const { end, start } = data.data; const requestUserId = authCheck.user.id; - const requestedUser = await prisma.user.findMany({ + const requestedUserId = (await params).user; + + const requestedUser = await prisma.user.findFirst({ where: { - id: { - in: userIds, - }, + id: requestedUserId, }, select: { meetingParts: { @@ -72,9 +57,6 @@ export const GET = auth(async function GET(req) { gte: start, }, }, - status: { - not: 'DECLINED', - }, }, orderBy: { meeting: { @@ -82,7 +64,6 @@ export const GET = auth(async function GET(req) { }, }, select: { - user_id: true, meeting: { select: { id: true, @@ -155,7 +136,6 @@ export const GET = auth(async function GET(req) { start_time: 'asc', }, select: { - user_id: true, id: true, reason: true, start_time: true, @@ -173,64 +153,46 @@ export const GET = auth(async function GET(req) { if (!requestedUser) return returnZodTypeCheckedResponse( ErrorResponseSchema, - { success: false, message: 'User/s not found' }, + { success: false, message: 'User not found' }, { status: 404 }, ); const calendar: z.input = []; - for (const event of requestedUser.map((r) => r.meetingParts).flat()) { + for (const event of requestedUser.meetingParts) { if ( event.meeting.participants.some((p) => p.user.id === requestUserId) || event.meeting.organizer_id === requestUserId ) { - calendar.push({ - ...event.meeting, - type: 'event', - users: event.meeting.participants - .map((p) => p.user.id) - .filter((id) => userIds.includes(id)), - }); + calendar.push({ ...event.meeting, type: 'event' }); } else { calendar.push({ id: event.meeting.id, start_time: event.meeting.start_time, end_time: event.meeting.end_time, type: 'blocked_private', - users: event.meeting.participants - .map((p) => p.user.id) - .filter((id) => userIds.includes(id)), }); } } - for (const event of requestedUser.map((r) => r.meetingsOrg).flat()) { + for (const event of requestedUser.meetingsOrg) { if ( event.participants.some((p) => p.user.id === requestUserId) || event.organizer_id === requestUserId ) { - calendar.push({ - ...event, - type: 'event', - users: event.participants - .map((p) => p.user.id) - .filter((id) => userIds.includes(id)), - }); + calendar.push({ ...event, type: 'event' }); } else { calendar.push({ id: event.id, start_time: event.start_time, end_time: event.end_time, type: 'blocked_private', - users: event.participants - .map((p) => p.user.id) - .filter((id) => userIds.includes(id)), }); } } - for (const slot of requestedUser.map((r) => r.blockedSlots).flat()) { - if (requestUserId === userIds[0] && userIds.length === 1) { + for (const slot of requestedUser.blockedSlots) { + if (requestUserId === requestedUserId) { calendar.push({ start_time: slot.start_time, end_time: slot.end_time, @@ -242,7 +204,6 @@ export const GET = auth(async function GET(req) { created_at: slot.created_at, updated_at: slot.updated_at, type: 'blocked_owned', - users: [requestUserId], }); } else { calendar.push({ @@ -250,7 +211,6 @@ export const GET = auth(async function GET(req) { end_time: slot.end_time, id: slot.id, type: 'blocked_private', - users: [slot.user_id], }); } } diff --git a/src/app/api/calendar/swagger.ts b/src/app/api/user/[user]/calendar/swagger.ts similarity index 77% rename from src/app/api/calendar/swagger.ts rename to src/app/api/user/[user]/calendar/swagger.ts index b4f5898..fb48629 100644 --- a/src/app/api/calendar/swagger.ts +++ b/src/app/api/user/[user]/calendar/swagger.ts @@ -7,12 +7,17 @@ import { userNotFoundResponse, } from '@/lib/defaultApiResponses'; import { OpenAPIRegistry } from '@asteasolutions/zod-to-openapi'; +import zod from 'zod/v4'; +import { UserIdParamSchema } from '@/app/api/validation'; export default function registerSwaggerPaths(registry: OpenAPIRegistry) { registry.registerPath({ method: 'get', - path: '/api/calendar', + path: '/api/user/{user}/calendar', request: { + params: zod.object({ + user: UserIdParamSchema, + }), query: userCalendarQuerySchema, }, responses: { @@ -27,6 +32,6 @@ export default function registerSwaggerPaths(registry: OpenAPIRegistry) { ...notAuthenticatedResponse, ...userNotFoundResponse, }, - tags: ['Calendar'], + tags: ['User'], }); } diff --git a/src/app/api/calendar/validation.ts b/src/app/api/user/[user]/calendar/validation.ts similarity index 91% rename from src/app/api/calendar/validation.ts rename to src/app/api/user/[user]/calendar/validation.ts index 5bf45a6..1572793 100644 --- a/src/app/api/calendar/validation.ts +++ b/src/app/api/user/[user]/calendar/validation.ts @@ -14,8 +14,6 @@ export const BlockedSlotSchema = zod end_time: eventEndTimeSchema, type: zod.literal('blocked_private'), id: zod.string(), - users: zod.string().array(), - user_id: zod.string().optional(), }) .openapi('BlockedSlotSchema', { description: 'Blocked time slot in the user calendar', @@ -33,21 +31,17 @@ export const OwnedBlockedSlotSchema = zod created_at: zod.date().nullish(), updated_at: zod.date().nullish(), type: zod.literal('blocked_owned'), - users: zod.string().array(), - user_id: zod.string().optional(), }) .openapi('OwnedBlockedSlotSchema', { description: 'Blocked slot owned by the user', }); export const VisibleSlotSchema = EventSchema.omit({ - participants: true, organizer: true, + participants: true, }) .extend({ type: zod.literal('event'), - users: zod.string().array(), - user_id: zod.string().optional(), }) .openapi('VisibleSlotSchema', { description: 'Visible time slot in the user calendar', @@ -92,7 +86,6 @@ export const userCalendarQuerySchema = zod ); return endOfWeek; }), - userIds: zod.string().array(), }) .openapi('UserCalendarQuerySchema', { description: 'Query parameters for filtering the user calendar', diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx deleted file mode 100644 index 6a3c299..0000000 --- a/src/app/not-found.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import Link from 'next/link'; -import { Button } from '@/components/ui/button'; - -export default function NotFound() { - return ( -
-
-
-

404

-

Page Not Found

-

- Sorry, we couldn't find the page you're looking for. It - might have been moved, deleted, or doesn't exist. -

-
- -
- - -
-
-
- ); -} diff --git a/src/auth.ts b/src/auth.ts index 18b3b2d..cb8ae40 100644 --- a/src/auth.ts +++ b/src/auth.ts @@ -2,15 +2,8 @@ import NextAuth, { CredentialsSignin } from 'next-auth'; import { Prisma } from '@/generated/prisma'; import type { Provider } from 'next-auth/providers'; - import Credentials from 'next-auth/providers/credentials'; -import AuthentikProvider from 'next-auth/providers/authentik'; -import DiscordProvider from 'next-auth/providers/discord'; -import FacebookProvider from 'next-auth/providers/facebook'; -import GithubProvider from 'next-auth/providers/github'; -import GitlabProvider from 'next-auth/providers/gitlab'; -import GoogleProvider from 'next-auth/providers/google'; -import KeycloakProvider from 'next-auth/providers/keycloak'; +import Authentik from 'next-auth/providers/authentik'; import { PrismaAdapter } from '@auth/prisma-adapter'; import { prisma } from '@/prisma'; @@ -95,16 +88,8 @@ const providers: Provider[] = [ } }, }), - - process.env.AUTH_DISCORD_ID && DiscordProvider, - process.env.AUTH_FACEBOOK_ID && FacebookProvider, - process.env.AUTH_GITHUB_ID && GithubProvider, - process.env.AUTH_GITLAB_ID && GitlabProvider, - process.env.AUTH_GOOGLE_ID && GoogleProvider, - process.env.AUTH_KEYCLOAK_ID && KeycloakProvider, - process.env.AUTH_AUTHENTIK_ID && - AuthentikProvider({ + Authentik({ profile(profile) { return { id: profile.sub, diff --git a/src/components/calendar.tsx b/src/components/calendar.tsx index ab23c35..a8d6005 100644 --- a/src/components/calendar.tsx +++ b/src/components/calendar.tsx @@ -7,6 +7,7 @@ import '@/components/react-big-calendar.css'; import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'; import CustomToolbar from '@/components/custom-toolbar'; import React from 'react'; +import { useGetApiUserUserCalendar } from '@/generated/api/user/user'; import { useRouter } from 'next/navigation'; import { usePatchApiEventEventID } from '@/generated/api/event/event'; import { useSession } from 'next-auth/react'; @@ -16,7 +17,6 @@ import { ErrorBoundary } from 'react-error-boundary'; import { Button } from '@/components/ui/button'; import { fromZodIssue } from 'zod-validation-error/v4'; import type { $ZodIssue } from 'zod/v4/core'; -import { useGetApiCalendar } from '@/generated/api/calendar/calendar'; moment.updateLocale('en', { week: { @@ -25,28 +25,12 @@ moment.updateLocale('en', { }, }); -function eventPropGetter(event: { - id: string; - start: Date; - end: Date; - type: UserCalendarSchemaItem['type']; - userId?: string; - colorOverride?: string; -}) { - return { - style: event.colorOverride - ? { backgroundColor: event.colorOverride } - : undefined, - }; -} - const DaDRBCalendar = withDragAndDrop< { id: string; start: Date; end: Date; type: UserCalendarSchemaItem['type']; - userId?: string; }, { id: string; @@ -60,21 +44,9 @@ const localizer = momentLocalizer(moment); export default function Calendar({ userId, height, - additionalEvents = [], - className, }: { - userId?: string | string[]; + userId?: string; height: string; - additionalEvents?: { - id: string; - title: string; - start: Date; - end: Date; - type: UserCalendarSchemaItem['type']; - userId?: string; - colorOverride?: string; - }[]; - className?: string; }) { return ( @@ -95,26 +67,10 @@ export default function Calendar({
)} > - {typeof userId === 'string' ? ( - - ) : Array.isArray(userId) && userId.length > 0 ? ( - + {userId ? ( + ) : ( - + )} )} @@ -125,21 +81,9 @@ export default function Calendar({ function CalendarWithUserEvents({ userId, height, - additionalEvents, - className, }: { userId: string; height: string; - additionalEvents?: { - id: string; - title: string; - start: Date; - end: Date; - type: UserCalendarSchemaItem['type']; - userId?: string; - colorOverride?: string; - }[]; - className?: string; }) { const sesstion = useSession(); const [currentView, setCurrentView] = React.useState< @@ -148,9 +92,9 @@ function CalendarWithUserEvents({ const [currentDate, setCurrentDate] = React.useState(new Date()); const router = useRouter(); - const { data, refetch, error, isError } = useGetApiCalendar( + const { data, refetch, error, isError } = useGetApiUserUserCalendar( + userId, { - userIds: [userId], start: moment(currentDate) .startOf( currentView === 'agenda' @@ -193,8 +137,6 @@ function CalendarWithUserEvents({ return ( { setCurrentDate(date); }} - events={[ - ...(data?.data.calendar.map((event) => ({ + events={ + data?.data.calendar.map((event) => ({ id: event.id, title: event.type === 'event' ? event.title : 'Blocker', start: new Date(event.start_time), end: new Date(event.end_time), type: event.type, - userId: event.users[0], - })) ?? []), - ...(additionalEvents ?? []), - ]} + })) ?? [] + } onSelectEvent={(event) => { router.push(`/events/${event.id}`); }} @@ -288,116 +228,7 @@ function CalendarWithUserEvents({ ); } -function CalendarWithMultiUserEvents({ - userIds, - height, - additionalEvents, - className, -}: { - userIds: string[]; - height: string; - additionalEvents?: { - id: string; - title: string; - start: Date; - end: Date; - type: UserCalendarSchemaItem['type']; - userId?: string; - colorOverride?: string; - }[]; - className?: string; -}) { - const [currentView, setCurrentView] = React.useState< - 'month' | 'week' | 'day' | 'agenda' | 'work_week' - >('week'); - const [currentDate, setCurrentDate] = React.useState(new Date()); - - const { data, error, isError } = useGetApiCalendar( - { - userIds: userIds, - start: moment(currentDate) - .startOf( - currentView === 'agenda' - ? 'month' - : currentView === 'work_week' - ? 'week' - : currentView, - ) - .toISOString(), - end: moment(currentDate) - .endOf( - currentView === 'agenda' - ? 'month' - : currentView === 'work_week' - ? 'week' - : currentView, - ) - .toISOString(), - }, - { - query: { - refetchOnWindowFocus: true, - refetchOnReconnect: true, - refetchOnMount: true, - }, - }, - ); - - if (isError) { - throw error.response?.data || 'Failed to fetch calendar data'; - } - - return ( - { - setCurrentDate(date); - }} - events={[ - ...(data?.data.calendar.map((event) => ({ - id: event.id, - title: event.type === 'event' ? event.title : 'Blocker', - start: new Date(event.start_time), - end: new Date(event.end_time), - type: event.type, - userId: event.users[0], - })) ?? []), - ...(additionalEvents ?? []), - ]} - /> - ); -} - -function CalendarWithoutUserEvents({ - height, - additionalEvents, - className, -}: { - height: string; - additionalEvents?: { - id: string; - title: string; - start: Date; - end: Date; - type: UserCalendarSchemaItem['type']; - userId?: string; - colorOverride?: string; - }[]; - className?: string; -}) { +function CalendarWithoutUserEvents({ height }: { height: string }) { const [currentView, setCurrentView] = React.useState< 'month' | 'week' | 'day' | 'agenda' | 'work_week' >('week'); @@ -405,8 +236,6 @@ function CalendarWithoutUserEvents({ return ( { setCurrentDate(date); }} - events={additionalEvents} /> ); } diff --git a/src/components/custom-toolbar.css b/src/components/custom-toolbar.css index 1230384..3fba69f 100644 --- a/src/components/custom-toolbar.css +++ b/src/components/custom-toolbar.css @@ -1,15 +1,11 @@ /* Container der Toolbar */ .custom-toolbar { display: flex; - gap: 8px; + flex-direction: column; + gap: 12px; padding: calc(var(--spacing) * 2); padding-left: calc(50px + var(--spacing)); - - @media (max-width: 870px) { - padding-left: 0; - flex-direction: column; - } - box-shadow: none; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } @@ -29,12 +25,6 @@ display: flex; gap: 8px; justify-content: center; - align-items: center; -} - -.custom-toolbar .navigation-controls .handleWeek { - display: grid; - grid-template-columns: 1fr 1fr; } .custom-toolbar .navigation-controls button { @@ -87,8 +77,6 @@ border-radius: 11px; justify-items: center; align-items: center; - display: flex; - gap: 8px; } .custom-toolbar .navigation-controls .handleWeek button { @@ -105,9 +93,6 @@ padding: 0 8px; border-radius: 11px; justify-items: center; - display: flex; - justify-content: center; - align-items: center; } .right-section .datepicker-box { @@ -115,12 +100,8 @@ background-color: #c6c6c6; height: 36px; border-radius: 11px; - font-size: 14px; + font-size: 12px; align-self: center; - font-family: 'Varela Round', sans-serif; - display: flex; - align-items: center; - justify-content: center; } .datepicker { diff --git a/src/components/custom-toolbar.tsx b/src/components/custom-toolbar.tsx index 76e59ee..36c8fff 100644 --- a/src/components/custom-toolbar.tsx +++ b/src/components/custom-toolbar.tsx @@ -171,9 +171,12 @@ const CustomToolbar: React.FC = ({ }; return ( -
+
-
+
)} - {participants && - participants.some( - (p) => p.user.id === session.data?.user?.id, - ) && ( -
- -
- )}
diff --git a/src/components/custom-ui/participant-list-entry.tsx b/src/components/custom-ui/participant-list-entry.tsx index 2ec9c02..27827cf 100644 --- a/src/components/custom-ui/participant-list-entry.tsx +++ b/src/components/custom-ui/participant-list-entry.tsx @@ -10,7 +10,6 @@ type ParticipantListEntryProps = zod.output; export default function ParticipantListEntry({ user, - status, }: ParticipantListEntryProps) { const { resolvedTheme } = useTheme(); const defaultImage = @@ -22,7 +21,6 @@ export default function ParticipantListEntry({
Avatar {user.name} - {status}
); } diff --git a/src/components/forms/event-form.tsx b/src/components/forms/event-form.tsx index d4fb216..a445080 100644 --- a/src/components/forms/event-form.tsx +++ b/src/components/forms/event-form.tsx @@ -21,16 +21,6 @@ import { useSearchParams } from 'next/navigation'; import zod from 'zod/v4'; import { PublicUserSchema } from '@/app/api/user/validation'; -import Calendar from '@/components/calendar'; -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, - DialogTrigger, -} from '../ui/dialog'; type User = zod.output; @@ -51,12 +41,7 @@ const EventForm: React.FC = (props) => { const startFromUrl = searchParams.get('start'); const endFromUrl = searchParams.get('end'); - const { - mutateAsync: createEvent, - status, - isSuccess, - error, - } = usePostApiEvent(); + const { mutate: createEvent, status, isSuccess, error } = usePostApiEvent(); const { data, isLoading, error: fetchError } = useGetApiUserMe(); const { data: eventData } = useGetApiEventEventID(props.eventId!, { query: { enabled: props.type === 'edit' }, @@ -83,8 +68,6 @@ const EventForm: React.FC = (props) => { const [location, setLocation] = React.useState(''); const [description, setDescription] = React.useState(''); - const [calendarOpen, setCalendarOpen] = React.useState(false); - // Update state when event data loads React.useEffect(() => { if (props.type === 'edit' && event) { @@ -155,10 +138,8 @@ const EventForm: React.FC = (props) => { participants: selectedParticipants.map((u) => u.id), }; - let eventID: string | undefined; - if (props.type === 'edit' && props.eventId) { - const mutationResult = await patchEvent.mutateAsync({ + await patchEvent.mutateAsync({ eventID: props.eventId, data: { title: data.title, @@ -169,12 +150,9 @@ const EventForm: React.FC = (props) => { participants: data.participants, }, }); - eventID = mutationResult.data.event.id; console.log('Updating event'); } else { console.log('Creating event'); - const mutationResult = await createEvent({ data }); - eventID = mutationResult.data.event.id; createEvent({ data }); } @@ -183,7 +161,7 @@ const EventForm: React.FC = (props) => { toastId={t} title='Event saved' description={event?.title} - onAction={() => router.push(`/events/${eventID}`)} + onAction={() => router.push(`/events/${event?.id}`)} variant='success' buttonText='show' /> @@ -216,183 +194,149 @@ const EventForm: React.FC = (props) => { return
Error loading event.
; return ( - <> - -
-
-
-
- -
-
- setTitle(e.target.value)} - /> -
-
-
-
-
- -
-
- -
-
- setLocation(e.target.value)} - /> -
-
-
- - -
-
- -

- {updatedAtDisplay} -

-
-
-
-
-
-
-
- - -
-
-
- setDescription(e.target.value)} - > -
-
-
- - { - setSelectedParticipants((current) => - current.find((u) => u.id === user.id) - ? current - : [...current, user], - ); - }} - removeUserAction={(user) => { - setSelectedParticipants((current) => - current.filter((u) => u.id !== user.id), - ); - }} - /> - - - -
- {selectedParticipants.map((user) => ( - - ))} -
-
-
- -
-
- -
-
- -
-
- {isSuccess &&

Event created!

} - {error &&

Error: {error.message}

} + +
+
+
+
- - - - Calendar - - Calendar for selected participants - - - - u.id)} - additionalEvents={[ - { - id: 'temp-event', - title: title || 'New Event', - start: startDate ? new Date(startDate) : new Date(), - end: endDate ? new Date(endDate) : new Date(), - type: 'event', - userId: 'create-event', - colorOverride: '#ff9800', - }, - ]} - height='600px' +
+ setTitle(e.target.value)} /> - - -
- +
+
+
+
+
+ +
+
+ +
+
+ setLocation(e.target.value)} + /> +
+
+
+ + +
+
+ +

+ {updatedAtDisplay} +

+
+
+
+
+
+
+
+ + +
+
+
+ setDescription(e.target.value)} + > +
+
+
+ + { + setSelectedParticipants((current) => + current.find((u) => u.id === user.id) + ? current + : [...current, user], + ); + }} + removeUserAction={(user) => { + setSelectedParticipants((current) => + current.filter((u) => u.id !== user.id), + ); + }} + /> +
+ {selectedParticipants.map((user) => ( + + ))} +
+
+
+ +
+
+ +
+
+ +
+
+ {isSuccess &&

Event created!

} + {error &&

Error: {error.message}

} +

+ ); }; diff --git a/src/components/react-big-calendar.css b/src/components/react-big-calendar.css index 88f3d78..675898c 100644 --- a/src/components/react-big-calendar.css +++ b/src/components/react-big-calendar.css @@ -83,7 +83,7 @@ button.rbc-input::-moz-focus-inner { } .rbc-off-range-bg { - background: var(--color-neutral-700); + background: #e6e6e6; } .rbc-header { @@ -596,7 +596,7 @@ button.rbc-input::-moz-focus-inner { min-height: 100%; /*Own changes 06*/ - background-color: var(--color-neutral-700); + background-color: #383838; /*Own changes 06*/ } .rbc-time-column .rbc-timeslot-group { @@ -606,7 +606,7 @@ button.rbc-input::-moz-focus-inner { } .rbc-timeslot-group { - border-bottom: 1px solid var(--color-neutral-300); /*#ddd*/ + border-bottom: 1px solid #8d8d8d; /*#ddd*/ min-height: 40px; display: -webkit-box; display: -ms-flexbox; @@ -624,13 +624,12 @@ button.rbc-input::-moz-focus-inner { flex: none; /*Own changes 07*/ - background-color: var(--color-neutral-500); + background-color: #8d8d8d; /*Own changes 07*/ } .rbc-label { padding: 0 5px; - width: 54.3542px; } .rbc-day-slot { @@ -687,7 +686,7 @@ button.rbc-input::-moz-focus-inner { min-height: 1em; } .rbc-day-slot .rbc-time-slot { - border-top: 1px solid transparent; /*#f7f7f7*/ + border-top: 1px solid #383838; /*#f7f7f7*/ } .rbc-time-view-resources .rbc-time-gutter, @@ -783,7 +782,7 @@ button.rbc-input::-moz-focus-inner { position: relative; /*Own changes 05*/ - background-color: var(--color-neutral-500); + background-color: #555555; /*Own changes 05*/ } .rbc-time-view .rbc-allday-cell + .rbc-allday-cell { @@ -810,10 +809,6 @@ button.rbc-input::-moz-focus-inner { -webkit-box-direction: normal; -ms-flex-direction: row; flex-direction: row; - margin-right: 14px; - @media ((hover: none) and (pointer: coarse)) { - margin-right: 0px; - } } .rbc-rtl .rbc-time-header.rbc-overflowing { border-right-width: 0; @@ -877,7 +872,7 @@ button.rbc-input::-moz-focus-inner { } .rbc-time-header-content { - border-bottom: 2px solid var(--color-neutral-400); /*#ddd*/ + border-bottom: 2px solid #717171; /*#ddd*/ } .rbc-time-column :last-child { @@ -895,7 +890,7 @@ button.rbc-input::-moz-focus-inner { /*Own changes 09*/ } .rbc-time-content > * + * > * { - border-left: 1px solid var(--color-neutral-300); /*#ddd*/ + border-left: 1px solid #c6c6c6; /*#ddd*/ } .rbc-rtl .rbc-time-content > * + * > * { border-left-width: 0; diff --git a/yarn.lock b/yarn.lock index 3d5b0ce..c28c3d8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -436,21 +436,21 @@ __metadata: languageName: node linkType: hard -"@eslint/config-array@npm:^0.21.0": - version: 0.21.0 - resolution: "@eslint/config-array@npm:0.21.0" +"@eslint/config-array@npm:^0.20.1": + version: 0.20.1 + resolution: "@eslint/config-array@npm:0.20.1" dependencies: "@eslint/object-schema": "npm:^2.1.6" debug: "npm:^4.3.1" minimatch: "npm:^3.1.2" - checksum: 10c0/0ea801139166c4aa56465b309af512ef9b2d3c68f9198751bbc3e21894fe70f25fbf26e1b0e9fffff41857bc21bfddeee58649ae6d79aadcd747db0c5dca771f + checksum: 10c0/709108c3925d83c2166024646829ab61ba5fa85c6568daefd32508899f46ed8dc36d7153042df6dcc7e58ad543bc93298b646575daecb5eb4e39a43d838dab42 languageName: node linkType: hard -"@eslint/config-helpers@npm:^0.3.0": - version: 0.3.0 - resolution: "@eslint/config-helpers@npm:0.3.0" - checksum: 10c0/013ae7b189eeae8b30cc2ee87bc5c9c091a9cd615579003290eb28bebad5d78806a478e74ba10b3fe08ed66975b52af7d2cd4b4b43990376412b14e5664878c8 +"@eslint/config-helpers@npm:^0.2.1": + version: 0.2.3 + resolution: "@eslint/config-helpers@npm:0.2.3" + checksum: 10c0/8fd36d7f33013628787947c81894807c7498b31eacf6648efa6d7c7a99aac6bf0d59a8a4d14f968ec2aeebefb76a1a7e4fd4cd556a296323d4711b3d7a7cda22 languageName: node linkType: hard @@ -489,10 +489,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.30.0": - version: 9.30.0 - resolution: "@eslint/js@npm:9.30.0" - checksum: 10c0/aec2df7f4e4e884d693dc27dbf4713c1a48afa327bfadac25ebd0e61a2797ce906f2f2a9be0d7d922acb68ccd68cc88779737811f9769eb4933d1f5e574c469e +"@eslint/js@npm:9.29.0": + version: 9.29.0 + resolution: "@eslint/js@npm:9.29.0" + checksum: 10c0/d0ccf37063fa27a3fae9347cb044f84ca10b5a2fa19ffb2b3fedf3b96843ac1ff359ea9f0ab0e80f2f16fda4cb0dc61ea0fed0375090f050fe0a029e7d6de3a3 languageName: node linkType: hard @@ -1013,15 +1013,6 @@ __metadata: languageName: node linkType: hard -"@marko19907/string-to-color@npm:^1.0.0": - version: 1.0.0 - resolution: "@marko19907/string-to-color@npm:1.0.0" - dependencies: - esm-seedrandom: "npm:^3.0.5" - checksum: 10c0/fd297937fb5a8a0c3372bc614f9a4e21842e3392ef54e0d323f0fe730ec5b5a441f921e63b3b151bd39ca64a120ddfe905d1a67ca62ed637ade69c96c55fe641 - languageName: node - linkType: hard - "@napi-rs/wasm-runtime@npm:^0.2.11": version: 0.2.11 resolution: "@napi-rs/wasm-runtime@npm:0.2.11" @@ -3252,21 +3243,21 @@ __metadata: languageName: node linkType: hard -"@tanstack/query-core@npm:5.81.5": - version: 5.81.5 - resolution: "@tanstack/query-core@npm:5.81.5" - checksum: 10c0/8d3af841f0e2f3fba1dfd100a8bbc2d07e7b1d5b8570d3d0d8bba5127601c2788e5f7c84839f93f3b0cbe54f14866c1fef8e3f9e745f02d27afaaeea33b7b755 +"@tanstack/query-core@npm:5.81.2": + version: 5.81.2 + resolution: "@tanstack/query-core@npm:5.81.2" + checksum: 10c0/36a6bddec2e7512015bcfbb0d7b0876fab418de9e0ef21ad403598276960e0b7d53efd62832ce462738ad22d9883e31cb5403eafc65dfd9b2f6744c22a9d8e42 languageName: node linkType: hard "@tanstack/react-query@npm:^5.80.7": - version: 5.81.5 - resolution: "@tanstack/react-query@npm:5.81.5" + version: 5.81.2 + resolution: "@tanstack/react-query@npm:5.81.2" dependencies: - "@tanstack/query-core": "npm:5.81.5" + "@tanstack/query-core": "npm:5.81.2" peerDependencies: react: ^18 || ^19 - checksum: 10c0/04803dd7d7d6ac73ba8c55bb25d76aa2f74d173d1574a113c35559bf6a8ac7d04a75db5e470e9a2e4132fee5e0ce1ab23bd8c27d17f70a461aca0e7ae740e76b + checksum: 10c0/a80a2e7401a02bfc8d7a926c9e48b0a108cae21a2c47691632e754de44ca08838c105fb0c30804f4ccbf173bee04ad9a3e45a8bebdfc1d1a32468bbb9d960e19 languageName: node linkType: hard @@ -3387,12 +3378,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:22.15.34": - version: 22.15.34 - resolution: "@types/node@npm:22.15.34" +"@types/node@npm:22.15.33": + version: 22.15.33 + resolution: "@types/node@npm:22.15.33" dependencies: undici-types: "npm:~6.21.0" - checksum: 10c0/fb6a6b36daaa1b484aaba3d33b4d1e7b37ea993e29f20b7a676affa76ed6ff6acd2ded4d5003469bc8dbc815b3d224533b4560896037ef6d5b5d552721ab7d57 + checksum: 10c0/ee040c29c891aa37fffc27d04a8529318c391356346933646b7692eaf62236831ad532f6ebaf43ebd6a2ef1f0f091860d8a0a83a4e3c5a4f66d37aa1b2c99f31 languageName: node linkType: hard @@ -5676,17 +5667,17 @@ __metadata: languageName: node linkType: hard -"eslint@npm:9.30.0": - version: 9.30.0 - resolution: "eslint@npm:9.30.0" +"eslint@npm:9.29.0": + version: 9.29.0 + resolution: "eslint@npm:9.29.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.2.0" "@eslint-community/regexpp": "npm:^4.12.1" - "@eslint/config-array": "npm:^0.21.0" - "@eslint/config-helpers": "npm:^0.3.0" + "@eslint/config-array": "npm:^0.20.1" + "@eslint/config-helpers": "npm:^0.2.1" "@eslint/core": "npm:^0.14.0" "@eslint/eslintrc": "npm:^3.3.1" - "@eslint/js": "npm:9.30.0" + "@eslint/js": "npm:9.29.0" "@eslint/plugin-kit": "npm:^0.3.1" "@humanfs/node": "npm:^0.16.6" "@humanwhocodes/module-importer": "npm:^1.0.1" @@ -5722,14 +5713,7 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10c0/ebc4b17cfd96f308ebaeb12dfab133a551eb03200c80109ecf663fbeb9af83c4eb3c143407c1b04522d23b5f5844fe9a629b00d409adfc460c1aadf5108da86a - languageName: node - linkType: hard - -"esm-seedrandom@npm:^3.0.5": - version: 3.0.5 - resolution: "esm-seedrandom@npm:3.0.5" - checksum: 10c0/6fe5a33f31bce0e733814df884cdfc27812e4b04ce973f7cf008b7b8500ecab6706f54591841bd12dd3bdd9bbb153abf04fb8efd4934a5b0ab273bff114cdb3b + checksum: 10c0/75e3f841e0f8b0fa93dbb2ba6ae538bd8b611c3654117bc3dadf90bb009923dfd2c15ec2948dc6e6b8b571317cc125c5cceb9255da8cd644ee740020df645dd8 languageName: node linkType: hard @@ -7598,12 +7582,12 @@ __metadata: languageName: node linkType: hard -"lucide-react@npm:^0.525.0": - version: 0.525.0 - resolution: "lucide-react@npm:0.525.0" +"lucide-react@npm:^0.523.0": + version: 0.523.0 + resolution: "lucide-react@npm:0.523.0" peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: 10c0/81c4438e2cf1c86ea2ebe0a97b378201512450894283cccce766a89bc6a4e47c8df1d9115d845d98f582bb0a10be31c454aa232520fea35018dac1cd8466ea9b + checksum: 10c0/dafd180d05326edc81d928f37d6574930f0592da08b91c536ae5675d0b764295368e8daf9bcc81a5283c18e2f19412060d1da475b4f26112168245e708bd562f languageName: node linkType: hard @@ -7699,7 +7683,6 @@ __metadata: "@fortawesome/free-solid-svg-icons": "npm:^6.7.2" "@fortawesome/react-fontawesome": "npm:^0.2.2" "@hookform/resolvers": "npm:^5.0.1" - "@marko19907/string-to-color": "npm:^1.0.0" "@prisma/client": "npm:^6.9.0" "@radix-ui/react-avatar": "npm:^1.1.10" "@radix-ui/react-collapsible": "npm:^1.1.11" @@ -7717,7 +7700,7 @@ __metadata: "@radix-ui/react-tooltip": "npm:^1.2.7" "@tailwindcss/postcss": "npm:4.1.11" "@tanstack/react-query": "npm:^5.80.7" - "@types/node": "npm:22.15.34" + "@types/node": "npm:22.15.33" "@types/react": "npm:19.1.8" "@types/react-big-calendar": "npm:1.16.2" "@types/react-dom": "npm:19.1.6" @@ -7730,10 +7713,10 @@ __metadata: cypress: "npm:14.5.0" date-fns: "npm:^4.1.0" dotenv-cli: "npm:8.0.0" - eslint: "npm:9.30.0" + eslint: "npm:9.29.0" eslint-config-next: "npm:15.3.4" eslint-config-prettier: "npm:10.1.5" - lucide-react: "npm:^0.525.0" + lucide-react: "npm:^0.523.0" next: "npm:15.3.4" next-auth: "npm:^5.0.0-beta.25" next-swagger-doc: "npm:^0.4.1" @@ -9035,11 +9018,11 @@ __metadata: linkType: hard "react-hook-form@npm:^7.56.4": - version: 7.59.0 - resolution: "react-hook-form@npm:7.59.0" + version: 7.58.1 + resolution: "react-hook-form@npm:7.58.1" peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 - checksum: 10c0/6be30ce65121f4be0f491c2929142e2d9a390a8802f58fb7a41ab978ab8daa6fcd2c442258dbd1b053e6864a83c8f4b1d83de9c95f0efdf5c2120d3c21bd838e + checksum: 10c0/981b9982b7eb497c3afff86c219175fdd4d92a24e3533518035239545dda23f5ebc6ba647fb29a250f768bdf811ba413094ce0103fbf36b436737fc18aa1bb49 languageName: node linkType: hard