1
0

Implement header, notes and google in dashboard

This commit is contained in:
2025-07-18 10:08:03 +02:00
parent d3f733638e
commit 68480757dc
16 changed files with 1945 additions and 14 deletions

View File

@@ -19,7 +19,10 @@
}, },
"dependencies": { "dependencies": {
"@electron-toolkit/preload": "^3.0.1", "@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^4.0.0" "@electron-toolkit/utils": "^4.0.0",
"react-icons": "^5.5.0",
"react-router-dom": "^7.7.0",
"sass": "^1.89.2"
}, },
"devDependencies": { "devDependencies": {
"@electron-toolkit/eslint-config": "^2.0.0", "@electron-toolkit/eslint-config": "^2.0.0",

317
dashboard/pnpm-lock.yaml generated
View File

@@ -14,6 +14,15 @@ importers:
'@electron-toolkit/utils': '@electron-toolkit/utils':
specifier: ^4.0.0 specifier: ^4.0.0
version: 4.0.0(electron@35.7.2) version: 4.0.0(electron@35.7.2)
react-icons:
specifier: ^5.5.0
version: 5.5.0(react@19.1.0)
react-router-dom:
specifier: ^7.7.0
version: 7.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
sass:
specifier: ^1.89.2
version: 1.89.2
devDependencies: devDependencies:
'@electron-toolkit/eslint-config': '@electron-toolkit/eslint-config':
specifier: ^2.0.0 specifier: ^2.0.0
@@ -23,7 +32,7 @@ importers:
version: 3.0.0(eslint@9.31.0)(prettier@3.6.2) version: 3.0.0(eslint@9.31.0)(prettier@3.6.2)
'@vitejs/plugin-react': '@vitejs/plugin-react':
specifier: ^4.3.4 specifier: ^4.3.4
version: 4.7.0(vite@6.3.5(@types/node@24.0.14)) version: 4.7.0(vite@6.3.5(@types/node@24.0.14)(sass@1.89.2))
electron: electron:
specifier: ^35.1.5 specifier: ^35.1.5
version: 35.7.2 version: 35.7.2
@@ -32,7 +41,7 @@ importers:
version: 25.1.8(electron-builder-squirrel-windows@25.1.8) version: 25.1.8(electron-builder-squirrel-windows@25.1.8)
electron-vite: electron-vite:
specifier: ^3.1.0 specifier: ^3.1.0
version: 3.1.0(vite@6.3.5(@types/node@24.0.14)) version: 3.1.0(vite@6.3.5(@types/node@24.0.14)(sass@1.89.2))
eslint: eslint:
specifier: ^9.24.0 specifier: ^9.24.0
version: 9.31.0 version: 9.31.0
@@ -56,7 +65,7 @@ importers:
version: 19.1.0(react@19.1.0) version: 19.1.0(react@19.1.0)
vite: vite:
specifier: ^6.2.6 specifier: ^6.2.6
version: 6.3.5(@types/node@24.0.14) version: 6.3.5(@types/node@24.0.14)(sass@1.89.2)
packages: packages:
@@ -467,6 +476,88 @@ packages:
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
deprecated: This functionality has been moved to @npmcli/fs deprecated: This functionality has been moved to @npmcli/fs
'@parcel/watcher-android-arm64@2.5.1':
resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [android]
'@parcel/watcher-darwin-arm64@2.5.1':
resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [darwin]
'@parcel/watcher-darwin-x64@2.5.1':
resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [darwin]
'@parcel/watcher-freebsd-x64@2.5.1':
resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [freebsd]
'@parcel/watcher-linux-arm-glibc@2.5.1':
resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
'@parcel/watcher-linux-arm-musl@2.5.1':
resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
engines: {node: '>= 10.0.0'}
cpu: [arm]
os: [linux]
'@parcel/watcher-linux-arm64-glibc@2.5.1':
resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
'@parcel/watcher-linux-arm64-musl@2.5.1':
resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [linux]
'@parcel/watcher-linux-x64-glibc@2.5.1':
resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
'@parcel/watcher-linux-x64-musl@2.5.1':
resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [linux]
'@parcel/watcher-win32-arm64@2.5.1':
resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
engines: {node: '>= 10.0.0'}
cpu: [arm64]
os: [win32]
'@parcel/watcher-win32-ia32@2.5.1':
resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
engines: {node: '>= 10.0.0'}
cpu: [ia32]
os: [win32]
'@parcel/watcher-win32-x64@2.5.1':
resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
engines: {node: '>= 10.0.0'}
cpu: [x64]
os: [win32]
'@parcel/watcher@2.5.1':
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
engines: {node: '>= 10.0.0'}
'@pkgjs/parseargs@0.11.0': '@pkgjs/parseargs@0.11.0':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'} engines: {node: '>=14'}
@@ -823,6 +914,10 @@ packages:
brace-expansion@2.0.2: brace-expansion@2.0.2:
resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==}
braces@3.0.3:
resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
engines: {node: '>=8'}
browserslist@4.25.1: browserslist@4.25.1:
resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -883,6 +978,10 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'} engines: {node: '>=10'}
chokidar@4.0.3:
resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
engines: {node: '>= 14.16.0'}
chownr@2.0.0: chownr@2.0.0:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -960,6 +1059,10 @@ packages:
convert-source-map@2.0.0: convert-source-map@2.0.0:
resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
cookie@1.0.2:
resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==}
engines: {node: '>=18'}
core-util-is@1.0.2: core-util-is@1.0.2:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
@@ -1032,6 +1135,11 @@ packages:
delegates@1.0.0: delegates@1.0.0:
resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==} resolution: {integrity: sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==}
detect-libc@1.0.3:
resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
engines: {node: '>=0.10'}
hasBin: true
detect-libc@2.0.4: detect-libc@2.0.4:
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
engines: {node: '>=8'} engines: {node: '>=8'}
@@ -1293,6 +1401,10 @@ packages:
filelist@1.0.4: filelist@1.0.4:
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
engines: {node: '>=8'}
find-up@5.0.0: find-up@5.0.0:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'} engines: {node: '>=10'}
@@ -1506,6 +1618,9 @@ packages:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'} engines: {node: '>= 4'}
immutable@5.1.3:
resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==}
import-fresh@3.3.1: import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'} engines: {node: '>=6'}
@@ -1611,6 +1726,10 @@ packages:
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
is-number@7.0.0:
resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
engines: {node: '>=0.12.0'}
is-regex@1.2.1: is-regex@1.2.1:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -1802,6 +1921,10 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
micromatch@4.0.8:
resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
engines: {node: '>=8.6'}
mime-db@1.52.0: mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'} engines: {node: '>= 0.6'}
@@ -1908,6 +2031,9 @@ packages:
node-addon-api@1.7.2: node-addon-api@1.7.2:
resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==}
node-addon-api@7.1.1:
resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
node-api-version@0.2.1: node-api-version@0.2.1:
resolution: {integrity: sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==} resolution: {integrity: sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==}
@@ -2036,6 +2162,10 @@ packages:
picocolors@1.1.1: picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
picomatch@2.3.1:
resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
engines: {node: '>=8.6'}
picomatch@4.0.3: picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'} engines: {node: '>=12'}
@@ -2103,6 +2233,11 @@ packages:
peerDependencies: peerDependencies:
react: ^19.1.0 react: ^19.1.0
react-icons@5.5.0:
resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==}
peerDependencies:
react: '*'
react-is@16.13.1: react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
@@ -2110,6 +2245,23 @@ packages:
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
react-router-dom@7.7.0:
resolution: {integrity: sha512-wwGS19VkNBkneVh9/YD0pK3IsjWxQUVMDD6drlG7eJpo1rXBtctBqDyBm/k+oKHRAm1x9XWT3JFC82QI9YOXXA==}
engines: {node: '>=20.0.0'}
peerDependencies:
react: '>=18'
react-dom: '>=18'
react-router@7.7.0:
resolution: {integrity: sha512-3FUYSwlvB/5wRJVTL/aavqHmfUKe0+Xm9MllkYgGo9eDwNdkvwlJGjpPxono1kCycLt6AnDTgjmXvK3/B4QGuw==}
engines: {node: '>=20.0.0'}
peerDependencies:
react: '>=18'
react-dom: '>=18'
peerDependenciesMeta:
react-dom:
optional: true
react@19.1.0: react@19.1.0:
resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
engines: {node: '>=0.10.0'} engines: {node: '>=0.10.0'}
@@ -2128,6 +2280,10 @@ packages:
readdir-glob@1.1.3: readdir-glob@1.1.3:
resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==}
readdirp@4.1.2:
resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
engines: {node: '>= 14.18.0'}
reflect.getprototypeof@1.0.10: reflect.getprototypeof@1.0.10:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -2204,6 +2360,11 @@ packages:
sanitize-filename@1.6.3: sanitize-filename@1.6.3:
resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==} resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==}
sass@1.89.2:
resolution: {integrity: sha512-xCmtksBKd/jdJ9Bt9p7nPKiuqrlBMBuuGkQlkhZjjQk3Ty48lv93k5Dq6OPkKt4XwxDJ7tvlfrTa1MPA9bf+QA==}
engines: {node: '>=14.0.0'}
hasBin: true
sax@1.4.1: sax@1.4.1:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
@@ -2229,6 +2390,9 @@ packages:
set-blocking@2.0.0: set-blocking@2.0.0:
resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==} resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
set-cookie-parser@2.7.1:
resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==}
set-function-length@1.2.2: set-function-length@1.2.2:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'} engines: {node: '>= 0.4'}
@@ -2401,6 +2565,10 @@ packages:
resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==} resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
engines: {node: '>=14.14'} engines: {node: '>=14.14'}
to-regex-range@5.0.1:
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
engines: {node: '>=8.0'}
truncate-utf8-bytes@1.0.2: truncate-utf8-bytes@1.0.2:
resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==}
@@ -3007,6 +3175,67 @@ snapshots:
mkdirp: 1.0.4 mkdirp: 1.0.4
rimraf: 3.0.2 rimraf: 3.0.2
'@parcel/watcher-android-arm64@2.5.1':
optional: true
'@parcel/watcher-darwin-arm64@2.5.1':
optional: true
'@parcel/watcher-darwin-x64@2.5.1':
optional: true
'@parcel/watcher-freebsd-x64@2.5.1':
optional: true
'@parcel/watcher-linux-arm-glibc@2.5.1':
optional: true
'@parcel/watcher-linux-arm-musl@2.5.1':
optional: true
'@parcel/watcher-linux-arm64-glibc@2.5.1':
optional: true
'@parcel/watcher-linux-arm64-musl@2.5.1':
optional: true
'@parcel/watcher-linux-x64-glibc@2.5.1':
optional: true
'@parcel/watcher-linux-x64-musl@2.5.1':
optional: true
'@parcel/watcher-win32-arm64@2.5.1':
optional: true
'@parcel/watcher-win32-ia32@2.5.1':
optional: true
'@parcel/watcher-win32-x64@2.5.1':
optional: true
'@parcel/watcher@2.5.1':
dependencies:
detect-libc: 1.0.3
is-glob: 4.0.3
micromatch: 4.0.8
node-addon-api: 7.1.1
optionalDependencies:
'@parcel/watcher-android-arm64': 2.5.1
'@parcel/watcher-darwin-arm64': 2.5.1
'@parcel/watcher-darwin-x64': 2.5.1
'@parcel/watcher-freebsd-x64': 2.5.1
'@parcel/watcher-linux-arm-glibc': 2.5.1
'@parcel/watcher-linux-arm-musl': 2.5.1
'@parcel/watcher-linux-arm64-glibc': 2.5.1
'@parcel/watcher-linux-arm64-musl': 2.5.1
'@parcel/watcher-linux-x64-glibc': 2.5.1
'@parcel/watcher-linux-x64-musl': 2.5.1
'@parcel/watcher-win32-arm64': 2.5.1
'@parcel/watcher-win32-ia32': 2.5.1
'@parcel/watcher-win32-x64': 2.5.1
optional: true
'@pkgjs/parseargs@0.11.0': '@pkgjs/parseargs@0.11.0':
optional: true optional: true
@@ -3156,7 +3385,7 @@ snapshots:
'@types/node': 22.16.4 '@types/node': 22.16.4
optional: true optional: true
'@vitejs/plugin-react@4.7.0(vite@6.3.5(@types/node@24.0.14))': '@vitejs/plugin-react@4.7.0(vite@6.3.5(@types/node@24.0.14)(sass@1.89.2))':
dependencies: dependencies:
'@babel/core': 7.28.0 '@babel/core': 7.28.0
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0) '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.0)
@@ -3164,7 +3393,7 @@ snapshots:
'@rolldown/pluginutils': 1.0.0-beta.27 '@rolldown/pluginutils': 1.0.0-beta.27
'@types/babel__core': 7.20.5 '@types/babel__core': 7.20.5
react-refresh: 0.17.0 react-refresh: 0.17.0
vite: 6.3.5(@types/node@24.0.14) vite: 6.3.5(@types/node@24.0.14)(sass@1.89.2)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -3408,6 +3637,11 @@ snapshots:
dependencies: dependencies:
balanced-match: 1.0.2 balanced-match: 1.0.2
braces@3.0.3:
dependencies:
fill-range: 7.1.1
optional: true
browserslist@4.25.1: browserslist@4.25.1:
dependencies: dependencies:
caniuse-lite: 1.0.30001727 caniuse-lite: 1.0.30001727
@@ -3515,6 +3749,10 @@ snapshots:
ansi-styles: 4.3.0 ansi-styles: 4.3.0
supports-color: 7.2.0 supports-color: 7.2.0
chokidar@4.0.3:
dependencies:
readdirp: 4.1.2
chownr@2.0.0: {} chownr@2.0.0: {}
chromium-pickle-js@0.2.0: {} chromium-pickle-js@0.2.0: {}
@@ -3581,6 +3819,8 @@ snapshots:
convert-source-map@2.0.0: {} convert-source-map@2.0.0: {}
cookie@1.0.2: {}
core-util-is@1.0.2: core-util-is@1.0.2:
optional: true optional: true
@@ -3654,6 +3894,9 @@ snapshots:
delegates@1.0.0: {} delegates@1.0.0: {}
detect-libc@1.0.3:
optional: true
detect-libc@2.0.4: {} detect-libc@2.0.4: {}
detect-node@2.1.0: detect-node@2.1.0:
@@ -3755,7 +3998,7 @@ snapshots:
electron-to-chromium@1.5.187: {} electron-to-chromium@1.5.187: {}
electron-vite@3.1.0(vite@6.3.5(@types/node@24.0.14)): electron-vite@3.1.0(vite@6.3.5(@types/node@24.0.14)(sass@1.89.2)):
dependencies: dependencies:
'@babel/core': 7.28.0 '@babel/core': 7.28.0
'@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.0) '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.0)
@@ -3763,7 +4006,7 @@ snapshots:
esbuild: 0.25.6 esbuild: 0.25.6
magic-string: 0.30.17 magic-string: 0.30.17
picocolors: 1.1.1 picocolors: 1.1.1
vite: 6.3.5(@types/node@24.0.14) vite: 6.3.5(@types/node@24.0.14)(sass@1.89.2)
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
@@ -4078,6 +4321,11 @@ snapshots:
dependencies: dependencies:
minimatch: 5.1.6 minimatch: 5.1.6
fill-range@7.1.1:
dependencies:
to-regex-range: 5.0.1
optional: true
find-up@5.0.0: find-up@5.0.0:
dependencies: dependencies:
locate-path: 6.0.0 locate-path: 6.0.0
@@ -4348,6 +4596,8 @@ snapshots:
ignore@5.3.2: {} ignore@5.3.2: {}
immutable@5.1.3: {}
import-fresh@3.3.1: import-fresh@3.3.1:
dependencies: dependencies:
parent-module: 1.0.1 parent-module: 1.0.1
@@ -4453,6 +4703,9 @@ snapshots:
call-bound: 1.0.4 call-bound: 1.0.4
has-tostringtag: 1.0.2 has-tostringtag: 1.0.2
is-number@7.0.0:
optional: true
is-regex@1.2.1: is-regex@1.2.1:
dependencies: dependencies:
call-bound: 1.0.4 call-bound: 1.0.4
@@ -4653,6 +4906,12 @@ snapshots:
math-intrinsics@1.1.0: {} math-intrinsics@1.1.0: {}
micromatch@4.0.8:
dependencies:
braces: 3.0.3
picomatch: 2.3.1
optional: true
mime-db@1.52.0: {} mime-db@1.52.0: {}
mime-types@2.1.35: mime-types@2.1.35:
@@ -4739,6 +4998,9 @@ snapshots:
node-addon-api@1.7.2: node-addon-api@1.7.2:
optional: true optional: true
node-addon-api@7.1.1:
optional: true
node-api-version@0.2.1: node-api-version@0.2.1:
dependencies: dependencies:
semver: 7.7.2 semver: 7.7.2
@@ -4887,6 +5149,9 @@ snapshots:
picocolors@1.1.1: {} picocolors@1.1.1: {}
picomatch@2.3.1:
optional: true
picomatch@4.0.3: {} picomatch@4.0.3: {}
plist@3.1.0: plist@3.1.0:
@@ -4942,10 +5207,28 @@ snapshots:
react: 19.1.0 react: 19.1.0
scheduler: 0.26.0 scheduler: 0.26.0
react-icons@5.5.0(react@19.1.0):
dependencies:
react: 19.1.0
react-is@16.13.1: {} react-is@16.13.1: {}
react-refresh@0.17.0: {} react-refresh@0.17.0: {}
react-router-dom@7.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
react: 19.1.0
react-dom: 19.1.0(react@19.1.0)
react-router: 7.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
react-router@7.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
dependencies:
cookie: 1.0.2
react: 19.1.0
set-cookie-parser: 2.7.1
optionalDependencies:
react-dom: 19.1.0(react@19.1.0)
react@19.1.0: {} react@19.1.0: {}
read-binary-file-arch@1.0.6: read-binary-file-arch@1.0.6:
@@ -4974,6 +5257,8 @@ snapshots:
dependencies: dependencies:
minimatch: 5.1.6 minimatch: 5.1.6
readdirp@4.1.2: {}
reflect.getprototypeof@1.0.10: reflect.getprototypeof@1.0.10:
dependencies: dependencies:
call-bind: 1.0.8 call-bind: 1.0.8
@@ -5090,6 +5375,14 @@ snapshots:
dependencies: dependencies:
truncate-utf8-bytes: 1.0.2 truncate-utf8-bytes: 1.0.2
sass@1.89.2:
dependencies:
chokidar: 4.0.3
immutable: 5.1.3
source-map-js: 1.2.1
optionalDependencies:
'@parcel/watcher': 2.5.1
sax@1.4.1: {} sax@1.4.1: {}
scheduler@0.26.0: {} scheduler@0.26.0: {}
@@ -5108,6 +5401,8 @@ snapshots:
set-blocking@2.0.0: {} set-blocking@2.0.0: {}
set-cookie-parser@2.7.1: {}
set-function-length@1.2.2: set-function-length@1.2.2:
dependencies: dependencies:
define-data-property: 1.1.4 define-data-property: 1.1.4
@@ -5339,6 +5634,11 @@ snapshots:
tmp@0.2.3: {} tmp@0.2.3: {}
to-regex-range@5.0.1:
dependencies:
is-number: 7.0.0
optional: true
truncate-utf8-bytes@1.0.2: truncate-utf8-bytes@1.0.2:
dependencies: dependencies:
utf8-byte-length: 1.0.5 utf8-byte-length: 1.0.5
@@ -5429,7 +5729,7 @@ snapshots:
extsprintf: 1.4.1 extsprintf: 1.4.1
optional: true optional: true
vite@6.3.5(@types/node@24.0.14): vite@6.3.5(@types/node@24.0.14)(sass@1.89.2):
dependencies: dependencies:
esbuild: 0.25.6 esbuild: 0.25.6
fdir: 6.4.6(picomatch@4.0.3) fdir: 6.4.6(picomatch@4.0.3)
@@ -5440,6 +5740,7 @@ snapshots:
optionalDependencies: optionalDependencies:
'@types/node': 24.0.14 '@types/node': 24.0.14
fsevents: 2.3.3 fsevents: 2.3.3
sass: 1.89.2
wcwidth@1.0.1: wcwidth@1.0.1:
dependencies: dependencies:

