diff --git a/dashboard/package.json b/dashboard/package.json
index 55fd69c..378db80 100644
--- a/dashboard/package.json
+++ b/dashboard/package.json
@@ -19,7 +19,10 @@
},
"dependencies": {
"@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": {
"@electron-toolkit/eslint-config": "^2.0.0",
diff --git a/dashboard/pnpm-lock.yaml b/dashboard/pnpm-lock.yaml
index 544ef21..036c9f1 100644
--- a/dashboard/pnpm-lock.yaml
+++ b/dashboard/pnpm-lock.yaml
@@ -14,6 +14,15 @@ importers:
'@electron-toolkit/utils':
specifier: ^4.0.0
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:
'@electron-toolkit/eslint-config':
specifier: ^2.0.0
@@ -23,7 +32,7 @@ importers:
version: 3.0.0(eslint@9.31.0)(prettier@3.6.2)
'@vitejs/plugin-react':
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:
specifier: ^35.1.5
version: 35.7.2
@@ -32,7 +41,7 @@ importers:
version: 25.1.8(electron-builder-squirrel-windows@25.1.8)
electron-vite:
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:
specifier: ^9.24.0
version: 9.31.0
@@ -56,7 +65,7 @@ importers:
version: 19.1.0(react@19.1.0)
vite:
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:
@@ -467,6 +476,88 @@ packages:
engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0}
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':
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
@@ -823,6 +914,10 @@ packages:
brace-expansion@2.0.2:
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:
resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==}
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -883,6 +978,10 @@ packages:
resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
engines: {node: '>=10'}
+ chokidar@4.0.3:
+ resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+ engines: {node: '>= 14.16.0'}
+
chownr@2.0.0:
resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
engines: {node: '>=10'}
@@ -960,6 +1059,10 @@ packages:
convert-source-map@2.0.0:
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:
resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==}
@@ -1032,6 +1135,11 @@ packages:
delegates@1.0.0:
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:
resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==}
engines: {node: '>=8'}
@@ -1293,6 +1401,10 @@ packages:
filelist@1.0.4:
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:
resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
engines: {node: '>=10'}
@@ -1506,6 +1618,9 @@ packages:
resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
engines: {node: '>= 4'}
+ immutable@5.1.3:
+ resolution: {integrity: sha512-+chQdDfvscSF1SJqv2gn4SRO2ZyS3xL3r7IW/wWEEzrzLisnOlKiQu5ytC/BVNcS15C39WT2Hg/bjKjDMcu+zg==}
+
import-fresh@3.3.1:
resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
engines: {node: '>=6'}
@@ -1611,6 +1726,10 @@ packages:
resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
engines: {node: '>= 0.4'}
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
is-regex@1.2.1:
resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
engines: {node: '>= 0.4'}
@@ -1802,6 +1921,10 @@ packages:
resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
engines: {node: '>= 0.4'}
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
mime-db@1.52.0:
resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
engines: {node: '>= 0.6'}
@@ -1908,6 +2031,9 @@ packages:
node-addon-api@1.7.2:
resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==}
+ node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
node-api-version@0.2.1:
resolution: {integrity: sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==}
@@ -2036,6 +2162,10 @@ packages:
picocolors@1.1.1:
resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+ picomatch@2.3.1:
+ resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
+ engines: {node: '>=8.6'}
+
picomatch@4.0.3:
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
engines: {node: '>=12'}
@@ -2103,6 +2233,11 @@ packages:
peerDependencies:
react: ^19.1.0
+ react-icons@5.5.0:
+ resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==}
+ peerDependencies:
+ react: '*'
+
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
@@ -2110,6 +2245,23 @@ packages:
resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==}
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:
resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
engines: {node: '>=0.10.0'}
@@ -2128,6 +2280,10 @@ packages:
readdir-glob@1.1.3:
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:
resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
engines: {node: '>= 0.4'}
@@ -2204,6 +2360,11 @@ packages:
sanitize-filename@1.6.3:
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:
resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
@@ -2229,6 +2390,9 @@ packages:
set-blocking@2.0.0:
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:
resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
engines: {node: '>= 0.4'}
@@ -2401,6 +2565,10 @@ packages:
resolution: {integrity: sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==}
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:
resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==}
@@ -3007,6 +3175,67 @@ snapshots:
mkdirp: 1.0.4
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':
optional: true
@@ -3156,7 +3385,7 @@ snapshots:
'@types/node': 22.16.4
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:
'@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
'@types/babel__core': 7.20.5
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:
- supports-color
@@ -3408,6 +3637,11 @@ snapshots:
dependencies:
balanced-match: 1.0.2
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+ optional: true
+
browserslist@4.25.1:
dependencies:
caniuse-lite: 1.0.30001727
@@ -3515,6 +3749,10 @@ snapshots:
ansi-styles: 4.3.0
supports-color: 7.2.0
+ chokidar@4.0.3:
+ dependencies:
+ readdirp: 4.1.2
+
chownr@2.0.0: {}
chromium-pickle-js@0.2.0: {}
@@ -3581,6 +3819,8 @@ snapshots:
convert-source-map@2.0.0: {}
+ cookie@1.0.2: {}
+
core-util-is@1.0.2:
optional: true
@@ -3654,6 +3894,9 @@ snapshots:
delegates@1.0.0: {}
+ detect-libc@1.0.3:
+ optional: true
+
detect-libc@2.0.4: {}
detect-node@2.1.0:
@@ -3755,7 +3998,7 @@ snapshots:
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:
'@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
magic-string: 0.30.17
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:
- supports-color
@@ -4078,6 +4321,11 @@ snapshots:
dependencies:
minimatch: 5.1.6
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+ optional: true
+
find-up@5.0.0:
dependencies:
locate-path: 6.0.0
@@ -4348,6 +4596,8 @@ snapshots:
ignore@5.3.2: {}
+ immutable@5.1.3: {}
+
import-fresh@3.3.1:
dependencies:
parent-module: 1.0.1
@@ -4453,6 +4703,9 @@ snapshots:
call-bound: 1.0.4
has-tostringtag: 1.0.2
+ is-number@7.0.0:
+ optional: true
+
is-regex@1.2.1:
dependencies:
call-bound: 1.0.4
@@ -4653,6 +4906,12 @@ snapshots:
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-types@2.1.35:
@@ -4739,6 +4998,9 @@ snapshots:
node-addon-api@1.7.2:
optional: true
+ node-addon-api@7.1.1:
+ optional: true
+
node-api-version@0.2.1:
dependencies:
semver: 7.7.2
@@ -4887,6 +5149,9 @@ snapshots:
picocolors@1.1.1: {}
+ picomatch@2.3.1:
+ optional: true
+
picomatch@4.0.3: {}
plist@3.1.0:
@@ -4942,10 +5207,28 @@ snapshots:
react: 19.1.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-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: {}
read-binary-file-arch@1.0.6:
@@ -4974,6 +5257,8 @@ snapshots:
dependencies:
minimatch: 5.1.6
+ readdirp@4.1.2: {}
+
reflect.getprototypeof@1.0.10:
dependencies:
call-bind: 1.0.8
@@ -5090,6 +5375,14 @@ snapshots:
dependencies:
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: {}
scheduler@0.26.0: {}
@@ -5108,6 +5401,8 @@ snapshots:
set-blocking@2.0.0: {}
+ set-cookie-parser@2.7.1: {}
+
set-function-length@1.2.2:
dependencies:
define-data-property: 1.1.4
@@ -5339,6 +5634,11 @@ snapshots:
tmp@0.2.3: {}
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+ optional: true
+
truncate-utf8-bytes@1.0.2:
dependencies:
utf8-byte-length: 1.0.5
@@ -5429,7 +5729,7 @@ snapshots:
extsprintf: 1.4.1
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:
esbuild: 0.25.6
fdir: 6.4.6(picomatch@4.0.3)
@@ -5440,6 +5740,7 @@ snapshots:
optionalDependencies:
'@types/node': 24.0.14
fsevents: 2.3.3
+ sass: 1.89.2
wcwidth@1.0.1:
dependencies:
diff --git a/dashboard/src/renderer/index.html b/dashboard/src/renderer/index.html
index 0022e5e..d9a8c51 100644
--- a/dashboard/src/renderer/index.html
+++ b/dashboard/src/renderer/index.html
@@ -5,7 +5,7 @@
OpenWall
diff --git a/dashboard/src/renderer/src/App.jsx b/dashboard/src/renderer/src/App.jsx
index 8ff24e0..d171581 100644
--- a/dashboard/src/renderer/src/App.jsx
+++ b/dashboard/src/renderer/src/App.jsx
@@ -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 = () => {
return (
- <>
- Not implemented yet
- >
+
+
+
+
+
+ } />
+ } />
+ } />
+ } />
+
+
+
+
)
}
-export default App;
\ No newline at end of file
+export default App
\ No newline at end of file
diff --git a/dashboard/src/renderer/src/common/Header/Header.jsx b/dashboard/src/renderer/src/common/Header/Header.jsx
new file mode 100644
index 0000000..38252de
--- /dev/null
+++ b/dashboard/src/renderer/src/common/Header/Header.jsx
@@ -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 (
+
+
+
+ )
+}
+
+export default Header
\ No newline at end of file
diff --git a/dashboard/src/renderer/src/common/Header/index.js b/dashboard/src/renderer/src/common/Header/index.js
new file mode 100644
index 0000000..a99a897
--- /dev/null
+++ b/dashboard/src/renderer/src/common/Header/index.js
@@ -0,0 +1 @@
+export { default } from './Header'
\ No newline at end of file
diff --git a/dashboard/src/renderer/src/common/Header/styles.sass b/dashboard/src/renderer/src/common/Header/styles.sass
new file mode 100644
index 0000000..1fab696
--- /dev/null
+++ b/dashboard/src/renderer/src/common/Header/styles.sass
@@ -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
\ No newline at end of file
diff --git a/dashboard/src/renderer/src/index.sass b/dashboard/src/renderer/src/index.sass
new file mode 100644
index 0000000..e4c5e6f
--- /dev/null
+++ b/dashboard/src/renderer/src/index.sass
@@ -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
\ No newline at end of file
diff --git a/dashboard/src/renderer/src/main.jsx b/dashboard/src/renderer/src/main.jsx
index 4ac475c..e290eef 100644
--- a/dashboard/src/renderer/src/main.jsx
+++ b/dashboard/src/renderer/src/main.jsx
@@ -1,5 +1,6 @@
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
+import './index.sass'
import App from './App'
createRoot(document.getElementById('root')).render(
diff --git a/dashboard/src/renderer/src/pages/Calendar.jsx b/dashboard/src/renderer/src/pages/Calendar.jsx
new file mode 100644
index 0000000..33269bb
--- /dev/null
+++ b/dashboard/src/renderer/src/pages/Calendar.jsx
@@ -0,0 +1,9 @@
+const Calendar = () => {
+ return (
+
+
Kalender
+
+ )
+}
+
+export default Calendar
diff --git a/dashboard/src/renderer/src/pages/Notes.jsx b/dashboard/src/renderer/src/pages/Notes.jsx
new file mode 100644
index 0000000..4fe36d1
--- /dev/null
+++ b/dashboard/src/renderer/src/pages/Notes.jsx
@@ -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 (
+
+
+ {/* Toolbar */}
+
+
+
+
+ {showColorPicker && (
+
+
+ {colors.map((color) => (
+
+
+ )}
+
+
+
+
+
+
+
+ {/* Canvas Container */}
+
+
+
+
+
+ )
+}
+
+export default Notes
diff --git a/dashboard/src/renderer/src/pages/Notes.sass b/dashboard/src/renderer/src/pages/Notes.sass
new file mode 100644
index 0000000..776a910
--- /dev/null
+++ b/dashboard/src/renderer/src/pages/Notes.sass
@@ -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,') 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)
diff --git a/dashboard/src/renderer/src/pages/Search.jsx b/dashboard/src/renderer/src/pages/Search.jsx
new file mode 100644
index 0000000..997d1c7
--- /dev/null
+++ b/dashboard/src/renderer/src/pages/Search.jsx
@@ -0,0 +1,20 @@
+import './Search.sass'
+
+const Search = () => {
+ return (
+
+ )
+}
+
+export default Search
diff --git a/dashboard/src/renderer/src/pages/Search.sass b/dashboard/src/renderer/src/pages/Search.sass
new file mode 100644
index 0000000..573a373
--- /dev/null
+++ b/dashboard/src/renderer/src/pages/Search.sass
@@ -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
diff --git a/dashboard/src/renderer/src/pages/Shopping.jsx b/dashboard/src/renderer/src/pages/Shopping.jsx
new file mode 100644
index 0000000..debc603
--- /dev/null
+++ b/dashboard/src/renderer/src/pages/Shopping.jsx
@@ -0,0 +1,9 @@
+const Shopping = () => {
+ return (
+
+
Einkaufen
+
+ )
+}
+
+export default Shopping
diff --git a/dashboard/src/renderer/src/pages/pages.sass b/dashboard/src/renderer/src/pages/pages.sass
new file mode 100644
index 0000000..47550ce
--- /dev/null
+++ b/dashboard/src/renderer/src/pages/pages.sass
@@ -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