From 9a306897aaa98dea810f6bf5fcdb9e8f5d065381 Mon Sep 17 00:00:00 2001 From: Finnick223 Date: Fri, 10 Jan 2025 21:40:55 +0100 Subject: [PATCH 01/10] feat: create page with interactive calendar - install necessary dependencies - add route and update side menu - create calendar component with predefined events for testing purpose based on react-big-calendar and place it inside dashboard/calendar --- package-lock.json | 221 +++++- package.json | 3 + src/app/dashboard/_components/SideMenu.tsx | 10 +- .../_components/BigCalendar/BigCalendar.tsx | 95 +++ .../BigCalendar/bigCalendarStyling.css | 676 ++++++++++++++++++ src/app/dashboard/calendar/page.tsx | 10 + 6 files changed, 1000 insertions(+), 15 deletions(-) create mode 100644 src/app/dashboard/calendar/_components/BigCalendar/BigCalendar.tsx create mode 100644 src/app/dashboard/calendar/_components/BigCalendar/bigCalendarStyling.css create mode 100644 src/app/dashboard/calendar/page.tsx diff --git a/package-lock.json b/package-lock.json index 7ece654..f986bda 100644 --- a/package-lock.json +++ b/package-lock.json @@ -20,8 +20,10 @@ "clsx": "^2.1.1", "iron-session": "^8.0.4", "lucide-react": "^0.454.0", + "moment": "^2.30.1", "next": "^15.1.4", "react": "19.0.0-rc-02c0e824-20241028", + "react-big-calendar": "^1.17.1", "react-dom": "19.0.0-rc-02c0e824-20241028", "react-hook-form": "^7.53.2", "react-icons": "^5.3.0", @@ -37,6 +39,7 @@ "@testing-library/user-event": "^14.5.2", "@types/node": "^20", "@types/react": "^18", + "@types/react-big-calendar": "^1.16.0", "@types/react-dom": "^18", "@typescript-eslint/eslint-plugin": "^8.13.0", "@typescript-eslint/parser": "^8.13.0", @@ -361,7 +364,6 @@ "version": "7.26.0", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", - "dev": true, "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" @@ -886,6 +888,16 @@ "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@radix-ui/number": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.0.tgz", @@ -2224,6 +2236,13 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/date-arithmetic": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/@types/date-arithmetic/-/date-arithmetic-4.1.4.tgz", + "integrity": "sha512-p9eZ2X9B80iKiTW4ukVj8B4K6q9/+xFtQ5MGYA5HWToY9nL4EkhV9+6ftT2VHpVMEZb5Tv00Iel516bVdO+yRw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -2249,19 +2268,29 @@ "node_modules/@types/prop-types": { "version": "15.7.13", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.13.tgz", - "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==", - "devOptional": true + "integrity": "sha512-hCZTSvwbzWGvhqxp/RqVqwU999pBf2vp7hzIjiYOsl8wqOmUxkQ6ddw1cV3l8811+kdUFus/q4d1Y3E3SyEifA==" }, "node_modules/@types/react": { "version": "18.3.12", "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.12.tgz", "integrity": "sha512-D2wOSq/d6Agt28q7rSI3jhU7G6aiuzljDGZ2hTZHIkrTLUI+AF3WMeKkEZ9nN2fkBAlcktT6vcZjDFiIhMYEQw==", - "devOptional": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, + "node_modules/@types/react-big-calendar": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/@types/react-big-calendar/-/react-big-calendar-1.16.0.tgz", + "integrity": "sha512-1w2GXAJWlGmaPZOd9J9cyWA/XBNOGRZ4MmRNypEQhwEMIIL9cfd1UdcvzSrQsnBm0qYF/scqmsISNbUzPBE1vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/date-arithmetic": "*", + "@types/prop-types": "*", + "@types/react": "*" + } + }, "node_modules/@types/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", @@ -2271,6 +2300,12 @@ "@types/react": "*" } }, + "node_modules/@types/warning": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/warning/-/warning-3.0.3.tgz", + "integrity": "sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==", + "license": "MIT" + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.13.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.13.0.tgz", @@ -3478,8 +3513,7 @@ "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==" }, "node_modules/damerau-levenshtein": { "version": "1.0.8", @@ -3552,6 +3586,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/date-arithmetic": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/date-arithmetic/-/date-arithmetic-4.1.0.tgz", + "integrity": "sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg==", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.13", + "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", + "integrity": "sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", @@ -3640,7 +3686,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -3690,6 +3735,16 @@ "dev": true, "license": "MIT" }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -4830,6 +4885,11 @@ "node": ">=10.13.0" } }, + "node_modules/globalize": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/globalize/-/globalize-0.1.1.tgz", + "integrity": "sha512-5e01v8eLGfuQSOvx2MsDMOWS0GFtCx1wPzQSmcHw4hkxFzrQDBO3Xwg/m8Hr/7qXMrHeOIE29qWVzyv06u1TZA==" + }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -5956,7 +6016,12 @@ "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true, + "license": "MIT" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", "license": "MIT" }, "node_modules/lodash.merge": { @@ -6135,6 +6200,15 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, + "node_modules/luxon": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/luxon/-/luxon-3.5.0.tgz", + "integrity": "sha512-rh+Zjr6DNfUYR3bPwJEnuwDdqMbxZW7LOQfUN4B54+Cl+0o5zaU9RJ6bcidfDtC1cWCZXQ+nvX8bf6bAji37QQ==", + "license": "MIT", + "engines": { + "node": ">=12" + } + }, "node_modules/lz-string": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", @@ -6155,6 +6229,12 @@ "@jridgewell/sourcemap-codec": "^1.5.0" } }, + "node_modules/memoize-one": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz", + "integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==", + "license": "MIT" + }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", @@ -6268,6 +6348,27 @@ "node": ">=16 || 14 >=14.17" } }, + "node_modules/moment": { + "version": "2.30.1", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", + "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.46", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.46.tgz", + "integrity": "sha512-ZXm9b36esbe7OmdABqIWJuBBiLLwAjrN7CE+7sYdCCx82Nabt1wHDj8TVseS59QIlfFPbOoiBPm6ca9BioG4hw==", + "license": "MIT", + "dependencies": { + "moment": "^2.29.4" + }, + "engines": { + "node": "*" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -7065,7 +7166,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -7114,6 +7214,90 @@ "node": ">=0.10.0" } }, + "node_modules/react-big-calendar": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.17.1.tgz", + "integrity": "sha512-LltUAMSGODWQBKx4013bRe6R0jaINV9hrs970+F860KedpozwRGGMT66esV9mA3mAhfSKoazF/QH1WCyLkXYZA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.7", + "clsx": "^1.2.1", + "date-arithmetic": "^4.1.0", + "dayjs": "^1.11.7", + "dom-helpers": "^5.2.1", + "globalize": "^0.1.1", + "invariant": "^2.2.4", + "lodash": "^4.17.21", + "lodash-es": "^4.17.21", + "luxon": "^3.2.1", + "memoize-one": "^6.0.0", + "moment": "^2.29.4", + "moment-timezone": "^0.5.40", + "prop-types": "^15.8.1", + "react-overlays": "^5.2.1", + "uncontrollable": "^7.2.1" + }, + "peerDependencies": { + "react": "^16.14.0 || ^17 || ^18", + "react-dom": "^16.14.0 || ^17 || ^18" + } + }, + "node_modules/react-big-calendar/node_modules/clsx": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz", + "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react-big-calendar/node_modules/react-overlays": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/react-overlays/-/react-overlays-5.2.1.tgz", + "integrity": "sha512-GLLSOLWr21CqtJn8geSwQfoJufdt3mfdsnIiQswouuQ2MMPns+ihZklxvsTDKD3cR2tF8ELbi5xUsvqVhR6WvA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.13.8", + "@popperjs/core": "^2.11.6", + "@restart/hooks": "^0.4.7", + "@types/warning": "^3.0.0", + "dom-helpers": "^5.2.0", + "prop-types": "^15.7.2", + "uncontrollable": "^7.2.1", + "warning": "^4.0.3" + }, + "peerDependencies": { + "react": ">=16.3.0", + "react-dom": ">=16.3.0" + } + }, + "node_modules/react-big-calendar/node_modules/react-overlays/node_modules/@restart/hooks": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@restart/hooks/-/hooks-0.4.16.tgz", + "integrity": "sha512-f7aCv7c+nU/3mF7NWLtVVr0Ra80RqsO89hO72r+Y/nvQr5+q0UFGkocElTH6MJApvReVh6JHUFYn2cw1WdHF3w==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3" + }, + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/react-big-calendar/node_modules/uncontrollable": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/uncontrollable/-/uncontrollable-7.2.1.tgz", + "integrity": "sha512-svtcfoTADIB0nT9nltgjujTi7BzVmwjZClOmskKu/E8FW9BXzg9os8OLr4f8Dlnk0rYWJIWr4wv9eKUXiQvQwQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.6.3", + "@types/react": ">=16.9.11", + "invariant": "^2.2.4", + "react-lifecycles-compat": "^3.0.4" + }, + "peerDependencies": { + "react": ">=15.0.0" + } + }, "node_modules/react-dom": { "version": "19.0.0-rc-02c0e824-20241028", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0-rc-02c0e824-20241028.tgz", @@ -7152,8 +7336,13 @@ "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + }, + "node_modules/react-lifecycles-compat": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz", + "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA==", + "license": "MIT" }, "node_modules/react-refresh": { "version": "0.14.2", @@ -7236,7 +7425,6 @@ "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "dev": true, "license": "MIT" }, "node_modules/regexp.prototype.flags": { @@ -8637,6 +8825,15 @@ "node": ">=18" } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/package.json b/package.json index 6adcdb9..324f21a 100644 --- a/package.json +++ b/package.json @@ -26,8 +26,10 @@ "clsx": "^2.1.1", "iron-session": "^8.0.4", "lucide-react": "^0.454.0", + "moment": "^2.30.1", "next": "^15.1.4", "react": "19.0.0-rc-02c0e824-20241028", + "react-big-calendar": "^1.17.1", "react-dom": "19.0.0-rc-02c0e824-20241028", "react-hook-form": "^7.53.2", "react-icons": "^5.3.0", @@ -43,6 +45,7 @@ "@testing-library/user-event": "^14.5.2", "@types/node": "^20", "@types/react": "^18", + "@types/react-big-calendar": "^1.16.0", "@types/react-dom": "^18", "@typescript-eslint/eslint-plugin": "^8.13.0", "@typescript-eslint/parser": "^8.13.0", diff --git a/src/app/dashboard/_components/SideMenu.tsx b/src/app/dashboard/_components/SideMenu.tsx index e28a007..62aa04e 100644 --- a/src/app/dashboard/_components/SideMenu.tsx +++ b/src/app/dashboard/_components/SideMenu.tsx @@ -48,9 +48,13 @@ const SideMenu = () => {
  • - - Kalendarz - + + + Kalendarz + +
  • diff --git a/src/app/dashboard/calendar/_components/BigCalendar/BigCalendar.tsx b/src/app/dashboard/calendar/_components/BigCalendar/BigCalendar.tsx new file mode 100644 index 0000000..2c2bdc3 --- /dev/null +++ b/src/app/dashboard/calendar/_components/BigCalendar/BigCalendar.tsx @@ -0,0 +1,95 @@ +'use client'; +import moment from 'moment'; +import 'moment/locale/pl'; +import { SetStateAction, useState } from 'react'; +import { Calendar, momentLocalizer, View, Views } from 'react-big-calendar'; +import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'; +import './bigCalendarStyling.css'; + +const messages = { + allDay: 'Cały dzień', + previous: 'Poprzedni', + next: 'Następny', + today: 'Dzisiaj', + month: 'Miesiąc', + week: 'Tydzień', + day: 'Dzień', + agenda: 'Agenda', + date: 'Data', + time: 'Godzina', + event: 'Wydarzenie', + noEventsInRange: 'Brak wydarzeń w tym zakresie dat.', +}; + +const events = [ + { + id: 1, + title: 'Morning Meeting', + start: new Date(2025, 1, 7, 9, 0, 0), // February 7, 2025, 9:00 AM + end: new Date(2025, 1, 7, 10, 0, 0), // February 7, 2025, 10:00 AM + }, + { + id: 2, + title: 'Lunch Break', + start: new Date(2025, 2, 13, 12, 0, 0), // March 13, 2025, 12:00 PM + end: new Date(2025, 2, 13, 13, 0, 0), // March 13, 2025, 1:00 PM + }, + { + id: 3, + title: 'Afternoon Workshop', + start: new Date(2025, 10, 6, 14, 0, 0), // November 6, 2025, 2:00 PM + end: new Date(2025, 10, 6, 16, 0, 0), // November 6, 2025, 4:00 PM + }, + { + id: 4, + title: 'All-Day Event', + start: new Date(2025, 3, 9), // April 9, 2025 (all-day) + end: new Date(2025, 3, 9), + allDay: true, + }, + { + id: 5, + title: 'Evening Networking', + start: new Date(2025, 3, 11, 18, 30, 0), // April 11, 2025, 6:30 PM + end: new Date(2025, 3, 11, 20, 0, 0), // April 11, 2025, 8:00 PM + }, +]; + +const BigCalendar = () => { + const [view, setView] = useState(Views.WEEK); + const [date, setDate] = useState(new Date()); + moment.locale('pl'); + const localizer = momentLocalizer(moment); + + const handleNavigate = (newDate: Date) => { + setDate(newDate); + }; + + const handleViewChange = (newView: SetStateAction) => { + setView(newView); + }; + + return ( + { + return ( + moment(start).format('DD-MM-YYYY') + + ' — ' + + moment(end).format('DD-MM-YYYY') + ); + }, + }} + /> + ); +}; +export default BigCalendar; diff --git a/src/app/dashboard/calendar/_components/BigCalendar/bigCalendarStyling.css b/src/app/dashboard/calendar/_components/BigCalendar/bigCalendarStyling.css new file mode 100644 index 0000000..0d9d841 --- /dev/null +++ b/src/app/dashboard/calendar/_components/BigCalendar/bigCalendarStyling.css @@ -0,0 +1,676 @@ +.rbc-btn { + color: inherit; + font: inherit; + margin: 0; +} +button.rbc-btn { + overflow: visible; + text-transform: none; + -webkit-appearance: button; + cursor: pointer; +} +button[disabled].rbc-btn { + cursor: not-allowed; +} +button.rbc-input::-moz-focus-inner { + border: 0; + padding: 0; +} +.rbc-calendar { + box-sizing: border-box; + height: 100%; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-align-items: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} +.rbc-calendar *, +.rbc-calendar *:before, +.rbc-calendar *:after { + box-sizing: inherit; +} +.rbc-abs-full, +.rbc-row-bg { + overflow: visible; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} +.rbc-ellipsis, +.rbc-event-label, +.rbc-row-segment .rbc-event-content, +.rbc-show-more { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.rbc-rtl { + direction: rtl; +} +.rbc-off-range { + color: hsl(var(--muted-foreground)); +} +.rbc-off-range-bg { + background: hsl(var(--muted)); +} +.rbc-header { + overflow: hidden; + -webkit-flex: 1 0 0%; + -ms-flex: 1 0 0%; + flex: 1 0 0%; + text-overflow: ellipsis; + white-space: nowrap; + padding: 0 3px; + text-align: center; + vertical-align: middle; + font-weight: bold; + font-size: 90%; + min-height: 0; + border-bottom: 1px solid hsl(var(--border)); + border-right: 1px solid hsl(var(--border)); +} +.rbc-rtl .rbc-header + .rbc-header { + border-left-width: 0; + border-right: 1px solid hsl(var(--border)); +} +.rbc-header > a, +.rbc-header > a:active, +.rbc-header > a:visited { + color: inherit; + text-decoration: none; +} +.rbc-row-content { + position: relative; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-select: none; + z-index: 4; +} +.rbc-today { + background-color: hsl(var(--accent)); +} +.rbc-toolbar { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-align-items: center; + -ms-flex-align: center; + align-items: center; + margin-bottom: 10px; + font-size: 16px; + justify-content: center; +} +.rbc-toolbar .rbc-toolbar-label { + -webkit-flex-grow: 1; + -ms-flex-positive: 1; + flex-grow: 1; + margin: 0 0.25rem; + text-align: center; + font-weight: 700; + min-width: 200px; +} +.rbc-btn-group { + display: inline-block; + white-space: nowrap; +} +.rbc-btn-group > button:first-child:not(:last-child) { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.rbc-btn-group > button:last-child:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.rbc-rtl .rbc-btn-group > button:first-child:not(:last-child) { + border-radius: var(--radius); + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.rbc-rtl .rbc-btn-group > button:last-child:not(:first-child) { + border-radius: var(--radius); + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.rbc-btn-group > button:not(:first-child):not(:last-child) { + border-radius: 0; +} +.rbc-btn-group button + button { + margin-left: -1px; +} +.rbc-rtl .rbc-btn-group button + button { + margin-left: 0; + margin-right: -1px; +} +.rbc-btn-group + .rbc-btn-group, +.rbc-btn-group + button { + margin-left: 10px; +} +.rbc-event { + background-color: hsl(var(--primary)); + color: hsl(var(--primary-foreground)); + padding: 5px; + border-radius: var(--radius); + cursor: pointer; +} +.rbc-slot-selecting .rbc-event { + cursor: inherit; + pointer-events: none; +} +.rbc-event-label { + font-size: 50%; +} +.rbc-event-overlaps { + box-shadow: -1px 1px 5px 0px rgba(51, 51, 51, 0.5); +} +.rbc-event-continues-prior { + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.rbc-event-continues-after { + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.rbc-event-continues-earlier { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.rbc-event-continues-later { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} +.rbc-event-continues-day-after { + border-bottom-left-radius: 0; + border-bottom-right-radius: 0; +} +.rbc-event-continues-day-prior { + border-top-left-radius: 0; + border-top-right-radius: 0; +} +.rbc-row { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; +} +.rbc-row-segment { + padding: 0 1px 1px 1px; +} +.rbc-selected-cell { + background-color: rgba(0, 0, 0, 0.1); +} +.rbc-show-more { + background-color: rgba(255, 255, 255, 0.3); + z-index: 4; + font-weight: bold; + font-size: 85%; + height: auto; + line-height: normal; + white-space: nowrap; +} +.rbc-month-view { + position: relative; + border: 1px solid hsl(var(--border)); + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-flex: 1 0 0; + -ms-flex: 1 0 0px; + flex: 1 0 0; + width: 100%; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-select: none; + height: 100%; +} +.rbc-month-header { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; +} +.rbc-month-row { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + position: relative; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-flex: 1 0 0; + -ms-flex: 1 0 0px; + flex: 1 0 0; + -webkit-flex-basis: 0px; + -ms-flex-preferred-size: 0px; + flex-basis: 0px; + overflow: hidden; + height: 100%; +} +.rbc-month-row + .rbc-month-row { + border-top: 1px solid hsl(var(--border)); +} +.rbc-date-cell { + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0px; + flex: 1 1 0; + min-width: 0; + padding-right: 5px; + text-align: right; +} +.rbc-date-cell.rbc-now { + font-weight: bold; +} +.rbc-date-cell > a, +.rbc-date-cell > a:active, +.rbc-date-cell > a:visited { + color: inherit; + text-decoration: none; +} +.rbc-row-bg { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; + -webkit-flex: 1 0 0; + -ms-flex: 1 0 0px; + flex: 1 0 0; + overflow: hidden; +} +.rbc-day-bg { + -webkit-flex: 1 0 0%; + -ms-flex: 1 0 0%; + flex: 1 0 0%; +} +.rbc-rtl .rbc-day-bg + .rbc-day-bg { + border-left-width: 0; + border-right: 1px solid hsl(var(--border)); +} +.rbc-overlay { + position: absolute; + z-index: 5; + border: 1px solid hsl(var(--border)); + background-color: hsl(var(--card)); + padding: 10px; +} +.rbc-overlay > * + * { + margin-top: 1px; +} +.rbc-overlay-header { + border-bottom: 1px solid hsl(var(--border)); + margin: -10px -10px 5px -10px; + padding: 2px 10px; +} +.rbc-agenda-view { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-flex: 1 0 0; + -ms-flex: 1 0 0px; + flex: 1 0 0; + overflow: auto; +} +.rbc-agenda-view table.rbc-agenda-table { + width: 100%; + border-bottom: 1px solid hsl(var(--border)); + border-spacing: 0; + border-collapse: collapse; +} +.rbc-agenda-view table.rbc-agenda-table tbody > tr > td { + padding: 5px 10px; + vertical-align: top; +} +.rbc-agenda-view table.rbc-agenda-table .rbc-agenda-time-cell { + padding-left: 15px; + padding-right: 15px; + text-transform: lowercase; +} +.rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td { + border-left: 1px solid hsl(var(--border)); +} +.rbc-rtl .rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td { + border-left-width: 0; + border-right: 1px solid hsl(var(--border)); +} +.rbc-agenda-view table.rbc-agenda-table tbody > tr + tr { + border-top: 1px solid hsl(var(--border)); +} +.rbc-agenda-view table.rbc-agenda-table thead > tr > th { + padding: 3px 5px; + text-align: left; + border-bottom: 1px solid hsl(var(--border)); +} +.rbc-rtl .rbc-agenda-view table.rbc-agenda-table thead > tr > th { + text-align: right; +} +.rbc-agenda-time-cell { + text-transform: lowercase; +} +.rbc-agenda-time-cell .rbc-continues-after:after { + content: ' »'; +} +.rbc-agenda-time-cell .rbc-continues-prior:before { + content: '« '; +} +.rbc-agenda-date-cell, +.rbc-agenda-time-cell { + white-space: nowrap; +} +.rbc-agenda-event-cell { + width: 100%; +} +.rbc-time-column { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + min-height: 100%; +} +.rbc-time-column .rbc-timeslot-group { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; +} +.rbc-timeslot-group { + border-bottom: 1px solid hsl(var(--border)); + min-height: 40px; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-flow: column nowrap; + -ms-flex-flow: column nowrap; + flex-flow: column nowrap; +} +.rbc-time-gutter, +.rbc-header-gutter { + -webkit-flex: none; + -ms-flex: none; + flex: none; +} +.rbc-label { + padding: 0 5px; +} +.rbc-day-slot { + position: relative; +} +.rbc-day-slot .rbc-events-container { + bottom: 0; + left: 0; + position: absolute; + right: 10px; + top: 0; +} +.rbc-day-slot .rbc-events-container.rbc-is-rtl { + left: 10px; + right: 0; +} +.rbc-day-slot .rbc-event { + border: 1px solid hsla(var(--primary), 0.9); + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + max-height: 100%; + min-height: 20px; + -webkit-flex-flow: column wrap; + -ms-flex-flow: column wrap; + flex-flow: column wrap; + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + overflow: scroll; + position: absolute; +} +.rbc-day-slot .rbc-event-label { + -webkit-flex: none; + -ms-flex: none; + flex: none; + padding-right: 5px; + width: auto; +} +.rbc-day-slot .rbc-event-content { + width: 100%; + -webkit-flex: 1 1 0; + -ms-flex: 1 1 0px; + flex: 1 1 0; + word-wrap: break-word; + line-height: 1; + height: 100%; + min-height: 1em; +} +.rbc-day-slot .rbc-time-slot { + border-top: 1px solid hsla(var(--border), 0.5); +} +.rbc-time-slot { + -webkit-flex: 1 0 0; + -ms-flex: 1 0 0px; + flex: 1 0 0; +} +.rbc-time-slot.rbc-now { + font-weight: bold; +} +.rbc-day-header { + text-align: center; +} +.rbc-slot-selection { + z-index: 10; + position: absolute; + background-color: rgba(0, 0, 0, 0.5); + color: white; + font-size: 75%; + width: 100%; + padding: 3px; +} +.rbc-slot-selecting { + cursor: move; +} +.rbc-time-view { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + width: 100%; + border: 1px solid hsl(var(--border)); + min-height: 0; +} +.rbc-time-view .rbc-time-gutter { + white-space: nowrap; +} +.rbc-time-view .rbc-allday-cell { + box-sizing: content-box; + width: 100%; + position: relative; +} +.rbc-time-view .rbc-allday-events { + position: relative; + z-index: 4; +} +.rbc-time-view .rbc-row { + box-sizing: border-box; + min-height: 20px; +} +.rbc-time-header { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex: 0 0 auto; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + -webkit-flex-direction: row; + -ms-flex-direction: row; + flex-direction: row; +} +.rbc-rtl .rbc-time-header.rbc-overflowing { + border-right-width: 0; + border-left: 1px solid hsl(var(--border)); +} +.rbc-time-header > .rbc-row:first-child { + border-bottom: 1px solid hsl(var(--border)); +} +.rbc-time-header > .rbc-row.rbc-row-resource { + border-bottom: 1px solid hsl(var(--border)); +} +.rbc-time-header-content { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + min-width: 0; + -webkit-flex-direction: column; + -ms-flex-direction: column; + flex-direction: column; + border-left: 1px solid hsl(var(--border)); +} +.rbc-rtl .rbc-time-header-content { + border-left-width: 0; + border-right: 1px solid hsl(var(--border)); +} +.rbc-time-content { + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + -webkit-flex: 1 0 0%; + -ms-flex: 1 0 0%; + flex: 1 0 0%; + -webkit-align-items: flex-start; + -ms-flex-align: start; + align-items: flex-start; + width: 100%; + border-top: 2px solid hsl(var(--border)); + overflow-y: auto; + position: relative; +} +.rbc-time-content > .rbc-time-gutter { + -webkit-flex: none; + -ms-flex: none; + flex: none; +} +.rbc-time-content > * + * > * { + border-left: 1px solid hsl(var(--border)); +} +.rbc-rtl .rbc-time-content > * + * > * { + border-left-width: 0; + border-right: 1px solid hsl(var(--border)); +} +.rbc-time-content > .rbc-day-slot { + width: 100%; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-select: none; +} +.rbc-current-time-indicator { + border-top: 2px solid hsl(var(--destructive)) !important; + width: 100%; + z-index: 10; + position: absolute; + pointer-events: none; +} +.rbc-calendar { + background-color: hsl(var(--background)); + border-radius: var(--radius); + color: hsl(var(--foreground)); + border: solid 1px hsl(var(--border)); + overflow: hidden; +} +.rbc-toolbar { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + font-size: 16px; + padding: 0 0.25rem 0 0.25rem; + border-bottom: solid 1px hsl(var(--border)); + margin-bottom: 0; +} +.rbc-toolbar button { + background-color: transparent; + border: none; + height: 40px; + padding: 8px 16px; + border-radius: var(--radius); + cursor: pointer; + transition: none; + margin-top: 0.25rem; + margin-bottom: 0.25rem; + box-shadow: none !important; + color: hsl(var(--foreground)) !important; +} +.rbc-toolbar button.rbc-active { + color: hsl(var(--accent-foreground)) !important; + background-color: hsl(var(--accent)) !important; +} +.rbc-toolbar button:hover { + background-color: hsl(var(--accent)) !important; + color: hsl(var(--accent-foreground)); +} +.rbc-time-view { + margin-top: 0; + border: none; +} +.rbc-time-header { + background-color: hsl(var(--card)); + border-bottom: 1px solid hsl(var(--border)); +} +.rbc-time-gutter { + background-color: hsl(var(--card)); +} +.rbc-day-bg { + background-color: hsl(var(--card)); + border-right: 1px solid hsl(var(--border)); +} +.rbc-month-view { + border: none; +} +.rbc-event:hover { + background-color: hsl(var(--ring)); +} +.event-component { + background-color: hsl(var(--primary)); + color: hsl(var(--primary-foreground)); + padding: 5px; + border-radius: var(--radius); + font-size: 14px; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; +} +.rbc-current-time-indicator { + background-color: 2px solid hsl(var(--destructive)) !important; + z-index: 3; + position: absolute; + pointer-events: none; + height: 1px; +} +.rbc-agenda-empty { + color: hsl(var(--muted-foreground)); + display: flex; + height: 100%; + align-items: center; + justify-content: center; +} diff --git a/src/app/dashboard/calendar/page.tsx b/src/app/dashboard/calendar/page.tsx new file mode 100644 index 0000000..48935ad --- /dev/null +++ b/src/app/dashboard/calendar/page.tsx @@ -0,0 +1,10 @@ +import BigCalendar from './_components/BigCalendar/BigCalendar'; + +const CalendarPage = () => { + return ( +
    + +
    + ); +}; +export default CalendarPage; From 616fa2d1fe554d51ec248b87b8fe27fa615da6c2 Mon Sep 17 00:00:00 2001 From: Finnick223 Date: Wed, 15 Jan 2025 23:30:22 +0100 Subject: [PATCH 02/10] feat: add dialog window for adding events to calendar --- package-lock.json | 359 ++++++++++++++---- package.json | 1 + .../calendar/_components/AddEventDialog.tsx | 96 +++++ src/app/dashboard/calendar/page.tsx | 2 + src/components/ui/dialog.tsx | 122 ++++++ 5 files changed, 514 insertions(+), 66 deletions(-) create mode 100644 src/app/dashboard/calendar/_components/AddEventDialog.tsx create mode 100644 src/components/ui/dialog.tsx diff --git a/package-lock.json b/package-lock.json index f986bda..99916fc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,6 +10,7 @@ "dependencies": { "@hookform/resolvers": "^3.9.1", "@radix-ui/react-checkbox": "^1.1.2", + "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-select": "^2.1.2", "@radix-ui/react-slot": "^1.1.1", @@ -1052,6 +1053,186 @@ } } }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.4.tgz", + "integrity": "sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.3", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-portal": "1.1.3", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-slot": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "^2.6.1" + }, + "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-dialog/node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "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-dialog/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz", + "integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@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-dialog/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz", + "integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "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-dialog/node_modules/@radix-ui/react-portal": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz", + "integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.1", + "@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-dialog/node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@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-dialog/node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.1" + }, + "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-direction": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.0.tgz", @@ -1478,72 +1659,6 @@ } } }, - "node_modules/@radix-ui/react-select/node_modules/react-remove-scroll/node_modules/react-style-singleton": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", - "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", - "license": "MIT", - "dependencies": { - "get-nonce": "^1.0.0", - "invariant": "^2.2.4", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/react-remove-scroll/node_modules/use-callback-ref": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", - "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", - "license": "MIT", - "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/react-remove-scroll/node_modules/use-sidecar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", - "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "license": "MIT", - "dependencies": { - "detect-node-es": "^1.1.0", - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-slot": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", @@ -7354,6 +7469,75 @@ "node": ">=0.10.0" } }, + "node_modules/react-remove-scroll": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz", + "integrity": "sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.1", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.2" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll/node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-toastify": { "version": "10.0.6", "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-10.0.6.tgz", @@ -8659,6 +8843,49 @@ "punycode": "^2.1.0" } }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", diff --git a/package.json b/package.json index 324f21a..8e4a4d9 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "dependencies": { "@hookform/resolvers": "^3.9.1", "@radix-ui/react-checkbox": "^1.1.2", + "@radix-ui/react-dialog": "^1.1.4", "@radix-ui/react-label": "^2.1.0", "@radix-ui/react-select": "^2.1.2", "@radix-ui/react-slot": "^1.1.1", diff --git a/src/app/dashboard/calendar/_components/AddEventDialog.tsx b/src/app/dashboard/calendar/_components/AddEventDialog.tsx new file mode 100644 index 0000000..b933ebb --- /dev/null +++ b/src/app/dashboard/calendar/_components/AddEventDialog.tsx @@ -0,0 +1,96 @@ +'use client'; + +import { Button } from '@/components/ui/button'; +import { + Dialog, + DialogClose, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from '@/components/ui/dialog'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { useState } from 'react'; + +export default function AddEventDialog() { + const [open, setOpen] = useState(false); + const [title, setTitle] = useState(''); + const [startDate, setStartDate] = useState(''); + const [endDate, setEndDate] = useState(''); + + const handleSubmit = (event: React.FormEvent) => { + event.preventDefault(); + console.log({ title, startDate, endDate }); + setOpen(false); + // Reset form fields + setTitle(''); + setStartDate(''); + setEndDate(''); + }; + + return ( + + + + + + + Dodaj nowe wydarzenie + + Wprowadź szczegóły wydarzenia. Zapisz zmiany gdy skończysz. + + +
    +
    +
    + + setTitle(e.target.value)} + className='col-span-3' + /> +
    +
    + + setStartDate(e.target.value)} + className='col-span-3' + /> +
    +
    + + setEndDate(e.target.value)} + className='col-span-3' + /> +
    +
    + + + + + + +
    +
    +
    + ); +} diff --git a/src/app/dashboard/calendar/page.tsx b/src/app/dashboard/calendar/page.tsx index 48935ad..566a275 100644 --- a/src/app/dashboard/calendar/page.tsx +++ b/src/app/dashboard/calendar/page.tsx @@ -1,8 +1,10 @@ +import AddEventDialog from './_components/AddEventDialog'; import BigCalendar from './_components/BigCalendar/BigCalendar'; const CalendarPage = () => { return (
    +
    ); diff --git a/src/components/ui/dialog.tsx b/src/components/ui/dialog.tsx new file mode 100644 index 0000000..6eea94c --- /dev/null +++ b/src/components/ui/dialog.tsx @@ -0,0 +1,122 @@ +'use client'; + +import * as React from 'react'; +import * as DialogPrimitive from '@radix-ui/react-dialog'; +import { X } from 'lucide-react'; + +import { cn } from '@/lib/utils'; + +const Dialog = DialogPrimitive.Root; + +const DialogTrigger = DialogPrimitive.Trigger; + +const DialogPortal = DialogPrimitive.Portal; + +const DialogClose = DialogPrimitive.Close; + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName; + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)); +DialogContent.displayName = DialogPrimitive.Content.displayName; + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
    +); +DialogHeader.displayName = 'DialogHeader'; + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
    +); +DialogFooter.displayName = 'DialogFooter'; + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogTitle.displayName = DialogPrimitive.Title.displayName; + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DialogDescription.displayName = DialogPrimitive.Description.displayName; + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogTrigger, + DialogClose, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +}; From 37ee8cd72b6930bb0d205d4e4d74c6449028aae9 Mon Sep 17 00:00:00 2001 From: Finnick223 Date: Tue, 21 Jan 2025 19:01:47 +0100 Subject: [PATCH 03/10] feat: configure React-Query and extract providers to separate file --- src/app/layout.tsx | 5 ++- src/lib/getQueryClient.ts | 38 ++++++++++++++++++++++ src/providers/ReactQueryClientProvider.tsx | 19 +++++++++++ src/providers/index.tsx | 7 ++++ 4 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 src/lib/getQueryClient.ts create mode 100644 src/providers/ReactQueryClientProvider.tsx create mode 100644 src/providers/index.tsx diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 46c11f2..1adf3fc 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,3 +1,4 @@ +import Providers from '@/providers'; import type { Metadata } from 'next'; import { ReactNode } from 'react'; import './globals.css'; @@ -14,7 +15,9 @@ export default function RootLayout({ }>) { return ( - {children} + + {children} + ); } diff --git a/src/lib/getQueryClient.ts b/src/lib/getQueryClient.ts new file mode 100644 index 0000000..d65f6cb --- /dev/null +++ b/src/lib/getQueryClient.ts @@ -0,0 +1,38 @@ +import { + QueryClient, + defaultShouldDehydrateQuery, + isServer, +} from '@tanstack/react-query'; + +function makeQueryClient() { + return new QueryClient({ + defaultOptions: { + queries: { + //stale time is needed for ssr + staleTime: 60 * 1000, + }, + dehydrate: { + // include pending queries in dehydration + shouldDehydrateQuery: (query) => + defaultShouldDehydrateQuery(query) || + query.state.status === 'pending', + }, + }, + }); +} + +let browserQueryClient: QueryClient | undefined = undefined; + +export function getQueryClient() { + if (isServer) { + // Server: always make a new query client + return makeQueryClient(); + } else { + // Browser: make a new query client if we don't already have one + // This is very important, so we don't re-make a new client if React + // suspends during the initial render. This may not be needed if we + // have a suspense boundary BELOW the creation of the query client + if (!browserQueryClient) browserQueryClient = makeQueryClient(); + return browserQueryClient; + } +} diff --git a/src/providers/ReactQueryClientProvider.tsx b/src/providers/ReactQueryClientProvider.tsx new file mode 100644 index 0000000..75781e0 --- /dev/null +++ b/src/providers/ReactQueryClientProvider.tsx @@ -0,0 +1,19 @@ +'use client'; + +import { getQueryClient } from '@/lib/getQueryClient'; +import { QueryClientProvider } from '@tanstack/react-query'; +import { ReactQueryDevtools } from '@tanstack/react-query-devtools'; + +export const ReactQueryClientProvider = ({ + children, +}: { + children: React.ReactNode; +}) => { + const queryClient = getQueryClient(); + return ( + + {children} + + + ); +}; diff --git a/src/providers/index.tsx b/src/providers/index.tsx new file mode 100644 index 0000000..7c0d5a6 --- /dev/null +++ b/src/providers/index.tsx @@ -0,0 +1,7 @@ +'use client'; +import type * as React from 'react'; +import { ReactQueryClientProvider } from './ReactQueryClientProvider'; + +export default function Providers({ children }: { children: React.ReactNode }) { + return {children}; +} From a8c4aede914f36eb564a8cdfef3f787e74526b00 Mon Sep 17 00:00:00 2001 From: Finnick223 Date: Tue, 21 Jan 2025 19:48:40 +0100 Subject: [PATCH 04/10] feat: communicate calendar with api, state management and UI improvements - install necessary dependencies - add and modify shadcn components - create dateTimePicker with hours - add get and post service functions - create query and mutation for events to manage state and synchronize data --- package-lock.json | 479 +++++++++++++++++- package.json | 15 +- .../calendar/_components/AddEventDialog.tsx | 146 +++--- .../_components/BigCalendar/BigCalendar.tsx | 55 +- src/app/dashboard/calendar/page.tsx | 29 +- src/components/ui/calendar.tsx | 112 ++++ src/components/ui/dateTimePicker.tsx | 95 ++++ src/components/ui/form.tsx | 179 +++++++ src/components/ui/popover.tsx | 33 ++ src/components/ui/scroll-area.tsx | 48 ++ src/components/ui/skeleton.tsx | 15 + src/constants/calendar.ts | 14 + src/services/events/fetchEvents.ts | 23 + src/services/events/sendEvent.ts | 15 + src/services/events/types/index.ts | 6 + 15 files changed, 1121 insertions(+), 143 deletions(-) create mode 100644 src/components/ui/calendar.tsx create mode 100644 src/components/ui/dateTimePicker.tsx create mode 100644 src/components/ui/form.tsx create mode 100644 src/components/ui/popover.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/components/ui/skeleton.tsx create mode 100644 src/constants/calendar.ts create mode 100644 src/services/events/fetchEvents.ts create mode 100644 src/services/events/sendEvent.ts create mode 100644 src/services/events/types/index.ts diff --git a/package-lock.json b/package-lock.json index 99916fc..719a6cc 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,32 +8,37 @@ "name": "drive-booker", "version": "0.1.0", "dependencies": { - "@hookform/resolvers": "^3.9.1", + "@hookform/resolvers": "^3.10.0", "@radix-ui/react-checkbox": "^1.1.2", "@radix-ui/react-dialog": "^1.1.4", - "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-label": "^2.1.1", + "@radix-ui/react-popover": "^1.1.4", + "@radix-ui/react-scroll-area": "^1.2.2", "@radix-ui/react-select": "^2.1.2", "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-tabs": "^1.1.1", - "@tanstack/react-query": "^5.59.20", + "@tanstack/react-query": "^5.64.2", "axios": "^1.7.7", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "date-fns": "^3.6.0", "iron-session": "^8.0.4", "lucide-react": "^0.454.0", "moment": "^2.30.1", "next": "^15.1.4", "react": "19.0.0-rc-02c0e824-20241028", "react-big-calendar": "^1.17.1", + "react-day-picker": "^8.10.1", "react-dom": "19.0.0-rc-02c0e824-20241028", - "react-hook-form": "^7.53.2", + "react-hook-form": "^7.54.2", "react-icons": "^5.3.0", "react-toastify": "^10.0.6", "tailwind-merge": "^2.5.4", "tailwindcss-animate": "^1.0.7", - "zod": "^3.23.8" + "zod": "^3.24.1" }, "devDependencies": { + "@tanstack/react-query-devtools": "^5.64.2", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.0.1", @@ -533,9 +538,9 @@ "license": "MIT" }, "node_modules/@hookform/resolvers": { - "version": "3.9.1", - "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.9.1.tgz", - "integrity": "sha512-ud2HqmGBM0P0IABqoskKWI6PEf6ZDDBZkFqe2Vnl+mTHCEHzr3ISjjZyCwTjC/qpL25JC9aIDkloQejvMeq0ug==", + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-3.10.0.tgz", + "integrity": "sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==", "license": "MIT", "peerDependencies": { "react-hook-form": "^7.0.0" @@ -1334,12 +1339,284 @@ } }, "node_modules/@radix-ui/react-label": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz", - "integrity": "sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.1.tgz", + "integrity": "sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==", "license": "MIT", "dependencies": { - "@radix-ui/react-primitive": "2.0.0" + "@radix-ui/react-primitive": "2.0.1" + }, + "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-label/node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.1" + }, + "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-popover": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.4.tgz", + "integrity": "sha512-aUACAkXx8LaFymDma+HQVji7WhvEhpFJ7+qPz17Nf4lLZqtreGOFRiNQWQmhzp7kEWg9cOyyQJpdIMUMPc/CPw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.3", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.1", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.1", + "@radix-ui/react-portal": "1.1.3", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-slot": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.1.0", + "aria-hidden": "^1.1.1", + "react-remove-scroll": "^2.6.1" + }, + "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-popover/node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-arrow": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz", + "integrity": "sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.1" + }, + "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-popover/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "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-popover/node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.3.tgz", + "integrity": "sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@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-popover/node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz", + "integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "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-popover/node_modules/@radix-ui/react-popper": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz", + "integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "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-popover/node_modules/@radix-ui/react-popper/node_modules/@floating-ui/react-dom": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-portal": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz", + "integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.0.1", + "@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-popover/node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@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-popover/node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.1" }, "peerDependencies": { "@types/react": "*", @@ -1551,6 +1828,105 @@ } } }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.2.tgz", + "integrity": "sha512-EFI1N/S3YxZEW/lJ/H1jY3njlvTd8tBmgKEn4GHi51+aMm94i6NmAJstsm5cu3yJwYqYc93gpCPm21FeAbFk6g==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-use-callback-ref": "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-scroll-area/node_modules/@radix-ui/primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-scroll-area/node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "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-scroll-area/node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@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-scroll-area/node_modules/@radix-ui/react-primitive": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", + "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.1.1" + }, + "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-select": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.2.tgz", @@ -2160,9 +2536,20 @@ } }, "node_modules/@tanstack/query-core": { - "version": "5.59.20", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.59.20.tgz", - "integrity": "sha512-e8vw0lf7KwfGe1if4uPFhvZRWULqHjFcz3K8AebtieXvnMOz5FSzlZe3mTLlPuUBcydCnBRqYs2YJ5ys68wwLg==", + "version": "5.64.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.64.2.tgz", + "integrity": "sha512-hdO8SZpWXoADNTWXV9We8CwTkXU88OVWRBcsiFrk7xJQnhm6WRlweDzMD+uH+GnuieTBVSML6xFa17C2cNV8+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.64.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.64.2.tgz", + "integrity": "sha512-3DautR5UpVZdk/qNIhioZVF7g8fdQZ1U98sBEEk4Tzz3tihSBNMPgwlP40nzgbPEDBIrn/j/oyyvNBVSo083Vw==", + "dev": true, "license": "MIT", "funding": { "type": "github", @@ -2170,12 +2557,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.59.20", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.59.20.tgz", - "integrity": "sha512-Zly0egsK0tFdfSbh5/mapSa+Zfc3Et0Zkar7Wo5sQkFzWyB3p3uZWOHR2wrlAEEV2L953eLuDBtbgFvMYiLvUw==", + "version": "5.64.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.64.2.tgz", + "integrity": "sha512-3pakNscZNm8KJkxmovvtZ4RaXLyiYYobwleTMvpIGUoKRa8j8VlrQKNl5W8VUEfVfZKkikvXVddLuWMbcSCA1Q==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.59.20" + "@tanstack/query-core": "5.64.2" }, "funding": { "type": "github", @@ -2185,6 +2572,24 @@ "react": "^18 || ^19" } }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.64.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.64.2.tgz", + "integrity": "sha512-+ZjJVnPzc8BUV/Eklu2k9T/IAyAyvwoCHqOaOrk2sbU33LFhM52BpX4eyENXn0bx5LwV3DJZgEQlIzucoemfGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tanstack/query-devtools": "5.64.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.64.2", + "react": "^18 || ^19" + } + }, "node_modules/@testing-library/dom": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.0.tgz", @@ -3707,6 +4112,16 @@ "integrity": "sha512-QWxYLR5P/6GStZcdem+V1xoto6DMadYWpMXU82ES3/RfR3Wdwr3D0+be7mgOJ+Ov0G9D5Dmb9T17sNLQYj9XOg==", "license": "MIT" }, + "node_modules/date-fns": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", + "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, "node_modules/dayjs": { "version": "1.11.13", "resolved": "https://registry.npmjs.org/dayjs/-/dayjs-1.11.13.tgz", @@ -7413,6 +7828,20 @@ "react": ">=15.0.0" } }, + "node_modules/react-day-picker": { + "version": "8.10.1", + "resolved": "https://registry.npmjs.org/react-day-picker/-/react-day-picker-8.10.1.tgz", + "integrity": "sha512-TMx7fNbhLk15eqcMt+7Z7S2KF7mfTId/XJDjKE8f+IUcFn0l08/kI4FiYTL/0yuOLmEcbR4Fwe3GJf/NiiMnPA==", + "license": "MIT", + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/gpbl" + }, + "peerDependencies": { + "date-fns": "^2.28.0 || ^3.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-dom": { "version": "19.0.0-rc-02c0e824-20241028", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0-rc-02c0e824-20241028.tgz", @@ -7425,9 +7854,9 @@ } }, "node_modules/react-hook-form": { - "version": "7.53.2", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.53.2.tgz", - "integrity": "sha512-YVel6fW5sOeedd1524pltpHX+jgU2u3DSDtXEaBORNdqiNrsX/nUI/iGXONegttg0mJVnfrIkiV0cmTU6Oo2xw==", + "version": "7.54.2", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.54.2.tgz", + "integrity": "sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==", "license": "MIT", "engines": { "node": ">=18.0.0" @@ -9391,9 +9820,9 @@ } }, "node_modules/zod": { - "version": "3.23.8", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.23.8.tgz", - "integrity": "sha512-XBx9AXhXktjUqnepgTiE5flcKIYWi/rme0Eaj+5Y0lftuGBq+jyRu/md4WnuxqgP1ubdpNCsYEYPxrzVHD8d6g==", + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", + "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/colinhacks" diff --git a/package.json b/package.json index 8e4a4d9..fda5704 100644 --- a/package.json +++ b/package.json @@ -14,32 +14,37 @@ "prepare": "husky" }, "dependencies": { - "@hookform/resolvers": "^3.9.1", + "@hookform/resolvers": "^3.10.0", "@radix-ui/react-checkbox": "^1.1.2", "@radix-ui/react-dialog": "^1.1.4", - "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-label": "^2.1.1", + "@radix-ui/react-popover": "^1.1.4", + "@radix-ui/react-scroll-area": "^1.2.2", "@radix-ui/react-select": "^2.1.2", "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-tabs": "^1.1.1", - "@tanstack/react-query": "^5.59.20", + "@tanstack/react-query": "^5.64.2", "axios": "^1.7.7", "class-variance-authority": "^0.7.0", "clsx": "^2.1.1", + "date-fns": "^3.6.0", "iron-session": "^8.0.4", "lucide-react": "^0.454.0", "moment": "^2.30.1", "next": "^15.1.4", "react": "19.0.0-rc-02c0e824-20241028", "react-big-calendar": "^1.17.1", + "react-day-picker": "^8.10.1", "react-dom": "19.0.0-rc-02c0e824-20241028", - "react-hook-form": "^7.53.2", + "react-hook-form": "^7.54.2", "react-icons": "^5.3.0", "react-toastify": "^10.0.6", "tailwind-merge": "^2.5.4", "tailwindcss-animate": "^1.0.7", - "zod": "^3.23.8" + "zod": "^3.24.1" }, "devDependencies": { + "@tanstack/react-query-devtools": "^5.64.2", "@testing-library/dom": "^10.4.0", "@testing-library/jest-dom": "^6.6.3", "@testing-library/react": "^16.0.1", diff --git a/src/app/dashboard/calendar/_components/AddEventDialog.tsx b/src/app/dashboard/calendar/_components/AddEventDialog.tsx index b933ebb..deedebd 100644 --- a/src/app/dashboard/calendar/_components/AddEventDialog.tsx +++ b/src/app/dashboard/calendar/_components/AddEventDialog.tsx @@ -1,6 +1,7 @@ 'use client'; import { Button } from '@/components/ui/button'; +import { DateTimePicker } from '@/components/ui/dateTimePicker'; import { Dialog, DialogClose, @@ -13,28 +14,58 @@ import { } from '@/components/ui/dialog'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; +import { getQueryClient } from '@/lib/getQueryClient'; +import { sendEvent } from '@/services/events/sendEvent'; +import { Event } from '@/services/events/types'; +import { useMutation } from '@tanstack/react-query'; import { useState } from 'react'; +import { FormProvider, useForm } from 'react-hook-form'; +import { toast } from 'react-toastify'; export default function AddEventDialog() { const [open, setOpen] = useState(false); - const [title, setTitle] = useState(''); - const [startDate, setStartDate] = useState(''); - const [endDate, setEndDate] = useState(''); + const queryClient = getQueryClient(); + const methods = useForm({ + defaultValues: { + title: '', + start: new Date(), + end: new Date(), + }, + }); - const handleSubmit = (event: React.FormEvent) => { - event.preventDefault(); - console.log({ title, startDate, endDate }); - setOpen(false); - // Reset form fields - setTitle(''); - setStartDate(''); - setEndDate(''); + const { mutate } = useMutation({ + mutationFn: sendEvent, + onSuccess: () => { + queryClient.invalidateQueries({ queryKey: ['events'] }); + toast.success('Wydarzenie zostało dodane'); + setOpen(false); + methods.reset(); + }, + onError: (error: Error) => { + console.error('Error creating event:', error); + toast.error('wystąpił błąd'); + }, + }); + + const onSubmit = (data: Event) => { + const { title, start, end } = data; + if (!title || !start || !end) { + toast.info('Please fill in all fields'); + return; + } + mutate({ + title, + start: start, + end: end, + }); }; return ( - + @@ -43,53 +74,52 @@ export default function AddEventDialog() { Wprowadź szczegóły wydarzenia. Zapisz zmiany gdy skończysz. -
    -
    -
    - - setTitle(e.target.value)} - className='col-span-3' - /> -
    -
    - - setStartDate(e.target.value)} - className='col-span-3' - /> -
    -
    - - setEndDate(e.target.value)} - className='col-span-3' - /> + + +
    +
    + + +
    +
    + +
    + methods.setValue('start', date)} + /> +
    +
    +
    + +
    + methods.setValue('end', date)} + /> +
    +
    -
    - - - - - - - + + + + + + + +
    ); diff --git a/src/app/dashboard/calendar/_components/BigCalendar/BigCalendar.tsx b/src/app/dashboard/calendar/_components/BigCalendar/BigCalendar.tsx index 2c2bdc3..726ed00 100644 --- a/src/app/dashboard/calendar/_components/BigCalendar/BigCalendar.tsx +++ b/src/app/dashboard/calendar/_components/BigCalendar/BigCalendar.tsx @@ -1,4 +1,7 @@ 'use client'; +import { messages } from '@/constants/calendar'; +import { eventsQueryOptions } from '@/services/events/fetchEvents'; +import { useSuspenseQuery } from '@tanstack/react-query'; import moment from 'moment'; import 'moment/locale/pl'; import { SetStateAction, useState } from 'react'; @@ -6,56 +9,8 @@ import { Calendar, momentLocalizer, View, Views } from 'react-big-calendar'; import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'; import './bigCalendarStyling.css'; -const messages = { - allDay: 'Cały dzień', - previous: 'Poprzedni', - next: 'Następny', - today: 'Dzisiaj', - month: 'Miesiąc', - week: 'Tydzień', - day: 'Dzień', - agenda: 'Agenda', - date: 'Data', - time: 'Godzina', - event: 'Wydarzenie', - noEventsInRange: 'Brak wydarzeń w tym zakresie dat.', -}; - -const events = [ - { - id: 1, - title: 'Morning Meeting', - start: new Date(2025, 1, 7, 9, 0, 0), // February 7, 2025, 9:00 AM - end: new Date(2025, 1, 7, 10, 0, 0), // February 7, 2025, 10:00 AM - }, - { - id: 2, - title: 'Lunch Break', - start: new Date(2025, 2, 13, 12, 0, 0), // March 13, 2025, 12:00 PM - end: new Date(2025, 2, 13, 13, 0, 0), // March 13, 2025, 1:00 PM - }, - { - id: 3, - title: 'Afternoon Workshop', - start: new Date(2025, 10, 6, 14, 0, 0), // November 6, 2025, 2:00 PM - end: new Date(2025, 10, 6, 16, 0, 0), // November 6, 2025, 4:00 PM - }, - { - id: 4, - title: 'All-Day Event', - start: new Date(2025, 3, 9), // April 9, 2025 (all-day) - end: new Date(2025, 3, 9), - allDay: true, - }, - { - id: 5, - title: 'Evening Networking', - start: new Date(2025, 3, 11, 18, 30, 0), // April 11, 2025, 6:30 PM - end: new Date(2025, 3, 11, 20, 0, 0), // April 11, 2025, 8:00 PM - }, -]; - const BigCalendar = () => { + const { data: events } = useSuspenseQuery(eventsQueryOptions); const [view, setView] = useState(Views.WEEK); const [date, setDate] = useState(new Date()); moment.locale('pl'); @@ -72,7 +27,7 @@ const BigCalendar = () => { return ( { +export default async function CalendarPage() { + const queryClient = getQueryClient(); + + // Prefetch the events on the server side + await queryClient.prefetchQuery(eventsQueryOptions); + return (
    - - + + + + +
    + } + > + + + ); -}; -export default CalendarPage; +} diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx new file mode 100644 index 0000000..0299293 --- /dev/null +++ b/src/components/ui/calendar.tsx @@ -0,0 +1,112 @@ +'use client'; + +import { buttonVariants } from '@/components/ui/button'; +import { ScrollArea } from '@/components/ui/scroll-area'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select'; +import { cn } from '@/lib/utils'; +import { ChevronLeft, ChevronRight } from 'lucide-react'; +import * as React from 'react'; +import { DayPicker, DropdownProps } from 'react-day-picker'; + +export type CalendarProps = React.ComponentProps; + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...props +}: CalendarProps) { + return ( + { + const options = React.Children.toArray( + children, + ) as React.ReactElement>[]; + const selected = options.find((child) => child.props.value === value); + const handleChange = (value: string) => { + const changeEvent = { + target: { value }, + } as React.ChangeEvent; + onChange?.(changeEvent); + }; + return ( + + ); + }, + IconLeft: () => , + IconRight: () => , + }} + {...props} + /> + ); +} +Calendar.displayName = 'Calendar'; + +export { Calendar }; diff --git a/src/components/ui/dateTimePicker.tsx b/src/components/ui/dateTimePicker.tsx new file mode 100644 index 0000000..27f3a63 --- /dev/null +++ b/src/components/ui/dateTimePicker.tsx @@ -0,0 +1,95 @@ +'use client'; + +import { cn } from '@/lib/utils'; +import { format } from 'date-fns'; +import { CalendarIcon } from 'lucide-react'; +import { useState } from 'react'; +import { Button } from './button'; +import { Calendar } from './calendar'; +import { Popover, PopoverContent, PopoverTrigger } from './popover'; +import { ScrollArea } from './scroll-area'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from './select'; + +type DateTimePickerProps = { + value: Date; + onChange: (value: Date) => void; +}; + +export function DateTimePicker({ value, onChange }: DateTimePickerProps) { + const [isOpen, setIsOpen] = useState(false); + const [time, setTime] = useState('12:00'); + const [date, setDate] = useState(value); + + return ( + + + + + + { + if (!selectedDate) return; + const [hours, minutes] = time.split(':'); + selectedDate?.setHours(parseInt(hours), parseInt(minutes)); + setDate(selectedDate); + onChange(selectedDate); + }} + onDayClick={() => setIsOpen(false)} + fromYear={2000} + toYear={new Date().getFullYear()} + disabled={(date) => + Number(date) < Date.now() - 1000 * 60 * 60 * 24 || + Number(date) > Date.now() + 1000 * 60 * 60 * 24 * 30 + } + /> + + + + ); +} diff --git a/src/components/ui/form.tsx b/src/components/ui/form.tsx new file mode 100644 index 0000000..6b5c3bb --- /dev/null +++ b/src/components/ui/form.tsx @@ -0,0 +1,179 @@ +'use client'; + +import * as React from 'react'; +import * as LabelPrimitive from '@radix-ui/react-label'; +import { Slot } from '@radix-ui/react-slot'; +import { + Controller, + ControllerProps, + FieldPath, + FieldValues, + FormProvider, + useFormContext, +} from 'react-hook-form'; + +import { cn } from '@/lib/utils'; +import { Label } from '@/components/ui/label'; + +const Form = FormProvider; + +type FormFieldContextValue< + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +> = { + name: TName; +}; + +const FormFieldContext = React.createContext( + {} as FormFieldContextValue, +); + +const FormField = < + TFieldValues extends FieldValues = FieldValues, + TName extends FieldPath = FieldPath, +>({ + ...props +}: ControllerProps) => { + return ( + + + + ); +}; + +const useFormField = () => { + const fieldContext = React.useContext(FormFieldContext); + const itemContext = React.useContext(FormItemContext); + const { getFieldState, formState } = useFormContext(); + + const fieldState = getFieldState(fieldContext.name, formState); + + if (!fieldContext) { + throw new Error('useFormField should be used within '); + } + + const { id } = itemContext; + + return { + id, + name: fieldContext.name, + formItemId: `${id}-form-item`, + formDescriptionId: `${id}-form-item-description`, + formMessageId: `${id}-form-item-message`, + ...fieldState, + }; +}; + +type FormItemContextValue = { + id: string; +}; + +const FormItemContext = React.createContext( + {} as FormItemContextValue, +); + +const FormItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const id = React.useId(); + + return ( + +
    + + ); +}); +FormItem.displayName = 'FormItem'; + +const FormLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => { + const { error, formItemId } = useFormField(); + + return ( +
    + } + > + + - - + + ); } diff --git a/src/services/events/deleteEvent.ts b/src/services/events/deleteEvent.ts new file mode 100644 index 0000000..6ce14eb --- /dev/null +++ b/src/services/events/deleteEvent.ts @@ -0,0 +1,11 @@ +import { axiosInstance } from '@/lib/axiosInstance'; +import { Event } from './types'; + +export async function deleteEvent(event: Event): Promise { + try { + return axiosInstance.delete(`/api/events/${event.id}`); + } catch (error) { + console.error('Failed to delete event:', error); + throw new Error('Could not delete event'); + } +} diff --git a/src/services/events/updateEvent.ts b/src/services/events/updateEvent.ts new file mode 100644 index 0000000..893d540 --- /dev/null +++ b/src/services/events/updateEvent.ts @@ -0,0 +1,15 @@ +import { axiosInstance } from '@/lib/axiosInstance'; +import { Event } from './types'; + +export async function updateEvent(event: Event): Promise { + try { + return axiosInstance.put(`/api/events/${event.id}`, { + title: event.title, + start: event.start, + end: event.end, + }); + } catch (error) { + console.error('Failed to update event:', error); + throw new Error('Could not update event'); + } +} From 5ccd8314052d9fa2d99f45d35b60e94921cd9a91 Mon Sep 17 00:00:00 2001 From: Finnick223 Date: Sat, 25 Jan 2025 23:04:08 +0100 Subject: [PATCH 06/10] fix: show hours list on open --- src/components/ui/dateTimePicker.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/components/ui/dateTimePicker.tsx b/src/components/ui/dateTimePicker.tsx index 27f3a63..d5831ef 100644 --- a/src/components/ui/dateTimePicker.tsx +++ b/src/components/ui/dateTimePicker.tsx @@ -23,7 +23,13 @@ type DateTimePickerProps = { export function DateTimePicker({ value, onChange }: DateTimePickerProps) { const [isOpen, setIsOpen] = useState(false); - const [time, setTime] = useState('12:00'); + + const initialTime = `${value.getHours().toString().padStart(2, '0')}:${value + .getMinutes() + .toString() + .padStart(2, '0')}`; + + const [time, setTime] = useState(initialTime); const [date, setDate] = useState(value); return ( @@ -49,7 +55,6 @@ export function DateTimePicker({ value, onChange }: DateTimePickerProps) { setDate(selectedDate); onChange(selectedDate); }} - onDayClick={() => setIsOpen(false)} fromYear={2000} toYear={new Date().getFullYear()} disabled={(date) => @@ -59,6 +64,7 @@ export function DateTimePicker({ value, onChange }: DateTimePickerProps) { /> + methods.setValue('driverId', value) + } + > + + + + + {drivers.length > 0 ? ( + drivers.map((driver) => ( + + {driver.name} + + )) + ) : ( + + Brak kursantów + + )} + + +
    +
    ); } diff --git a/src/providers/index.tsx b/src/providers/index.tsx index 7c0d5a6..3f14d33 100644 --- a/src/providers/index.tsx +++ b/src/providers/index.tsx @@ -1,7 +1,13 @@ 'use client'; import type * as React from 'react'; import { ReactQueryClientProvider } from './ReactQueryClientProvider'; +import { ToastContainer } from 'react-toastify'; export default function Providers({ children }: { children: React.ReactNode }) { - return {children}; + return ( + + {children} + + + ); } From 8746d4bf5605f15d1ff5f2f416f7c02ee05abce6 Mon Sep 17 00:00:00 2001 From: Finnick223 Date: Thu, 27 Feb 2025 23:49:59 +0100 Subject: [PATCH 10/10] fix: empty drivers array if user isnt instructor --- src/app/dashboard/calendar/page.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/dashboard/calendar/page.tsx b/src/app/dashboard/calendar/page.tsx index 3cc1b0a..267495d 100644 --- a/src/app/dashboard/calendar/page.tsx +++ b/src/app/dashboard/calendar/page.tsx @@ -15,12 +15,11 @@ export default async function CalendarPage() { // Determine role from session const { hasRole } = await checkUserRole(); const endpoint = hasRole(['instructor']) ? 'instructor' : 'driver'; + const drivers = hasRole(['instructor']) ? await fetchDrivers() : []; // Prefetch the events on the server side await queryClient.prefetchQuery(getEventsQueryOptions(endpoint)); - const drivers = await fetchDrivers(); - return (