View File

@@ -5,7 +5,7 @@
<title>OpenWall</title> <title>OpenWall</title>
<meta <meta
http-equiv="Content-Security-Policy" http-equiv="Content-Security-Policy"
content="default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:" content="default-src *; script-src * 'unsafe-inline' 'unsafe-eval'; style-src * 'unsafe-inline'; img-src * data: blob:; frame-src *; connect-src *; font-src *; media-src *; object-src *; child-src *;"
/> />
</head> </head>

View File

@@ -1,9 +1,38 @@
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import Header from './common/Header'
import Calendar from './pages/Calendar'
import Shopping from './pages/Shopping'
import Notes from './pages/Notes'
import Search from './pages/Search'
// Configuration
const BACKGROUND_IMAGE_URL = 'https://cdn.pixabay.com/photo/2018/11/19/03/26/iceland-3824494_1280.jpg'
const APP_BACKGROUND_STYLE = {
backgroundImage: `url(${BACKGROUND_IMAGE_URL})`,
backgroundSize: 'cover',
backgroundPosition: 'center',
backgroundRepeat: 'no-repeat',
backgroundAttachment: 'fixed',
minHeight: '100vh'
}
const App = () => { const App = () => {
return ( return (
<> <Router>
<h1>Not implemented yet</h1> <div className="app" style={APP_BACKGROUND_STYLE}>
</> <Header />
<main className="app__main">
<Routes>
<Route path="/" element={<Calendar />} />
<Route path="/shopping" element={<Shopping />} />
<Route path="/notes" element={<Notes />} />
<Route path="/search" element={<Search />} />
</Routes>
</main>
</div>
</Router>
) )
} }
export default App; export default App

