From d2a0f6125b9c8dc4ab837596b10cadb5a66bcf00 Mon Sep 17 00:00:00 2001 From: Semir Date: Thu, 15 May 2025 08:40:12 +0200 Subject: [PATCH 01/20] feat: Basic Calendar without any functions --- package.json | 3 +- src/app/home/page.tsx | 121 ++++++++++- yarn.lock | 484 +++++++++++++++++++++++++----------------- 3 files changed, 402 insertions(+), 206 deletions(-) diff --git a/package.json b/package.json index 9d282dd..dfd3eef 100644 --- a/package.json +++ b/package.json @@ -38,7 +38,8 @@ "next": "15.3.3", "next-auth": "^5.0.0-beta.25", "next-themes": "^0.4.6", - "react": "^19.0.0", + "react": "^19.1.0", + "react-calendar": "^5.1.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.2.0" }, diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index 4e6773b..527b231 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -1,15 +1,114 @@ -import { RedirectButton } from '@/components/user/redirect-button'; -import { ThemePicker } from '@/components/user/theme-picker'; +import React from 'react'; + +const Calendar: React.FC = () => { + const today = new Date(); + const currentYear = today.getFullYear(); + const currentMonth = today.getMonth(); + const currentDate = today.getDate(); + + const firstDayMonth = new Date(currentYear, currentMonth, 1); + const lastDayMonth = new Date(currentYear, currentMonth + 1, 0); + const startDay = (firstDayMonth.getDay() + 6) % 7; + const daysMonth = lastDayMonth.getDate(); + + const weeks: (number | null)[][] = []; + let currentDay = 1; + + const firstWeek: (number | null)[] = []; + for (let i = 0; i < 7; i++) { + if (i < startDay) { + firstWeek.push(null); + } else { + firstWeek.push(currentDay); + currentDay++; + } + } + weeks.push(firstWeek); + + while (currentDay <= daysMonth) { + const week: (number | null)[] = []; + for (let i = 0; i < 7; i++) { + if (currentDay <= daysMonth) { + week.push(currentDay); + currentDay++; + } else { + week.push(null); + } + } + weeks.push(week); + } + + const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; -export default function Home() { return ( -
-
{}
-
-

Home

- - -
+
+

+ {new Date(currentYear, currentMonth).toLocaleString('en-EN', { + month: 'long', + year: 'numeric' + })} +

+ + + + {weekdays.map((day, index) => ( + + ))} + + + + {weeks.map((week, weekIndex) => ( + + {week.map((day, dayIndex) => ( + + ))} + + ))} + +
+ {day} +
+ {day && ( +
+ {day} +
+ )} +
); -} +}; + +export default Calendar; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 3cfc4ae..0430071 100644 --- a/yarn.lock +++ b/yarn.lock @@ -104,20 +104,20 @@ __metadata: linkType: hard "@eslint/config-array@npm:^0.20.0": - version: 0.20.0 - resolution: "@eslint/config-array@npm:0.20.0" + 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/94bc5d0abb96dc5295ff559925242ff75a54eacfb3576677e95917e42f7175e1c4b87bf039aa2a872f949b4852ad9724bf2f7529aaea6b98f28bb3fca7f1d659 + checksum: 10c0/709108c3925d83c2166024646829ab61ba5fa85c6568daefd32508899f46ed8dc36d7153042df6dcc7e58ad543bc93298b646575daecb5eb4e39a43d838dab42 languageName: node linkType: hard "@eslint/config-helpers@npm:^0.2.1": - version: 0.2.2 - resolution: "@eslint/config-helpers@npm:0.2.2" - checksum: 10c0/98f7cefe484bb754674585d9e73cf1414a3ab4fd0783c385465288d13eb1a8d8e7d7b0611259fc52b76b396c11a13517be5036d1f48eeb877f6f0a6b9c4f03ad + version: 0.2.3 + resolution: "@eslint/config-helpers@npm:0.2.3" + checksum: 10c0/8fd36d7f33013628787947c81894807c7498b31eacf6648efa6d7c7a99aac6bf0d59a8a4d14f968ec2aeebefb76a1a7e4fd4cd556a296323d4711b3d7a7cda22 languageName: node linkType: hard @@ -130,6 +130,15 @@ __metadata: languageName: node linkType: hard +"@eslint/core@npm:^0.15.0": + version: 0.15.0 + resolution: "@eslint/core@npm:0.15.0" + dependencies: + "@types/json-schema": "npm:^7.0.15" + checksum: 10c0/9882c69acfe29743ce473a619d5248589c6687561afaabe8ec8d7ffed07592db16edcca3af022f33ea92fe5f6cfbe3545ee53e89292579d22a944ebaeddcf72d + languageName: node + linkType: hard + "@eslint/eslintrc@npm:3.3.1, @eslint/eslintrc@npm:^3.3.1": version: 3.3.1 resolution: "@eslint/eslintrc@npm:3.3.1" @@ -162,12 +171,12 @@ __metadata: linkType: hard "@eslint/plugin-kit@npm:^0.3.1": - version: 0.3.1 - resolution: "@eslint/plugin-kit@npm:0.3.1" + version: 0.3.2 + resolution: "@eslint/plugin-kit@npm:0.3.2" dependencies: - "@eslint/core": "npm:^0.14.0" + "@eslint/core": "npm:^0.15.0" levn: "npm:^0.4.1" - checksum: 10c0/a75f0b5d38430318a551b83e27bee570747eb50beeb76b03f64b0e78c2c27ef3d284cfda3443134df028db3251719bc0850c105f778122f6ad762d5270ec8063 + checksum: 10c0/e069b0a46eb9fa595a1ac7dea4540a9daa493afba88875ee054e9117609c1c41555e779303cb4cff36cf88f603ba6eba2556a927e8ced77002828206ee17fc7e languageName: node linkType: hard @@ -542,14 +551,14 @@ __metadata: languageName: node linkType: hard -"@napi-rs/wasm-runtime@npm:^0.2.10": - version: 0.2.10 - resolution: "@napi-rs/wasm-runtime@npm:0.2.10" +"@napi-rs/wasm-runtime@npm:^0.2.10, @napi-rs/wasm-runtime@npm:^0.2.11": + version: 0.2.11 + resolution: "@napi-rs/wasm-runtime@npm:0.2.11" dependencies: "@emnapi/core": "npm:^1.4.3" "@emnapi/runtime": "npm:^1.4.3" "@tybys/wasm-util": "npm:^0.9.0" - checksum: 10c0/4dce9bbb94a8969805574e1b55fdbeb7623348190265d77f6507ba32e535610deeb53a33ba0bb8b05a6520f379d418b92e8a01c5cd7b9486b136d2c0c26be0bd + checksum: 10c0/049bd14c58b99fbe0967b95e9921c5503df196b59be22948d2155f17652eb305cff6728efd8685338b855da7e476dd2551fbe3a313fc2d810938f0717478441e languageName: node linkType: hard @@ -1669,104 +1678,104 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": - version: 8.33.1 - resolution: "@typescript-eslint/eslint-plugin@npm:8.33.1" + version: 8.34.0 + resolution: "@typescript-eslint/eslint-plugin@npm:8.34.0" dependencies: "@eslint-community/regexpp": "npm:^4.10.0" - "@typescript-eslint/scope-manager": "npm:8.33.1" - "@typescript-eslint/type-utils": "npm:8.33.1" - "@typescript-eslint/utils": "npm:8.33.1" - "@typescript-eslint/visitor-keys": "npm:8.33.1" + "@typescript-eslint/scope-manager": "npm:8.34.0" + "@typescript-eslint/type-utils": "npm:8.34.0" + "@typescript-eslint/utils": "npm:8.34.0" + "@typescript-eslint/visitor-keys": "npm:8.34.0" graphemer: "npm:^1.4.0" ignore: "npm:^7.0.0" natural-compare: "npm:^1.4.0" ts-api-utils: "npm:^2.1.0" peerDependencies: - "@typescript-eslint/parser": ^8.33.1 + "@typescript-eslint/parser": ^8.34.0 eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10c0/35544068f175ca25296b42d0905065b40653a92c62e55414be68f62ddab580d7d768ee3c1276195fd8b8dd49de738ab7b41b8685e6fe2cd341cfca7320569166 + checksum: 10c0/905a05d15f4b0367838ec445f9890321d87470198bf7a589278fc0f38c82cf3ccc1efce4acd3c9c94ee6149d5579ef58606fb7c50f4db50c830de65af8c27c6d languageName: node linkType: hard "@typescript-eslint/parser@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": - version: 8.33.1 - resolution: "@typescript-eslint/parser@npm:8.33.1" + version: 8.34.0 + resolution: "@typescript-eslint/parser@npm:8.34.0" dependencies: - "@typescript-eslint/scope-manager": "npm:8.33.1" - "@typescript-eslint/types": "npm:8.33.1" - "@typescript-eslint/typescript-estree": "npm:8.33.1" - "@typescript-eslint/visitor-keys": "npm:8.33.1" + "@typescript-eslint/scope-manager": "npm:8.34.0" + "@typescript-eslint/types": "npm:8.34.0" + "@typescript-eslint/typescript-estree": "npm:8.34.0" + "@typescript-eslint/visitor-keys": "npm:8.34.0" debug: "npm:^4.3.4" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10c0/be1c1313c342d956f5adfbd56f79865894cc9cabf93992515a690559c3758538868270671b222f90e4cabc2dcab82256aeb3ccea7502de9cc69e47b9b17ed45f + checksum: 10c0/a829be00ea3455c1e50983c8b44476fbfc9329d019764e407c4d591a95dbd168f83f13e309751242bb4fdc02f89cb51ca5cdc912a12b10f69eebcb1c46dcc39b languageName: node linkType: hard -"@typescript-eslint/project-service@npm:8.33.1": - version: 8.33.1 - resolution: "@typescript-eslint/project-service@npm:8.33.1" +"@typescript-eslint/project-service@npm:8.34.0": + version: 8.34.0 + resolution: "@typescript-eslint/project-service@npm:8.34.0" dependencies: - "@typescript-eslint/tsconfig-utils": "npm:^8.33.1" - "@typescript-eslint/types": "npm:^8.33.1" + "@typescript-eslint/tsconfig-utils": "npm:^8.34.0" + "@typescript-eslint/types": "npm:^8.34.0" debug: "npm:^4.3.4" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10c0/b2ff7653aef4648bdff8aafc69b9de434184827216709f8a36427536ac7082a8adf1c5ac12a0a2bb023b46dfad8f6fee238028acc94af622956af7f22362de6f + checksum: 10c0/88e64b8daf7db9603277fcbeb9e585e70ec6d6e34fa10d4b60f421e48081cc7c1f6acb01e1ee9dd95e10c0601f164c1defbfe6c9d1edc9822089bb72dbb0fc80 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:8.33.1": - version: 8.33.1 - resolution: "@typescript-eslint/scope-manager@npm:8.33.1" +"@typescript-eslint/scope-manager@npm:8.34.0": + version: 8.34.0 + resolution: "@typescript-eslint/scope-manager@npm:8.34.0" dependencies: - "@typescript-eslint/types": "npm:8.33.1" - "@typescript-eslint/visitor-keys": "npm:8.33.1" - checksum: 10c0/03a6fd2b0a8ebeb62083a8f51658f0c42391cbfb632411542569a3a227d53bdb0332026ef4d5adc4780e5350d1d8b89e5b19667ed899afd26506e60c70192692 + "@typescript-eslint/types": "npm:8.34.0" + "@typescript-eslint/visitor-keys": "npm:8.34.0" + checksum: 10c0/35af36bddc4c227cb0bac42192c40b38179ced30866b6aac642781e21c3f3b1c72051eb4f685d7c99517c3296dd6ba83dd8360e4072e8dcf604aae266eece1b4 languageName: node linkType: hard -"@typescript-eslint/tsconfig-utils@npm:8.33.1, @typescript-eslint/tsconfig-utils@npm:^8.33.1": - version: 8.33.1 - resolution: "@typescript-eslint/tsconfig-utils@npm:8.33.1" +"@typescript-eslint/tsconfig-utils@npm:8.34.0, @typescript-eslint/tsconfig-utils@npm:^8.34.0": + version: 8.34.0 + resolution: "@typescript-eslint/tsconfig-utils@npm:8.34.0" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10c0/242e8f271d2e6e51446d337e1e59e8c91b66c0241da0fb861f536eb86cc3b53d1727c41e12e1ba070fa2451c8bc517c1ec50decaffa92a7c612b2aba29872777 + checksum: 10c0/98246f89d169d3feb453a6a8552c51d10225cb00c4ff1501549b7846e564ad0e218b644cd94ce779dceed07dcb9035c53fd32186b4c0223b7b2a1f7295b120c3 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:8.33.1": - version: 8.33.1 - resolution: "@typescript-eslint/type-utils@npm:8.33.1" +"@typescript-eslint/type-utils@npm:8.34.0": + version: 8.34.0 + resolution: "@typescript-eslint/type-utils@npm:8.34.0" dependencies: - "@typescript-eslint/typescript-estree": "npm:8.33.1" - "@typescript-eslint/utils": "npm:8.33.1" + "@typescript-eslint/typescript-estree": "npm:8.34.0" + "@typescript-eslint/utils": "npm:8.34.0" debug: "npm:^4.3.4" ts-api-utils: "npm:^2.1.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10c0/59843eeb7c652306d130104d7cb0f7dea1cc95a6cf6345609efbae130f24e3c4a9472780332af4247337e152b7955540b15fd9b907c04a5d265b888139818266 + checksum: 10c0/7c25d7f4186411190142390467160e81384d400cfb21183d8a305991c723da0a74e5528cdce30b5f2cb6d9d2f6af7c0981c20c18b45fc084b35632429270ae80 languageName: node linkType: hard -"@typescript-eslint/types@npm:8.33.1, @typescript-eslint/types@npm:^8.33.1": - version: 8.33.1 - resolution: "@typescript-eslint/types@npm:8.33.1" - checksum: 10c0/3083c184c882475eed1f9d1a8961dad30ef834c662bc826ff9a959ff1eed49aad21a73b2b93c4062799feafff5f5f24aebb1df17e198808aa19d4c8de1e64095 +"@typescript-eslint/types@npm:8.34.0, @typescript-eslint/types@npm:^8.34.0": + version: 8.34.0 + resolution: "@typescript-eslint/types@npm:8.34.0" + checksum: 10c0/5d32b2ac03e4cbc1ac1777a53ee83d6d7887a783363bab4f0a6f7550a9e9df0254971cdf71e13b988e2215f2939e7592404856b8acb086ec63c4479c0225c742 languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:8.33.1": - version: 8.33.1 - resolution: "@typescript-eslint/typescript-estree@npm:8.33.1" +"@typescript-eslint/typescript-estree@npm:8.34.0": + version: 8.34.0 + resolution: "@typescript-eslint/typescript-estree@npm:8.34.0" dependencies: - "@typescript-eslint/project-service": "npm:8.33.1" - "@typescript-eslint/tsconfig-utils": "npm:8.33.1" - "@typescript-eslint/types": "npm:8.33.1" - "@typescript-eslint/visitor-keys": "npm:8.33.1" + "@typescript-eslint/project-service": "npm:8.34.0" + "@typescript-eslint/tsconfig-utils": "npm:8.34.0" + "@typescript-eslint/types": "npm:8.34.0" + "@typescript-eslint/visitor-keys": "npm:8.34.0" debug: "npm:^4.3.4" fast-glob: "npm:^3.3.2" is-glob: "npm:^4.0.3" @@ -1775,156 +1784,177 @@ __metadata: ts-api-utils: "npm:^2.1.0" peerDependencies: typescript: ">=4.8.4 <5.9.0" - checksum: 10c0/293a93d25046e05fdc3887232191c3f3ee771c0f5b1426d63deaf0541db1cb80b4307a80805c78b092206c9b267884a7e6b5905dc1b3c26f28bb4de47fd9ee8f + checksum: 10c0/e678982b0009e895aee2b4ccc55bb9ea5473a32e846a97c63d0c6a978c72e1a29e506e6a5f9dda45e9b7803e6c3e3abcdf4c316af1c59146abef4e10e0e94129 languageName: node linkType: hard -"@typescript-eslint/utils@npm:8.33.1": - version: 8.33.1 - resolution: "@typescript-eslint/utils@npm:8.33.1" +"@typescript-eslint/utils@npm:8.34.0": + version: 8.34.0 + resolution: "@typescript-eslint/utils@npm:8.34.0" dependencies: "@eslint-community/eslint-utils": "npm:^4.7.0" - "@typescript-eslint/scope-manager": "npm:8.33.1" - "@typescript-eslint/types": "npm:8.33.1" - "@typescript-eslint/typescript-estree": "npm:8.33.1" + "@typescript-eslint/scope-manager": "npm:8.34.0" + "@typescript-eslint/types": "npm:8.34.0" + "@typescript-eslint/typescript-estree": "npm:8.34.0" peerDependencies: eslint: ^8.57.0 || ^9.0.0 typescript: ">=4.8.4 <5.9.0" - checksum: 10c0/12263df6eb32e8175236ad899687c062b50cfe4a0e66307d25ad2bf85a3e911faacbfbea4df180a59ebb5913fe1cc1f53fe3914695c7d802dd318bbc846fea26 + checksum: 10c0/d759cf6f1b1b23d7d8ab922345e7b68b7c829f4bad841164312cfa3a3e8e818b962dd0d96c1aca7fd7c10248d56538d9714df5f3cfec9f159ca0a139feac60b9 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:8.33.1": - version: 8.33.1 - resolution: "@typescript-eslint/visitor-keys@npm:8.33.1" +"@typescript-eslint/visitor-keys@npm:8.34.0": + version: 8.34.0 + resolution: "@typescript-eslint/visitor-keys@npm:8.34.0" dependencies: - "@typescript-eslint/types": "npm:8.33.1" + "@typescript-eslint/types": "npm:8.34.0" eslint-visitor-keys: "npm:^4.2.0" - checksum: 10c0/3eb99072e7c2741d5dfc38945d1e7617b15ed10d06b24658a6e919e4153983b3d3c5f5f775ce140f83a84dbde219948d187de97defb09c1a91f3cf0a96704a94 + checksum: 10c0/d50997e921a178589913d08ffe14d02eba40666c90bdc0c9751f2b87ce500598f64027e2d866dfc975647b2f8b907158503d0722d6b1976c8f1cf5dd8e1d6d69 languageName: node linkType: hard -"@unrs/resolver-binding-darwin-arm64@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-darwin-arm64@npm:1.7.11" +"@unrs/resolver-binding-android-arm-eabi@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-android-arm-eabi@npm:1.9.0" + conditions: os=android & cpu=arm + languageName: node + linkType: hard + +"@unrs/resolver-binding-android-arm64@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-android-arm64@npm:1.9.0" + conditions: os=android & cpu=arm64 + languageName: node + linkType: hard + +"@unrs/resolver-binding-darwin-arm64@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-darwin-arm64@npm:1.9.0" conditions: os=darwin & cpu=arm64 languageName: node linkType: hard -"@unrs/resolver-binding-darwin-x64@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-darwin-x64@npm:1.7.11" +"@unrs/resolver-binding-darwin-x64@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-darwin-x64@npm:1.9.0" conditions: os=darwin & cpu=x64 languageName: node linkType: hard -"@unrs/resolver-binding-freebsd-x64@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-freebsd-x64@npm:1.7.11" +"@unrs/resolver-binding-freebsd-x64@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-freebsd-x64@npm:1.9.0" conditions: os=freebsd & cpu=x64 languageName: node linkType: hard -"@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.7.11" +"@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-linux-arm-gnueabihf@npm:1.9.0" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@unrs/resolver-binding-linux-arm-musleabihf@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-linux-arm-musleabihf@npm:1.7.11" +"@unrs/resolver-binding-linux-arm-musleabihf@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-linux-arm-musleabihf@npm:1.9.0" conditions: os=linux & cpu=arm languageName: node linkType: hard -"@unrs/resolver-binding-linux-arm64-gnu@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-linux-arm64-gnu@npm:1.7.11" +"@unrs/resolver-binding-linux-arm64-gnu@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-linux-arm64-gnu@npm:1.9.0" conditions: os=linux & cpu=arm64 & libc=glibc languageName: node linkType: hard -"@unrs/resolver-binding-linux-arm64-musl@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-linux-arm64-musl@npm:1.7.11" +"@unrs/resolver-binding-linux-arm64-musl@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-linux-arm64-musl@npm:1.9.0" conditions: os=linux & cpu=arm64 & libc=musl languageName: node linkType: hard -"@unrs/resolver-binding-linux-ppc64-gnu@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-linux-ppc64-gnu@npm:1.7.11" +"@unrs/resolver-binding-linux-ppc64-gnu@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-linux-ppc64-gnu@npm:1.9.0" conditions: os=linux & cpu=ppc64 & libc=glibc languageName: node linkType: hard -"@unrs/resolver-binding-linux-riscv64-gnu@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-linux-riscv64-gnu@npm:1.7.11" +"@unrs/resolver-binding-linux-riscv64-gnu@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-linux-riscv64-gnu@npm:1.9.0" conditions: os=linux & cpu=riscv64 & libc=glibc languageName: node linkType: hard -"@unrs/resolver-binding-linux-riscv64-musl@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-linux-riscv64-musl@npm:1.7.11" +"@unrs/resolver-binding-linux-riscv64-musl@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-linux-riscv64-musl@npm:1.9.0" conditions: os=linux & cpu=riscv64 & libc=musl languageName: node linkType: hard -"@unrs/resolver-binding-linux-s390x-gnu@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-linux-s390x-gnu@npm:1.7.11" +"@unrs/resolver-binding-linux-s390x-gnu@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-linux-s390x-gnu@npm:1.9.0" conditions: os=linux & cpu=s390x & libc=glibc languageName: node linkType: hard -"@unrs/resolver-binding-linux-x64-gnu@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-linux-x64-gnu@npm:1.7.11" +"@unrs/resolver-binding-linux-x64-gnu@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-linux-x64-gnu@npm:1.9.0" conditions: os=linux & cpu=x64 & libc=glibc languageName: node linkType: hard -"@unrs/resolver-binding-linux-x64-musl@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-linux-x64-musl@npm:1.7.11" +"@unrs/resolver-binding-linux-x64-musl@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-linux-x64-musl@npm:1.9.0" conditions: os=linux & cpu=x64 & libc=musl languageName: node linkType: hard -"@unrs/resolver-binding-wasm32-wasi@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-wasm32-wasi@npm:1.7.11" +"@unrs/resolver-binding-wasm32-wasi@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-wasm32-wasi@npm:1.9.0" dependencies: - "@napi-rs/wasm-runtime": "npm:^0.2.10" + "@napi-rs/wasm-runtime": "npm:^0.2.11" conditions: cpu=wasm32 languageName: node linkType: hard -"@unrs/resolver-binding-win32-arm64-msvc@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-win32-arm64-msvc@npm:1.7.11" +"@unrs/resolver-binding-win32-arm64-msvc@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-win32-arm64-msvc@npm:1.9.0" conditions: os=win32 & cpu=arm64 languageName: node linkType: hard -"@unrs/resolver-binding-win32-ia32-msvc@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-win32-ia32-msvc@npm:1.7.11" +"@unrs/resolver-binding-win32-ia32-msvc@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-win32-ia32-msvc@npm:1.9.0" conditions: os=win32 & cpu=ia32 languageName: node linkType: hard -"@unrs/resolver-binding-win32-x64-msvc@npm:1.7.11": - version: 1.7.11 - resolution: "@unrs/resolver-binding-win32-x64-msvc@npm:1.7.11" +"@unrs/resolver-binding-win32-x64-msvc@npm:1.9.0": + version: 1.9.0 + resolution: "@unrs/resolver-binding-win32-x64-msvc@npm:1.9.0" conditions: os=win32 & cpu=x64 languageName: node linkType: hard +"@wojtekmaj/date-utils@npm:^1.1.3": + version: 1.5.1 + resolution: "@wojtekmaj/date-utils@npm:1.5.1" + checksum: 10c0/7c213cca5ab6b84ef61b9aea2b9fb8a04bf4c9764b28a97ffc4ee46a3e81560532a74d106a6f8aeef4792e1aaa6ea3dfd3c4a639dddbea560eb3f33cd62b8d7d + languageName: node + linkType: hard + "acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" @@ -1934,12 +1964,12 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.14.0": - version: 8.14.1 - resolution: "acorn@npm:8.14.1" +"acorn@npm:^8.15.0": + version: 8.15.0 + resolution: "acorn@npm:8.15.0" bin: acorn: bin/acorn - checksum: 10c0/dbd36c1ed1d2fa3550140000371fcf721578095b18777b85a79df231ca093b08edc6858d75d6e48c73e431c174dcf9214edbd7e6fa5911b93bd8abfa54e47123 + checksum: 10c0/dec73ff59b7d6628a01eebaece7f2bdb8bb62b9b5926dcad0f8931f2b8b79c2be21f6c68ac095592adb5adb15831a3635d9343e6a91d028bbe85d564875ec3ec languageName: node linkType: hard @@ -2139,21 +2169,21 @@ __metadata: linkType: hard "brace-expansion@npm:^1.1.7": - version: 1.1.11 - resolution: "brace-expansion@npm:1.1.11" + version: 1.1.12 + resolution: "brace-expansion@npm:1.1.12" dependencies: balanced-match: "npm:^1.0.0" concat-map: "npm:0.0.1" - checksum: 10c0/695a56cd058096a7cb71fb09d9d6a7070113c7be516699ed361317aca2ec169f618e28b8af352e02ab4233fb54eb0168460a40dc320bab0034b36ab59aaad668 + checksum: 10c0/975fecac2bb7758c062c20d0b3b6288c7cc895219ee25f0a64a9de662dbac981ff0b6e89909c3897c1f84fa353113a721923afdec5f8b2350255b097f12b1f73 languageName: node linkType: hard "brace-expansion@npm:^2.0.1": - version: 2.0.1 - resolution: "brace-expansion@npm:2.0.1" + version: 2.0.2 + resolution: "brace-expansion@npm:2.0.2" dependencies: balanced-match: "npm:^1.0.0" - checksum: 10c0/b358f2fe060e2d7a87aa015979ecea07f3c37d4018f8d6deb5bd4c229ad3a0384fe6029bb76cd8be63c81e516ee52d1a0673edbe2023d53a5191732ae3c3e49f + checksum: 10c0/6d117a4c793488af86b83172deb6af143e94c17bc53b0b3cec259733923b4ca84679d506ac261f4ba3c7ed37c46018e2ff442f9ce453af8643ecd64f4a54e6cf languageName: node linkType: hard @@ -2215,9 +2245,9 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30001579": - version: 1.0.30001721 - resolution: "caniuse-lite@npm:1.0.30001721" - checksum: 10c0/fa3a8926899824b385279f1f886fe34c5efb1321c9ece1b9df25c8d567a2706db8450cc5b4d969e769e641593e08ea644909324aba93636a43e4949a75f81c4c + version: 1.0.30001722 + resolution: "caniuse-lite@npm:1.0.30001722" + checksum: 10c0/a1e344c392e0b138f0b215525108877d725665217a5e8e7504897e30379a5a9b858bc44799ccc0e19f4a64bf1e05c15b4a58eb1c9032293f894aa24e8d9f470f languageName: node linkType: hard @@ -2254,7 +2284,7 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^2.1.1": +"clsx@npm:^2.0.0, clsx@npm:^2.1.1": version: 2.1.1 resolution: "clsx@npm:2.1.1" checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839 @@ -2813,12 +2843,12 @@ __metadata: linkType: hard "eslint-scope@npm:^8.3.0": - version: 8.3.0 - resolution: "eslint-scope@npm:8.3.0" + version: 8.4.0 + resolution: "eslint-scope@npm:8.4.0" dependencies: esrecurse: "npm:^4.3.0" estraverse: "npm:^5.2.0" - checksum: 10c0/23bf54345573201fdf06d29efa345ab508b355492f6c6cc9e2b9f6d02b896f369b6dd5315205be94b8853809776c4d13353b85c6b531997b164ff6c3328ecf5b + checksum: 10c0/407f6c600204d0f3705bd557f81bd0189e69cd7996f408f8971ab5779c0af733d1af2f1412066b40ee1588b085874fc37a2333986c6521669cdbdd36ca5058e0 languageName: node linkType: hard @@ -2829,10 +2859,10 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^4.2.0": - version: 4.2.0 - resolution: "eslint-visitor-keys@npm:4.2.0" - checksum: 10c0/2ed81c663b147ca6f578312919483eb040295bbab759e5a371953456c636c5b49a559883e2677112453728d66293c0a4c90ab11cab3428cf02a0236d2e738269 +"eslint-visitor-keys@npm:^4.2.0, eslint-visitor-keys@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-visitor-keys@npm:4.2.1" + checksum: 10c0/fcd43999199d6740db26c58dbe0c2594623e31ca307e616ac05153c9272f12f1364f5a0b1917a8e962268fdecc6f3622c1c2908b4fcc2e047a106fe6de69dc43 languageName: node linkType: hard @@ -2887,13 +2917,13 @@ __metadata: linkType: hard "espree@npm:^10.0.1, espree@npm:^10.3.0": - version: 10.3.0 - resolution: "espree@npm:10.3.0" + version: 10.4.0 + resolution: "espree@npm:10.4.0" dependencies: - acorn: "npm:^8.14.0" + acorn: "npm:^8.15.0" acorn-jsx: "npm:^5.3.2" - eslint-visitor-keys: "npm:^4.2.0" - checksum: 10c0/272beeaca70d0a1a047d61baff64db04664a33d7cfb5d144f84bc8a5c6194c6c8ebe9cc594093ca53add88baa23e59b01e69e8a0160ab32eac570482e165c462 + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10c0/c63fe06131c26c8157b4083313cb02a9a54720a08e21543300e55288c40e06c3fc284bdecf108d3a1372c5934a0a88644c98714f38b6ae8ed272b40d9ea08d6b languageName: node linkType: hard @@ -2986,14 +3016,14 @@ __metadata: linkType: hard "fdir@npm:^6.4.4": - version: 6.4.5 - resolution: "fdir@npm:6.4.5" + version: 6.4.6 + resolution: "fdir@npm:6.4.6" peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: picomatch: optional: true - checksum: 10c0/5d63330a1b97165e9b0fb20369fafc7cf826bc4b3e374efcb650bc77d7145ac01193b5da1a7591eab89ae6fd6b15cdd414085910b2a2b42296b1480c9f2677af + checksum: 10c0/45b559cff889934ebb8bc498351e5acba40750ada7e7d6bde197768d2fa67c149be8ae7f8ff34d03f4e1eb20f2764116e56440aaa2f6689e9a4aa7ef06acafe9 languageName: node linkType: hard @@ -3134,6 +3164,15 @@ __metadata: languageName: node linkType: hard +"get-user-locale@npm:^2.2.1": + version: 2.3.2 + resolution: "get-user-locale@npm:2.3.2" + dependencies: + mem: "npm:^8.0.0" + checksum: 10c0/2796b3fc3782b1f4826f31e899642cf72eeb23e296e1cf55280aab5caf7a25f4b906491ee1508a001519d6a410902ccf8fa8edaa895b7aee5dfd422ffe5523b9 + languageName: node + linkType: hard + "glob-parent@npm:^5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -3804,7 +3843,7 @@ __metadata: languageName: node linkType: hard -"loose-envify@npm:^1.4.0": +"loose-envify@npm:^1.0.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" dependencies: @@ -3833,6 +3872,15 @@ __metadata: languageName: node linkType: hard +"map-age-cleaner@npm:^0.1.3": + version: 0.1.3 + resolution: "map-age-cleaner@npm:0.1.3" + dependencies: + p-defer: "npm:^1.0.0" + checksum: 10c0/7495236c7b0950956c144fd8b4bc6399d4e78072a8840a4232fe1c4faccbb5eb5d842e5c0a56a60afc36d723f315c1c672325ca03c1b328650f7fcc478f385fd + languageName: node + linkType: hard + "math-intrinsics@npm:^1.1.0": version: 1.1.0 resolution: "math-intrinsics@npm:1.1.0" @@ -3878,7 +3926,8 @@ __metadata: postcss: "npm:8.5.5" prettier: "npm:3.5.3" prisma: "npm:6.9.0" - react: "npm:^19.0.0" + react: "npm:^19.1.0" + react-calendar: "npm:^5.1.0" react-dom: "npm:^19.0.0" tailwind-merge: "npm:^3.2.0" tailwindcss: "npm:4.1.10" @@ -3887,6 +3936,16 @@ __metadata: languageName: unknown linkType: soft +"mem@npm:^8.0.0": + version: 8.1.1 + resolution: "mem@npm:8.1.1" + dependencies: + map-age-cleaner: "npm:^0.1.3" + mimic-fn: "npm:^3.1.0" + checksum: 10c0/5829c404d024c1accaf76ebacbc7eae9b59e5ce5722d184aa24e8387a8097a499f6aa7e181021003c51eb87b2dcdc9a2270050c58753cce761de206643cba91c + languageName: node + linkType: hard + "merge2@npm:^1.3.0": version: 1.4.1 resolution: "merge2@npm:1.4.1" @@ -3904,6 +3963,13 @@ __metadata: languageName: node linkType: hard +"mimic-fn@npm:^3.1.0": + version: 3.1.0 + resolution: "mimic-fn@npm:3.1.0" + checksum: 10c0/a07cdd8ed6490c2dff5b11f889b245d9556b80f5a653a552a651d17cff5a2d156e632d235106c2369f00cccef4071704589574cf3601bc1b1400a1f620dff067 + languageName: node + linkType: hard + "minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -4080,9 +4146,9 @@ __metadata: linkType: hard "oauth4webapi@npm:^3.3.0": - version: 3.5.1 - resolution: "oauth4webapi@npm:3.5.1" - checksum: 10c0/5d57ba4299d61173b28ff0612fdfcc550b02c2ce4afcd1641103960c02af18268b55a70f26d47bbfc956680967c307546284b4a0b1f13845589e247f798ff395 + version: 3.5.2 + resolution: "oauth4webapi@npm:3.5.2" + checksum: 10c0/fd056001ce67c9e4aba9b170b6aabe26315bd9ce67787534575ea66a475115fdae7c68b8b193e8607156a8e97a56a3eef552abb34ec3447b28cfe7c18749468a languageName: node linkType: hard @@ -4193,6 +4259,13 @@ __metadata: languageName: node linkType: hard +"p-defer@npm:^1.0.0": + version: 1.0.0 + resolution: "p-defer@npm:1.0.0" + checksum: 10c0/ed603c3790e74b061ac2cb07eb6e65802cf58dce0fbee646c113a7b71edb711101329ad38f99e462bd2e343a74f6e9366b496a35f1d766c187084d3109900487 + languageName: node + linkType: hard + "p-limit@npm:^3.0.2": version: 3.1.0 resolution: "p-limit@npm:3.1.0" @@ -4280,7 +4353,7 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.5.5": +"postcss@npm:8.5.5, postcss@npm:^8.4.41": version: 8.5.5 resolution: "postcss@npm:8.5.5" dependencies: @@ -4291,17 +4364,6 @@ __metadata: languageName: node linkType: hard -"postcss@npm:^8.4.41": - version: 8.5.4 - resolution: "postcss@npm:8.5.4" - dependencies: - nanoid: "npm:^3.3.11" - picocolors: "npm:^1.1.1" - source-map-js: "npm:^1.2.1" - checksum: 10c0/0feff648614a834f7cd5396ea6b05b658ca0507e10a4eaad03b56c348f6aec93f42a885fc1b30522630c6a7e49ae53b38a061e3cba526f2d9857afbe095a22bb - languageName: node - linkType: hard - "preact-render-to-string@npm:6.5.11": version: 6.5.11 resolution: "preact-render-to-string@npm:6.5.11" @@ -4376,6 +4438,25 @@ __metadata: languageName: node linkType: hard +"react-calendar@npm:^5.1.0": + version: 5.1.0 + resolution: "react-calendar@npm:5.1.0" + dependencies: + "@wojtekmaj/date-utils": "npm:^1.1.3" + clsx: "npm:^2.0.0" + get-user-locale: "npm:^2.2.1" + warning: "npm:^4.0.0" + peerDependencies: + "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 10c0/27673f639c5d6296342a2a888436b31a5d602faeaae01be83b2beb98ff568b0a3d1514f5cc50fcacf3ac50b9c0b9d2fb423b0c001a8f5f1a22816671409e2616 + languageName: node + linkType: hard + "react-dom@npm:^19.0.0": version: 19.1.0 resolution: "react-dom@npm:19.1.0" @@ -4445,7 +4526,7 @@ __metadata: languageName: node linkType: hard -"react@npm:^19.0.0": +"react@npm:^19.1.0": version: 19.1.0 resolution: "react@npm:19.1.0" checksum: 10c0/530fb9a62237d54137a13d2cfb67a7db6a2156faed43eecc423f4713d9b20c6f2728b026b45e28fcd72e8eadb9e9ed4b089e99f5e295d2f0ad3134251bdd3698 @@ -5156,28 +5237,34 @@ __metadata: linkType: hard "unrs-resolver@npm:^1.6.2": - version: 1.7.11 - resolution: "unrs-resolver@npm:1.7.11" + version: 1.9.0 + resolution: "unrs-resolver@npm:1.9.0" dependencies: - "@unrs/resolver-binding-darwin-arm64": "npm:1.7.11" - "@unrs/resolver-binding-darwin-x64": "npm:1.7.11" - "@unrs/resolver-binding-freebsd-x64": "npm:1.7.11" - "@unrs/resolver-binding-linux-arm-gnueabihf": "npm:1.7.11" - "@unrs/resolver-binding-linux-arm-musleabihf": "npm:1.7.11" - "@unrs/resolver-binding-linux-arm64-gnu": "npm:1.7.11" - "@unrs/resolver-binding-linux-arm64-musl": "npm:1.7.11" - "@unrs/resolver-binding-linux-ppc64-gnu": "npm:1.7.11" - "@unrs/resolver-binding-linux-riscv64-gnu": "npm:1.7.11" - "@unrs/resolver-binding-linux-riscv64-musl": "npm:1.7.11" - "@unrs/resolver-binding-linux-s390x-gnu": "npm:1.7.11" - "@unrs/resolver-binding-linux-x64-gnu": "npm:1.7.11" - "@unrs/resolver-binding-linux-x64-musl": "npm:1.7.11" - "@unrs/resolver-binding-wasm32-wasi": "npm:1.7.11" - "@unrs/resolver-binding-win32-arm64-msvc": "npm:1.7.11" - "@unrs/resolver-binding-win32-ia32-msvc": "npm:1.7.11" - "@unrs/resolver-binding-win32-x64-msvc": "npm:1.7.11" + "@unrs/resolver-binding-android-arm-eabi": "npm:1.9.0" + "@unrs/resolver-binding-android-arm64": "npm:1.9.0" + "@unrs/resolver-binding-darwin-arm64": "npm:1.9.0" + "@unrs/resolver-binding-darwin-x64": "npm:1.9.0" + "@unrs/resolver-binding-freebsd-x64": "npm:1.9.0" + "@unrs/resolver-binding-linux-arm-gnueabihf": "npm:1.9.0" + "@unrs/resolver-binding-linux-arm-musleabihf": "npm:1.9.0" + "@unrs/resolver-binding-linux-arm64-gnu": "npm:1.9.0" + "@unrs/resolver-binding-linux-arm64-musl": "npm:1.9.0" + "@unrs/resolver-binding-linux-ppc64-gnu": "npm:1.9.0" + "@unrs/resolver-binding-linux-riscv64-gnu": "npm:1.9.0" + "@unrs/resolver-binding-linux-riscv64-musl": "npm:1.9.0" + "@unrs/resolver-binding-linux-s390x-gnu": "npm:1.9.0" + "@unrs/resolver-binding-linux-x64-gnu": "npm:1.9.0" + "@unrs/resolver-binding-linux-x64-musl": "npm:1.9.0" + "@unrs/resolver-binding-wasm32-wasi": "npm:1.9.0" + "@unrs/resolver-binding-win32-arm64-msvc": "npm:1.9.0" + "@unrs/resolver-binding-win32-ia32-msvc": "npm:1.9.0" + "@unrs/resolver-binding-win32-x64-msvc": "npm:1.9.0" napi-postinstall: "npm:^0.2.2" dependenciesMeta: + "@unrs/resolver-binding-android-arm-eabi": + optional: true + "@unrs/resolver-binding-android-arm64": + optional: true "@unrs/resolver-binding-darwin-arm64": optional: true "@unrs/resolver-binding-darwin-x64": @@ -5212,7 +5299,7 @@ __metadata: optional: true "@unrs/resolver-binding-win32-x64-msvc": optional: true - checksum: 10c0/37e6caf2884b7ce65f77fc5b945997b94523656d477ae0e67fb8df970939930b674091f3fac6beee93b0370fa64a925ad707edc76897aa8cb14866efbe4a6693 + checksum: 10c0/73c184514a82197145539c0506dd6633a28fc380192b1677d31348537c2783405e7392cf2bf18b96d84b8068f502868de3ae741edd580683ddb39f10d46d49e8 languageName: node linkType: hard @@ -5256,6 +5343,15 @@ __metadata: languageName: node linkType: hard +"warning@npm:^4.0.0": + version: 4.0.3 + resolution: "warning@npm:4.0.3" + dependencies: + loose-envify: "npm:^1.0.0" + checksum: 10c0/aebab445129f3e104c271f1637fa38e55eb25f968593e3825bd2f7a12bd58dc3738bb70dc8ec85826621d80b4acfed5a29ebc9da17397c6125864d72301b937e + languageName: node + linkType: hard + "which-boxed-primitive@npm:^1.1.0, which-boxed-primitive@npm:^1.1.1": version: 1.1.1 resolution: "which-boxed-primitive@npm:1.1.1" From a29580708e8a620c471d96b92e5dc032d1037710 Mon Sep 17 00:00:00 2001 From: Semir Date: Fri, 23 May 2025 15:23:01 +0200 Subject: [PATCH 02/20] feat: Calendar Update --- package.json | 3 +- src/app/home/page.tsx | 135 ++++---------------- yarn.lock | 285 ++++++++++++++++++++++++++++++++---------- 3 files changed, 248 insertions(+), 175 deletions(-) diff --git a/package.json b/package.json index dfd3eef..3717388 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "next-auth": "^5.0.0-beta.25", "next-themes": "^0.4.6", "react": "^19.1.0", - "react-calendar": "^5.1.0", + "react-big-calendar": "^1.18.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.2.0" }, @@ -48,6 +48,7 @@ "@tailwindcss/postcss": "4.1.10", "@types/node": "22.15.31", "@types/react": "19.1.8", + "@types/react-big-calendar": "^1", "@types/react-dom": "19.1.6", "dotenv-cli": "8.0.0", "eslint": "9.28.0", diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index 527b231..b20d042 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -1,114 +1,31 @@ -import React from 'react'; +"use client"; -const Calendar: React.FC = () => { - const today = new Date(); - const currentYear = today.getFullYear(); - const currentMonth = today.getMonth(); - const currentDate = today.getDate(); +import { Calendar, momentLocalizer } from 'react-big-calendar'; +import moment from 'moment'; +import 'react-big-calendar/lib/css/react-big-calendar.css'; +import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'; - const firstDayMonth = new Date(currentYear, currentMonth, 1); - const lastDayMonth = new Date(currentYear, currentMonth + 1, 0); - const startDay = (firstDayMonth.getDay() + 6) % 7; - const daysMonth = lastDayMonth.getDate(); +moment.updateLocale('en', { + week: { + dow: 1, + doy: 4, + }, +}); - const weeks: (number | null)[][] = []; - let currentDay = 1; +const localizer = momentLocalizer(moment) - const firstWeek: (number | null)[] = []; - for (let i = 0; i < 7; i++) { - if (i < startDay) { - firstWeek.push(null); - } else { - firstWeek.push(currentDay); - currentDay++; - } - } - weeks.push(firstWeek); +const MyCalendar = (props) => ( +
+ +
+) - while (currentDay <= daysMonth) { - const week: (number | null)[] = []; - for (let i = 0; i < 7; i++) { - if (currentDay <= daysMonth) { - week.push(currentDay); - currentDay++; - } else { - week.push(null); - } - } - weeks.push(week); - } - - const weekdays = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']; - - return ( -
-