View File

@@ -0,0 +1,119 @@
import { useState, useEffect } from 'react'
import { useNavigate, useLocation } from 'react-router-dom'
import { FiCalendar, FiShoppingBag, FiBookOpen, FiSearch } from 'react-icons/fi'
import './styles.sass'
// Static color configuration
const HEADER_COLORS = {
primary: 'rgba(76, 175, 160, 0.25)',
secondary: 'rgba(34, 139, 156, 0.35)',
tertiary: 'rgba(21, 94, 117, 0.4)',
accent: 'rgba(147, 197, 253, 0.3)'
}
// Icon-specific colors for better differentiation
const ICON_COLORS = {
calendar: '#FF6B6B', // Red
shopping: '#4ECDC4', // Teal
notes: '#45B7D1', // Blue
search: '#FFA726' // Orange
}
const Header = () => {
const [currentTime, setCurrentTime] = useState(new Date())
const navigate = useNavigate()
const location = useLocation()
useEffect(() => {
const timer = setInterval(() => {
setCurrentTime(new Date())
}, 1000)
return () => clearInterval(timer)
}, [])
const formatTime = (date) => {
return date.toLocaleTimeString('de-DE', {
hour: '2-digit',
minute: '2-digit'
})
}
const getCurrentMonth = (date) => {
return date.toLocaleDateString('de-DE', { month: 'long' })
}
const navigationItems = [
{ id: 'calendar', icon: FiCalendar, label: 'Kalender', path: '/' },
{ id: 'shopping', icon: FiShoppingBag, label: 'Einkaufen', path: '/shopping' },
{ id: 'notes', icon: FiBookOpen, label: 'Notizen', path: '/notes' },
{ id: 'search', icon: FiSearch, label: 'Suchen', path: '/search' }
]
const getActiveIcon = () => {
const currentPath = location.pathname
const activeItem = navigationItems.find(item => item.path === currentPath)
return activeItem ? activeItem.id : 'calendar'
}
const handleNavigation = (path) => {
navigate(path)
}
const headerStyle = {
background: `linear-gradient(135deg, ${HEADER_COLORS.primary}, ${HEADER_COLORS.secondary}, ${HEADER_COLORS.tertiary})`,
borderColor: HEADER_COLORS.accent
}
const buttonStyle = {
background: `linear-gradient(135deg, ${HEADER_COLORS.accent}, ${HEADER_COLORS.primary})`,
borderColor: HEADER_COLORS.accent,
color: '#ffffff',
boxShadow: `0 8px 32px ${HEADER_COLORS.tertiary}, inset 0 1px 0 ${HEADER_COLORS.accent}`
}
const activeButtonStyle = {
background: `linear-gradient(135deg, ${HEADER_COLORS.primary}, ${HEADER_COLORS.secondary})`,
borderColor: HEADER_COLORS.primary,
color: '#ffffff',
boxShadow: `0 12px 40px ${HEADER_COLORS.primary}, inset 0 2px 0 ${HEADER_COLORS.accent}`
}
return (
<header className="header" style={headerStyle}>
<nav className="header__nav">
<div className="header__left">
<span className="header__month">{getCurrentMonth(currentTime)}</span>
<span className="header__time">{formatTime(currentTime)}</span>
</div>
<div className="header__right">
{navigationItems.map(({ id, icon: Icon, label, path }) => {
const isActive = getActiveIcon() === id
const iconColor = ICON_COLORS[id]
const buttonStyleWithIcon = {
...(isActive ? activeButtonStyle : buttonStyle),
'--icon-color': iconColor
}
return (
<button
key={id}
className={`header__nav-item ${isActive ? 'header__nav-item--active' : ''}`}
onClick={() => handleNavigation(path)}
aria-label={label}
title={label}
style={buttonStyleWithIcon}
>
<Icon style={{ color: iconColor, filter: 'drop-shadow(0 1px 2px rgba(0, 0, 0, 0.3))' }} />
</button>
)
})}
</div>
</nav>
</header>
)
}
export default Header

View File

@@ -0,0 +1 @@
export { default } from './Header'

View File

@@ -0,0 +1,161 @@
// Header Component - Dynamic Color Extraction from Background
.header
position: fixed
top: 0.5rem
left: 0.5rem
right: 0.5rem
z-index: 1000
padding: 1rem 2rem
backdrop-filter: blur(80px) saturate(180%)
border-radius: 24px
border: 1px solid
box-shadow: 0 20px 80px rgba(0, 0, 0, 0.15), 0 8px 32px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.4)
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1)
&:hover
backdrop-filter: blur(100px) saturate(200%)
box-shadow: 0 24px 96px rgba(0, 0, 0, 0.25), 0 12px 48px rgba(0, 0, 0, 0.15), inset 0 2px 0 rgba(255, 255, 255, 0.5)
transform: translateY(-1px)
&__nav
display: flex
justify-content: space-between
align-items: center
max-width: 100%
margin: 0 auto
&__left
display: flex
flex-direction: column
align-items: flex-start
gap: 0.125rem
&__month
font-size: 1.5rem
font-weight: 700
color: rgba(255, 255, 255, 0.95)
letter-spacing: -0.02em
text-shadow: 0 2px 8px rgba(21, 94, 117, 0.6), 0 1px 2px rgba(0, 0, 0, 0.3)
line-height: 1.1
&__time
font-size: 2.25rem
font-weight: 800
color: #ffffff
font-variant-numeric: tabular-nums
text-shadow: 0 3px 12px rgba(21, 94, 117, 0.8), 0 1px 4px rgba(0, 0, 0, 0.4)
line-height: 0.9
letter-spacing: -0.05em
&__right
display: flex
align-items: center
gap: 1rem
&__nav-item
display: flex
align-items: center
justify-content: center
width: 56px
height: 56px
border: none
border-radius: 18px
cursor: pointer
transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1)
backdrop-filter: blur(40px)
border: 1px solid
font-size: 1.375rem
position: relative
overflow: hidden
// Allow custom icon colors
svg
transition: all 0.3s ease
&::before
content: ''
position: absolute
top: 0
left: 0
right: 0
bottom: 0
opacity: 0
transition: opacity 0.3s ease
border-radius: 18px
&:hover
transform: translateY(-3px) scale(1.08)
svg
filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.4)) !important
&::before
opacity: 1
&:active
transform: translateY(-1px) scale(1.03)
&--active
border: 1px solid
svg
filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.5)) !important
&::before
opacity: 1
&:hover
transform: translateY(-3px) scale(1.08)
// Responsive Design
@media (max-width: 768px)
.header
top: 0.375rem
left: 0.375rem
right: 0.375rem
padding: 0.875rem 1.75rem
border-radius: 20px
&__left
gap: 0.0625rem
&__month
font-size: 1.25rem
&__time
font-size: 1.875rem
&__right
gap: 0.875rem
&__nav-item
width: 52px
height: 52px
border-radius: 16px
font-size: 1.25rem
@media (max-width: 480px)
.header
top: 0.25rem
left: 0.25rem
right: 0.25rem
padding: 0.75rem 1.5rem
border-radius: 18px
&__left
gap: 0.0625rem
&__month
font-size: 1.125rem
&__time
font-size: 1.625rem
&__right
gap: 0.75rem
&__nav-item
width: 48px
height: 48px
border-radius: 14px
font-size: 1.125rem

View File