- {new Date(currentYear, currentMonth).toLocaleString('en-EN', { - month: 'long', - year: 'numeric' - })} -

- - - - {weekdays.map((day, index) => ( - - ))} - - - - {weeks.map((week, weekIndex) => ( - - {week.map((day, dayIndex) => ( - - ))} - - ))} - -
- {day} -
- {day && ( -
- {day} -
- )} -
-
- ); -}; - -export default Calendar; \ No newline at end of file +export default MyCalendar; \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 0430071..ba28418 100644 --- a/yarn.lock +++ b/yarn.lock @@ -57,6 +57,13 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.13.8, @babel/runtime@npm:^7.20.7, @babel/runtime@npm:^7.6.3, @babel/runtime@npm:^7.8.7": + version: 7.27.6 + resolution: "@babel/runtime@npm:7.27.6" + checksum: 10c0/89726be83f356f511dcdb74d3ea4d873a5f0cf0017d4530cb53aa27380c01ca102d573eff8b8b77815e624b1f8c24e7f0311834ad4fb632c90a770fda00bd4c8 + languageName: node + linkType: hard + "@emnapi/core@npm:^1.4.3": version: 1.4.3 resolution: "@emnapi/core@npm:1.4.3" @@ -675,6 +682,13 @@ __metadata: languageName: node linkType: hard +"@popperjs/core@npm:^2.11.6": + version: 2.11.8 + resolution: "@popperjs/core@npm:2.11.8" + checksum: 10c0/4681e682abc006d25eb380d0cf3efc7557043f53b6aea7a5057d0d1e7df849a00e281cd8ea79c902a35a414d7919621fc2ba293ecec05f413598e0b23d5a1e63 + languageName: node + linkType: hard + "@prisma/client@npm:^6.9.0": version: 6.9.0 resolution: "@prisma/client@npm:6.9.0" @@ -1424,6 +1438,17 @@ __metadata: languageName: node linkType: hard +"@restart/hooks@npm:^0.4.7": + version: 0.4.16 + resolution: "@restart/hooks@npm:0.4.16" + dependencies: + dequal: "npm:^2.0.3" + peerDependencies: + react: ">=16.8.0" + checksum: 10c0/b6a0f1db046cdec28737092ab5defdfb25fad498d37d218646f7f123aed02a5078b1c89ae631bda14d9ee35f7bb8c9e0f15379b1a45003144dc30cd15e8ba668 + languageName: node + linkType: hard + "@rtsao/scc@npm:^1.1.0": version: 1.1.0 resolution: "@rtsao/scc@npm:1.1.0" @@ -1629,6 +1654,13 @@ __metadata: languageName: node linkType: hard +"@types/date-arithmetic@npm:*": + version: 4.1.4 + resolution: "@types/date-arithmetic@npm:4.1.4" + checksum: 10c0/4ee68b5a422bd5f1cf08923d18a08db558e653bbdc597677e0465a330f1e807da0e79b06b72651b62b19b4b922a779470f84657cbd765805f84f33af518b408f + languageName: node + linkType: hard + "@types/estree@npm:^1.0.6": version: 1.0.8 resolution: "@types/estree@npm:1.0.8" @@ -1659,6 +1691,24 @@ __metadata: languageName: node linkType: hard +"@types/prop-types@npm:*": + version: 15.7.15 + resolution: "@types/prop-types@npm:15.7.15" + checksum: 10c0/b59aad1ad19bf1733cf524fd4e618196c6c7690f48ee70a327eb450a42aab8e8a063fbe59ca0a5701aebe2d92d582292c0fb845ea57474f6a15f6994b0e260b2 + languageName: node + linkType: hard + +"@types/react-big-calendar@npm:^1": + version: 1.16.2 + resolution: "@types/react-big-calendar@npm:1.16.2" + dependencies: + "@types/date-arithmetic": "npm:*" + "@types/prop-types": "npm:*" + "@types/react": "npm:*" + checksum: 10c0/a2ea4116b999cf8dac014fdc4a9f0c10fb2fd9d9886857e93649c0a601057e93e73e3d9096a756b76e227e08f68e4c979f91bf4cfd96692aea3ab7f3df0745d0 + languageName: node + linkType: hard + "@types/react-dom@npm:19.1.6": version: 19.1.6 resolution: "@types/react-dom@npm:19.1.6" @@ -1668,7 +1718,7 @@ __metadata: languageName: node linkType: hard -"@types/react@npm:19.1.8": +"@types/react@npm:*, @types/react@npm:19.1.8, @types/react@npm:>=16.9.11": version: 19.1.8 resolution: "@types/react@npm:19.1.8" dependencies: @@ -1677,6 +1727,13 @@ __metadata: languageName: node linkType: hard +"@types/warning@npm:^3.0.0": + version: 3.0.3 + resolution: "@types/warning@npm:3.0.3" + checksum: 10c0/82c1235bd05d7f6940f80012404844e225d589ad338aa4585b231a2c8deacc695b683f4168757c82c10047b81854cbeaaeefd60536dd67bb48f8a65e20410652 + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0": version: 8.34.0 resolution: "@typescript-eslint/eslint-plugin@npm:8.34.0" @@ -1948,13 +2005,6 @@ __metadata: languageName: node linkType: hard -"@wojtekmaj/date-utils@npm:^1.1.3": - version: 1.5.1 - resolution: "@wojtekmaj/date-utils@npm:1.5.1" - checksum: 10c0/7c213cca5ab6b84ef61b9aea2b9fb8a04bf4c9764b28a97ffc4ee46a3e81560532a74d106a6f8aeef4792e1aaa6ea3dfd3c4a639dddbea560eb3f33cd62b8d7d - languageName: node - linkType: hard - "acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" @@ -2284,7 +2334,14 @@ __metadata: languageName: node linkType: hard -"clsx@npm:^2.0.0, clsx@npm:^2.1.1": +"clsx@npm:^1.2.1": + version: 1.2.1 + resolution: "clsx@npm:1.2.1" + checksum: 10c0/34dead8bee24f5e96f6e7937d711978380647e936a22e76380290e35486afd8634966ce300fc4b74a32f3762c7d4c0303f442c3e259f4ce02374eb0c82834f27 + languageName: node + linkType: hard + +"clsx@npm:^2.1.1": version: 2.1.1 resolution: "clsx@npm:2.1.1" checksum: 10c0/c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839 @@ -2392,6 +2449,20 @@ __metadata: languageName: node linkType: hard +"date-arithmetic@npm:^4.1.0": + version: 4.1.0 + resolution: "date-arithmetic@npm:4.1.0" + checksum: 10c0/697774a1a6a1b226004b5527326599c01a095bf715a0d43089e0493a565a91e7f4342b1b73b855c0e7b0caaf4bc947a61bc35ec60d162d52ef3c3c08eab26b6e + languageName: node + linkType: hard + +"dayjs@npm:^1.11.7": + version: 1.11.13 + resolution: "dayjs@npm:1.11.13" + checksum: 10c0/a3caf6ac8363c7dade9d1ee797848ddcf25c1ace68d9fe8678ecf8ba0675825430de5d793672ec87b24a69bf04a1544b176547b2539982275d5542a7955f35b7 + languageName: node + linkType: hard + "debug@npm:^3.2.7": version: 3.2.7 resolution: "debug@npm:3.2.7" @@ -2442,6 +2513,13 @@ __metadata: languageName: node linkType: hard +"dequal@npm:^2.0.3": + version: 2.0.3 + resolution: "dequal@npm:2.0.3" + checksum: 10c0/f98860cdf58b64991ae10205137c0e97d384c3a4edc7f807603887b7c4b850af1224a33d88012009f150861cbee4fa2d322c4cc04b9313bee312e47f6ecaa888 + languageName: node + linkType: hard + "detect-libc@npm:^2.0.3, detect-libc@npm:^2.0.4": version: 2.0.4 resolution: "detect-libc@npm:2.0.4" @@ -2465,6 +2543,16 @@ __metadata: languageName: node linkType: hard +"dom-helpers@npm:^5.2.0, dom-helpers@npm:^5.2.1": + version: 5.2.1 + resolution: "dom-helpers@npm:5.2.1" + dependencies: + "@babel/runtime": "npm:^7.8.7" + csstype: "npm:^3.0.2" + checksum: 10c0/f735074d66dd759b36b158fa26e9d00c9388ee0e8c9b16af941c38f014a37fc80782de83afefd621681b19ac0501034b4f1c4a3bff5caa1b8667f0212b5e124c + languageName: node + linkType: hard + "dotenv-cli@npm:8.0.0": version: 8.0.0 resolution: "dotenv-cli@npm:8.0.0" @@ -3164,15 +3252,6 @@ __metadata: languageName: node linkType: hard -"get-user-locale@npm:^2.2.1": - version: 2.3.2 - resolution: "get-user-locale@npm:2.3.2" - dependencies: - mem: "npm:^8.0.0" - checksum: 10c0/2796b3fc3782b1f4826f31e899642cf72eeb23e296e1cf55280aab5caf7a25f4b906491ee1508a001519d6a410902ccf8fa8edaa895b7aee5dfd422ffe5523b9 - languageName: node - linkType: hard - "glob-parent@npm:^5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" @@ -3191,6 +3270,13 @@ __metadata: languageName: node linkType: hard +"globalize@npm:^0.1.1": + version: 0.1.1 + resolution: "globalize@npm:0.1.1" + checksum: 10c0/6d4687e7c52a38e7f16f77339aef9b3364c34ce8cc1c8b8495b76418013252eaf5b2453fbc2d8bb9e6e56c739262665484dc7ac51b729501ff0a3b822730116b + languageName: node + linkType: hard + "globals@npm:^14.0.0": version: 14.0.0 resolution: "globals@npm:14.0.0" @@ -3328,6 +3414,15 @@ __metadata: languageName: node linkType: hard +"invariant@npm:^2.2.4": + version: 2.2.4 + resolution: "invariant@npm:2.2.4" + dependencies: + loose-envify: "npm:^1.0.0" + checksum: 10c0/5af133a917c0bcf65e84e7f23e779e7abc1cd49cb7fdc62d00d1de74b0d8c1b5ee74ac7766099fb3be1b05b26dfc67bab76a17030d2fe7ea2eef867434362dfc + languageName: node + linkType: hard + "is-array-buffer@npm:^3.0.4, is-array-buffer@npm:^3.0.5": version: 3.0.5 resolution: "is-array-buffer@npm:3.0.5" @@ -3836,6 +3931,13 @@ __metadata: languageName: node linkType: hard +"lodash-es@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash-es@npm:4.17.21" + checksum: 10c0/fb407355f7e6cd523a9383e76e6b455321f0f153a6c9625e21a8827d10c54c2a2341bd2ae8d034358b60e07325e1330c14c224ff582d04612a46a4f0479ff2f2 + languageName: node + linkType: hard + "lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" @@ -3843,6 +3945,13 @@ __metadata: languageName: node linkType: hard +"lodash@npm:^4.17.21": + version: 4.17.21 + resolution: "lodash@npm:4.17.21" + checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c + languageName: node + linkType: hard + "loose-envify@npm:^1.0.0, loose-envify@npm:^1.4.0": version: 1.4.0 resolution: "loose-envify@npm:1.4.0" @@ -3863,6 +3972,13 @@ __metadata: languageName: node linkType: hard +"luxon@npm:^3.2.1": + version: 3.6.1 + resolution: "luxon@npm:3.6.1" + checksum: 10c0/906d57a9dc4d1de9383f2e9223e378c298607c1b4d17b6657b836a3cd120feb1c1de3b5d06d846a3417e1ca764de8476e8c23b3cd4083b5cdb870adcb06a99d5 + languageName: node + linkType: hard + "magic-string@npm:^0.30.17": version: 0.30.17 resolution: "magic-string@npm:0.30.17" @@ -3872,15 +3988,6 @@ __metadata: languageName: node linkType: hard -"map-age-cleaner@npm:^0.1.3": - version: 0.1.3 - resolution: "map-age-cleaner@npm:0.1.3" - dependencies: - p-defer: "npm:^1.0.0" - checksum: 10c0/7495236c7b0950956c144fd8b4bc6399d4e78072a8840a4232fe1c4faccbb5eb5d842e5c0a56a60afc36d723f315c1c672325ca03c1b328650f7fcc478f385fd - languageName: node - linkType: hard - "math-intrinsics@npm:^1.1.0": version: 1.1.0 resolution: "math-intrinsics@npm:1.1.0" @@ -3912,6 +4019,7 @@ __metadata: "@tailwindcss/postcss": "npm:4.1.10" "@types/node": "npm:22.15.31" "@types/react": "npm:19.1.8" + "@types/react-big-calendar": "npm:^1" "@types/react-dom": "npm:19.1.6" class-variance-authority: "npm:^0.7.1" clsx: "npm:^2.1.1" @@ -3927,7 +4035,7 @@ __metadata: prettier: "npm:3.5.3" prisma: "npm:6.9.0" react: "npm:^19.1.0" - react-calendar: "npm:^5.1.0" + react-big-calendar: "npm:^1.18.0" react-dom: "npm:^19.0.0" tailwind-merge: "npm:^3.2.0" tailwindcss: "npm:4.1.10" @@ -3936,13 +4044,10 @@ __metadata: languageName: unknown linkType: soft -"mem@npm:^8.0.0": - version: 8.1.1 - resolution: "mem@npm:8.1.1" - dependencies: - map-age-cleaner: "npm:^0.1.3" - mimic-fn: "npm:^3.1.0" - checksum: 10c0/5829c404d024c1accaf76ebacbc7eae9b59e5ce5722d184aa24e8387a8097a499f6aa7e181021003c51eb87b2dcdc9a2270050c58753cce761de206643cba91c +"memoize-one@npm:^6.0.0": + version: 6.0.0 + resolution: "memoize-one@npm:6.0.0" + checksum: 10c0/45c88e064fd715166619af72e8cf8a7a17224d6edf61f7a8633d740ed8c8c0558a4373876c9b8ffc5518c2b65a960266adf403cc215cb1e90f7e262b58991f54 languageName: node linkType: hard @@ -3963,13 +4068,6 @@ __metadata: languageName: node linkType: hard -"mimic-fn@npm:^3.1.0": - version: 3.1.0 - resolution: "mimic-fn@npm:3.1.0" - checksum: 10c0/a07cdd8ed6490c2dff5b11f889b245d9556b80f5a653a552a651d17cff5a2d156e632d235106c2369f00cccef4071704589574cf3601bc1b1400a1f620dff067 - languageName: node - linkType: hard - "minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -4020,6 +4118,22 @@ __metadata: languageName: node linkType: hard +"moment-timezone@npm:^0.5.40": + version: 0.5.48 + resolution: "moment-timezone@npm:0.5.48" + dependencies: + moment: "npm:^2.29.4" + checksum: 10c0/ab14ec9d94bc33f29ac18e5417b7f8aca0b17130b952c5cc9697b8fea839e5ece9313af5fd3c9703a05db472b1560ddbfc7ad2aa24aac9afd047d6da6c3c6033 + languageName: node + linkType: hard + +"moment@npm:^2.29.4": + version: 2.30.1 + resolution: "moment@npm:2.30.1" + checksum: 10c0/865e4279418c6de666fca7786607705fd0189d8a7b7624e2e56be99290ac846f90878a6f602e34b4e0455c549b85385b1baf9966845962b313699e7cb847543a + languageName: node + linkType: hard + "ms@npm:^2.1.1, ms@npm:^2.1.3": version: 2.1.3 resolution: "ms@npm:2.1.3" @@ -4259,13 +4373,6 @@ __metadata: languageName: node linkType: hard -"p-defer@npm:^1.0.0": - version: 1.0.0 - resolution: "p-defer@npm:1.0.0" - checksum: 10c0/ed603c3790e74b061ac2cb07eb6e65802cf58dce0fbee646c113a7b71edb711101329ad38f99e462bd2e343a74f6e9366b496a35f1d766c187084d3109900487 - languageName: node - linkType: hard - "p-limit@npm:^3.0.2": version: 3.1.0 resolution: "p-limit@npm:3.1.0" @@ -4413,7 +4520,7 @@ __metadata: languageName: node linkType: hard -"prop-types@npm:^15.8.1": +"prop-types@npm:^15.7.2, prop-types@npm:^15.8.1": version: 15.8.1 resolution: "prop-types@npm:15.8.1" dependencies: @@ -4438,22 +4545,30 @@ __metadata: languageName: node linkType: hard -"react-calendar@npm:^5.1.0": - version: 5.1.0 - resolution: "react-calendar@npm:5.1.0" +"react-big-calendar@npm:^1.18.0": + version: 1.19.2 + resolution: "react-big-calendar@npm:1.19.2" dependencies: - "@wojtekmaj/date-utils": "npm:^1.1.3" - clsx: "npm:^2.0.0" - get-user-locale: "npm:^2.2.1" - warning: "npm:^4.0.0" + "@babel/runtime": "npm:^7.20.7" + clsx: "npm:^1.2.1" + date-arithmetic: "npm:^4.1.0" + dayjs: "npm:^1.11.7" + dom-helpers: "npm:^5.2.1" + globalize: "npm:^0.1.1" + invariant: "npm:^2.2.4" + lodash: "npm:^4.17.21" + lodash-es: "npm:^4.17.21" + luxon: "npm:^3.2.1" + memoize-one: "npm:^6.0.0" + moment: "npm:^2.29.4" + moment-timezone: "npm:^0.5.40" + prop-types: "npm:^15.8.1" + react-overlays: "npm:^5.2.1" + uncontrollable: "npm:^7.2.1" peerDependencies: - "@types/react": ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 - peerDependenciesMeta: - "@types/react": - optional: true - checksum: 10c0/27673f639c5d6296342a2a888436b31a5d602faeaae01be83b2beb98ff568b0a3d1514f5cc50fcacf3ac50b9c0b9d2fb423b0c001a8f5f1a22816671409e2616 + react: ^16.14.0 || ^17 || ^18 || ^19 + react-dom: ^16.14.0 || ^17 || ^18 || ^19 + checksum: 10c0/d60b12733d6c0dfb1b3bf89226a18b613dff66b3a4e03d90b0e7a68ad90478a3f4150b859a803474bfa98730bad3be622dc94326e90ac998e2e78ab36cef74db languageName: node linkType: hard @@ -4475,6 +4590,32 @@ __metadata: languageName: node linkType: hard +"react-lifecycles-compat@npm:^3.0.4": + version: 3.0.4 + resolution: "react-lifecycles-compat@npm:3.0.4" + checksum: 10c0/1d0df3c85af79df720524780f00c064d53a9dd1899d785eddb7264b378026979acbddb58a4b7e06e7d0d12aa1494fd5754562ee55d32907b15601068dae82c27 + languageName: node + linkType: hard + +"react-overlays@npm:^5.2.1": + version: 5.2.1 + resolution: "react-overlays@npm:5.2.1" + dependencies: + "@babel/runtime": "npm:^7.13.8" + "@popperjs/core": "npm:^2.11.6" + "@restart/hooks": "npm:^0.4.7" + "@types/warning": "npm:^3.0.0" + dom-helpers: "npm:^5.2.0" + prop-types: "npm:^15.7.2" + uncontrollable: "npm:^7.2.1" + warning: "npm:^4.0.3" + peerDependencies: + react: ">=16.3.0" + react-dom: ">=16.3.0" + checksum: 10c0/61836490040cfcdebc792b6eddcfac47b7b7e159f99304165371e9eb389a6875f20ddba3433421413ccfb918e8da6042ab2829f9b1f6f5dd9f8476aa16ddcfbe + languageName: node + linkType: hard + "react-remove-scroll-bar@npm:^2.3.7": version: 2.3.8 resolution: "react-remove-scroll-bar@npm:2.3.8" @@ -5229,6 +5370,20 @@ __metadata: languageName: node linkType: hard +"uncontrollable@npm:^7.2.1": + version: 7.2.1 + resolution: "uncontrollable@npm:7.2.1" + dependencies: + "@babel/runtime": "npm:^7.6.3" + "@types/react": "npm:>=16.9.11" + invariant: "npm:^2.2.4" + react-lifecycles-compat: "npm:^3.0.4" + peerDependencies: + react: ">=15.0.0" + checksum: 10c0/81473e892027a99f1ead6b9afd16db65097651cd36c4b6db710728f206f1fc4b82ba9170ecb4a1127a23857e01ba51c0194d0a7cfeecfea61ba9418e0276cb56 + languageName: node + linkType: hard + "undici-types@npm:~6.21.0": version: 6.21.0 resolution: "undici-types@npm:6.21.0" @@ -5343,7 +5498,7 @@ __metadata: languageName: node linkType: hard -"warning@npm:^4.0.0": +"warning@npm:^4.0.3": version: 4.0.3 resolution: "warning@npm:4.0.3" dependencies: From 93b603317a8ea5c67f46c1a81b5f8b2496d61869 Mon Sep 17 00:00:00 2001 From: Semir Date: Fri, 23 May 2025 15:27:16 +0200 Subject: [PATCH 03/20] feat: Calendar Update 1.1 --- src/app/home/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index b20d042..fcee5ad 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -28,4 +28,4 @@ const MyCalendar = (props) => (
) -export default MyCalendar; \ No newline at end of file +export default MyCalendar; From 679c90cdbdab9e3d77cd06f676bb735b8f48fbf4 Mon Sep 17 00:00:00 2001 From: Semir Date: Wed, 28 May 2025 09:51:16 +0200 Subject: [PATCH 04/20] feat: Custom Calendar Toolbar Add Custom Calendar Toolbar --- package.json | 1 + src/app/home/page.tsx | 7 + src/components/custom-toolbar.css | 139 +++++++++++++++++++ src/components/custom-toolbar.tsx | 218 ++++++++++++++++++++++++++++++ yarn.lock | 8 ++ 5 files changed, 373 insertions(+) create mode 100644 src/components/custom-toolbar.css create mode 100644 src/components/custom-toolbar.tsx diff --git a/package.json b/package.json index 3717388..b4c9bfb 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@radix-ui/react-tabs": "^1.1.11", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "date-fns": "^4.1.0", "lucide-react": "^0.511.0", "next": "15.3.3", "next-auth": "^5.0.0-beta.25", diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index fcee5ad..6149d47 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -4,6 +4,7 @@ import { Calendar, momentLocalizer } from 'react-big-calendar'; import moment from 'moment'; import 'react-big-calendar/lib/css/react-big-calendar.css'; import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'; +import CustomToolbar from '@/components/custom-toolbar'; moment.updateLocale('en', { week: { @@ -24,6 +25,12 @@ const MyCalendar = (props) => ( style={{ height: 500 }} culture="de-DE" defaultView='week' + + /*CustomToolbar*/ + components={{ + toolbar: CustomToolbar + }} + /*CustomToolbar*/ /> ) diff --git a/src/components/custom-toolbar.css b/src/components/custom-toolbar.css new file mode 100644 index 0000000..16e86ed --- /dev/null +++ b/src/components/custom-toolbar.css @@ -0,0 +1,139 @@ +/* custom-toolbar.css */ + +/* Container der Toolbar */ +.custom-toolbar { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px; + background-color: #ffffff; + border: 1px solid #e0e0e0; + /*border-radius: 8px;*/ + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +/* Style für den Bereich, in dem die Ansichten (Month, Week, etc.) gewechselt werden */ +.custom-toolbar .view-change .view-switcher { + display: flex; + gap: 8px; + justify-content: center; +} + +.custom-toolbar .view-change .view-switcher button { + padding: 8px 16px; + background-color: #c1830d; + /*border: 1px solid #ccc;*/ + border-radius: 11px; + font-size: 12px; + cursor: pointer; + transition: background-color 0.2s, border-color 0.2s; + height: 30px; + margin-top: 3.5px; + color: #ffffff; +} + +.custom-toolbar .view-change .view-switcher button:hover:not(:disabled) { + background-color: #e0e0e0; + border-color: #999; +} + +.custom-toolbar .view-change .view-switcher button:disabled { + background-color: #d0d0d0; + border-color: #aaa; + cursor: default; +} + +/* Anzeige des aktuellen Datums (Monat und Jahr) */ +.custom-toolbar .current-date { + font-weight: bold; + font-size: 12px; + text-align: center; + color: #ffffff; + margin: 4px 0; + background-color: #717171; + width: 178px; + height: 37px; + border-radius: 11px; +} + +/* Navigationsbereich (Today, Prev, Next) */ +.custom-toolbar .navigation-controls { + display: flex; + gap: 8px; + justify-content: center; +} + +.custom-toolbar .navigation-controls button { + padding: 8px 12px; + /*background-color: #2196F3;*/ + color: #ffffff; + border: none; + border-radius: 11px; + font-size: 12px; + cursor: pointer; + transition: background-color 0.2s; +} + +.custom-toolbar .navigation-controls button:hover { + background-color: #1976D2; +} + +.custom-toolbar .navigation-controls button:active { + background-color: #1565C0; +} + +/* Dropdown-Bereich für Woche und Jahr */ +.custom-toolbar .dropdowns { + display: flex; + gap: 8px; + justify-content: center; + height: 30px; + font-size: 10px; + margin-top: 3.5px; + border-radius: 11px; +} + +.custom-toolbar .dropdowns select { + padding: 8px 12px; + /*border: 1px solid #ccc;*/ + border-radius: 11px; + font-size: 10px; + background-color: #555555; + color: #ffffff; + cursor: pointer; + transition: border-color 0.2s; +} + +.custom-toolbar .dropdowns select:hover { + border-color: #999; +} + +.right-section { + background-color: #717171; + width: 393px; + height: 37px; + border-radius: 11px; +} + +.custom-toolbar .navigation-controls .handleWeek button { + background-color: #717171; + height: 30px; + width: 30px; + margin-bottom: 3.5px; +} + +.custom-toolbar .navigation-controls .today button { + background-color: #c6c6c6; + height: 30px; + width: 100px; + color: #000000; + margin-top: 3.5px; +} + +.view-change { + background-color: #717171; + height: 37px; + width: 290px; + border-radius: 11px; +} diff --git a/src/components/custom-toolbar.tsx b/src/components/custom-toolbar.tsx new file mode 100644 index 0000000..10feafa --- /dev/null +++ b/src/components/custom-toolbar.tsx @@ -0,0 +1,218 @@ +import React, { useState, useEffect } from 'react'; +import { format } from 'date-fns'; +import './custom-toolbar.css'; + +interface CustomToolbarProps { + // Das aktuell angezeigte Datum (wird z. B. von der Calendar-Komponente geliefert) + date: Date; + // Aktuelle Ansicht: "month", "week", "day" oder "agenda" + view: 'month' | 'week' | 'day' | 'agenda'; + /** + * onNavigate ermöglicht das Wechseln des angezeigten Datums. + * Action kann bspw. 'TODAY' oder 'SET_DATE' sein; newDate wird übergeben, wenn benötigt. + */ + onNavigate: (action: string, newDate?: Date) => void; + // onView wechselt die Ansicht + onView: (newView: 'month' | 'week' | 'day' | 'agenda') => void; +} + +const CustomToolbar: React.FC = ({ date, view, onNavigate, onView }) => { + + // Hilfsfunktion, um die ISO-Wochennummer eines Datums zu ermitteln + const getISOWeek = (date: Date): number => { + const tmp = new Date(date.getTime()); + // Verschiebe das Datum so, dass der nächste Donnerstag erreicht wird (ISO: Woche beginnt am Montag) + tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); + const yearStart = new Date(tmp.getFullYear(), 0, 1); + const weekNo = Math.ceil((((tmp.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); + return weekNo; + }; + + // Neue Funktion: Ermittelt das ISO-Wochenjahr eines Datums. + // Das ISO-Wochenjahr entspricht dem Jahr des Donnerstags in dieser Woche. + const getISOWeekYear = (date: Date): number => { + const tmp = new Date(date.getTime()); + tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); + return tmp.getFullYear(); + }; + + // Ermittelt die Anzahl der ISO-Wochen im Jahr + const getISOWeeksInYear = (year: number): number => { + const d = new Date(year, 11, 31); + const week = getISOWeek(d); + return week === 1 ? getISOWeek(new Date(year, 11, 24)) : week; + }; + + /* + Berechnet den Montag der gewünschten ISO-Woche eines Jahres. + Wir ermitteln zunächst den ersten Montag der ersten ISO-Woche und addieren dann (week - 1) * 7 Tage. + */ + const getDateOfISOWeek = (week: number, year: number): Date => { + const jan1 = new Date(year, 0, 1); + const dayOfWeek = jan1.getDay(); + const isoDayOfWeek = dayOfWeek === 0 ? 7 : dayOfWeek; + let firstMonday: Date; + if (isoDayOfWeek <= 4) { + // Jan 1 gehört zur ersten ISO-Woche – bestimme den Montag dieser Woche + firstMonday = new Date(year, 0, 1 - isoDayOfWeek + 1); + } else { + // Andernfalls liegt der erste Montag in der darauffolgenden Woche + firstMonday = new Date(year, 0, 1 + (8 - isoDayOfWeek)); + } + firstMonday.setDate(firstMonday.getDate() + (week - 1) * 7); + return firstMonday; + }; + + // Lokaler State für Woche und ISO-Wochenjahr (statt des reinen Kalenderjahrs) + const [selectedWeek, setSelectedWeek] = useState(getISOWeek(date)); + const [selectedYear, setSelectedYear] = useState(getISOWeekYear(date)); + + // Aktualisiere die Auswahl, wenn sich die Prop "date" ändert + useEffect(() => { + setSelectedWeek(getISOWeek(date)); + setSelectedYear(getISOWeekYear(date)); + }, [date]); + + // Für die Dropdown-Liste der Wochen: Liste von 1 bis totalWeeks + const totalWeeks = getISOWeeksInYear(selectedYear); + const weekOptions = Array.from({ length: totalWeeks }, (_, i) => i + 1); + + // Beispielhafte Jahresliste: aktuelles ISO-Wochenjahr ± 10 + const yearOptions = Array.from({ length: 21 }, (_, i) => selectedYear - 10 + i); + + // Berechne den Start (Montag) und das Ende (Sonntag) der aktuell angezeigten Woche + const weekStartDate = getDateOfISOWeek(selectedWeek, selectedYear); + const weekEndDate = new Date(weekStartDate); + weekEndDate.setDate(weekStartDate.getDate() + 6); + + // Ermittele Monat und Jahr von Start- und Enddatum (normales Kalenderjahr) + const monthStart = format(weekStartDate, 'MMMM'); + const monthEnd = format(weekEndDate, 'MMMM'); + const yearAtStart = format(weekStartDate, 'yyyy'); + const yearAtEnd = format(weekEndDate, 'yyyy'); + + // Erstelle das Label: + // 1. Falls der Wochenanfang und das Wochenende in unterschiedlichen Jahren liegen, + // wird z. B. "Dezember 2025 - Januar 2026" angezeigt. + // 2. Liegen beide im gleichen Jahr, wird unterschieden zwischen gleichem Monat und unterschiedlichem Monat. + let dateLabel: string; + if (yearAtStart !== yearAtEnd) { + dateLabel = `${monthStart} ${yearAtStart} - ${monthEnd} ${yearAtEnd}`; + } else if (monthStart !== monthEnd) { + dateLabel = `${monthStart} - ${monthEnd} ${yearAtStart}`; + } else { + dateLabel = `${monthStart} ${yearAtStart}`; + } + + // Handler zum Wechseln der Ansicht + const handleViewChange = (newView: 'month' | 'week' | 'day' | 'agenda') => { + onView(newView); + }; + + // "Today"-Button: setzt das Datum auf das heutige Datum (unter Verwendung des ISO-Wochenjahrs) + const handleToday = () => { + const today = new Date(); + setSelectedWeek(getISOWeek(today)); + setSelectedYear(getISOWeekYear(today)); + onNavigate('TODAY', today); + }; + + // Wechselt zur vorherigen Woche. Bei Woche < 1, wird ins Vorjahr gewechselt. + const handlePrevWeek = () => { + let newWeek = selectedWeek - 1; + let newYear = selectedYear; + if (newWeek < 1) { + newYear = selectedYear - 1; + newWeek = getISOWeeksInYear(newYear); + } + setSelectedWeek(newWeek); + setSelectedYear(newYear); + const newDate = getDateOfISOWeek(newWeek, newYear); + onNavigate('SET_DATE', newDate); + }; + + // Wechselt zur nächsten Woche. Überschreitet die Woche die maximale Zahl, wechselt ins nächste Jahr. + const handleNextWeek = () => { + let newWeek = selectedWeek + 1; + let newYear = selectedYear; + if (newWeek > getISOWeeksInYear(selectedYear)) { + newYear = selectedYear + 1; + newWeek = 1; + } + setSelectedWeek(newWeek); + setSelectedYear(newYear); + const newDate = getDateOfISOWeek(newWeek, newYear); + onNavigate('SET_DATE', newDate); + }; + + // Handler, wenn der Nutzer über das Dropdown eine Woche auswählt + const handleWeekChange = (event: React.ChangeEvent) => { + const newWeek = parseInt(event.target.value, 10); + setSelectedWeek(newWeek); + const newDate = getDateOfISOWeek(newWeek, selectedYear); + onNavigate('SET_DATE', newDate); + }; + + // Handler, wenn der Nutzer über das Dropdown ein Jahr auswählt + const handleYearChange = (event: React.ChangeEvent) => { + const newYear = parseInt(event.target.value, 10); + setSelectedYear(newYear); + const totalWeeksInNewYear = getISOWeeksInYear(newYear); + const newWeek = Math.min(selectedWeek, totalWeeksInNewYear); + setSelectedWeek(newWeek); + const newDate = getDateOfISOWeek(newWeek, newYear); + onNavigate('SET_DATE', newDate); + }; + + return ( +
+ {/* Anzeige des Datums-Labels */} +
+ {dateLabel} +
+ + {/* Ansicht wechseln */} +
+
+ + + + +
+
+ +
+ {/* Navigationsbuttons */} +
+
+ + +
+
+ +
+
+ + {/* DropDowns für Woche und Jahr */} +
+ + +
+
+
+ ); +}; + +export default CustomToolbar; diff --git a/yarn.lock b/yarn.lock index ba28418..61defe3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2456,6 +2456,13 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:^4.1.0": + version: 4.1.0 + resolution: "date-fns@npm:4.1.0" + checksum: 10c0/b79ff32830e6b7faa009590af6ae0fb8c3fd9ffad46d930548fbb5acf473773b4712ae887e156ba91a7b3dc30591ce0f517d69fd83bd9c38650fdc03b4e0bac8 + languageName: node + linkType: hard + "dayjs@npm:^1.11.7": version: 1.11.13 resolution: "dayjs@npm:1.11.13" @@ -4023,6 +4030,7 @@ __metadata: "@types/react-dom": "npm:19.1.6" class-variance-authority: "npm:^0.7.1" clsx: "npm:^2.1.1" + date-fns: "npm:^4.1.0" dotenv-cli: "npm:8.0.0" eslint: "npm:9.28.0" eslint-config-next: "npm:15.3.3" From 0c529c3cb8208e1ca1a8eb2714de72c72c01bda4 Mon Sep 17 00:00:00 2001 From: Semir Date: Fri, 6 Jun 2025 10:43:25 +0200 Subject: [PATCH 05/20] feat: Calendar Layout and Function Update --- package.json | 1 + src/app/home/page.tsx | 39 +- src/components/calendar.tsx | 38 ++ src/components/custom-toolbar.css | 70 +- src/components/custom-toolbar.tsx | 249 ++++--- src/components/react-big-calendar.css | 904 ++++++++++++++++++++++++++ yarn.lock | 38 +- 7 files changed, 1172 insertions(+), 167 deletions(-) create mode 100644 src/components/calendar.tsx create mode 100644 src/components/react-big-calendar.css diff --git a/package.json b/package.json index b4c9bfb..1dbd9d4 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "next-themes": "^0.4.6", "react": "^19.1.0", "react-big-calendar": "^1.18.0", + "react-datepicker": "^8.4.0", "react-dom": "^19.0.0", "tailwind-merge": "^3.2.0" }, diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index 6149d47..0b7515a 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -1,38 +1,3 @@ -"use client"; +import Calendar from '@/components/calendar'; -import { Calendar, momentLocalizer } from 'react-big-calendar'; -import moment from 'moment'; -import 'react-big-calendar/lib/css/react-big-calendar.css'; -import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'; -import CustomToolbar from '@/components/custom-toolbar'; - -moment.updateLocale('en', { - week: { - dow: 1, - doy: 4, - }, -}); - -const localizer = momentLocalizer(moment) - -const MyCalendar = (props) => ( -
- -
-) - -export default MyCalendar; +export default function home () {return } \ No newline at end of file diff --git a/src/components/calendar.tsx b/src/components/calendar.tsx new file mode 100644 index 0000000..defedab --- /dev/null +++ b/src/components/calendar.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { Calendar, momentLocalizer } from 'react-big-calendar'; +import moment from 'moment'; +import '@/components/react-big-calendar.css'; +import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'; +import CustomToolbar from '@/components/custom-toolbar'; + +moment.updateLocale('en', { + week: { + dow: 1, + doy: 4, + }, +}); + +const localizer = momentLocalizer(moment) + +const MyCalendar = (props) => ( +
+ +
+) + +export default MyCalendar; diff --git a/src/components/custom-toolbar.css b/src/components/custom-toolbar.css index 16e86ed..e16a8e4 100644 --- a/src/components/custom-toolbar.css +++ b/src/components/custom-toolbar.css @@ -13,8 +13,7 @@ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } -/* Style für den Bereich, in dem die Ansichten (Month, Week, etc.) gewechselt werden */ -.custom-toolbar .view-change .view-switcher { +/*.custom-toolbar .view-change .view-switcher { display: flex; gap: 8px; justify-content: center; @@ -24,7 +23,7 @@ padding: 8px 16px; background-color: #c1830d; /*border: 1px solid #ccc;*/ - border-radius: 11px; +/* border-radius: 11px; font-size: 12px; cursor: pointer; transition: background-color 0.2s, border-color 0.2s; @@ -42,7 +41,7 @@ background-color: #d0d0d0; border-color: #aaa; cursor: default; -} +}*/ /* Anzeige des aktuellen Datums (Monat und Jahr) */ .custom-toolbar .current-date { @@ -50,7 +49,7 @@ font-size: 12px; text-align: center; color: #ffffff; - margin: 4px 0; + /*margin: 4px 0;*/ background-color: #717171; width: 178px; height: 37px; @@ -110,30 +109,53 @@ } .right-section { - background-color: #717171; - width: 393px; - height: 37px; - border-radius: 11px; + background-color: #717171; + width: 393px; + height: 48px; + border-radius: 11px; + justify-items: center; + align-items: center; } .custom-toolbar .navigation-controls .handleWeek button { - background-color: #717171; - height: 30px; - width: 30px; - margin-bottom: 3.5px; + background-color: #717171; + height: 30px; + width: 30px; + margin-bottom: 3.5px; } -.custom-toolbar .navigation-controls .today button { - background-color: #c6c6c6; - height: 30px; - width: 100px; - color: #000000; - margin-top: 3.5px; -} +/*.custom-toolbar .navigation-controls .today button { + background-color: #c6c6c6; + height: 30px; + width: 100px; + color: #000000; + margin-top: 3.5px; +}*/ .view-change { - background-color: #717171; - height: 37px; - width: 290px; - border-radius: 11px; + background-color: #717171; + height: 48px; + width: 323px; + border-radius: 11px; + justify-items: center; +} + +.right-section .datepicker-box { + color: #000000; + background-color: #c6c6c6; + height: 36px; + width: 85px; + border-radius: 11px; + font-size: 12px; + align-self: center; +} + +.datepicker { + text-align: center; + width: 85px; + height: 30px; +} + +.datepicker-box { + z-index: 9999; } diff --git a/src/components/custom-toolbar.tsx b/src/components/custom-toolbar.tsx index 10feafa..5f9a2cf 100644 --- a/src/components/custom-toolbar.tsx +++ b/src/components/custom-toolbar.tsx @@ -1,214 +1,253 @@ import React, { useState, useEffect } from 'react'; import { format } from 'date-fns'; import './custom-toolbar.css'; +import { Button } from '@/components/custom-ui/button'; +import DatePicker from 'react-datepicker'; +import 'react-datepicker/dist/react-datepicker.css'; interface CustomToolbarProps { - // Das aktuell angezeigte Datum (wird z. B. von der Calendar-Komponente geliefert) + //Aktuell angezeigtes Datum date: Date; - // Aktuelle Ansicht: "month", "week", "day" oder "agenda" + //Aktuelle Ansicht view: 'month' | 'week' | 'day' | 'agenda'; - /** - * onNavigate ermöglicht das Wechseln des angezeigten Datums. - * Action kann bspw. 'TODAY' oder 'SET_DATE' sein; newDate wird übergeben, wenn benötigt. - */ + onNavigate: (action: string, newDate?: Date) => void; - // onView wechselt die Ansicht + //Ansichtwechsel onView: (newView: 'month' | 'week' | 'day' | 'agenda') => void; } const CustomToolbar: React.FC = ({ date, view, onNavigate, onView }) => { - // Hilfsfunktion, um die ISO-Wochennummer eines Datums zu ermitteln + //ISO-Wochennummer eines Datums ermitteln const getISOWeek = (date: Date): number => { const tmp = new Date(date.getTime()); - // Verschiebe das Datum so, dass der nächste Donnerstag erreicht wird (ISO: Woche beginnt am Montag) + //Datum so verschieben, dass der nächste Donnerstag erreicht wird (ISO: Woche beginnt am Montag) tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); const yearStart = new Date(tmp.getFullYear(), 0, 1); const weekNo = Math.ceil((((tmp.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); return weekNo; }; - // Neue Funktion: Ermittelt das ISO-Wochenjahr eines Datums. - // Das ISO-Wochenjahr entspricht dem Jahr des Donnerstags in dieser Woche. + //ISO-Wochenjahr eines Datums ermitteln const getISOWeekYear = (date: Date): number => { const tmp = new Date(date.getTime()); tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); return tmp.getFullYear(); }; - // Ermittelt die Anzahl der ISO-Wochen im Jahr + //Ermittlung der Anzahl der Wochen im Jahr const getISOWeeksInYear = (year: number): number => { const d = new Date(year, 11, 31); const week = getISOWeek(d); return week === 1 ? getISOWeek(new Date(year, 11, 24)) : week; }; - /* - Berechnet den Montag der gewünschten ISO-Woche eines Jahres. - Wir ermitteln zunächst den ersten Montag der ersten ISO-Woche und addieren dann (week - 1) * 7 Tage. - */ const getDateOfISOWeek = (week: number, year: number): Date => { const jan1 = new Date(year, 0, 1); const dayOfWeek = jan1.getDay(); const isoDayOfWeek = dayOfWeek === 0 ? 7 : dayOfWeek; let firstMonday: Date; if (isoDayOfWeek <= 4) { - // Jan 1 gehört zur ersten ISO-Woche – bestimme den Montag dieser Woche + //1. Januar gehört zur ersten ISO-Woche (Montag dieser Woche bestimmen) firstMonday = new Date(year, 0, 1 - isoDayOfWeek + 1); } else { - // Andernfalls liegt der erste Montag in der darauffolgenden Woche + //Ansonsten liegt der erste Montag in der darauffolgenden Woche firstMonday = new Date(year, 0, 1 + (8 - isoDayOfWeek)); } firstMonday.setDate(firstMonday.getDate() + (week - 1) * 7); return firstMonday; }; - // Lokaler State für Woche und ISO-Wochenjahr (statt des reinen Kalenderjahrs) + //Lokaler State für Woche und ISO-Wochenjahr (statt des reinen Kalenderjahrs) const [selectedWeek, setSelectedWeek] = useState(getISOWeek(date)); const [selectedYear, setSelectedYear] = useState(getISOWeekYear(date)); - // Aktualisiere die Auswahl, wenn sich die Prop "date" ändert + //Auswahl aktualisieren, wenn sich die Prop "date" ändert useEffect(() => { setSelectedWeek(getISOWeek(date)); setSelectedYear(getISOWeekYear(date)); }, [date]); - // Für die Dropdown-Liste der Wochen: Liste von 1 bis totalWeeks + //Dropdown-Liste der Wochen const totalWeeks = getISOWeeksInYear(selectedYear); const weekOptions = Array.from({ length: totalWeeks }, (_, i) => i + 1); - // Beispielhafte Jahresliste: aktuelles ISO-Wochenjahr ± 10 + //Jahresliste const yearOptions = Array.from({ length: 21 }, (_, i) => selectedYear - 10 + i); - // Berechne den Start (Montag) und das Ende (Sonntag) der aktuell angezeigten Woche + //Start (Montag) und Ende (Sonntag) der aktuell angezeigten Woche berechnen const weekStartDate = getDateOfISOWeek(selectedWeek, selectedYear); const weekEndDate = new Date(weekStartDate); weekEndDate.setDate(weekStartDate.getDate() + 6); - // Ermittele Monat und Jahr von Start- und Enddatum (normales Kalenderjahr) + //Monat und Jahr von Start- und Enddatum ermitteln const monthStart = format(weekStartDate, 'MMMM'); const monthEnd = format(weekEndDate, 'MMMM'); const yearAtStart = format(weekStartDate, 'yyyy'); const yearAtEnd = format(weekEndDate, 'yyyy'); - // Erstelle das Label: - // 1. Falls der Wochenanfang und das Wochenende in unterschiedlichen Jahren liegen, - // wird z. B. "Dezember 2025 - Januar 2026" angezeigt. - // 2. Liegen beide im gleichen Jahr, wird unterschieden zwischen gleichem Monat und unterschiedlichem Monat. - let dateLabel: string; - if (yearAtStart !== yearAtEnd) { - dateLabel = `${monthStart} ${yearAtStart} - ${monthEnd} ${yearAtEnd}`; - } else if (monthStart !== monthEnd) { - dateLabel = `${monthStart} - ${monthEnd} ${yearAtStart}`; - } else { - dateLabel = `${monthStart} ${yearAtStart}`; - } - - // Handler zum Wechseln der Ansicht + //Ansichtwechsel const handleViewChange = (newView: 'month' | 'week' | 'day' | 'agenda') => { onView(newView); }; - // "Today"-Button: setzt das Datum auf das heutige Datum (unter Verwendung des ISO-Wochenjahrs) + //Today-Button aktualisiert das Datum im DatePicker auf das heutige const handleToday = () => { const today = new Date(); + setSelectedDate(today); setSelectedWeek(getISOWeek(today)); setSelectedYear(getISOWeekYear(today)); onNavigate('TODAY', today); }; - // Wechselt zur vorherigen Woche. Bei Woche < 1, wird ins Vorjahr gewechselt. - const handlePrevWeek = () => { - let newWeek = selectedWeek - 1; - let newYear = selectedYear; - if (newWeek < 1) { - newYear = selectedYear - 1; - newWeek = getISOWeeksInYear(newYear); + //Pfeiltaste nach Vorne + const handleNext = () => { + let newDate: Date; + if (view === 'day' || view === 'agenda') { + newDate = new Date(date); + newDate.setDate(newDate.getDate() + 1); + } else if (view === 'week') { + let newWeek = selectedWeek + 1; + let newYear = selectedYear; + if (newWeek > getISOWeeksInYear(selectedYear)) { + newYear = selectedYear + 1; + newWeek = 1; + } + setSelectedWeek(newWeek); + setSelectedYear(newYear); + newDate = getDateOfISOWeek(newWeek, newYear); + } else if (view === 'month') { + newDate = new Date(date.getFullYear(), date.getMonth() + 1, 1); + } else { + newDate = new Date(date); } - setSelectedWeek(newWeek); - setSelectedYear(newYear); - const newDate = getDateOfISOWeek(newWeek, newYear); + //Datum im DatePicker aktualisieren + setSelectedDate(newDate); onNavigate('SET_DATE', newDate); }; - // Wechselt zur nächsten Woche. Überschreitet die Woche die maximale Zahl, wechselt ins nächste Jahr. - const handleNextWeek = () => { - let newWeek = selectedWeek + 1; - let newYear = selectedYear; - if (newWeek > getISOWeeksInYear(selectedYear)) { - newYear = selectedYear + 1; - newWeek = 1; + //Pfeiltaste nach Hinten + const handlePrev = () => { + let newDate: Date; + if (view === 'day' || view === 'agenda') { + newDate = new Date(date); + newDate.setDate(newDate.getDate() - 1); + } else if (view === 'week') { + let newWeek = selectedWeek - 1; + let newYear = selectedYear; + if (newWeek < 1) { + newYear = selectedYear - 1; + newWeek = getISOWeeksInYear(newYear); + } + setSelectedWeek(newWeek); + setSelectedYear(newYear); + newDate = getDateOfISOWeek(newWeek, newYear); + } else if (view === 'month') { + newDate = new Date(date.getFullYear(), date.getMonth() - 1, 1); + } else { + newDate = new Date(date); } - setSelectedWeek(newWeek); - setSelectedYear(newYear); - const newDate = getDateOfISOWeek(newWeek, newYear); + //Datum im DatePicker aktualisieren + setSelectedDate(newDate); onNavigate('SET_DATE', newDate); }; - // Handler, wenn der Nutzer über das Dropdown eine Woche auswählt - const handleWeekChange = (event: React.ChangeEvent) => { - const newWeek = parseInt(event.target.value, 10); - setSelectedWeek(newWeek); - const newDate = getDateOfISOWeek(newWeek, selectedYear); - onNavigate('SET_DATE', newDate); + const [selectedDate, setSelectedDate] = useState(new Date()); + + const handleDateChange = (date: Date | null) => { + setSelectedDate(date); + if (date) { + if (view === 'week') { + const newWeek = getISOWeek(date); + const newYear = getISOWeekYear(date); + setSelectedWeek(newWeek); + setSelectedYear(newYear); + const newDate = getDateOfISOWeek(newWeek, newYear); + onNavigate('SET_DATE', newDate); + } else if (view === 'day') { + onNavigate('SET_DATE', date); + } else if (view === 'month') { + const newDate = new Date(date.getFullYear(), date.getMonth(), 1); + onNavigate('SET_DATE', newDate); + } else if (view === 'agenda') { + onNavigate('SET_DATE', date); + } + } }; - // Handler, wenn der Nutzer über das Dropdown ein Jahr auswählt - const handleYearChange = (event: React.ChangeEvent) => { - const newYear = parseInt(event.target.value, 10); - setSelectedYear(newYear); - const totalWeeksInNewYear = getISOWeeksInYear(newYear); - const newWeek = Math.min(selectedWeek, totalWeeksInNewYear); - setSelectedWeek(newWeek); - const newDate = getDateOfISOWeek(newWeek, newYear); - onNavigate('SET_DATE', newDate); - }; return (
- {/* Anzeige des Datums-Labels */} -
- {dateLabel} -
- - {/* Ansicht wechseln */}
- - - - + + + +
- {/* Navigationsbuttons */}
- - + +
- +
- {/* DropDowns für Woche und Jahr */} -
- - +
+
diff --git a/src/components/react-big-calendar.css b/src/components/react-big-calendar.css new file mode 100644 index 0000000..305dce0 --- /dev/null +++ b/src/components/react-big-calendar.css @@ -0,0 +1,904 @@ +@charset "UTF-8"; +.rbc-btn { + color: inherit; + font: inherit; + margin: 0; +} + +button.rbc-btn { + overflow: visible; + text-transform: none; + -webkit-appearance: button; + -moz-appearance: button; + appearance: button; + cursor: pointer; +} + +button[disabled].rbc-btn { + cursor: not-allowed; +} + +button.rbc-input::-moz-focus-inner { + border: 0; + padding: 0; +} + +.rbc-calendar { + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 100%; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.rbc-m-b-negative-3 { + margin-bottom: -3px; +} + +.rbc-h-full { + height: 100%; +} + +.rbc-calendar *, +.rbc-calendar *:before, +.rbc-calendar *:after { + -webkit-box-sizing: inherit; + box-sizing: inherit; +} + +.rbc-abs-full, .rbc-row-bg { + overflow: hidden; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.rbc-ellipsis, .rbc-show-more, .rbc-row-segment .rbc-event-content, .rbc-event-label { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.rbc-rtl { + direction: rtl; +} + +.rbc-off-range { + color: #999999; +} + +.rbc-off-range-bg { + background: #e6e6e6; +} + +.rbc-header { + overflow: hidden; + -webkit-box-flex: 1; + -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 #ddd; +} +.rbc-header + .rbc-header { + border-left: 1px solid #c6c6c6; /*#ddd*/ +} +.rbc-rtl .rbc-header + .rbc-header { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.rbc-header > a, .rbc-header > a:active, .rbc-header > a:visited { + color: inherit; + text-decoration: none; +} + +.rbc-button-link { + color: inherit; + background: none; + margin: 0; + padding: 0; + border: none; + cursor: pointer; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.rbc-row-content { + position: relative; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-select: none; + z-index: 4; +} + +.rbc-row-content-scrollable { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + height: 100%; +} +.rbc-row-content-scrollable .rbc-row-content-scroll-container { + height: 100%; + overflow-y: scroll; + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + /* Hide scrollbar for Chrome, Safari and Opera */ +} +.rbc-row-content-scrollable .rbc-row-content-scroll-container::-webkit-scrollbar { + display: none; +} + +.rbc-today { + background-color: #5770ff; /*#eaf6ff*/ +} +/*Own changes 10*/ +.rbc-allday-cell .rbc-row-bg .rbc-day-bg.rbc-today { + background-color: transparent !important; + /*border: none !important;*/ +} +/*Own changes 10*/ + +/*Own changes 11*/ +.rbc-time-header-cell .rbc-header:first-child.rbc-today { + border-top-left-radius: 11px !important; +} + +.rbc-time-header-cell .rbc-header:last-child.rbc-today { + border-top-right-radius: 11px !important; +} +/*Own changes 11*/ + +.rbc-toolbar { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + margin-bottom: 10px; + font-size: 16px; +} +.rbc-toolbar .rbc-toolbar-label { + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 0 10px; + text-align: center; + + /*Own changes 01*/ + background-color: #717171; + color: #ffffff; + /*Own changes 01*/ +} +.rbc-toolbar button { + color: #373a3c; + display: inline-block; + margin: 0; + text-align: center; + vertical-align: middle; + background: none; + background-image: none; + border: 1px solid #ccc; + padding: 0.375rem 1rem; + border-radius: 4px; + line-height: normal; + white-space: nowrap; +} +.rbc-toolbar button:active, .rbc-toolbar button.rbc-active { + background-image: none; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + background-color: #e6e6e6; + border-color: #adadad; +} +.rbc-toolbar button:active:hover, .rbc-toolbar button:active:focus, .rbc-toolbar button.rbc-active:hover, .rbc-toolbar button.rbc-active:focus { + color: #373a3c; + background-color: #d4d4d4; + border-color: #8c8c8c; +} +.rbc-toolbar button:focus { + color: #373a3c; + background-color: #e6e6e6; + border-color: #adadad; +} +.rbc-toolbar button:hover { + color: #373a3c; + cursor: pointer; + background-color: #e6e6e6; + border-color: #adadad; +} + +.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; + + /*Own changes 02*/ + background-color: #c6c6c6; + color: #000000; + /*Own changes 02*/ +} +.rbc-btn-group > button:last-child:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + + /*Own changes 03*/ + background-color: #c6c6c6; + color: #000000; + /*Own changes 03*/ +} +.rbc-rtl .rbc-btn-group > button:first-child:not(:last-child) { + border-radius: 4px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.rbc-rtl .rbc-btn-group > button:last-child:not(:first-child) { + border-radius: 4px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.rbc-btn-group > button:not(:first-child):not(:last-child) { + border-radius: 0; + + /*Own changes 04*/ + background-color: #c6c6c6; + color: #000000; + /*Own changes 04*/ +} +.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; +} + +@media (max-width: 767px) { + .rbc-toolbar { + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + } +} +.rbc-event, .rbc-day-slot .rbc-background-event { + border: none; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -webkit-box-shadow: none; + box-shadow: none; + margin: 0; + padding: 2px 5px; + background-color: #3174ad; + border-radius: 5px; + color: #fff; + cursor: pointer; + width: 100%; + text-align: left; +} +.rbc-slot-selecting .rbc-event, .rbc-slot-selecting .rbc-day-slot .rbc-background-event, .rbc-day-slot .rbc-slot-selecting .rbc-background-event { + cursor: inherit; + pointer-events: none; +} +.rbc-event.rbc-selected, .rbc-day-slot .rbc-selected.rbc-background-event { + background-color: #265985; +} +.rbc-event:focus, .rbc-day-slot .rbc-background-event:focus { + outline: 5px auto #3b99fc; +} + +.rbc-event-label { + font-size: 80%; +} + +.rbc-event-overlaps { + -webkit-box-shadow: -1px 1px 5px 0px rgba(51, 51, 51, 0.5); + 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-row { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -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; + color: #3174ad; +} +.rbc-show-more:hover, .rbc-show-more:focus { + color: #265985; +} + +.rbc-month-view { + position: relative; + border: 1px solid #ddd; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -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-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; +} + +.rbc-month-row { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + position: relative; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + -ms-flex-preferred-size: 0px; + flex-basis: 0px; + overflow: hidden; + height: 100%; +} +.rbc-month-row + .rbc-month-row { + border-top: 1px solid #ddd; +} + +.rbc-date-cell { + -webkit-box-flex: 1; + -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-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + overflow: hidden; + right: 1px; +} + +.rbc-day-bg { + -webkit-box-flex: 1; + -ms-flex: 1 0 0%; + flex: 1 0 0%; +} +.rbc-day-bg + .rbc-day-bg { + border-left: 1px solid #ddd; +} +.rbc-rtl .rbc-day-bg + .rbc-day-bg { + border-left-width: 0; + border-right: 1px solid #ddd; +} + +.rbc-overlay { + position: absolute; + z-index: 5; + border: 1px solid #e5e5e5; + background-color: #fff; + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.25); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.25); + padding: 10px; +} +.rbc-overlay > * + * { + margin-top: 1px; +} + +.rbc-overlay-header { + border-bottom: 1px solid #e5e5e5; + margin: -10px -10px 5px -10px; + padding: 2px 10px; +} + +.rbc-agenda-view { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + overflow: auto; +} +.rbc-agenda-view table.rbc-agenda-table { + width: 100%; + border: 1px solid #ddd; + 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 #ddd; +} +.rbc-rtl .rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.rbc-agenda-view table.rbc-agenda-table tbody > tr + tr { + border-top: 1px solid #ddd; +} +.rbc-agenda-view table.rbc-agenda-table thead > tr > th { + padding: 3px 5px; + text-align: left; + border-bottom: 1px solid #ddd; +} +.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-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + min-height: 100%; + + /*Own changes 06*/ + background-color: #383838; + /*Own changes 06*/ +} +.rbc-time-column .rbc-timeslot-group { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.rbc-timeslot-group { + border-bottom: 1px solid #8d8d8d; /*#ddd*/ + min-height: 40px; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-flow: column nowrap; + flex-flow: column nowrap; +} + +.rbc-time-gutter, +.rbc-header-gutter { + -webkit-box-flex: 0; + -ms-flex: none; + flex: none; + + /*Own changes 07*/ + background-color: #8d8d8d; + /*Own changes 07*/ +} + +.rbc-label { + padding: 0 5px; +} + +.rbc-day-slot { + position: relative; +} +.rbc-day-slot .rbc-events-container { + bottom: 0; + left: 0; + position: absolute; + right: 0; + margin-right: 10px; + top: 0; +} +.rbc-day-slot .rbc-events-container.rbc-rtl { + left: 10px; + right: 0; +} +.rbc-day-slot .rbc-event, .rbc-day-slot .rbc-background-event { + border: 1px solid #265985; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + max-height: 100%; + min-height: 20px; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-flow: column wrap; + flex-flow: column wrap; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; + overflow: hidden; + position: absolute; +} +.rbc-day-slot .rbc-background-event { + opacity: 0.75; +} +.rbc-day-slot .rbc-event-label { + -webkit-box-flex: 0; + -ms-flex: none; + flex: none; + padding-right: 5px; + width: auto; +} +.rbc-day-slot .rbc-event-content { + width: 100%; + -webkit-box-flex: 1; + -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 #383838; /*#f7f7f7*/ +} + +.rbc-time-view-resources .rbc-time-gutter, +.rbc-time-view-resources .rbc-time-header-gutter { + position: sticky; + left: 0; + background-color: white; + border-right: 1px solid #ddd; + z-index: 10; + margin-right: -1px; +} +.rbc-time-view-resources .rbc-time-header { + overflow: hidden; +} +.rbc-time-view-resources .rbc-time-header-content { + min-width: auto; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + -ms-flex-preferred-size: 0px; + flex-basis: 0px; +} +.rbc-time-view-resources .rbc-time-header-cell-single-day { + display: none; +} +.rbc-time-view-resources .rbc-day-slot { + min-width: 140px; +} +.rbc-time-view-resources .rbc-header, +.rbc-time-view-resources .rbc-day-bg { + width: 140px; + -webkit-box-flex: 1; + -ms-flex: 1 1 0px; + flex: 1 1 0; + -ms-flex-preferred-size: 0 px; + flex-basis: 0 px; +} + +.rbc-time-header-content + .rbc-time-header-content { + margin-left: -1px; +} + +.rbc-time-slot { + -webkit-box-flex: 1; + -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-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + width: 100%; + border: 1px solid #ddd; + min-height: 0; +} +.rbc-time-view .rbc-time-gutter { + white-space: nowrap; + text-align: right; +} +.rbc-time-view .rbc-allday-cell { + -webkit-box-sizing: content-box; + box-sizing: content-box; + width: 100%; + height: 100%; + position: relative; + + /*Own changes 05*/ + background-color: #555555; + /*Own changes 05*/ +} +.rbc-time-view .rbc-allday-cell + .rbc-allday-cell { + border-left: 1px solid #ddd; +} +.rbc-time-view .rbc-allday-events { + position: relative; + z-index: 4; +} +.rbc-time-view .rbc-row { + -webkit-box-sizing: border-box; + box-sizing: border-box; + min-height: 20px; +} + +.rbc-time-header { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; +} +.rbc-time-header.rbc-overflowing { + border-right: 1px solid #ddd; +} +.rbc-rtl .rbc-time-header.rbc-overflowing { + border-right-width: 0; + border-left: 1px solid #ddd; +} +.rbc-time-header > .rbc-row:first-child { + border-bottom: 1px solid #ddd; +} +.rbc-time-header > .rbc-row.rbc-row-resource { + border-bottom: 1px solid #ddd; +} + +.rbc-time-header-cell-single-day { + display: none; +} + +.rbc-time-header-content { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + min-width: 0; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + border-left: 1px solid #ddd; + + /*Own changes 08*/ + background-color: #c6c6c6; + color: #000000; + border-top-left-radius: 11px; + border-top-right-radius: 11px; + /*Own changes 08*/ +} +.rbc-rtl .rbc-time-header-content { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.rbc-time-header-content > .rbc-row.rbc-row-resource { + border-bottom: 1px solid #ddd; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.rbc-time-content { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 1; + -ms-flex: 1 0 0%; + flex: 1 0 0%; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; + width: 100%; + border-top: 2px solid #717171; /*#ddd*/ + overflow-y: auto; + position: relative; +} +.rbc-time-content > .rbc-time-gutter { + -webkit-box-flex: 0; + -ms-flex: none; + flex: none; + + /*Own changes 09*/ + border-top-left-radius: 11px; + border-bottom-left-radius: 11px; + /*Own changes 09*/ +} +.rbc-time-content > * + * > * { + border-left: 1px solid #c6c6c6; /*#ddd*/ +} +.rbc-rtl .rbc-time-content > * + * > * { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.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 { + position: absolute; + z-index: 3; + left: 0; + right: 0; + height: 1px; + background-color: #74ad31; + pointer-events: none; +} + +.rbc-resource-grouping.rbc-time-header-content { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; +} +.rbc-resource-grouping .rbc-row .rbc-header { + width: 141px; +} + +/*# sourceMappingURL=react-big-calendar.css.map */ \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 61defe3..08d5235 100644 --- a/yarn.lock +++ b/yarn.lock @@ -206,7 +206,7 @@ __metadata: languageName: node linkType: hard -"@floating-ui/react-dom@npm:^2.0.0": +"@floating-ui/react-dom@npm:^2.0.0, @floating-ui/react-dom@npm:^2.1.3": version: 2.1.3 resolution: "@floating-ui/react-dom@npm:2.1.3" dependencies: @@ -218,6 +218,20 @@ __metadata: languageName: node linkType: hard +"@floating-ui/react@npm:^0.27.3": + version: 0.27.12 + resolution: "@floating-ui/react@npm:0.27.12" + dependencies: + "@floating-ui/react-dom": "npm:^2.1.3" + "@floating-ui/utils": "npm:^0.2.9" + tabbable: "npm:^6.0.0" + peerDependencies: + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 10c0/da453965074bd4ded8e3de97ceb2c0833df8df2ecd9eff5ae4d336413443ea5abde5c9e37b092956901b97e7b47f9138d51d4896fa82da68e77eb0090289bf64 + languageName: node + linkType: hard + "@floating-ui/utils@npm:^0.2.9": version: 0.2.9 resolution: "@floating-ui/utils@npm:0.2.9" @@ -4044,6 +4058,7 @@ __metadata: prisma: "npm:6.9.0" react: "npm:^19.1.0" react-big-calendar: "npm:^1.18.0" + react-datepicker: "npm:^8.4.0" react-dom: "npm:^19.0.0" tailwind-merge: "npm:^3.2.0" tailwindcss: "npm:4.1.10" @@ -4580,6 +4595,20 @@ __metadata: languageName: node linkType: hard +"react-datepicker@npm:^8.4.0": + version: 8.4.0 + resolution: "react-datepicker@npm:8.4.0" + dependencies: + "@floating-ui/react": "npm:^0.27.3" + clsx: "npm:^2.1.1" + date-fns: "npm:^4.1.0" + peerDependencies: + react: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc + checksum: 10c0/e96ba4f2b54476f66bfa33aa0c21729095c83d293671b07a9ddd96ab48dad85b6530acc5ca016e83ef8907feeebfefb7133bf7a02dc550175c48c5d9f66d70ac + languageName: node + linkType: hard + "react-dom@npm:^19.0.0": version: 19.1.0 resolution: "react-dom@npm:19.1.0" @@ -5195,6 +5224,13 @@ __metadata: languageName: node linkType: hard +"tabbable@npm:^6.0.0": + version: 6.2.0 + resolution: "tabbable@npm:6.2.0" + checksum: 10c0/ced8b38f05f2de62cd46836d77c2646c42b8c9713f5bd265daf0e78ff5ac73d3ba48a7ca45f348bafeef29b23da7187c72250742d37627883ef89cbd7fa76898 + languageName: node + linkType: hard + "tailwind-merge@npm:^3.2.0": version: 3.3.1 resolution: "tailwind-merge@npm:3.3.1" From 0e0c89fdd79e02fa9a922edb1c88f012e6e79018 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Thu, 12 Jun 2025 12:00:27 +0000 Subject: [PATCH 06/20] chore(deps): update ghcr.io/di0ik/forgejo_runner_container:main docker digest to c4667f2 --- .github/workflows/container-scan.yml | 2 +- .github/workflows/docker-build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/container-scan.yml b/.github/workflows/container-scan.yml index 2a266dd..c87c433 100644 --- a/.github/workflows/container-scan.yml +++ b/.github/workflows/container-scan.yml @@ -9,7 +9,7 @@ jobs: name: Container Scan runs-on: docker container: - image: ghcr.io/di0ik/forgejo_runner_container:main@sha256:c66a37d9af18f8f0f34d16890082bc08d842d52ff2a2bc36d993e3d347b498ac + image: ghcr.io/di0ik/forgejo_runner_container:main@sha256:c4667f2702c32b91b4c92db2ff20739edd00409a44a691c0598cf4a09a47743a steps: - name: Checkout code uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 5d8a383..07fe75e 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -13,7 +13,7 @@ jobs: docker: runs-on: docker container: - image: ghcr.io/di0ik/forgejo_runner_container:main@sha256:c66a37d9af18f8f0f34d16890082bc08d842d52ff2a2bc36d993e3d347b498ac + image: ghcr.io/di0ik/forgejo_runner_container:main@sha256:c4667f2702c32b91b4c92db2ff20739edd00409a44a691c0598cf4a09a47743a steps: - name: Login to Docker Hub uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3 From 210bd132cc3e87cc188579534e6f7def78bac2f3 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Fri, 13 Jun 2025 18:00:50 +0000 Subject: [PATCH 07/20] chore(deps): update dependency eslint to v9.29.0 --- package.json | 2 +- yarn.lock | 73 +++++++++++++++++++++++++++++++++++----------------- 2 files changed, 51 insertions(+), 24 deletions(-) diff --git a/package.json b/package.json index 9d282dd..ab44a28 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "@types/react": "19.1.8", "@types/react-dom": "19.1.6", "dotenv-cli": "8.0.0", - "eslint": "9.28.0", + "eslint": "9.29.0", "eslint-config-next": "15.3.3", "eslint-config-prettier": "10.1.5", "postcss": "8.5.5", diff --git a/yarn.lock b/yarn.lock index 3cfc4ae..42a5d59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -103,14 +103,14 @@ __metadata: languageName: node linkType: hard -"@eslint/config-array@npm:^0.20.0": - version: 0.20.0 - resolution: "@eslint/config-array@npm:0.20.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/94bc5d0abb96dc5295ff559925242ff75a54eacfb3576677e95917e42f7175e1c4b87bf039aa2a872f949b4852ad9724bf2f7529aaea6b98f28bb3fca7f1d659 + checksum: 10c0/709108c3925d83c2166024646829ab61ba5fa85c6568daefd32508899f46ed8dc36d7153042df6dcc7e58ad543bc93298b646575daecb5eb4e39a43d838dab42 languageName: node linkType: hard @@ -147,10 +147,10 @@ __metadata: languageName: node linkType: hard -"@eslint/js@npm:9.28.0": - version: 9.28.0 - resolution: "@eslint/js@npm:9.28.0" - checksum: 10c0/5a6759542490dd9f778993edfbc8d2f55168fd0f7336ceed20fe3870c65499d72fc0bca8d1ae00ea246b0923ea4cba2e0758a8a5507a3506ddcf41c92282abb8 +"@eslint/js@npm:9.29.0": + version: 9.29.0 + resolution: "@eslint/js@npm:9.29.0" + checksum: 10c0/d0ccf37063fa27a3fae9347cb044f84ca10b5a2fa19ffb2b3fedf3b96843ac1ff359ea9f0ab0e80f2f16fda4cb0dc61ea0fed0375090f050fe0a029e7d6de3a3 languageName: node linkType: hard @@ -1943,6 +1943,15 @@ __metadata: languageName: node linkType: hard +"acorn@npm:^8.15.0": + version: 8.15.0 + resolution: "acorn@npm:8.15.0" + bin: + acorn: bin/acorn + checksum: 10c0/dec73ff59b7d6628a01eebaece7f2bdb8bb62b9b5926dcad0f8931f2b8b79c2be21f6c68ac095592adb5adb15831a3635d9343e6a91d028bbe85d564875ec3ec + languageName: node + linkType: hard + "ajv@npm:^6.12.4": version: 6.12.6 resolution: "ajv@npm:6.12.6" @@ -2812,13 +2821,13 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^8.3.0": - version: 8.3.0 - resolution: "eslint-scope@npm:8.3.0" +"eslint-scope@npm:^8.4.0": + version: 8.4.0 + resolution: "eslint-scope@npm:8.4.0" dependencies: esrecurse: "npm:^4.3.0" estraverse: "npm:^5.2.0" - checksum: 10c0/23bf54345573201fdf06d29efa345ab508b355492f6c6cc9e2b9f6d02b896f369b6dd5315205be94b8853809776c4d13353b85c6b531997b164ff6c3328ecf5b + checksum: 10c0/407f6c600204d0f3705bd557f81bd0189e69cd7996f408f8971ab5779c0af733d1af2f1412066b40ee1588b085874fc37a2333986c6521669cdbdd36ca5058e0 languageName: node linkType: hard @@ -2836,17 +2845,24 @@ __metadata: languageName: node linkType: hard -"eslint@npm:9.28.0": - version: 9.28.0 - resolution: "eslint@npm:9.28.0" +"eslint-visitor-keys@npm:^4.2.1": + version: 4.2.1 + resolution: "eslint-visitor-keys@npm:4.2.1" + checksum: 10c0/fcd43999199d6740db26c58dbe0c2594623e31ca307e616ac05153c9272f12f1364f5a0b1917a8e962268fdecc6f3622c1c2908b4fcc2e047a106fe6de69dc43 + languageName: node + linkType: hard + +"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.20.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.28.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" @@ -2858,9 +2874,9 @@ __metadata: cross-spawn: "npm:^7.0.6" debug: "npm:^4.3.2" escape-string-regexp: "npm:^4.0.0" - eslint-scope: "npm:^8.3.0" - eslint-visitor-keys: "npm:^4.2.0" - espree: "npm:^10.3.0" + eslint-scope: "npm:^8.4.0" + eslint-visitor-keys: "npm:^4.2.1" + espree: "npm:^10.4.0" esquery: "npm:^1.5.0" esutils: "npm:^2.0.2" fast-deep-equal: "npm:^3.1.3" @@ -2882,11 +2898,11 @@ __metadata: optional: true bin: eslint: bin/eslint.js - checksum: 10c0/513ea7e69d88a0905d4ed35cef3a8f31ebce7ca9f2cdbda3474495c63ad6831d52357aad65094be7a144d6e51850980ced7d25efb807e8ab06a427241f7cd730 + checksum: 10c0/75e3f841e0f8b0fa93dbb2ba6ae538bd8b611c3654117bc3dadf90bb009923dfd2c15ec2948dc6e6b8b571317cc125c5cceb9255da8cd644ee740020df645dd8 languageName: node linkType: hard -"espree@npm:^10.0.1, espree@npm:^10.3.0": +"espree@npm:^10.0.1": version: 10.3.0 resolution: "espree@npm:10.3.0" dependencies: @@ -2897,6 +2913,17 @@ __metadata: languageName: node linkType: hard +"espree@npm:^10.4.0": + version: 10.4.0 + resolution: "espree@npm:10.4.0" + dependencies: + acorn: "npm:^8.15.0" + acorn-jsx: "npm:^5.3.2" + eslint-visitor-keys: "npm:^4.2.1" + checksum: 10c0/c63fe06131c26c8157b4083313cb02a9a54720a08e21543300e55288c40e06c3fc284bdecf108d3a1372c5934a0a88644c98714f38b6ae8ed272b40d9ea08d6b + languageName: node + linkType: hard + "esquery@npm:^1.5.0": version: 1.6.0 resolution: "esquery@npm:1.6.0" @@ -3868,7 +3895,7 @@ __metadata: class-variance-authority: "npm:^0.7.1" clsx: "npm:^2.1.1" dotenv-cli: "npm:8.0.0" - eslint: "npm:9.28.0" + eslint: "npm:9.29.0" eslint-config-next: "npm:15.3.3" eslint-config-prettier: "npm:10.1.5" lucide-react: "npm:^0.511.0" From 4e87c11ec35a303e60610ad871e0b97a0c7ea422 Mon Sep 17 00:00:00 2001 From: Dominik Stahl Date: Wed, 28 May 2025 13:08:07 +0200 Subject: [PATCH 08/20] feat: implement credentials login implements the credentials login functionality --- package.json | 6 +- src/app/login/page.tsx | 12 +- src/auth.ts | 83 +++++++- src/components/labeled-input.tsx | 7 +- src/components/user/login-form.tsx | 299 ++++++++++++++++++++--------- src/lib/auth/login.ts | 27 +++ src/lib/auth/register.ts | 75 ++++++++ src/lib/hooks/useZodForm.tsx | 14 ++ src/lib/validation/user.ts | 67 +++++++ yarn.lock | 47 +++++ 10 files changed, 522 insertions(+), 115 deletions(-) create mode 100644 src/lib/auth/login.ts create mode 100644 src/lib/auth/register.ts create mode 100644 src/lib/hooks/useZodForm.tsx create mode 100644 src/lib/validation/user.ts diff --git a/package.json b/package.json index ab44a28..f11d01e 100644 --- a/package.json +++ b/package.json @@ -22,6 +22,7 @@ "@fortawesome/free-regular-svg-icons": "^6.7.2", "@fortawesome/free-solid-svg-icons": "^6.7.2", "@fortawesome/react-fontawesome": "^0.2.2", + "@hookform/resolvers": "^5.0.1", "@prisma/client": "^6.9.0", "@radix-ui/react-dropdown-menu": "^2.1.14", "@radix-ui/react-hover-card": "^1.1.13", @@ -32,6 +33,7 @@ "@radix-ui/react-slot": "^1.2.2", "@radix-ui/react-switch": "^1.2.4", "@radix-ui/react-tabs": "^1.1.11", + "bcryptjs": "^3.0.2", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "lucide-react": "^0.511.0", @@ -40,7 +42,9 @@ "next-themes": "^0.4.6", "react": "^19.0.0", "react-dom": "^19.0.0", - "tailwind-merge": "^3.2.0" + "react-hook-form": "^7.56.4", + "tailwind-merge": "^3.2.0", + "zod": "^3.25.60" }, "devDependencies": { "@eslint/eslintrc": "3.3.1", diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx index adaa1c3..2933872 100644 --- a/src/app/login/page.tsx +++ b/src/app/login/page.tsx @@ -6,8 +6,6 @@ import { Button } from '@/components/custom-ui/button'; import Image from 'next/image'; import { Separator } from '@/components/custom-ui/separator'; import Logo from '@/components/logo'; - -import '@/app/globals.css'; import { Card, CardContent, @@ -28,12 +26,12 @@ export default async function LoginPage() { } return ( -
-
-
+
+
+
-
+
@@ -43,8 +41,6 @@ export default async function LoginPage() { - {providerMap.length > 0} - {providerMap.map((provider) => ( ) { return (
@@ -27,7 +30,9 @@ export default function LabeledInput({ id={name} name={name} autoComplete={autocomplete} + {...rest} /> + {error &&

{error}

}
); } diff --git a/src/components/user/login-form.tsx b/src/components/user/login-form.tsx index 8a00749..a0167e2 100644 --- a/src/components/user/login-form.tsx +++ b/src/components/user/login-form.tsx @@ -1,107 +1,214 @@ 'use client'; -import { signIn } from '@/auth'; + +import React, { useState, useRef } from 'react'; +import { useRouter } from 'next/navigation'; + import LabeledInput from '@/components/labeled-input'; import { Button } from '@/components/custom-ui/button'; -import { AuthError } from 'next-auth'; -import { redirect } from 'next/navigation'; -import { useRef, useState } from 'react'; +import useZodForm from '@/lib/hooks/useZodForm'; +import { loginSchema, registerSchema } from '@/lib/validation/user'; +import { loginAction } from '@/lib/auth/login'; +import { registerAction } from '@/lib/auth/register'; -const SIGNIN_ERROR_URL = '/error'; +function LoginFormElement({ + setIsSignUp, + formRef, +}: { + setIsSignUp: (value: boolean | ((prev: boolean) => boolean)) => void; + formRef?: React.RefObject; +}) { + const { handleSubmit, formState, register, setError } = + useZodForm(loginSchema); + const router = useRouter(); + + const onSubmit = handleSubmit(async (data) => { + try { + const { error } = await loginAction(data); + + if (error) { + setError('root', { + message: error, + }); + return; + } else { + router.push('/home'); + router.refresh(); + return; + } + } catch (error: unknown) { + if (error instanceof Error) + setError('root', { + message: error?.message, + }); + else + setError('root', { + message: 'An unknown error occurred.', + }); + } + }); + + return ( +
+ + +
+ + +
+
+ {formState.errors.root?.message && ( +

{formState.errors.root?.message}

+ )} +
+ + ); +} + +function RegisterFormElement({ + setIsSignUp, + formRef, +}: { + setIsSignUp: (value: boolean | ((prev: boolean) => boolean)) => void; + formRef?: React.RefObject; +}) { + const { handleSubmit, formState, register, setError } = + useZodForm(registerSchema); + + const onSubmit = handleSubmit(async (data) => { + try { + const { error } = await registerAction(data); + + if (error) { + setError('root', { + message: error, + }); + return; + } else { + formRef?.current?.reset(); + setIsSignUp(false); + // TODO: Show registration success message (reminder to verify email) + return; + } + } catch (error: unknown) { + if (error instanceof Error) + setError('root', { + message: error?.message, + }); + else + setError('root', { + message: 'An unknown error occurred.', + }); + } + }); + + return ( +
+ + + + + + +
+ + +
+
+ {formState.errors.root?.message && ( +

{formState.errors.root?.message}

+ )} +
+ + ); +} export default function LoginForm() { const [isSignUp, setIsSignUp] = useState(false); const formRef = useRef(null); - return ( -
{ - 'use client'; - try { - if (isSignUp) { - // handle sign up logic here - } else { - await signIn('credentials', formData); - } - } catch (error) { - if (error instanceof AuthError) { - return redirect(`${SIGNIN_ERROR_URL}?error=${error.type}`); - } - throw error; - } - }} - > - {isSignUp ? ( - <> - - - - - - - ) : ( - <> - - - - )} -
- - -
- - ); + if (isSignUp) { + return ; + } + return ; } diff --git a/src/lib/auth/login.ts b/src/lib/auth/login.ts new file mode 100644 index 0000000..0019ae0 --- /dev/null +++ b/src/lib/auth/login.ts @@ -0,0 +1,27 @@ +'use server'; + +import { z } from 'zod'; +import { loginSchema } from '@/lib/validation/user'; +import { signIn } from '@/auth'; + +export async function loginAction(data: z.infer) { + try { + await signIn('credentials', { + ...data, + redirect: false, + }); + + return { + error: undefined, + }; + } catch (error: unknown) { + if (error instanceof Error) { + return { + error: error.message.toString(), + }; + } + return { + error: 'An unknown error occurred.', + }; + } +} diff --git a/src/lib/auth/register.ts b/src/lib/auth/register.ts new file mode 100644 index 0000000..9eba8e9 --- /dev/null +++ b/src/lib/auth/register.ts @@ -0,0 +1,75 @@ +'use server'; + +import type { z } from 'zod'; +import bcrypt from 'bcryptjs'; +import { registerSchema } from '@/lib/validation/user'; +import { prisma } from '@/prisma'; + +export async function registerAction(data: z.infer) { + try { + const result = await registerSchema.safeParseAsync(data); + + if (!result.success) { + return { + error: result.error.errors[0].message, + }; + } + + const { email, password, firstName, lastName, username } = result.data; + + const user = await prisma.user.findUnique({ + where: { + email, + }, + }); + + if (user) { + return { + error: 'User already exist with this email', + }; + } + + const existingUsername = await prisma.user.findUnique({ + where: { + name: username, + }, + }); + + if (existingUsername) { + return { + error: 'Username already exists', + }; + } + + const passwordHash = await bcrypt.hash(password, 10); + + await prisma.$transaction(async (tx) => { + const { id } = await tx.user.create({ + data: { + email, + name: username, + password_hash: passwordHash, + first_name: firstName, + last_name: lastName, + emailVerified: new Date(), // TODO: handle email verification + }, + }); + + await tx.account.create({ + data: { + userId: id, + type: 'credentials', + provider: 'credentials', + providerAccountId: id, + }, + }); + }); + + return {}; + // eslint-disable-next-line @typescript-eslint/no-unused-vars + } catch (_error) { + return { + error: 'System error. Please contact support', + }; + } +} diff --git a/src/lib/hooks/useZodForm.tsx b/src/lib/hooks/useZodForm.tsx new file mode 100644 index 0000000..8b8eb62 --- /dev/null +++ b/src/lib/hooks/useZodForm.tsx @@ -0,0 +1,14 @@ +import { zodResolver } from '@hookform/resolvers/zod'; +import { useForm } from 'react-hook-form'; +import { z } from 'zod'; + +export default function useZodForm< + // eslint-disable-next-line @typescript-eslint/no-explicit-any + Schema extends z.ZodType, + Values extends z.infer, +>(schema: Schema, defaultValues?: Values) { + return useForm({ + resolver: zodResolver(schema), + defaultValues, + }); +} diff --git a/src/lib/validation/user.ts b/src/lib/validation/user.ts new file mode 100644 index 0000000..a2efa5e --- /dev/null +++ b/src/lib/validation/user.ts @@ -0,0 +1,67 @@ +import zod from 'zod'; + +export const loginSchema = zod.object({ + email: zod + .string() + .email('Invalid email address') + .min(3, 'Email is required') + .or( + zod + .string() + .min(3, 'Username is required') + .max(32, 'Username must be at most 32 characters long') + .regex( + /^[a-zA-Z0-9_]+$/, + 'Username can only contain letters, numbers, and underscores', + ), + ), + password: zod.string().min(1, 'Password is required'), +}); + +export const registerSchema = zod + .object({ + firstName: zod + .string() + .min(1, 'First name is required') + .max(32, 'First name must be at most 32 characters long'), + lastName: zod + .string() + .min(1, 'Last name is required') + .max(32, 'Last name must be at most 32 characters long'), + email: zod + .string() + .email('Invalid email address') + .min(3, 'Email is required'), + password: zod + .string() + .min(8, 'Password must be at least 8 characters long') + .max(128, 'Password must be at most 128 characters long'), + confirmPassword: zod + .string() + .min(8, 'Password must be at least 8 characters long') + .max(128, 'Password must be at most 128 characters long'), + username: zod + .string() + .min(3, 'Username is required') + .max(32, 'Username must be at most 32 characters long') + .regex( + /^[a-zA-Z0-9_]+$/, + 'Username can only contain letters, numbers, and underscores', + ), + }) + .refine((data) => data.password === data.confirmPassword, { + message: 'Passwords do not match', + path: ['confirmPassword'], + }) + .refine( + (data) => + !data.password.includes(data.firstName) && + !data.password.includes(data.lastName) && + !data.password.includes(data.email) && + !data.password.includes(data.username), + { + message: + 'Password cannot contain your first name, last name, email, or username', + path: ['password'], + }, + ); diff --git a/yarn.lock b/yarn.lock index 42a5d59..a05f6e9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -264,6 +264,17 @@ __metadata: languageName: node linkType: hard +"@hookform/resolvers@npm:^5.0.1": + version: 5.1.0 + resolution: "@hookform/resolvers@npm:5.1.0" + dependencies: + "@standard-schema/utils": "npm:^0.3.0" + peerDependencies: + react-hook-form: ^7.55.0 + checksum: 10c0/5bd28ef58a182102f40b7fa2bc73a5e423c8bcf9cb25fee91cb8933026e7efba6f9adfda1ee637294de59c5467c938f4fb99c77a73bbb8c6180482c69d31cbbd + languageName: node + linkType: hard + "@humanfs/core@npm:^0.19.1": version: 0.19.1 resolution: "@humanfs/core@npm:0.19.1" @@ -1429,6 +1440,13 @@ __metadata: languageName: node linkType: hard +"@standard-schema/utils@npm:^0.3.0": + version: 0.3.0 + resolution: "@standard-schema/utils@npm:0.3.0" + checksum: 10c0/6eb74cd13e52d5fc74054df51e37d947ef53f3ab9e02c085665dcca3c38c60ece8d735cebbdf18fbb13c775fbcb9becb3f53109b0e092a63f0f7389ce0993fd0 + languageName: node + linkType: hard + "@swc/counter@npm:0.1.3": version: 0.1.3 resolution: "@swc/counter@npm:0.1.3" @@ -2147,6 +2165,15 @@ __metadata: languageName: node linkType: hard +"bcryptjs@npm:^3.0.2": + version: 3.0.2 + resolution: "bcryptjs@npm:3.0.2" + bin: + bcrypt: bin/bcrypt + checksum: 10c0/a0923cac99f83e913f8f4e4f42df6a27c6593b24d509900331d1280c4050b1544e602a0ac67b43f7bb5c969991c3ed77fd72f19b7dc873be8ee794da3d925c7e + languageName: node + linkType: hard + "brace-expansion@npm:^1.1.7": version: 1.1.11 resolution: "brace-expansion@npm:1.1.11" @@ -3878,6 +3905,7 @@ __metadata: "@fortawesome/free-regular-svg-icons": "npm:^6.7.2" "@fortawesome/free-solid-svg-icons": "npm:^6.7.2" "@fortawesome/react-fontawesome": "npm:^0.2.2" + "@hookform/resolvers": "npm:^5.0.1" "@prisma/client": "npm:^6.9.0" "@radix-ui/react-dropdown-menu": "npm:^2.1.14" "@radix-ui/react-hover-card": "npm:^1.1.13" @@ -3892,6 +3920,7 @@ __metadata: "@types/node": "npm:22.15.31" "@types/react": "npm:19.1.8" "@types/react-dom": "npm:19.1.6" + bcryptjs: "npm:^3.0.2" class-variance-authority: "npm:^0.7.1" clsx: "npm:^2.1.1" dotenv-cli: "npm:8.0.0" @@ -3907,10 +3936,12 @@ __metadata: prisma: "npm:6.9.0" react: "npm:^19.0.0" react-dom: "npm:^19.0.0" + react-hook-form: "npm:^7.56.4" tailwind-merge: "npm:^3.2.0" tailwindcss: "npm:4.1.10" tw-animate-css: "npm:1.3.4" typescript: "npm:5.8.3" + zod: "npm:^3.25.60" languageName: unknown linkType: soft @@ -4414,6 +4445,15 @@ __metadata: languageName: node linkType: hard +"react-hook-form@npm:^7.56.4": + version: 7.57.0 + resolution: "react-hook-form@npm:7.57.0" + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + checksum: 10c0/6db0b44b2e88d4db541514e96557723e39381ce9f71b3787bf041635f829143dbd0ae46a1f6c16dee23afe3413fd25539484ba02bf2a35d90aaa1b7483193ea9 + languageName: node + linkType: hard + "react-is@npm:^16.13.1": version: 16.13.1 resolution: "react-is@npm:16.13.1" @@ -5375,3 +5415,10 @@ __metadata: checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f languageName: node linkType: hard + +"zod@npm:^3.25.60": + version: 3.25.60 + resolution: "zod@npm:3.25.60" + checksum: 10c0/4b5c0fc9fc9020a1b41c9e4093e722181f9535dc4e917cd5a63ce764c405f49ff49d7f5ef4d8c92665d250d92c2216c4c0b0145175c755209762be4ec305c5d2 + languageName: node + linkType: hard From 378b88dbdbe856718717c9b8e465ecd59a2a1e5d Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 14 Jun 2025 10:01:22 +0000 Subject: [PATCH 09/20] fix(deps): update dependency @hookform/resolvers to v5.1.1 --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index a05f6e9..121de32 100644 --- a/yarn.lock +++ b/yarn.lock @@ -265,13 +265,13 @@ __metadata: linkType: hard "@hookform/resolvers@npm:^5.0.1": - version: 5.1.0 - resolution: "@hookform/resolvers@npm:5.1.0" + version: 5.1.1 + resolution: "@hookform/resolvers@npm:5.1.1" dependencies: "@standard-schema/utils": "npm:^0.3.0" peerDependencies: react-hook-form: ^7.55.0 - checksum: 10c0/5bd28ef58a182102f40b7fa2bc73a5e423c8bcf9cb25fee91cb8933026e7efba6f9adfda1ee637294de59c5467c938f4fb99c77a73bbb8c6180482c69d31cbbd + checksum: 10c0/74601ba4abb3159bbaa25175af9459a2c0337a28d8c0a5be95c7ae7b0a76ddafcf63c03eea8561fd099fe80b226194ad09e3824c53b9beda38393ff9fd264a03 languageName: node linkType: hard From b7d443e7a10f7666db29ee20c8c17a5bd76a5fa8 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sat, 14 Jun 2025 11:02:02 +0000 Subject: [PATCH 10/20] fix(deps): update dependency zod to v3.25.64 --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 121de32..0785433 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5417,8 +5417,8 @@ __metadata: linkType: hard "zod@npm:^3.25.60": - version: 3.25.60 - resolution: "zod@npm:3.25.60" - checksum: 10c0/4b5c0fc9fc9020a1b41c9e4093e722181f9535dc4e917cd5a63ce764c405f49ff49d7f5ef4d8c92665d250d92c2216c4c0b0145175c755209762be4ec305c5d2 + version: 3.25.64 + resolution: "zod@npm:3.25.64" + checksum: 10c0/00d76093a999e377e4ffd037fa7185e861c35917e8c4272f514115c206a0654995168f57fb71708b11e0a9243206d988b7f63b543404e1796402e50d346a6bd7 languageName: node linkType: hard From cfe73652e24f2d7ff999e72b8ba5081d00298a7e Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Sun, 15 Jun 2025 12:01:00 +0000 Subject: [PATCH 11/20] fix(deps): update dependency react-hook-form to v7.58.0 --- yarn.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/yarn.lock b/yarn.lock index 0785433..d81c319 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4446,11 +4446,11 @@ __metadata: linkType: hard "react-hook-form@npm:^7.56.4": - version: 7.57.0 - resolution: "react-hook-form@npm:7.57.0" + version: 7.58.0 + resolution: "react-hook-form@npm:7.58.0" peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 - checksum: 10c0/6db0b44b2e88d4db541514e96557723e39381ce9f71b3787bf041635f829143dbd0ae46a1f6c16dee23afe3413fd25539484ba02bf2a35d90aaa1b7483193ea9 + checksum: 10c0/9e87bf1dfb43157ffec1b112092e8c5b2b9d0056c2a8bdea6c15e08d510b365915101f499f2e7698e16e94541ac82b26ab8e3b05af5b0cccfc82d29cf31c1b0b languageName: node linkType: hard From d769cdcd5cc283495b7601b8e67d444b473d1df1 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 16 Jun 2025 09:00:52 +0000 Subject: [PATCH 12/20] chore(deps): update dependency @types/node to v22.15.32 --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index f11d01e..110e8e3 100644 --- a/package.json +++ b/package.json @@ -49,7 +49,7 @@ "devDependencies": { "@eslint/eslintrc": "3.3.1", "@tailwindcss/postcss": "4.1.10", - "@types/node": "22.15.31", + "@types/node": "22.15.32", "@types/react": "19.1.8", "@types/react-dom": "19.1.6", "dotenv-cli": "8.0.0", diff --git a/yarn.lock b/yarn.lock index d81c319..972e786 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1659,12 +1659,12 @@ __metadata: languageName: node linkType: hard -"@types/node@npm:22.15.31": - version: 22.15.31 - resolution: "@types/node@npm:22.15.31" +"@types/node@npm:22.15.32": + version: 22.15.32 + resolution: "@types/node@npm:22.15.32" dependencies: undici-types: "npm:~6.21.0" - checksum: 10c0/ef7d5dc890da41cfd554d35ab8998bc18be9e3a0caa642e720599ac4410a94a4879766e52b3c9cafa06c66b7b8aebdc51f322cf67df23a6489927890196a316d + checksum: 10c0/63a2fa52adf1134d1b3bee8b1862d4b8e4550fffc190551068d3d41a41d9e5c0c8f1cb81faa18767b260637360f662115c26c5e4e7718868ead40c4a57cbc0e3 languageName: node linkType: hard @@ -3917,7 +3917,7 @@ __metadata: "@radix-ui/react-switch": "npm:^1.2.4" "@radix-ui/react-tabs": "npm:^1.1.11" "@tailwindcss/postcss": "npm:4.1.10" - "@types/node": "npm:22.15.31" + "@types/node": "npm:22.15.32" "@types/react": "npm:19.1.8" "@types/react-dom": "npm:19.1.6" bcryptjs: "npm:^3.0.2" From 882464e6cba501221f3122f967286c6400a14017 Mon Sep 17 00:00:00 2001 From: Renovate Bot Date: Mon, 16 Jun 2025 14:00:55 +0000 Subject: [PATCH 13/20] chore(deps): update dependency postcss to v8.5.6 --- package.json | 2 +- yarn.lock | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 110e8e3..99b975d 100644 --- a/package.json +++ b/package.json @@ -56,7 +56,7 @@ "eslint": "9.29.0", "eslint-config-next": "15.3.3", "eslint-config-prettier": "10.1.5", - "postcss": "8.5.5", + "postcss": "8.5.6", "prettier": "3.5.3", "prisma": "6.9.0", "tailwindcss": "4.1.10", diff --git a/yarn.lock b/yarn.lock index 972e786..8deaa5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3931,7 +3931,7 @@ __metadata: next: "npm:15.3.3" next-auth: "npm:^5.0.0-beta.25" next-themes: "npm:^0.4.6" - postcss: "npm:8.5.5" + postcss: "npm:8.5.6" prettier: "npm:3.5.3" prisma: "npm:6.9.0" react: "npm:^19.0.0" @@ -4338,14 +4338,14 @@ __metadata: languageName: node linkType: hard -"postcss@npm:8.5.5": - version: 8.5.5 - resolution: "postcss@npm:8.5.5" +"postcss@npm:8.5.6": + version: 8.5.6 + resolution: "postcss@npm:8.5.6" dependencies: nanoid: "npm:^3.3.11" picocolors: "npm:^1.1.1" source-map-js: "npm:^1.2.1" - checksum: 10c0/6415873fab84de05c2d8fd18f72ea6654bca437bb4b9f02ca819c438501e4b3a450023e575e17587c6eaa5bedddaaa4dad3af210f5cf166e30cec09cac58baf8 + checksum: 10c0/5127cc7c91ed7a133a1b7318012d8bfa112da9ef092dddf369ae699a1f10ebbd89b1b9f25f3228795b84585c72aabd5ced5fc11f2ba467eedf7b081a66fad024 languageName: node linkType: hard From ea7ef2a779192d2b108f6767e55494b0dc84d606 Mon Sep 17 00:00:00 2001 From: Dominik Stahl Date: Mon, 16 Jun 2025 19:44:09 +0200 Subject: [PATCH 14/20] style: format code --- src/app/home/page.tsx | 4 +- src/components/calendar.tsx | 15 +- src/components/custom-toolbar.css | 4 +- src/components/custom-toolbar.tsx | 43 ++-- src/components/react-big-calendar.css | 273 ++++++++++++++------------ 5 files changed, 190 insertions(+), 149 deletions(-) diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index 0b7515a..68e51bf 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -1,3 +1,5 @@ import Calendar from '@/components/calendar'; -export default function home () {return } \ No newline at end of file +export default function home() { + return ; +} diff --git a/src/components/calendar.tsx b/src/components/calendar.tsx index defedab..6e10dab 100644 --- a/src/components/calendar.tsx +++ b/src/components/calendar.tsx @@ -1,4 +1,4 @@ -"use client"; +'use client'; import { Calendar, momentLocalizer } from 'react-big-calendar'; import moment from 'moment'; @@ -13,26 +13,25 @@ moment.updateLocale('en', { }, }); -const localizer = momentLocalizer(moment) +const localizer = momentLocalizer(moment); const MyCalendar = (props) => (
-) +); export default MyCalendar; diff --git a/src/components/custom-toolbar.css b/src/components/custom-toolbar.css index e16a8e4..55e9b77 100644 --- a/src/components/custom-toolbar.css +++ b/src/components/custom-toolbar.css @@ -75,11 +75,11 @@ } .custom-toolbar .navigation-controls button:hover { - background-color: #1976D2; + background-color: #1976d2; } .custom-toolbar .navigation-controls button:active { - background-color: #1565C0; + background-color: #1565c0; } /* Dropdown-Bereich für Woche und Jahr */ diff --git a/src/components/custom-toolbar.tsx b/src/components/custom-toolbar.tsx index 5f9a2cf..bcbb9f9 100644 --- a/src/components/custom-toolbar.tsx +++ b/src/components/custom-toolbar.tsx @@ -16,15 +16,21 @@ interface CustomToolbarProps { onView: (newView: 'month' | 'week' | 'day' | 'agenda') => void; } -const CustomToolbar: React.FC = ({ date, view, onNavigate, onView }) => { - +const CustomToolbar: React.FC = ({ + date, + view, + onNavigate, + onView, +}) => { //ISO-Wochennummer eines Datums ermitteln const getISOWeek = (date: Date): number => { const tmp = new Date(date.getTime()); //Datum so verschieben, dass der nächste Donnerstag erreicht wird (ISO: Woche beginnt am Montag) tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); const yearStart = new Date(tmp.getFullYear(), 0, 1); - const weekNo = Math.ceil((((tmp.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); + const weekNo = Math.ceil( + ((tmp.getTime() - yearStart.getTime()) / 86400000 + 1) / 7, + ); return weekNo; }; @@ -60,7 +66,9 @@ const CustomToolbar: React.FC = ({ date, view, onNavigate, o //Lokaler State für Woche und ISO-Wochenjahr (statt des reinen Kalenderjahrs) const [selectedWeek, setSelectedWeek] = useState(getISOWeek(date)); - const [selectedYear, setSelectedYear] = useState(getISOWeekYear(date)); + const [selectedYear, setSelectedYear] = useState( + getISOWeekYear(date), + ); //Auswahl aktualisieren, wenn sich die Prop "date" ändert useEffect(() => { @@ -73,7 +81,10 @@ const CustomToolbar: React.FC = ({ date, view, onNavigate, o const weekOptions = Array.from({ length: totalWeeks }, (_, i) => i + 1); //Jahresliste - const yearOptions = Array.from({ length: 21 }, (_, i) => selectedYear - 10 + i); + const yearOptions = Array.from( + { length: 21 }, + (_, i) => selectedYear - 10 + i, + ); //Start (Montag) und Ende (Sonntag) der aktuell angezeigten Woche berechnen const weekStartDate = getDateOfISOWeek(selectedWeek, selectedYear); @@ -175,11 +186,13 @@ const CustomToolbar: React.FC = ({ date, view, onNavigate, o } }; - return ( -
+
-
+
-
-
-
+
+
+
-
+
) diff --git a/src/components/custom-toolbar.css b/src/components/custom-toolbar.css new file mode 100644 index 0000000..16e86ed --- /dev/null +++ b/src/components/custom-toolbar.css @@ -0,0 +1,139 @@ +/* custom-toolbar.css */ + +/* Container der Toolbar */ +.custom-toolbar { + display: flex; + flex-direction: column; + gap: 12px; + padding: 16px; + background-color: #ffffff; + border: 1px solid #e0e0e0; + /*border-radius: 8px;*/ + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; +} + +/* Style für den Bereich, in dem die Ansichten (Month, Week, etc.) gewechselt werden */ +.custom-toolbar .view-change .view-switcher { + display: flex; + gap: 8px; + justify-content: center; +} + +.custom-toolbar .view-change .view-switcher button { + padding: 8px 16px; + background-color: #c1830d; + /*border: 1px solid #ccc;*/ + border-radius: 11px; + font-size: 12px; + cursor: pointer; + transition: background-color 0.2s, border-color 0.2s; + height: 30px; + margin-top: 3.5px; + color: #ffffff; +} + +.custom-toolbar .view-change .view-switcher button:hover:not(:disabled) { + background-color: #e0e0e0; + border-color: #999; +} + +.custom-toolbar .view-change .view-switcher button:disabled { + background-color: #d0d0d0; + border-color: #aaa; + cursor: default; +} + +/* Anzeige des aktuellen Datums (Monat und Jahr) */ +.custom-toolbar .current-date { + font-weight: bold; + font-size: 12px; + text-align: center; + color: #ffffff; + margin: 4px 0; + background-color: #717171; + width: 178px; + height: 37px; + border-radius: 11px; +} + +/* Navigationsbereich (Today, Prev, Next) */ +.custom-toolbar .navigation-controls { + display: flex; + gap: 8px; + justify-content: center; +} + +.custom-toolbar .navigation-controls button { + padding: 8px 12px; + /*background-color: #2196F3;*/ + color: #ffffff; + border: none; + border-radius: 11px; + font-size: 12px; + cursor: pointer; + transition: background-color 0.2s; +} + +.custom-toolbar .navigation-controls button:hover { + background-color: #1976D2; +} + +.custom-toolbar .navigation-controls button:active { + background-color: #1565C0; +} + +/* Dropdown-Bereich für Woche und Jahr */ +.custom-toolbar .dropdowns { + display: flex; + gap: 8px; + justify-content: center; + height: 30px; + font-size: 10px; + margin-top: 3.5px; + border-radius: 11px; +} + +.custom-toolbar .dropdowns select { + padding: 8px 12px; + /*border: 1px solid #ccc;*/ + border-radius: 11px; + font-size: 10px; + background-color: #555555; + color: #ffffff; + cursor: pointer; + transition: border-color 0.2s; +} + +.custom-toolbar .dropdowns select:hover { + border-color: #999; +} + +.right-section { + background-color: #717171; + width: 393px; + height: 37px; + border-radius: 11px; +} + +.custom-toolbar .navigation-controls .handleWeek button { + background-color: #717171; + height: 30px; + width: 30px; + margin-bottom: 3.5px; +} + +.custom-toolbar .navigation-controls .today button { + background-color: #c6c6c6; + height: 30px; + width: 100px; + color: #000000; + margin-top: 3.5px; +} + +.view-change { + background-color: #717171; + height: 37px; + width: 290px; + border-radius: 11px; +} diff --git a/src/components/custom-toolbar.tsx b/src/components/custom-toolbar.tsx new file mode 100644 index 0000000..10feafa --- /dev/null +++ b/src/components/custom-toolbar.tsx @@ -0,0 +1,218 @@ +import React, { useState, useEffect } from 'react'; +import { format } from 'date-fns'; +import './custom-toolbar.css'; + +interface CustomToolbarProps { + // Das aktuell angezeigte Datum (wird z. B. von der Calendar-Komponente geliefert) + date: Date; + // Aktuelle Ansicht: "month", "week", "day" oder "agenda" + view: 'month' | 'week' | 'day' | 'agenda'; + /** + * onNavigate ermöglicht das Wechseln des angezeigten Datums. + * Action kann bspw. 'TODAY' oder 'SET_DATE' sein; newDate wird übergeben, wenn benötigt. + */ + onNavigate: (action: string, newDate?: Date) => void; + // onView wechselt die Ansicht + onView: (newView: 'month' | 'week' | 'day' | 'agenda') => void; +} + +const CustomToolbar: React.FC = ({ date, view, onNavigate, onView }) => { + + // Hilfsfunktion, um die ISO-Wochennummer eines Datums zu ermitteln + const getISOWeek = (date: Date): number => { + const tmp = new Date(date.getTime()); + // Verschiebe das Datum so, dass der nächste Donnerstag erreicht wird (ISO: Woche beginnt am Montag) + tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); + const yearStart = new Date(tmp.getFullYear(), 0, 1); + const weekNo = Math.ceil((((tmp.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); + return weekNo; + }; + + // Neue Funktion: Ermittelt das ISO-Wochenjahr eines Datums. + // Das ISO-Wochenjahr entspricht dem Jahr des Donnerstags in dieser Woche. + const getISOWeekYear = (date: Date): number => { + const tmp = new Date(date.getTime()); + tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); + return tmp.getFullYear(); + }; + + // Ermittelt die Anzahl der ISO-Wochen im Jahr + const getISOWeeksInYear = (year: number): number => { + const d = new Date(year, 11, 31); + const week = getISOWeek(d); + return week === 1 ? getISOWeek(new Date(year, 11, 24)) : week; + }; + + /* + Berechnet den Montag der gewünschten ISO-Woche eines Jahres. + Wir ermitteln zunächst den ersten Montag der ersten ISO-Woche und addieren dann (week - 1) * 7 Tage. + */ + const getDateOfISOWeek = (week: number, year: number): Date => { + const jan1 = new Date(year, 0, 1); + const dayOfWeek = jan1.getDay(); + const isoDayOfWeek = dayOfWeek === 0 ? 7 : dayOfWeek; + let firstMonday: Date; + if (isoDayOfWeek <= 4) { + // Jan 1 gehört zur ersten ISO-Woche – bestimme den Montag dieser Woche + firstMonday = new Date(year, 0, 1 - isoDayOfWeek + 1); + } else { + // Andernfalls liegt der erste Montag in der darauffolgenden Woche + firstMonday = new Date(year, 0, 1 + (8 - isoDayOfWeek)); + } + firstMonday.setDate(firstMonday.getDate() + (week - 1) * 7); + return firstMonday; + }; + + // Lokaler State für Woche und ISO-Wochenjahr (statt des reinen Kalenderjahrs) + const [selectedWeek, setSelectedWeek] = useState(getISOWeek(date)); + const [selectedYear, setSelectedYear] = useState(getISOWeekYear(date)); + + // Aktualisiere die Auswahl, wenn sich die Prop "date" ändert + useEffect(() => { + setSelectedWeek(getISOWeek(date)); + setSelectedYear(getISOWeekYear(date)); + }, [date]); + + // Für die Dropdown-Liste der Wochen: Liste von 1 bis totalWeeks + const totalWeeks = getISOWeeksInYear(selectedYear); + const weekOptions = Array.from({ length: totalWeeks }, (_, i) => i + 1); + + // Beispielhafte Jahresliste: aktuelles ISO-Wochenjahr ± 10 + const yearOptions = Array.from({ length: 21 }, (_, i) => selectedYear - 10 + i); + + // Berechne den Start (Montag) und das Ende (Sonntag) der aktuell angezeigten Woche + const weekStartDate = getDateOfISOWeek(selectedWeek, selectedYear); + const weekEndDate = new Date(weekStartDate); + weekEndDate.setDate(weekStartDate.getDate() + 6); + + // Ermittele Monat und Jahr von Start- und Enddatum (normales Kalenderjahr) + const monthStart = format(weekStartDate, 'MMMM'); + const monthEnd = format(weekEndDate, 'MMMM'); + const yearAtStart = format(weekStartDate, 'yyyy'); + const yearAtEnd = format(weekEndDate, 'yyyy'); + + // Erstelle das Label: + // 1. Falls der Wochenanfang und das Wochenende in unterschiedlichen Jahren liegen, + // wird z. B. "Dezember 2025 - Januar 2026" angezeigt. + // 2. Liegen beide im gleichen Jahr, wird unterschieden zwischen gleichem Monat und unterschiedlichem Monat. + let dateLabel: string; + if (yearAtStart !== yearAtEnd) { + dateLabel = `${monthStart} ${yearAtStart} - ${monthEnd} ${yearAtEnd}`; + } else if (monthStart !== monthEnd) { + dateLabel = `${monthStart} - ${monthEnd} ${yearAtStart}`; + } else { + dateLabel = `${monthStart} ${yearAtStart}`; + } + + // Handler zum Wechseln der Ansicht + const handleViewChange = (newView: 'month' | 'week' | 'day' | 'agenda') => { + onView(newView); + }; + + // "Today"-Button: setzt das Datum auf das heutige Datum (unter Verwendung des ISO-Wochenjahrs) + const handleToday = () => { + const today = new Date(); + setSelectedWeek(getISOWeek(today)); + setSelectedYear(getISOWeekYear(today)); + onNavigate('TODAY', today); + }; + + // Wechselt zur vorherigen Woche. Bei Woche < 1, wird ins Vorjahr gewechselt. + const handlePrevWeek = () => { + let newWeek = selectedWeek - 1; + let newYear = selectedYear; + if (newWeek < 1) { + newYear = selectedYear - 1; + newWeek = getISOWeeksInYear(newYear); + } + setSelectedWeek(newWeek); + setSelectedYear(newYear); + const newDate = getDateOfISOWeek(newWeek, newYear); + onNavigate('SET_DATE', newDate); + }; + + // Wechselt zur nächsten Woche. Überschreitet die Woche die maximale Zahl, wechselt ins nächste Jahr. + const handleNextWeek = () => { + let newWeek = selectedWeek + 1; + let newYear = selectedYear; + if (newWeek > getISOWeeksInYear(selectedYear)) { + newYear = selectedYear + 1; + newWeek = 1; + } + setSelectedWeek(newWeek); + setSelectedYear(newYear); + const newDate = getDateOfISOWeek(newWeek, newYear); + onNavigate('SET_DATE', newDate); + }; + + // Handler, wenn der Nutzer über das Dropdown eine Woche auswählt + const handleWeekChange = (event: React.ChangeEvent) => { + const newWeek = parseInt(event.target.value, 10); + setSelectedWeek(newWeek); + const newDate = getDateOfISOWeek(newWeek, selectedYear); + onNavigate('SET_DATE', newDate); + }; + + // Handler, wenn der Nutzer über das Dropdown ein Jahr auswählt + const handleYearChange = (event: React.ChangeEvent) => { + const newYear = parseInt(event.target.value, 10); + setSelectedYear(newYear); + const totalWeeksInNewYear = getISOWeeksInYear(newYear); + const newWeek = Math.min(selectedWeek, totalWeeksInNewYear); + setSelectedWeek(newWeek); + const newDate = getDateOfISOWeek(newWeek, newYear); + onNavigate('SET_DATE', newDate); + }; + + return ( +
+ {/* Anzeige des Datums-Labels */} +
+ {dateLabel} +
+ + {/* Ansicht wechseln */} +
+
+ + + + +
+
+ +
+ {/* Navigationsbuttons */} +
+
+ + +
+
+ +
+
+ + {/* DropDowns für Woche und Jahr */} +
+ + +
+
+
+ ); +}; + +export default CustomToolbar; diff --git a/yarn.lock b/yarn.lock index 245196a..7ce9778 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2483,6 +2483,13 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:^4.1.0": + version: 4.1.0 + resolution: "date-fns@npm:4.1.0" + checksum: 10c0/b79ff32830e6b7faa009590af6ae0fb8c3fd9ffad46d930548fbb5acf473773b4712ae887e156ba91a7b3dc30591ce0f517d69fd83bd9c38650fdc03b4e0bac8 + languageName: node + linkType: hard + "dayjs@npm:^1.11.7": version: 1.11.13 resolution: "dayjs@npm:1.11.13" @@ -4052,6 +4059,7 @@ __metadata: bcryptjs: "npm:^3.0.2" class-variance-authority: "npm:^0.7.1" clsx: "npm:^2.1.1" + date-fns: "npm:^4.1.0" dotenv-cli: "npm:8.0.0" eslint: "npm:9.29.0" eslint-config-next: "npm:15.3.3" From baff346e662328ac6b5bfbb9bcf7344016ef19be Mon Sep 17 00:00:00 2001 From: Semir Date: Fri, 6 Jun 2025 10:43:25 +0200 Subject: [PATCH 19/20] feat: Calendar Layout and Function Update --- package.json | 1 + src/app/home/page.tsx | 39 +- src/components/calendar.tsx | 38 ++ src/components/custom-toolbar.css | 70 +- src/components/custom-toolbar.tsx | 249 ++++--- src/components/react-big-calendar.css | 904 ++++++++++++++++++++++++++ yarn.lock | 38 +- 7 files changed, 1172 insertions(+), 167 deletions(-) create mode 100644 src/components/calendar.tsx create mode 100644 src/components/react-big-calendar.css diff --git a/package.json b/package.json index 1315ce6..0e7bb30 100644 --- a/package.json +++ b/package.json @@ -43,6 +43,7 @@ "next-themes": "^0.4.6", "react": "^19.1.0", "react-big-calendar": "^1.18.0", + "react-datepicker": "^8.4.0", "react-dom": "^19.0.0", "react-hook-form": "^7.56.4", "tailwind-merge": "^3.2.0", diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index 6149d47..0b7515a 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -1,38 +1,3 @@ -"use client"; +import Calendar from '@/components/calendar'; -import { Calendar, momentLocalizer } from 'react-big-calendar'; -import moment from 'moment'; -import 'react-big-calendar/lib/css/react-big-calendar.css'; -import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'; -import CustomToolbar from '@/components/custom-toolbar'; - -moment.updateLocale('en', { - week: { - dow: 1, - doy: 4, - }, -}); - -const localizer = momentLocalizer(moment) - -const MyCalendar = (props) => ( -
- -
-) - -export default MyCalendar; +export default function home () {return } \ No newline at end of file diff --git a/src/components/calendar.tsx b/src/components/calendar.tsx new file mode 100644 index 0000000..defedab --- /dev/null +++ b/src/components/calendar.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { Calendar, momentLocalizer } from 'react-big-calendar'; +import moment from 'moment'; +import '@/components/react-big-calendar.css'; +import 'react-big-calendar/lib/addons/dragAndDrop/styles.css'; +import CustomToolbar from '@/components/custom-toolbar'; + +moment.updateLocale('en', { + week: { + dow: 1, + doy: 4, + }, +}); + +const localizer = momentLocalizer(moment) + +const MyCalendar = (props) => ( +
+ +
+) + +export default MyCalendar; diff --git a/src/components/custom-toolbar.css b/src/components/custom-toolbar.css index 16e86ed..e16a8e4 100644 --- a/src/components/custom-toolbar.css +++ b/src/components/custom-toolbar.css @@ -13,8 +13,7 @@ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; } -/* Style für den Bereich, in dem die Ansichten (Month, Week, etc.) gewechselt werden */ -.custom-toolbar .view-change .view-switcher { +/*.custom-toolbar .view-change .view-switcher { display: flex; gap: 8px; justify-content: center; @@ -24,7 +23,7 @@ padding: 8px 16px; background-color: #c1830d; /*border: 1px solid #ccc;*/ - border-radius: 11px; +/* border-radius: 11px; font-size: 12px; cursor: pointer; transition: background-color 0.2s, border-color 0.2s; @@ -42,7 +41,7 @@ background-color: #d0d0d0; border-color: #aaa; cursor: default; -} +}*/ /* Anzeige des aktuellen Datums (Monat und Jahr) */ .custom-toolbar .current-date { @@ -50,7 +49,7 @@ font-size: 12px; text-align: center; color: #ffffff; - margin: 4px 0; + /*margin: 4px 0;*/ background-color: #717171; width: 178px; height: 37px; @@ -110,30 +109,53 @@ } .right-section { - background-color: #717171; - width: 393px; - height: 37px; - border-radius: 11px; + background-color: #717171; + width: 393px; + height: 48px; + border-radius: 11px; + justify-items: center; + align-items: center; } .custom-toolbar .navigation-controls .handleWeek button { - background-color: #717171; - height: 30px; - width: 30px; - margin-bottom: 3.5px; + background-color: #717171; + height: 30px; + width: 30px; + margin-bottom: 3.5px; } -.custom-toolbar .navigation-controls .today button { - background-color: #c6c6c6; - height: 30px; - width: 100px; - color: #000000; - margin-top: 3.5px; -} +/*.custom-toolbar .navigation-controls .today button { + background-color: #c6c6c6; + height: 30px; + width: 100px; + color: #000000; + margin-top: 3.5px; +}*/ .view-change { - background-color: #717171; - height: 37px; - width: 290px; - border-radius: 11px; + background-color: #717171; + height: 48px; + width: 323px; + border-radius: 11px; + justify-items: center; +} + +.right-section .datepicker-box { + color: #000000; + background-color: #c6c6c6; + height: 36px; + width: 85px; + border-radius: 11px; + font-size: 12px; + align-self: center; +} + +.datepicker { + text-align: center; + width: 85px; + height: 30px; +} + +.datepicker-box { + z-index: 9999; } diff --git a/src/components/custom-toolbar.tsx b/src/components/custom-toolbar.tsx index 10feafa..5f9a2cf 100644 --- a/src/components/custom-toolbar.tsx +++ b/src/components/custom-toolbar.tsx @@ -1,214 +1,253 @@ import React, { useState, useEffect } from 'react'; import { format } from 'date-fns'; import './custom-toolbar.css'; +import { Button } from '@/components/custom-ui/button'; +import DatePicker from 'react-datepicker'; +import 'react-datepicker/dist/react-datepicker.css'; interface CustomToolbarProps { - // Das aktuell angezeigte Datum (wird z. B. von der Calendar-Komponente geliefert) + //Aktuell angezeigtes Datum date: Date; - // Aktuelle Ansicht: "month", "week", "day" oder "agenda" + //Aktuelle Ansicht view: 'month' | 'week' | 'day' | 'agenda'; - /** - * onNavigate ermöglicht das Wechseln des angezeigten Datums. - * Action kann bspw. 'TODAY' oder 'SET_DATE' sein; newDate wird übergeben, wenn benötigt. - */ + onNavigate: (action: string, newDate?: Date) => void; - // onView wechselt die Ansicht + //Ansichtwechsel onView: (newView: 'month' | 'week' | 'day' | 'agenda') => void; } const CustomToolbar: React.FC = ({ date, view, onNavigate, onView }) => { - // Hilfsfunktion, um die ISO-Wochennummer eines Datums zu ermitteln + //ISO-Wochennummer eines Datums ermitteln const getISOWeek = (date: Date): number => { const tmp = new Date(date.getTime()); - // Verschiebe das Datum so, dass der nächste Donnerstag erreicht wird (ISO: Woche beginnt am Montag) + //Datum so verschieben, dass der nächste Donnerstag erreicht wird (ISO: Woche beginnt am Montag) tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); const yearStart = new Date(tmp.getFullYear(), 0, 1); const weekNo = Math.ceil((((tmp.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); return weekNo; }; - // Neue Funktion: Ermittelt das ISO-Wochenjahr eines Datums. - // Das ISO-Wochenjahr entspricht dem Jahr des Donnerstags in dieser Woche. + //ISO-Wochenjahr eines Datums ermitteln const getISOWeekYear = (date: Date): number => { const tmp = new Date(date.getTime()); tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); return tmp.getFullYear(); }; - // Ermittelt die Anzahl der ISO-Wochen im Jahr + //Ermittlung der Anzahl der Wochen im Jahr const getISOWeeksInYear = (year: number): number => { const d = new Date(year, 11, 31); const week = getISOWeek(d); return week === 1 ? getISOWeek(new Date(year, 11, 24)) : week; }; - /* - Berechnet den Montag der gewünschten ISO-Woche eines Jahres. - Wir ermitteln zunächst den ersten Montag der ersten ISO-Woche und addieren dann (week - 1) * 7 Tage. - */ const getDateOfISOWeek = (week: number, year: number): Date => { const jan1 = new Date(year, 0, 1); const dayOfWeek = jan1.getDay(); const isoDayOfWeek = dayOfWeek === 0 ? 7 : dayOfWeek; let firstMonday: Date; if (isoDayOfWeek <= 4) { - // Jan 1 gehört zur ersten ISO-Woche – bestimme den Montag dieser Woche + //1. Januar gehört zur ersten ISO-Woche (Montag dieser Woche bestimmen) firstMonday = new Date(year, 0, 1 - isoDayOfWeek + 1); } else { - // Andernfalls liegt der erste Montag in der darauffolgenden Woche + //Ansonsten liegt der erste Montag in der darauffolgenden Woche firstMonday = new Date(year, 0, 1 + (8 - isoDayOfWeek)); } firstMonday.setDate(firstMonday.getDate() + (week - 1) * 7); return firstMonday; }; - // Lokaler State für Woche und ISO-Wochenjahr (statt des reinen Kalenderjahrs) + //Lokaler State für Woche und ISO-Wochenjahr (statt des reinen Kalenderjahrs) const [selectedWeek, setSelectedWeek] = useState(getISOWeek(date)); const [selectedYear, setSelectedYear] = useState(getISOWeekYear(date)); - // Aktualisiere die Auswahl, wenn sich die Prop "date" ändert + //Auswahl aktualisieren, wenn sich die Prop "date" ändert useEffect(() => { setSelectedWeek(getISOWeek(date)); setSelectedYear(getISOWeekYear(date)); }, [date]); - // Für die Dropdown-Liste der Wochen: Liste von 1 bis totalWeeks + //Dropdown-Liste der Wochen const totalWeeks = getISOWeeksInYear(selectedYear); const weekOptions = Array.from({ length: totalWeeks }, (_, i) => i + 1); - // Beispielhafte Jahresliste: aktuelles ISO-Wochenjahr ± 10 + //Jahresliste const yearOptions = Array.from({ length: 21 }, (_, i) => selectedYear - 10 + i); - // Berechne den Start (Montag) und das Ende (Sonntag) der aktuell angezeigten Woche + //Start (Montag) und Ende (Sonntag) der aktuell angezeigten Woche berechnen const weekStartDate = getDateOfISOWeek(selectedWeek, selectedYear); const weekEndDate = new Date(weekStartDate); weekEndDate.setDate(weekStartDate.getDate() + 6); - // Ermittele Monat und Jahr von Start- und Enddatum (normales Kalenderjahr) + //Monat und Jahr von Start- und Enddatum ermitteln const monthStart = format(weekStartDate, 'MMMM'); const monthEnd = format(weekEndDate, 'MMMM'); const yearAtStart = format(weekStartDate, 'yyyy'); const yearAtEnd = format(weekEndDate, 'yyyy'); - // Erstelle das Label: - // 1. Falls der Wochenanfang und das Wochenende in unterschiedlichen Jahren liegen, - // wird z. B. "Dezember 2025 - Januar 2026" angezeigt. - // 2. Liegen beide im gleichen Jahr, wird unterschieden zwischen gleichem Monat und unterschiedlichem Monat. - let dateLabel: string; - if (yearAtStart !== yearAtEnd) { - dateLabel = `${monthStart} ${yearAtStart} - ${monthEnd} ${yearAtEnd}`; - } else if (monthStart !== monthEnd) { - dateLabel = `${monthStart} - ${monthEnd} ${yearAtStart}`; - } else { - dateLabel = `${monthStart} ${yearAtStart}`; - } - - // Handler zum Wechseln der Ansicht + //Ansichtwechsel const handleViewChange = (newView: 'month' | 'week' | 'day' | 'agenda') => { onView(newView); }; - // "Today"-Button: setzt das Datum auf das heutige Datum (unter Verwendung des ISO-Wochenjahrs) + //Today-Button aktualisiert das Datum im DatePicker auf das heutige const handleToday = () => { const today = new Date(); + setSelectedDate(today); setSelectedWeek(getISOWeek(today)); setSelectedYear(getISOWeekYear(today)); onNavigate('TODAY', today); }; - // Wechselt zur vorherigen Woche. Bei Woche < 1, wird ins Vorjahr gewechselt. - const handlePrevWeek = () => { - let newWeek = selectedWeek - 1; - let newYear = selectedYear; - if (newWeek < 1) { - newYear = selectedYear - 1; - newWeek = getISOWeeksInYear(newYear); + //Pfeiltaste nach Vorne + const handleNext = () => { + let newDate: Date; + if (view === 'day' || view === 'agenda') { + newDate = new Date(date); + newDate.setDate(newDate.getDate() + 1); + } else if (view === 'week') { + let newWeek = selectedWeek + 1; + let newYear = selectedYear; + if (newWeek > getISOWeeksInYear(selectedYear)) { + newYear = selectedYear + 1; + newWeek = 1; + } + setSelectedWeek(newWeek); + setSelectedYear(newYear); + newDate = getDateOfISOWeek(newWeek, newYear); + } else if (view === 'month') { + newDate = new Date(date.getFullYear(), date.getMonth() + 1, 1); + } else { + newDate = new Date(date); } - setSelectedWeek(newWeek); - setSelectedYear(newYear); - const newDate = getDateOfISOWeek(newWeek, newYear); + //Datum im DatePicker aktualisieren + setSelectedDate(newDate); onNavigate('SET_DATE', newDate); }; - // Wechselt zur nächsten Woche. Überschreitet die Woche die maximale Zahl, wechselt ins nächste Jahr. - const handleNextWeek = () => { - let newWeek = selectedWeek + 1; - let newYear = selectedYear; - if (newWeek > getISOWeeksInYear(selectedYear)) { - newYear = selectedYear + 1; - newWeek = 1; + //Pfeiltaste nach Hinten + const handlePrev = () => { + let newDate: Date; + if (view === 'day' || view === 'agenda') { + newDate = new Date(date); + newDate.setDate(newDate.getDate() - 1); + } else if (view === 'week') { + let newWeek = selectedWeek - 1; + let newYear = selectedYear; + if (newWeek < 1) { + newYear = selectedYear - 1; + newWeek = getISOWeeksInYear(newYear); + } + setSelectedWeek(newWeek); + setSelectedYear(newYear); + newDate = getDateOfISOWeek(newWeek, newYear); + } else if (view === 'month') { + newDate = new Date(date.getFullYear(), date.getMonth() - 1, 1); + } else { + newDate = new Date(date); } - setSelectedWeek(newWeek); - setSelectedYear(newYear); - const newDate = getDateOfISOWeek(newWeek, newYear); + //Datum im DatePicker aktualisieren + setSelectedDate(newDate); onNavigate('SET_DATE', newDate); }; - // Handler, wenn der Nutzer über das Dropdown eine Woche auswählt - const handleWeekChange = (event: React.ChangeEvent) => { - const newWeek = parseInt(event.target.value, 10); - setSelectedWeek(newWeek); - const newDate = getDateOfISOWeek(newWeek, selectedYear); - onNavigate('SET_DATE', newDate); + const [selectedDate, setSelectedDate] = useState(new Date()); + + const handleDateChange = (date: Date | null) => { + setSelectedDate(date); + if (date) { + if (view === 'week') { + const newWeek = getISOWeek(date); + const newYear = getISOWeekYear(date); + setSelectedWeek(newWeek); + setSelectedYear(newYear); + const newDate = getDateOfISOWeek(newWeek, newYear); + onNavigate('SET_DATE', newDate); + } else if (view === 'day') { + onNavigate('SET_DATE', date); + } else if (view === 'month') { + const newDate = new Date(date.getFullYear(), date.getMonth(), 1); + onNavigate('SET_DATE', newDate); + } else if (view === 'agenda') { + onNavigate('SET_DATE', date); + } + } }; - // Handler, wenn der Nutzer über das Dropdown ein Jahr auswählt - const handleYearChange = (event: React.ChangeEvent) => { - const newYear = parseInt(event.target.value, 10); - setSelectedYear(newYear); - const totalWeeksInNewYear = getISOWeeksInYear(newYear); - const newWeek = Math.min(selectedWeek, totalWeeksInNewYear); - setSelectedWeek(newWeek); - const newDate = getDateOfISOWeek(newWeek, newYear); - onNavigate('SET_DATE', newDate); - }; return (
- {/* Anzeige des Datums-Labels */} -
- {dateLabel} -
- - {/* Ansicht wechseln */}
- - - - + + + +
- {/* Navigationsbuttons */}
- - + +
- +
- {/* DropDowns für Woche und Jahr */} -
- - +
+
diff --git a/src/components/react-big-calendar.css b/src/components/react-big-calendar.css new file mode 100644 index 0000000..305dce0 --- /dev/null +++ b/src/components/react-big-calendar.css @@ -0,0 +1,904 @@ +@charset "UTF-8"; +.rbc-btn { + color: inherit; + font: inherit; + margin: 0; +} + +button.rbc-btn { + overflow: visible; + text-transform: none; + -webkit-appearance: button; + -moz-appearance: button; + appearance: button; + cursor: pointer; +} + +button[disabled].rbc-btn { + cursor: not-allowed; +} + +button.rbc-input::-moz-focus-inner { + border: 0; + padding: 0; +} + +.rbc-calendar { + -webkit-box-sizing: border-box; + box-sizing: border-box; + height: 100%; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-align: stretch; + -ms-flex-align: stretch; + align-items: stretch; +} + +.rbc-m-b-negative-3 { + margin-bottom: -3px; +} + +.rbc-h-full { + height: 100%; +} + +.rbc-calendar *, +.rbc-calendar *:before, +.rbc-calendar *:after { + -webkit-box-sizing: inherit; + box-sizing: inherit; +} + +.rbc-abs-full, .rbc-row-bg { + overflow: hidden; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; +} + +.rbc-ellipsis, .rbc-show-more, .rbc-row-segment .rbc-event-content, .rbc-event-label { + display: block; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.rbc-rtl { + direction: rtl; +} + +.rbc-off-range { + color: #999999; +} + +.rbc-off-range-bg { + background: #e6e6e6; +} + +.rbc-header { + overflow: hidden; + -webkit-box-flex: 1; + -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 #ddd; +} +.rbc-header + .rbc-header { + border-left: 1px solid #c6c6c6; /*#ddd*/ +} +.rbc-rtl .rbc-header + .rbc-header { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.rbc-header > a, .rbc-header > a:active, .rbc-header > a:visited { + color: inherit; + text-decoration: none; +} + +.rbc-button-link { + color: inherit; + background: none; + margin: 0; + padding: 0; + border: none; + cursor: pointer; + -webkit-user-select: text; + -moz-user-select: text; + -ms-user-select: text; + user-select: text; +} + +.rbc-row-content { + position: relative; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -webkit-user-select: none; + z-index: 4; +} + +.rbc-row-content-scrollable { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + height: 100%; +} +.rbc-row-content-scrollable .rbc-row-content-scroll-container { + height: 100%; + overflow-y: scroll; + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ + /* Hide scrollbar for Chrome, Safari and Opera */ +} +.rbc-row-content-scrollable .rbc-row-content-scroll-container::-webkit-scrollbar { + display: none; +} + +.rbc-today { + background-color: #5770ff; /*#eaf6ff*/ +} +/*Own changes 10*/ +.rbc-allday-cell .rbc-row-bg .rbc-day-bg.rbc-today { + background-color: transparent !important; + /*border: none !important;*/ +} +/*Own changes 10*/ + +/*Own changes 11*/ +.rbc-time-header-cell .rbc-header:first-child.rbc-today { + border-top-left-radius: 11px !important; +} + +.rbc-time-header-cell .rbc-header:last-child.rbc-today { + border-top-right-radius: 11px !important; +} +/*Own changes 11*/ + +.rbc-toolbar { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -ms-flex-wrap: wrap; + flex-wrap: wrap; + -webkit-box-pack: center; + -ms-flex-pack: center; + justify-content: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + margin-bottom: 10px; + font-size: 16px; +} +.rbc-toolbar .rbc-toolbar-label { + -webkit-box-flex: 1; + -ms-flex-positive: 1; + flex-grow: 1; + padding: 0 10px; + text-align: center; + + /*Own changes 01*/ + background-color: #717171; + color: #ffffff; + /*Own changes 01*/ +} +.rbc-toolbar button { + color: #373a3c; + display: inline-block; + margin: 0; + text-align: center; + vertical-align: middle; + background: none; + background-image: none; + border: 1px solid #ccc; + padding: 0.375rem 1rem; + border-radius: 4px; + line-height: normal; + white-space: nowrap; +} +.rbc-toolbar button:active, .rbc-toolbar button.rbc-active { + background-image: none; + -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + box-shadow: inset 0 3px 5px rgba(0, 0, 0, 0.125); + background-color: #e6e6e6; + border-color: #adadad; +} +.rbc-toolbar button:active:hover, .rbc-toolbar button:active:focus, .rbc-toolbar button.rbc-active:hover, .rbc-toolbar button.rbc-active:focus { + color: #373a3c; + background-color: #d4d4d4; + border-color: #8c8c8c; +} +.rbc-toolbar button:focus { + color: #373a3c; + background-color: #e6e6e6; + border-color: #adadad; +} +.rbc-toolbar button:hover { + color: #373a3c; + cursor: pointer; + background-color: #e6e6e6; + border-color: #adadad; +} + +.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; + + /*Own changes 02*/ + background-color: #c6c6c6; + color: #000000; + /*Own changes 02*/ +} +.rbc-btn-group > button:last-child:not(:first-child) { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + + /*Own changes 03*/ + background-color: #c6c6c6; + color: #000000; + /*Own changes 03*/ +} +.rbc-rtl .rbc-btn-group > button:first-child:not(:last-child) { + border-radius: 4px; + border-top-left-radius: 0; + border-bottom-left-radius: 0; +} +.rbc-rtl .rbc-btn-group > button:last-child:not(:first-child) { + border-radius: 4px; + border-top-right-radius: 0; + border-bottom-right-radius: 0; +} +.rbc-btn-group > button:not(:first-child):not(:last-child) { + border-radius: 0; + + /*Own changes 04*/ + background-color: #c6c6c6; + color: #000000; + /*Own changes 04*/ +} +.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; +} + +@media (max-width: 767px) { + .rbc-toolbar { + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + } +} +.rbc-event, .rbc-day-slot .rbc-background-event { + border: none; + -webkit-box-sizing: border-box; + box-sizing: border-box; + -webkit-box-shadow: none; + box-shadow: none; + margin: 0; + padding: 2px 5px; + background-color: #3174ad; + border-radius: 5px; + color: #fff; + cursor: pointer; + width: 100%; + text-align: left; +} +.rbc-slot-selecting .rbc-event, .rbc-slot-selecting .rbc-day-slot .rbc-background-event, .rbc-day-slot .rbc-slot-selecting .rbc-background-event { + cursor: inherit; + pointer-events: none; +} +.rbc-event.rbc-selected, .rbc-day-slot .rbc-selected.rbc-background-event { + background-color: #265985; +} +.rbc-event:focus, .rbc-day-slot .rbc-background-event:focus { + outline: 5px auto #3b99fc; +} + +.rbc-event-label { + font-size: 80%; +} + +.rbc-event-overlaps { + -webkit-box-shadow: -1px 1px 5px 0px rgba(51, 51, 51, 0.5); + 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-row { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -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; + color: #3174ad; +} +.rbc-show-more:hover, .rbc-show-more:focus { + color: #265985; +} + +.rbc-month-view { + position: relative; + border: 1px solid #ddd; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -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-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; +} + +.rbc-month-row { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + position: relative; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + -ms-flex-preferred-size: 0px; + flex-basis: 0px; + overflow: hidden; + height: 100%; +} +.rbc-month-row + .rbc-month-row { + border-top: 1px solid #ddd; +} + +.rbc-date-cell { + -webkit-box-flex: 1; + -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-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + overflow: hidden; + right: 1px; +} + +.rbc-day-bg { + -webkit-box-flex: 1; + -ms-flex: 1 0 0%; + flex: 1 0 0%; +} +.rbc-day-bg + .rbc-day-bg { + border-left: 1px solid #ddd; +} +.rbc-rtl .rbc-day-bg + .rbc-day-bg { + border-left-width: 0; + border-right: 1px solid #ddd; +} + +.rbc-overlay { + position: absolute; + z-index: 5; + border: 1px solid #e5e5e5; + background-color: #fff; + -webkit-box-shadow: 0 5px 15px rgba(0, 0, 0, 0.25); + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.25); + padding: 10px; +} +.rbc-overlay > * + * { + margin-top: 1px; +} + +.rbc-overlay-header { + border-bottom: 1px solid #e5e5e5; + margin: -10px -10px 5px -10px; + padding: 2px 10px; +} + +.rbc-agenda-view { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + overflow: auto; +} +.rbc-agenda-view table.rbc-agenda-table { + width: 100%; + border: 1px solid #ddd; + 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 #ddd; +} +.rbc-rtl .rbc-agenda-view table.rbc-agenda-table tbody > tr > td + td { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.rbc-agenda-view table.rbc-agenda-table tbody > tr + tr { + border-top: 1px solid #ddd; +} +.rbc-agenda-view table.rbc-agenda-table thead > tr > th { + padding: 3px 5px; + text-align: left; + border-bottom: 1px solid #ddd; +} +.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-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + min-height: 100%; + + /*Own changes 06*/ + background-color: #383838; + /*Own changes 06*/ +} +.rbc-time-column .rbc-timeslot-group { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; +} + +.rbc-timeslot-group { + border-bottom: 1px solid #8d8d8d; /*#ddd*/ + min-height: 40px; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-flow: column nowrap; + flex-flow: column nowrap; +} + +.rbc-time-gutter, +.rbc-header-gutter { + -webkit-box-flex: 0; + -ms-flex: none; + flex: none; + + /*Own changes 07*/ + background-color: #8d8d8d; + /*Own changes 07*/ +} + +.rbc-label { + padding: 0 5px; +} + +.rbc-day-slot { + position: relative; +} +.rbc-day-slot .rbc-events-container { + bottom: 0; + left: 0; + position: absolute; + right: 0; + margin-right: 10px; + top: 0; +} +.rbc-day-slot .rbc-events-container.rbc-rtl { + left: 10px; + right: 0; +} +.rbc-day-slot .rbc-event, .rbc-day-slot .rbc-background-event { + border: 1px solid #265985; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + max-height: 100%; + min-height: 20px; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-flow: column wrap; + flex-flow: column wrap; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; + overflow: hidden; + position: absolute; +} +.rbc-day-slot .rbc-background-event { + opacity: 0.75; +} +.rbc-day-slot .rbc-event-label { + -webkit-box-flex: 0; + -ms-flex: none; + flex: none; + padding-right: 5px; + width: auto; +} +.rbc-day-slot .rbc-event-content { + width: 100%; + -webkit-box-flex: 1; + -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 #383838; /*#f7f7f7*/ +} + +.rbc-time-view-resources .rbc-time-gutter, +.rbc-time-view-resources .rbc-time-header-gutter { + position: sticky; + left: 0; + background-color: white; + border-right: 1px solid #ddd; + z-index: 10; + margin-right: -1px; +} +.rbc-time-view-resources .rbc-time-header { + overflow: hidden; +} +.rbc-time-view-resources .rbc-time-header-content { + min-width: auto; + -webkit-box-flex: 1; + -ms-flex: 1 0 0px; + flex: 1 0 0; + -ms-flex-preferred-size: 0px; + flex-basis: 0px; +} +.rbc-time-view-resources .rbc-time-header-cell-single-day { + display: none; +} +.rbc-time-view-resources .rbc-day-slot { + min-width: 140px; +} +.rbc-time-view-resources .rbc-header, +.rbc-time-view-resources .rbc-day-bg { + width: 140px; + -webkit-box-flex: 1; + -ms-flex: 1 1 0px; + flex: 1 1 0; + -ms-flex-preferred-size: 0 px; + flex-basis: 0 px; +} + +.rbc-time-header-content + .rbc-time-header-content { + margin-left: -1px; +} + +.rbc-time-slot { + -webkit-box-flex: 1; + -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-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + width: 100%; + border: 1px solid #ddd; + min-height: 0; +} +.rbc-time-view .rbc-time-gutter { + white-space: nowrap; + text-align: right; +} +.rbc-time-view .rbc-allday-cell { + -webkit-box-sizing: content-box; + box-sizing: content-box; + width: 100%; + height: 100%; + position: relative; + + /*Own changes 05*/ + background-color: #555555; + /*Own changes 05*/ +} +.rbc-time-view .rbc-allday-cell + .rbc-allday-cell { + border-left: 1px solid #ddd; +} +.rbc-time-view .rbc-allday-events { + position: relative; + z-index: 4; +} +.rbc-time-view .rbc-row { + -webkit-box-sizing: border-box; + box-sizing: border-box; + min-height: 20px; +} + +.rbc-time-header { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 0; + -ms-flex: 0 0 auto; + flex: 0 0 auto; + -webkit-box-orient: horizontal; + -webkit-box-direction: normal; + -ms-flex-direction: row; + flex-direction: row; +} +.rbc-time-header.rbc-overflowing { + border-right: 1px solid #ddd; +} +.rbc-rtl .rbc-time-header.rbc-overflowing { + border-right-width: 0; + border-left: 1px solid #ddd; +} +.rbc-time-header > .rbc-row:first-child { + border-bottom: 1px solid #ddd; +} +.rbc-time-header > .rbc-row.rbc-row-resource { + border-bottom: 1px solid #ddd; +} + +.rbc-time-header-cell-single-day { + display: none; +} + +.rbc-time-header-content { + -webkit-box-flex: 1; + -ms-flex: 1; + flex: 1; + display: -webkit-box; + display: -ms-flexbox; + display: flex; + min-width: 0; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; + border-left: 1px solid #ddd; + + /*Own changes 08*/ + background-color: #c6c6c6; + color: #000000; + border-top-left-radius: 11px; + border-top-right-radius: 11px; + /*Own changes 08*/ +} +.rbc-rtl .rbc-time-header-content { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.rbc-time-header-content > .rbc-row.rbc-row-resource { + border-bottom: 1px solid #ddd; + -ms-flex-negative: 0; + flex-shrink: 0; +} + +.rbc-time-content { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-flex: 1; + -ms-flex: 1 0 0%; + flex: 1 0 0%; + -webkit-box-align: start; + -ms-flex-align: start; + align-items: flex-start; + width: 100%; + border-top: 2px solid #717171; /*#ddd*/ + overflow-y: auto; + position: relative; +} +.rbc-time-content > .rbc-time-gutter { + -webkit-box-flex: 0; + -ms-flex: none; + flex: none; + + /*Own changes 09*/ + border-top-left-radius: 11px; + border-bottom-left-radius: 11px; + /*Own changes 09*/ +} +.rbc-time-content > * + * > * { + border-left: 1px solid #c6c6c6; /*#ddd*/ +} +.rbc-rtl .rbc-time-content > * + * > * { + border-left-width: 0; + border-right: 1px solid #ddd; +} +.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 { + position: absolute; + z-index: 3; + left: 0; + right: 0; + height: 1px; + background-color: #74ad31; + pointer-events: none; +} + +.rbc-resource-grouping.rbc-time-header-content { + display: -webkit-box; + display: -ms-flexbox; + display: flex; + -webkit-box-orient: vertical; + -webkit-box-direction: normal; + -ms-flex-direction: column; + flex-direction: column; +} +.rbc-resource-grouping .rbc-row .rbc-header { + width: 141px; +} + +/*# sourceMappingURL=react-big-calendar.css.map */ \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index 7ce9778..03f45f1 100644 --- a/yarn.lock +++ b/yarn.lock @@ -206,7 +206,7 @@ __metadata: languageName: node linkType: hard -"@floating-ui/react-dom@npm:^2.0.0": +"@floating-ui/react-dom@npm:^2.0.0, @floating-ui/react-dom@npm:^2.1.3": version: 2.1.3 resolution: "@floating-ui/react-dom@npm:2.1.3" dependencies: @@ -218,6 +218,20 @@ __metadata: languageName: node linkType: hard +"@floating-ui/react@npm:^0.27.3": + version: 0.27.12 + resolution: "@floating-ui/react@npm:0.27.12" + dependencies: + "@floating-ui/react-dom": "npm:^2.1.3" + "@floating-ui/utils": "npm:^0.2.9" + tabbable: "npm:^6.0.0" + peerDependencies: + react: ">=17.0.0" + react-dom: ">=17.0.0" + checksum: 10c0/da453965074bd4ded8e3de97ceb2c0833df8df2ecd9eff5ae4d336413443ea5abde5c9e37b092956901b97e7b47f9138d51d4896fa82da68e77eb0090289bf64 + languageName: node + linkType: hard + "@floating-ui/utils@npm:^0.2.9": version: 0.2.9 resolution: "@floating-ui/utils@npm:0.2.9" @@ -4073,6 +4087,7 @@ __metadata: prisma: "npm:6.9.0" react: "npm:^19.1.0" react-big-calendar: "npm:^1.18.0" + react-datepicker: "npm:^8.4.0" react-dom: "npm:^19.0.0" react-hook-form: "npm:^7.56.4" tailwind-merge: "npm:^3.2.0" @@ -4611,6 +4626,20 @@ __metadata: languageName: node linkType: hard +"react-datepicker@npm:^8.4.0": + version: 8.4.0 + resolution: "react-datepicker@npm:8.4.0" + dependencies: + "@floating-ui/react": "npm:^0.27.3" + clsx: "npm:^2.1.1" + date-fns: "npm:^4.1.0" + peerDependencies: + react: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc + react-dom: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc + checksum: 10c0/e96ba4f2b54476f66bfa33aa0c21729095c83d293671b07a9ddd96ab48dad85b6530acc5ca016e83ef8907feeebfefb7133bf7a02dc550175c48c5d9f66d70ac + languageName: node + linkType: hard + "react-dom@npm:^19.0.0": version: 19.1.0 resolution: "react-dom@npm:19.1.0" @@ -5235,6 +5264,13 @@ __metadata: languageName: node linkType: hard +"tabbable@npm:^6.0.0": + version: 6.2.0 + resolution: "tabbable@npm:6.2.0" + checksum: 10c0/ced8b38f05f2de62cd46836d77c2646c42b8c9713f5bd265daf0e78ff5ac73d3ba48a7ca45f348bafeef29b23da7187c72250742d37627883ef89cbd7fa76898 + languageName: node + linkType: hard + "tailwind-merge@npm:^3.2.0": version: 3.3.1 resolution: "tailwind-merge@npm:3.3.1" From 4b53ce906c09c87e432053614c5e299457635e2a Mon Sep 17 00:00:00 2001 From: Dominik Stahl Date: Mon, 16 Jun 2025 19:44:09 +0200 Subject: [PATCH 20/20] style: format code --- src/app/home/page.tsx | 4 +- src/components/calendar.tsx | 15 +- src/components/custom-toolbar.css | 4 +- src/components/custom-toolbar.tsx | 43 ++-- src/components/react-big-calendar.css | 273 ++++++++++++++------------ 5 files changed, 190 insertions(+), 149 deletions(-) diff --git a/src/app/home/page.tsx b/src/app/home/page.tsx index 0b7515a..68e51bf 100644 --- a/src/app/home/page.tsx +++ b/src/app/home/page.tsx @@ -1,3 +1,5 @@ import Calendar from '@/components/calendar'; -export default function home () {return } \ No newline at end of file +export default function home() { + return ; +} diff --git a/src/components/calendar.tsx b/src/components/calendar.tsx index defedab..6e10dab 100644 --- a/src/components/calendar.tsx +++ b/src/components/calendar.tsx @@ -1,4 +1,4 @@ -"use client"; +'use client'; import { Calendar, momentLocalizer } from 'react-big-calendar'; import moment from 'moment'; @@ -13,26 +13,25 @@ moment.updateLocale('en', { }, }); -const localizer = momentLocalizer(moment) +const localizer = momentLocalizer(moment); const MyCalendar = (props) => (
-) +); export default MyCalendar; diff --git a/src/components/custom-toolbar.css b/src/components/custom-toolbar.css index e16a8e4..55e9b77 100644 --- a/src/components/custom-toolbar.css +++ b/src/components/custom-toolbar.css @@ -75,11 +75,11 @@ } .custom-toolbar .navigation-controls button:hover { - background-color: #1976D2; + background-color: #1976d2; } .custom-toolbar .navigation-controls button:active { - background-color: #1565C0; + background-color: #1565c0; } /* Dropdown-Bereich für Woche und Jahr */ diff --git a/src/components/custom-toolbar.tsx b/src/components/custom-toolbar.tsx index 5f9a2cf..bcbb9f9 100644 --- a/src/components/custom-toolbar.tsx +++ b/src/components/custom-toolbar.tsx @@ -16,15 +16,21 @@ interface CustomToolbarProps { onView: (newView: 'month' | 'week' | 'day' | 'agenda') => void; } -const CustomToolbar: React.FC = ({ date, view, onNavigate, onView }) => { - +const CustomToolbar: React.FC = ({ + date, + view, + onNavigate, + onView, +}) => { //ISO-Wochennummer eines Datums ermitteln const getISOWeek = (date: Date): number => { const tmp = new Date(date.getTime()); //Datum so verschieben, dass der nächste Donnerstag erreicht wird (ISO: Woche beginnt am Montag) tmp.setDate(tmp.getDate() + 4 - (tmp.getDay() || 7)); const yearStart = new Date(tmp.getFullYear(), 0, 1); - const weekNo = Math.ceil((((tmp.getTime() - yearStart.getTime()) / 86400000) + 1) / 7); + const weekNo = Math.ceil( + ((tmp.getTime() - yearStart.getTime()) / 86400000 + 1) / 7, + ); return weekNo; }; @@ -60,7 +66,9 @@ const CustomToolbar: React.FC = ({ date, view, onNavigate, o //Lokaler State für Woche und ISO-Wochenjahr (statt des reinen Kalenderjahrs) const [selectedWeek, setSelectedWeek] = useState(getISOWeek(date)); - const [selectedYear, setSelectedYear] = useState(getISOWeekYear(date)); + const [selectedYear, setSelectedYear] = useState( + getISOWeekYear(date), + ); //Auswahl aktualisieren, wenn sich die Prop "date" ändert useEffect(() => { @@ -73,7 +81,10 @@ const CustomToolbar: React.FC = ({ date, view, onNavigate, o const weekOptions = Array.from({ length: totalWeeks }, (_, i) => i + 1); //Jahresliste - const yearOptions = Array.from({ length: 21 }, (_, i) => selectedYear - 10 + i); + const yearOptions = Array.from( + { length: 21 }, + (_, i) => selectedYear - 10 + i, + ); //Start (Montag) und Ende (Sonntag) der aktuell angezeigten Woche berechnen const weekStartDate = getDateOfISOWeek(selectedWeek, selectedYear); @@ -175,11 +186,13 @@ const CustomToolbar: React.FC = ({ date, view, onNavigate, o } }; - return ( -
+
-
+
-
-
-
+
+
+
-
+