@@ -0,0 +1,71 @@
// Global Styles - Light Mode with Waterfall Background
// Reset and base styles
*
margin: 0
padding: 0
box-sizing: border-box
html, body
height: 100vh
width: 100vw
overflow: hidden
body
font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif
background: linear-gradient(rgba(255, 255, 255, 0.1), rgba(255, 255, 255, 0.3)), url('https://cdn.pixabay.com/photo/2024/07/16/23/33/waterfall-8900207_1280.png')
background-size: cover
background-position: center
background-attachment: fixed
color: #1e293b
font-weight: 400
letter-spacing: -0.01em
line-height: 1.5
-webkit-font-smoothing: antialiased
-moz-osx-font-smoothing: grayscale
#root
height: 100vh
width: 100vw
display: flex
flex-direction: column
// App Layout Styles
.app
height: 100vh
width: 100vw
display: flex
flex-direction: column
&__main
flex: 1
padding-top: 5rem
display: flex
align-items: center
justify-content: center
&__content
text-align: center
backdrop-filter: blur(40px) saturate(180%)
background: rgba(255, 255, 255, 0.25)
padding: 4rem
border-radius: 32px
border: 1px solid rgba(255, 255, 255, 0.3)
box-shadow: 0 16px 64px rgba(0, 0, 0, 0.1), 0 8px 32px rgba(0, 0, 0, 0.05), inset 0 1px 0 rgba(255, 255, 255, 0.4)
max-width: 600px
margin: 0 auto
h1
font-size: 3rem
font-weight: 800
margin-bottom: 1.5rem
background: linear-gradient(135deg, #1e293b, #475569, #0f766e)
-webkit-background-clip: text
-webkit-text-fill-color: transparent
background-clip: text
line-height: 1.2
p
font-size: 1.25rem
color: rgba(30, 41, 59, 0.8)
font-weight: 500
line-height: 1.6

View File

@@ -1,5 +1,6 @@
import { StrictMode } from 'react' import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client' import { createRoot } from 'react-dom/client'
import './index.sass'
import App from './App' import App from './App'
createRoot(document.getElementById('root')).render( createRoot(document.getElementById('root')).render(

View File

@@ -0,0 +1,9 @@
const Calendar = () => {
return (
<div>
<h1>Kalender</h1>
</div>
)
}
export default Calendar

View File

@@ -0,0 +1,203 @@
import { useRef, useEffect, useState } from 'react'
import { FiEdit3, FiTrash, FiChevronDown } from 'react-icons/fi'
import { TbEraser } from 'react-icons/tb'
import './Notes.sass'
const Notes = () => {
const canvasRef = useRef(null)
const [isDrawing, setIsDrawing] = useState(false)
const [tool, setTool] = useState('pen') // 'pen' or 'eraser'
const [penColor, setPenColor] = useState('#2d3748')
const [showColorPicker, setShowColorPicker] = useState(false)
const colors = [
'#2d3748', // Dark gray
'#000000', // Black
'#e53e3e', // Red
'#3182ce', // Blue
'#38a169', // Green
'#d69e2e', // Yellow
'#805ad5', // Purple
'#dd6b20', // Orange
'#e91e63', // Pink
'#00acc1' // Cyan
]
useEffect(() => {
const canvas = canvasRef.current
const ctx = canvas.getContext('2d')
// Set canvas size
const resizeCanvas = () => {
const container = canvas.parentElement
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height)
canvas.width = container.clientWidth
canvas.height = container.clientHeight
// Set drawing properties
ctx.lineCap = 'round'
ctx.lineJoin = 'round'
ctx.imageSmoothingEnabled = true
// Restore canvas content after resize
ctx.putImageData(imageData, 0, 0)
// Load saved canvas data
loadCanvasFromStorage()
}
resizeCanvas()
window.addEventListener('resize', resizeCanvas)
return () => window.removeEventListener('resize', resizeCanvas)
}, [])
const saveCanvasToStorage = () => {
const canvas = canvasRef.current
const dataURL = canvas.toDataURL()
localStorage.setItem('notes-canvas', dataURL)
}
const loadCanvasFromStorage = () => {
const canvas = canvasRef.current
const ctx = canvas.getContext('2d')
const savedData = localStorage.getItem('notes-canvas')
if (savedData) {
const img = new Image()
img.onload = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.drawImage(img, 0, 0)
}
img.src = savedData
}
}
const startDrawing = (e) => {
setIsDrawing(true)
const canvas = canvasRef.current
const ctx = canvas.getContext('2d')
const rect = canvas.getBoundingClientRect()
const x = e.clientX - rect.left
const y = e.clientY - rect.top
ctx.beginPath()
ctx.moveTo(x, y)
}
const draw = (e) => {
if (!isDrawing) return
const canvas = canvasRef.current
const ctx = canvas.getContext('2d')
const rect = canvas.getBoundingClientRect()
const x = e.clientX - rect.left
const y = e.clientY - rect.top
if (tool === 'pen') {
ctx.globalCompositeOperation = 'source-over'
ctx.strokeStyle = penColor
ctx.lineWidth = 3
ctx.lineTo(x, y)
ctx.stroke()
} else if (tool === 'eraser') {
ctx.globalCompositeOperation = 'destination-out'
ctx.lineWidth = 40 // Increased eraser size
ctx.lineTo(x, y)
ctx.stroke()
}
}
const stopDrawing = () => {
setIsDrawing(false)
// Save canvas state after drawing
saveCanvasToStorage()
}
const clearCanvas = () => {
const canvas = canvasRef.current
const ctx = canvas.getContext('2d')
ctx.clearRect(0, 0, canvas.width, canvas.height)
// Clear saved state
localStorage.removeItem('notes-canvas')
}
return (
<div className="notes-page">
<div className="notes-container">
{/* Toolbar */}
<div className="notes-toolbar">
<div className="pen-tool-group">
<button
className={`tool-button ${tool === 'pen' ? 'active' : ''}`}
onClick={() => setTool('pen')}
title="Stift"
style={{ backgroundColor: tool === 'pen' ? penColor + '20' : undefined }}
>
<FiEdit3 style={{ color: tool === 'pen' ? penColor : undefined }} />
</button>
<button
className="color-picker-button"
onClick={() => setShowColorPicker(!showColorPicker)}
title="Farbe wählen"
style={{ backgroundColor: penColor }}
>
<FiChevronDown />
</button>
{showColorPicker && (
<div className="color-picker-popover">
<div className="color-grid">
{colors.map((color) => (
<button
key={color}
className={`color-option ${penColor === color ? 'active' : ''}`}
style={{ backgroundColor: color }}
onClick={() => {
setPenColor(color)
setShowColorPicker(false)
}}
title={`Farbe: ${color}`}
/>
))}
</div>
</div>
)}
</div>
<button
className={`tool-button ${tool === 'eraser' ? 'active' : ''}`}
onClick={() => setTool('eraser')}
title="Radiergummi"
>
<TbEraser />
</button>
<button
className="tool-button clear-button"
onClick={clearCanvas}
title="Alles löschen"
>
<FiTrash />
</button>
</div>
{/* Canvas Container */}
<div className="canvas-container">
<canvas
ref={canvasRef}
className="drawing-canvas"
onMouseDown={startDrawing}
onMouseMove={draw}
onMouseUp={stopDrawing}
onMouseLeave={stopDrawing}
/>
</div>
</div>
</div>
)
}
export default Notes

View File

@@ -0,0 +1,249 @@
// Notes Page - Drawing Canvas with Glassmorphism (Matching Search Page Structure)
.notes-page
padding: 2rem 0.75rem 0.75rem 0.75rem
height: calc(100vh - 6rem)
width: 100%
.notes-container
position: relative
width: 100%
height: 100%
backdrop-filter: blur(60px) saturate(200%)
background: linear-gradient(135deg, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.2))
border-radius: 28px
border: 1px solid rgba(255, 255, 255, 0.5)
box-shadow: 0 16px 64px rgba(0, 0, 0, 0.1), 0 8px 32px rgba(0, 0, 0, 0.05), inset 0 2px 0 rgba(255, 255, 255, 0.6), inset 0 -1px 0 rgba(0, 0, 0, 0.1)
padding: 1.5rem
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1)
overflow: hidden
&:hover
backdrop-filter: blur(80px) saturate(220%)
background: linear-gradient(135deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.3))
box-shadow: 0 20px 80px rgba(0, 0, 0, 0.15), 0 10px 40px rgba(0, 0, 0, 0.08), inset 0 2px 0 rgba(255, 255, 255, 0.7)
transform: translateY(-2px)
.notes-toolbar
position: absolute
bottom: 1rem
left: 50%
transform: translateX(-50%)
z-index: 10
display: flex
align-items: center
gap: 0.75rem
padding: 0.75rem 1rem
backdrop-filter: blur(40px) saturate(180%)
background: rgba(255, 255, 255, 0.2)
border-radius: 16px
border: 1px solid rgba(255, 255, 255, 0.3)
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.1), inset 0 1px 0 rgba(255, 255, 255, 0.4)
.pen-tool-group
position: relative
display: flex
align-items: center
gap: 0.25rem
.color-picker-button
display: flex
align-items: center
justify-content: center
width: 24px
height: 44px
border: none
border-radius: 0 14px 14px 0
border: 1px solid rgba(255, 255, 255, 0.4)
border-left: none
cursor: pointer
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1)
font-size: 0.875rem
color: white
&:hover
transform: translateY(-2px)
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15)
.color-picker-popover
position: absolute
bottom: calc(100% + 0.5rem)
left: 0
backdrop-filter: blur(40px) saturate(180%)
background: rgba(255, 255, 255, 0.25)
border-radius: 16px
border: 1px solid rgba(255, 255, 255, 0.3)
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.15), inset 0 1px 0 rgba(255, 255, 255, 0.4)
padding: 0.75rem
z-index: 20
.color-grid
display: grid
grid-template-columns: repeat(5, 1fr)
gap: 0.5rem
.color-option
width: 32px
height: 32px
border: 2px solid rgba(255, 255, 255, 0.3)
border-radius: 12px
cursor: pointer
transition: all 0.2s ease
position: relative
&:hover
transform: scale(1.1)
border-color: rgba(255, 255, 255, 0.6)
&.active
transform: scale(1.15)
border-color: rgba(255, 255, 255, 0.8)
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2)
&::after
content: ''
position: absolute
top: 50%
left: 50%
transform: translate(-50%, -50%)
color: white
font-size: 0.875rem
font-weight: bold
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.8)
.toolbar-section
display: flex
align-items: center
gap: 0.75rem
.tool-button
display: flex
align-items: center
justify-content: center
width: 44px
height: 44px
border: none
border-radius: 14px
background: rgba(255, 255, 255, 0.3)
backdrop-filter: blur(20px)
border: 1px solid rgba(255, 255, 255, 0.4)
color: rgba(50, 50, 50, 0.8)
cursor: pointer
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1)
font-size: 1.125rem
&:hover
background: rgba(255, 255, 255, 0.5)
color: rgba(50, 50, 50, 1)
transform: translateY(-2px)
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.15)
&.active
background: rgba(255, 255, 255, 0.6)
color: rgba(50, 50, 50, 1)
border-color: rgba(255, 255, 255, 0.6)
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2)
&.clear-button
background: rgba(239, 68, 68, 0.3)
border-color: rgba(239, 68, 68, 0.4)
color: rgba(50, 50, 50, 0.8)
&:hover
background: rgba(239, 68, 68, 0.5)
color: rgba(50, 50, 50, 1)
transform: translateY(-2px)
box-shadow: 0 8px 24px rgba(239, 68, 68, 0.2)
// Special styling for pen tool in group
.pen-tool-group &:first-child
border-radius: 14px 0 0 14px
border-right: none
.canvas-container
width: 100%
height: 100%
border-radius: 20px
background: rgba(255, 255, 255, 0.9)
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5)
overflow: hidden
.drawing-canvas
width: 100%
height: 100%
cursor: crosshair
display: block
background: #ffffff
border-radius: 20px
&.eraser-mode
cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20"><circle cx="10" cy="10" r="8" fill="none" stroke="black" stroke-width="2"/></svg>') 10 10, auto
// Responsive Design
@media (max-width: 768px)
.notes-page
padding: 1.5rem 0.5rem 0.5rem 0.5rem
.notes-container
border-radius: 24px
padding: 1.25rem
.canvas-container
border-radius: 16px
.drawing-canvas
border-radius: 16px
.notes-toolbar
bottom: 0.75rem
padding: 0.625rem 0.875rem
gap: 0.625rem
.tool-button
width: 40px
height: 40px
font-size: 1rem
.color-picker-button
width: 20px
height: 40px
.color-option
width: 28px
height: 28px
@media (max-width: 480px)
.notes-page
padding: 1rem 0.375rem 0.375rem 0.375rem
.notes-container
border-radius: 20px
padding: 1rem
.canvas-container
border-radius: 12px
.drawing-canvas
border-radius: 12px
.notes-toolbar
bottom: 0.5rem
padding: 0.5rem 0.75rem
gap: 0.5rem
border-radius: 14px
.tool-button
width: 36px
height: 36px
border-radius: 12px
font-size: 0.875rem
.color-picker-button
width: 18px
height: 36px
.color-option
width: 24px
height: 24px
.color-grid
grid-template-columns: repeat(4, 1fr)

View File

@@ -0,0 +1,20 @@
import './Search.sass'
const Search = () => {
return (
<div className="search-page">
<div className="search-container">
<div className="google-embed-container">
<iframe
src="https://www.google.com/search?igu=1"
className="google-iframe"
title="Google Search"
allow="microphone"
/>
</div>
</div>
</div>
)
}
export default Search

View File

@@ -0,0 +1,58 @@
// Search Page - Full Screen Glassmorphism Google Embed
.search-page
padding: 2rem 0.75rem 0.75rem 0.75rem
height: calc(100vh - 6rem)
width: 100%
.search-container
width: 100%
height: 100%
.google-embed-container
width: 100%
height: 100%
backdrop-filter: blur(60px) saturate(200%)
background: linear-gradient(135deg, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.2))
border-radius: 28px
border: 1px solid rgba(255, 255, 255, 0.5)
box-shadow: 0 16px 64px rgba(0, 0, 0, 0.1), 0 8px 32px rgba(0, 0, 0, 0.05), inset 0 2px 0 rgba(255, 255, 255, 0.6), inset 0 -1px 0 rgba(0, 0, 0, 0.1)
padding: 1.5rem
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1)
overflow: hidden
&:hover
backdrop-filter: blur(80px) saturate(220%)
background: linear-gradient(135deg, rgba(255, 255, 255, 0.5), rgba(255, 255, 255, 0.3))
box-shadow: 0 20px 80px rgba(0, 0, 0, 0.15), 0 10px 40px rgba(0, 0, 0, 0.08), inset 0 2px 0 rgba(255, 255, 255, 0.7)
transform: translateY(-2px)
.google-iframe
width: 100%
height: 100%
border: none
border-radius: 20px
background: rgba(255, 255, 255, 0.9)
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5)
// Responsive Design
@media (max-width: 768px)
.search-page
padding: 1.5rem 0.5rem 0.5rem 0.5rem
.google-embed-container
border-radius: 24px
padding: 1.25rem
.google-iframe
border-radius: 16px
@media (max-width: 480px)
.search-page
padding: 1rem 0.375rem 0.375rem 0.375rem
.google-embed-container
border-radius: 20px
padding: 1rem
.google-iframe
border-radius: 12px

View File

@@ -0,0 +1,9 @@
const Shopping = () => {
return (
<div>
<h1>Einkaufen</h1>
</div>
)
}
export default Shopping

View File

@@ -0,0 +1,697 @@
// Pages Styles - Light Mode with Glass Effects
// Common page container
.page-container
padding: 2rem
min-height: calc(100vh - 6rem)
overflow-y: auto
// Calendar Styles
.calendar-container
max-width: 1200px
margin: 0 auto
.calendar-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 2rem
flex-wrap: wrap
gap: 1rem
h1
font-size: 2rem
font-weight: 700
color: #1e293b
margin: 0
.calendar-nav
display: flex
align-items: center
gap: 1rem
background: rgba(255, 255, 255, 0.3)
padding: 0.75rem 1.5rem
border-radius: 16px
backdrop-filter: blur(20px)
border: 1px solid rgba(255, 255, 255, 0.4)
h2
font-size: 1.25rem
font-weight: 600
color: #1e293b
margin: 0
min-width: 200px
text-align: center
.nav-btn, .add-event-btn
background: linear-gradient(135deg, rgba(255, 255, 255, 0.4), rgba(255, 255, 255, 0.2))
border: 1px solid rgba(255, 255, 255, 0.5)
border-radius: 12px
padding: 0.75rem 1rem
color: #475569
cursor: pointer
transition: all 0.3s ease
backdrop-filter: blur(20px)
font-weight: 500
&:hover
background: linear-gradient(135deg, rgba(255, 255, 255, 0.6), rgba(255, 255, 255, 0.4))
transform: translateY(-2px)
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1)
.add-event-btn
display: flex
align-items: center
gap: 0.5rem
font-size: 0.875rem
.calendar-grid
background: rgba(255, 255, 255, 0.25)
border-radius: 24px
padding: 2rem
margin-bottom: 2rem
backdrop-filter: blur(40px)
border: 1px solid rgba(255, 255, 255, 0.3)
box-shadow: 0 16px 64px rgba(0, 0, 0, 0.1)
.calendar-days-header
display: grid
grid-template-columns: repeat(7, 1fr)
gap: 1rem
margin-bottom: 1rem
.day-header
text-align: center
font-weight: 600
color: #475569
padding: 0.75rem
.calendar-days
display: grid
grid-template-columns: repeat(7, 1fr)
gap: 1rem
.calendar-day
aspect-ratio: 1
display: flex
align-items: center
justify-content: center
border-radius: 12px
cursor: pointer
font-weight: 500
transition: all 0.3s ease
&.empty
cursor: default
&.active
background: rgba(255, 255, 255, 0.2)
color: #1e293b
&:hover
background: rgba(255, 255, 255, 0.4)
transform: scale(1.05)
&.today
background: linear-gradient(135deg, rgba(59, 130, 246, 0.3), rgba(37, 99, 235, 0.3))
color: white
font-weight: 700
&.selected
background: linear-gradient(135deg, rgba(168, 85, 247, 0.3), rgba(147, 51, 234, 0.3))
color: white
// Shopping Styles
.shopping-container
max-width: 800px
margin: 0 auto
.shopping-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 2rem
h1
display: flex
align-items: center
gap: 0.75rem
font-size: 2rem
font-weight: 700
color: #1e293b
margin: 0
.shopping-stats
display: flex
gap: 1rem
span
background: rgba(255, 255, 255, 0.3)
padding: 0.5rem 1rem
border-radius: 12px
font-size: 0.875rem
font-weight: 500
color: #475569
.add-item-form
background: rgba(255, 255, 255, 0.25)
border-radius: 20px
padding: 1.5rem
margin-bottom: 2rem
backdrop-filter: blur(40px)
border: 1px solid rgba(255, 255, 255, 0.3)
.input-group
display: flex
gap: 1rem
input
flex: 1
padding: 0.75rem 1rem
border: 1px solid rgba(255, 255, 255, 0.4)
border-radius: 12px
background: rgba(255, 255, 255, 0.3)
color: #1e293b
font-size: 1rem
backdrop-filter: blur(20px)
&::placeholder
color: rgba(30, 41, 59, 0.6)
&:focus
outline: none
border-color: rgba(59, 130, 246, 0.5)
background: rgba(255, 255, 255, 0.4)
.add-btn
background: linear-gradient(135deg, rgba(16, 185, 129, 0.3), rgba(5, 150, 105, 0.3))
border: 1px solid rgba(255, 255, 255, 0.4)
border-radius: 12px
padding: 0.75rem 1rem
color: white
cursor: pointer
transition: all 0.3s ease
&:hover
background: linear-gradient(135deg, rgba(16, 185, 129, 0.4), rgba(5, 150, 105, 0.4))
transform: translateY(-2px)
.shopping-section
margin-bottom: 2rem
h3
font-size: 1.25rem
font-weight: 600
color: #1e293b
margin-bottom: 1rem
.items-list
background: rgba(255, 255, 255, 0.25)
border-radius: 20px
padding: 1.5rem
backdrop-filter: blur(40px)
border: 1px solid rgba(255, 255, 255, 0.3)
.shopping-item
display: flex
align-items: center
gap: 1rem
padding: 1rem 0
border-bottom: 1px solid rgba(255, 255, 255, 0.2)
&:last-child
border-bottom: none
&.completed
opacity: 0.6
.item-name
text-decoration: line-through
.check-btn
width: 32px
height: 32px
border-radius: 50%
border: 2px solid rgba(16, 185, 129, 0.5)
background: transparent
color: transparent
cursor: pointer
transition: all 0.3s ease
&.checked
background: linear-gradient(135deg, rgba(16, 185, 129, 0.8), rgba(5, 150, 105, 0.8))
color: white
&:hover
border-color: rgba(16, 185, 129, 0.8)
background: rgba(16, 185, 129, 0.1)
.item-details
flex: 1
display: flex
flex-direction: column
gap: 0.25rem
.item-name
font-weight: 500
color: #1e293b
.item-quantity, .item-category
font-size: 0.875rem
color: #64748b
.delete-btn
background: transparent
border: none
color: #ef4444
cursor: pointer
padding: 0.5rem
border-radius: 8px
transition: all 0.3s ease
&:hover
background: rgba(239, 68, 68, 0.1)
color: #dc2626
// Notes Styles
.notes-container
max-width: 1200px
margin: 0 auto
.notes-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 2rem
h1
font-size: 2rem
font-weight: 700
color: #1e293b
margin: 0
.add-note-btn
display: flex
align-items: center
gap: 0.5rem
background: linear-gradient(135deg, rgba(139, 92, 246, 0.3), rgba(124, 58, 237, 0.3))
border: 1px solid rgba(255, 255, 255, 0.4)
border-radius: 12px
padding: 0.75rem 1.5rem
color: white
cursor: pointer
font-weight: 500
transition: all 0.3s ease
&:hover
background: linear-gradient(135deg, rgba(139, 92, 246, 0.4), rgba(124, 58, 237, 0.4))
transform: translateY(-2px)
.note-editor
background: rgba(255, 255, 255, 0.25)
border-radius: 20px
padding: 2rem
margin-bottom: 2rem
backdrop-filter: blur(40px)
border: 1px solid rgba(255, 255, 255, 0.3)
.editor-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 1rem
gap: 1rem
.title-input
flex: 1
padding: 0.75rem 1rem
border: 1px solid rgba(255, 255, 255, 0.4)
border-radius: 12px
background: rgba(255, 255, 255, 0.3)
color: #1e293b
font-size: 1.125rem
font-weight: 600
&:focus
outline: none
border-color: rgba(59, 130, 246, 0.5)
.color-picker
display: flex
gap: 0.5rem
.color-option
width: 32px
height: 32px
border-radius: 50%
border: 2px solid transparent
cursor: pointer
transition: all 0.3s ease
&.selected
border-color: rgba(255, 255, 255, 0.8)
transform: scale(1.1)
.content-input
width: 100%
padding: 1rem
border: 1px solid rgba(255, 255, 255, 0.4)
border-radius: 12px
background: rgba(255, 255, 255, 0.3)
color: #1e293b
font-size: 1rem
line-height: 1.6
resize: vertical
margin-bottom: 1rem
&:focus
outline: none
border-color: rgba(59, 130, 246, 0.5)
.editor-actions
display: flex
gap: 1rem
.save-btn, .cancel-btn
display: flex
align-items: center
gap: 0.5rem
padding: 0.75rem 1.5rem
border-radius: 12px
cursor: pointer
font-weight: 500
transition: all 0.3s ease
.save-btn
background: linear-gradient(135deg, rgba(16, 185, 129, 0.3), rgba(5, 150, 105, 0.3))
border: 1px solid rgba(255, 255, 255, 0.4)
color: white
&:hover
background: linear-gradient(135deg, rgba(16, 185, 129, 0.4), rgba(5, 150, 105, 0.4))
.cancel-btn
background: rgba(255, 255, 255, 0.3)
border: 1px solid rgba(255, 255, 255, 0.4)
color: #475569
&:hover
background: rgba(255, 255, 255, 0.4)
.notes-grid
display: grid
grid-template-columns: repeat(auto-fill, minmax(350px, 1fr))
gap: 1.5rem
.note-card
background: rgba(255, 255, 255, 0.25)
border-radius: 20px
padding: 1.5rem
backdrop-filter: blur(40px)
border: 1px solid rgba(255, 255, 255, 0.3)
border-left: 4px solid #3b82f6
transition: all 0.3s ease
&:hover
transform: translateY(-4px)
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15)
.note-header
display: flex
justify-content: space-between
align-items: flex-start
margin-bottom: 1rem
h3
font-size: 1.125rem
font-weight: 600
color: #1e293b
margin: 0
.note-actions
display: flex
gap: 0.5rem
button
background: transparent
border: none
color: #64748b
cursor: pointer
padding: 0.25rem
border-radius: 6px
transition: all 0.3s ease
&:hover
background: rgba(100, 116, 139, 0.1)
color: #475569
.note-content
margin-bottom: 1rem
p
color: #475569
line-height: 1.6
margin-bottom: 0.5rem
&:last-child
margin-bottom: 0
.note-footer
border-top: 1px solid rgba(255, 255, 255, 0.2)
padding-top: 1rem
.note-date
font-size: 0.875rem
color: #64748b
// Search Styles
.search-container
max-width: 800px
margin: 0 auto
.search-header
margin-bottom: 2rem
h1
font-size: 2rem
font-weight: 700
color: #1e293b
margin-bottom: 1.5rem
.search-input-container
position: relative
background: rgba(255, 255, 255, 0.25)
border-radius: 20px
padding: 1.5rem
backdrop-filter: blur(40px)
border: 1px solid rgba(255, 255, 255, 0.3)
.search-icon
position: absolute
left: 2.5rem
top: 50%
transform: translateY(-50%)
color: #64748b
font-size: 1.25rem
.search-input
width: 100%
padding: 1rem 1rem 1rem 3rem
border: none
background: transparent
color: #1e293b
font-size: 1.125rem
outline: none
&::placeholder
color: rgba(30, 41, 59, 0.6)
.search-suggestions
display: grid
grid-template-columns: 1fr 1fr
gap: 2rem
.recent-searches, .quick-actions
background: rgba(255, 255, 255, 0.25)
border-radius: 20px
padding: 1.5rem
backdrop-filter: blur(40px)
border: 1px solid rgba(255, 255, 255, 0.3)
h3
font-size: 1.125rem
font-weight: 600
color: #1e293b
margin-bottom: 1rem
.recent-list
display: flex
flex-direction: column
gap: 0.75rem
.recent-item
display: flex
align-items: center
gap: 0.75rem
padding: 0.75rem 1rem
background: rgba(255, 255, 255, 0.3)
border: 1px solid rgba(255, 255, 255, 0.4)
border-radius: 12px
color: #475569
cursor: pointer
transition: all 0.3s ease
&:hover
background: rgba(255, 255, 255, 0.4)
transform: translateX(4px)
.actions-grid
display: grid
grid-template-columns: 1fr 1fr
gap: 1rem
.quick-action
display: flex
flex-direction: column
align-items: center
gap: 0.5rem
padding: 1rem
background: rgba(255, 255, 255, 0.3)
border: 1px solid rgba(255, 255, 255, 0.4)
border-radius: 12px
color: #475569
cursor: pointer
transition: all 0.3s ease
text-align: center
&:hover
background: rgba(255, 255, 255, 0.4)
transform: translateY(-2px)
svg
font-size: 1.5rem
span
font-size: 0.875rem
font-weight: 500
.search-results
.results-header
margin-bottom: 1.5rem
h3
font-size: 1.125rem
font-weight: 600
color: #1e293b
.results-list
display: flex
flex-direction: column
gap: 1rem
.result-item
display: flex
align-items: flex-start
gap: 1rem
padding: 1.5rem
background: rgba(255, 255, 255, 0.25)
border-radius: 16px
backdrop-filter: blur(40px)
border: 1px solid rgba(255, 255, 255, 0.3)
cursor: pointer
transition: all 0.3s ease
&:hover
transform: translateY(-2px)
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1)
.result-icon
width: 48px
height: 48px
border-radius: 12px
display: flex
align-items: center
justify-content: center
color: white
font-size: 1.25rem
.result-content
flex: 1
.result-header
display: flex
justify-content: space-between
align-items: center
margin-bottom: 0.5rem
h4
font-size: 1.125rem
font-weight: 600
color: #1e293b
margin: 0
.result-type
font-size: 0.875rem
font-weight: 500
.result-description
color: #475569
line-height: 1.5
margin-bottom: 0.5rem
.result-date
font-size: 0.875rem
color: #64748b
.no-results
text-align: center
padding: 3rem
color: #64748b
svg
margin-bottom: 1rem
opacity: 0.5
h3
font-size: 1.25rem
font-weight: 600
margin-bottom: 0.5rem
p
font-size: 1rem
// Responsive Design
@media (max-width: 768px)
.page-container
padding: 1.5rem
.calendar-header, .shopping-header, .notes-header
flex-direction: column
align-items: flex-start
gap: 1rem
.search-suggestions
grid-template-columns: 1fr
.actions-grid
grid-template-columns: 1fr
.notes-grid
grid-template-columns: 1fr
.editor-header
flex-direction: column
align-items: stretch
@media (max-width: 480px)
.page-container
padding: 1rem
.calendar-days
gap: 0.5rem
.input-group
flex-direction: column
.result-item
padding: 1rem