diff --git a/package-lock.json b/package-lock.json index 2e6f93b..fffcf8b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,8 +14,8 @@ "@emotion/styled": "^11.11.0", "@google-cloud/storage": "^7.14.0", "@heroicons/react": "^1.0.6", - "@mui/icons-material": "^5.15.12", - "@mui/material": "^5.15.12", + "@mui/icons-material": "^5.17.1", + "@mui/material": "^5.17.1", "@prisma/client": "^5.10.2", "@t3-oss/env-nextjs": "^0.9.2", "@tanstack/react-query": "^4.36.1", @@ -27,14 +27,17 @@ "cheerio": "^1.0.0-rc.12", "html2pdf.js": "^0.10.1", "multer": "^1.4.5-lts.1", - "next": "^14.1.0", + "next": "^15.3.1", "next-auth": "^4.24.6", "next-connect": "^1.0.0", "puppeteer": "^22.8.0", - "react": "18.2.0", - "react-dom": "18.2.0", - "styled-components": "^6.1.8", + "quill": "^2.0.3", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-quill": "^2.0.0", + "styled-components": "^6.1.16", "superjson": "^2.2.1", + "typesense": "^2.0.3", "zod": "^3.22.4" }, "devDependencies": { @@ -667,6 +670,15 @@ "@jridgewell/sourcemap-codec": "^1.4.10" } }, + "node_modules/@emnapi/runtime": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emotion/babel-plugin": { "version": "11.11.0", "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", @@ -686,17 +698,27 @@ } }, "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, + "node_modules/@emotion/cache/node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==" + }, + "node_modules/@emotion/cache/node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==" + }, "node_modules/@emotion/hash": { "version": "0.9.1", "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", @@ -751,9 +773,9 @@ } }, "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==" }, "node_modules/@emotion/styled": { "version": "11.11.0", @@ -791,9 +813,9 @@ } }, "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==" }, "node_modules/@emotion/weak-memoize": { "version": "0.3.1", @@ -1246,40 +1268,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@floating-ui/core": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.0.tgz", - "integrity": "sha512-PcF++MykgmTj3CIyOQbKA/hDzOAiqI3mhuoN44WRCopIs1sgoDoU4oty4Jtqaj/y3oDU6fnVSm4QG0a3t5i0+g==", - "dependencies": { - "@floating-ui/utils": "^0.2.1" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.3.tgz", - "integrity": "sha512-RnDthu3mzPlQ31Ss/BTwQ1zjzIhr3lk1gZB1OC56h/1vEtaXkESrOqL5fQVMfXpwGtRwX+YsZBdyHtJMQnkArw==", - "dependencies": { - "@floating-ui/core": "^1.0.0", - "@floating-ui/utils": "^0.2.0" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.8.tgz", - "integrity": "sha512-HOdqOt3R3OGeTKidaLvJKcgg75S6tibQ3Tif4eyd91QnIJWr0NLvoXFpJA/j8HqkFSL68GDca9AuyWEHlhyClw==", - "dependencies": { - "@floating-ui/dom": "^1.6.1" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.1.tgz", - "integrity": "sha512-9TANp6GPoMtYzQdt54kfAyMmz1+osLlXdg2ENroU7zzrtflTLrrC/lgrIfaSe+Wu0b89GKccT7vxXA0MoAIO+Q==" - }, "node_modules/@google-cloud/paginator": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/@google-cloud/paginator/-/paginator-5.0.2.tgz", @@ -1396,6 +1384,363 @@ "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==", "dev": true }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz", + "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz", + "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", + "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", + "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", + "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", + "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", + "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "cpu": [ + "ppc64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", + "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", + "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", + "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", + "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz", + "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==", + "cpu": [ + "arm" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz", + "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz", + "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==", + "cpu": [ + "s390x" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz", + "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz", + "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz", + "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz", + "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", + "cpu": [ + "wasm32" + ], + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.4.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz", + "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz", + "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1956,50 +2301,19 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mui/base": { - "version": "5.0.0-beta.38", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.38.tgz", - "integrity": "sha512-AsjD6Y1X5A1qndxz8xCcR8LDqv31aiwlgWMPxFAX/kCKiIGKlK65yMeVZ62iQr/6LBz+9hSKLiD1i4TZdAHKcQ==", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@floating-ui/react-dom": "^2.0.8", - "@mui/types": "^7.2.13", - "@mui/utils": "^5.15.12", - "@popperjs/core": "^2.11.8", - "clsx": "^2.1.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui-org" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.15.12", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.15.12.tgz", - "integrity": "sha512-brRO+tMFLpGyjEYHrX97bzqeF6jZmKpqqe1rY0LyIHAwP6xRVzh++zSecOQorDOCaZJg4XkGT9xfD+RWOWxZBA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.17.1.tgz", + "integrity": "sha512-OcZj+cs6EfUD39IoPBOgN61zf1XFVY+imsGoBDwXeSq2UHJZE3N59zzBOVjclck91Ne3e9gudONOeILvHCIhUA==", "funding": { "type": "opencollective", "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "5.15.12", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.15.12.tgz", - "integrity": "sha512-3BXiDlOd3AexZoEXa/VqpIpVIvosCzjLHsdMWzKMXbZdnBiJjmb9ECdqfjn5SpTClO49qvkKLhkTqdBH3fSFGw==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.17.1.tgz", + "integrity": "sha512-CN86LocjkunFGG0yPlO4bgqHkNGgaEOEc3X/jG5Bzm401qYw79/SaLrofA7yAKCCXAGdIGnLoMHohc3+ubs95A==", "dependencies": { "@babel/runtime": "^7.23.9" }, @@ -2012,8 +2326,8 @@ }, "peerDependencies": { "@mui/material": "^5.0.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -2022,21 +2336,21 @@ } }, "node_modules/@mui/material": { - "version": "5.15.12", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.15.12.tgz", - "integrity": "sha512-vXJGg6KNKucsvbW6l7w9zafnpOp0CWc0Wx4mDykuABTpQ5QQBnZxP7+oB4yAS1hDZQ1WobbeIl0CjxK4EEahkA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.17.1.tgz", + "integrity": "sha512-2B33kQf+GmPnrvXXweWAx+crbiUEsxCdCN979QDYnlH9ox4pd+0/IBriWLV+l6ORoBF60w39cWjFnJYGFdzXcw==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/base": "5.0.0-beta.38", - "@mui/core-downloads-tracker": "^5.15.12", - "@mui/system": "^5.15.12", - "@mui/types": "^7.2.13", - "@mui/utils": "^5.15.12", + "@mui/core-downloads-tracker": "^5.17.1", + "@mui/system": "^5.17.1", + "@mui/types": "~7.2.15", + "@mui/utils": "^5.17.1", + "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.10", "clsx": "^2.1.0", "csstype": "^3.1.3", "prop-types": "^15.8.1", - "react-is": "^18.2.0", + "react-is": "^19.0.0", "react-transition-group": "^4.4.5" }, "engines": { @@ -2049,9 +2363,9 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -2066,17 +2380,17 @@ } }, "node_modules/@mui/material/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==" }, "node_modules/@mui/private-theming": { - "version": "5.15.12", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.15.12.tgz", - "integrity": "sha512-cqoSo9sgA5HE+8vZClbLrq9EkyOnYysooepi5eKaKvJ41lReT2c5wOZAeDDM1+xknrMDos+0mT2zr3sZmUiRRA==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.17.1.tgz", + "integrity": "sha512-XMxU0NTYcKqdsG8LRmSoxERPXwMbp16sIXPcLVgLGII/bVNagX0xaheWAwFv8+zDK7tI3ajllkuD3GZZE++ICQ==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/utils": "^5.15.12", + "@mui/utils": "^5.17.1", "prop-types": "^15.8.1" }, "engines": { @@ -2087,8 +2401,8 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -2097,12 +2411,12 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.15.11", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.15.11.tgz", - "integrity": "sha512-So21AhAngqo07ces4S/JpX5UaMU2RHXpEA6hNzI6IQjd/1usMPxpgK8wkGgTe3JKmC2KDmH8cvoycq5H3Ii7/w==", + "version": "5.16.14", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.16.14.tgz", + "integrity": "sha512-UAiMPZABZ7p8mUW4akDV6O7N3+4DatStpXMZwPlt+H/dA0lt67qawN021MNND+4QTpjaiMYxbhKZeQcyWCbuKw==", "dependencies": { "@babel/runtime": "^7.23.9", - "@emotion/cache": "^11.11.0", + "@emotion/cache": "^11.13.5", "csstype": "^3.1.3", "prop-types": "^15.8.1" }, @@ -2116,7 +2430,7 @@ "peerDependencies": { "@emotion/react": "^11.4.1", "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -2128,15 +2442,15 @@ } }, "node_modules/@mui/system": { - "version": "5.15.12", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.15.12.tgz", - "integrity": "sha512-/pq+GO6yN3X7r3hAwFTrzkAh7K1bTF5r8IzS79B9eyKJg7v6B/t4/zZYMR6OT9qEPtwf6rYN2Utg1e6Z7F1OgQ==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.17.1.tgz", + "integrity": "sha512-aJrmGfQpyF0U4D4xYwA6ueVtQcEMebET43CUmKMP7e7iFh3sMIF3sBR0l8Urb4pqx1CBjHAaWgB0ojpND4Q3Jg==", "dependencies": { "@babel/runtime": "^7.23.9", - "@mui/private-theming": "^5.15.12", - "@mui/styled-engine": "^5.15.11", - "@mui/types": "^7.2.13", - "@mui/utils": "^5.15.12", + "@mui/private-theming": "^5.17.1", + "@mui/styled-engine": "^5.16.14", + "@mui/types": "~7.2.15", + "@mui/utils": "^5.17.1", "clsx": "^2.1.0", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -2151,8 +2465,8 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -2167,11 +2481,11 @@ } }, "node_modules/@mui/types": { - "version": "7.2.13", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.13.tgz", - "integrity": "sha512-qP9OgacN62s+l8rdDhSFRe05HWtLLJ5TGclC9I1+tQngbssu0m2dmFZs+Px53AcOs9fD7TbYd4gc9AXzVqO/+g==", + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -2180,14 +2494,16 @@ } }, "node_modules/@mui/utils": { - "version": "5.15.12", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.15.12.tgz", - "integrity": "sha512-8SDGCnO2DY9Yy+5bGzu00NZowSDtuyHP4H8gunhHGQoIlhlY2Z3w64wBzAOLpYw/ZhJNzksDTnS/i8qdJvxuow==", + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.17.1.tgz", + "integrity": "sha512-jEZ8FTqInt2WzxDV8bhImWBqeQRD99c/id/fq83H0ER9tFl+sfZlaAoCdznGvbSQQ9ividMxqSV2c7cC1vBcQg==", "dependencies": { "@babel/runtime": "^7.23.9", - "@types/prop-types": "^15.7.11", + "@mui/types": "~7.2.15", + "@types/prop-types": "^15.7.12", + "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "react-is": "^19.0.0" }, "engines": { "node": ">=12.0.0" @@ -2197,8 +2513,8 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -2207,14 +2523,14 @@ } }, "node_modules/@mui/utils/node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==" }, "node_modules/@next/env": { - "version": "14.1.3", - "resolved": "https://registry.npmjs.org/@next/env/-/env-14.1.3.tgz", - "integrity": "sha512-VhgXTvrgeBRxNPjyfBsDIMvgsKDxjlpw4IAUsHCX8Gjl1vtHUYRT3+xfQ/wwvLPDd/6kqfLqk9Pt4+7gysuCKQ==" + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.1.tgz", + "integrity": "sha512-cwK27QdzrMblHSn9DZRV+DQscHXRuJv6MydlJRpFSqJWZrTYMLzKDeyueJNN9MGd8NNiUKzDQADAf+dMLXX7YQ==" }, "node_modules/@next/eslint-plugin-next": { "version": "14.1.3", @@ -2226,9 +2542,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "14.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-14.1.3.tgz", - "integrity": "sha512-LALu0yIBPRiG9ANrD5ncB3pjpO0Gli9ZLhxdOu6ZUNf3x1r3ea1rd9Q+4xxUkGrUXLqKVK9/lDkpYIJaCJ6AHQ==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.1.tgz", + "integrity": "sha512-hjDw4f4/nla+6wysBL07z52Gs55Gttp5Bsk5/8AncQLJoisvTBP0pRIBK/B16/KqQyH+uN4Ww8KkcAqJODYH3w==", "cpu": [ "arm64" ], @@ -2241,9 +2557,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "14.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-14.1.3.tgz", - "integrity": "sha512-E/9WQeXxkqw2dfcn5UcjApFgUq73jqNKaE5bysDm58hEUdUGedVrnRhblhJM7HbCZNhtVl0j+6TXsK0PuzXTCg==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.1.tgz", + "integrity": "sha512-q+aw+cJ2ooVYdCEqZVk+T4Ni10jF6Fo5DfpEV51OupMaV5XL6pf3GCzrk6kSSZBsMKZtVC1Zm/xaNBFpA6bJ2g==", "cpu": [ "x64" ], @@ -2256,9 +2572,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "14.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-14.1.3.tgz", - "integrity": "sha512-USArX9B+3rZSXYLFvgy0NVWQgqh6LHWDmMt38O4lmiJNQcwazeI6xRvSsliDLKt+78KChVacNiwvOMbl6g6BBw==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.1.tgz", + "integrity": "sha512-wBQ+jGUI3N0QZyWmmvRHjXjTWFy8o+zPFLSOyAyGFI94oJi+kK/LIZFJXeykvgXUk1NLDAEFDZw/NVINhdk9FQ==", "cpu": [ "arm64" ], @@ -2271,9 +2587,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "14.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-14.1.3.tgz", - "integrity": "sha512-esk1RkRBLSIEp1qaQXv1+s6ZdYzuVCnDAZySpa62iFTMGTisCyNQmqyCTL9P+cLJ4N9FKCI3ojtSfsyPHJDQNw==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.1.tgz", + "integrity": "sha512-IIxXEXRti/AulO9lWRHiCpUUR8AR/ZYLPALgiIg/9ENzMzLn3l0NSxVdva7R/VDcuSEBo0eGVCe3evSIHNz0Hg==", "cpu": [ "arm64" ], @@ -2286,9 +2602,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "14.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-14.1.3.tgz", - "integrity": "sha512-8uOgRlYEYiKo0L8YGeS+3TudHVDWDjPVDUcST+z+dUzgBbTEwSSIaSgF/vkcC1T/iwl4QX9iuUyUdQEl0Kxalg==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.1.tgz", + "integrity": "sha512-bfI4AMhySJbyXQIKH5rmLJ5/BP7bPwuxauTvVEiJ/ADoddaA9fgyNNCcsbu9SlqfHDoZmfI6g2EjzLwbsVTr5A==", "cpu": [ "x64" ], @@ -2301,9 +2617,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "14.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-14.1.3.tgz", - "integrity": "sha512-DX2zqz05ziElLoxskgHasaJBREC5Y9TJcbR2LYqu4r7naff25B4iXkfXWfcp69uD75/0URmmoSgT8JclJtrBoQ==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.1.tgz", + "integrity": "sha512-FeAbR7FYMWR+Z+M5iSGytVryKHiAsc0x3Nc3J+FD5NVbD5Mqz7fTSy8CYliXinn7T26nDMbpExRUI/4ekTvoiA==", "cpu": [ "x64" ], @@ -2316,9 +2632,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "14.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-14.1.3.tgz", - "integrity": "sha512-HjssFsCdsD4GHstXSQxsi2l70F/5FsRTRQp8xNgmQs15SxUfUJRvSI9qKny/jLkY3gLgiCR3+6A7wzzK0DBlfA==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.1.tgz", + "integrity": "sha512-yP7FueWjphQEPpJQ2oKmshk/ppOt+0/bB8JC8svPUZNy0Pi3KbPx2Llkzv1p8CoQa+D2wknINlJpHf3vtChVBw==", "cpu": [ "arm64" ], @@ -2330,25 +2646,10 @@ "node": ">= 10" } }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "14.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-14.1.3.tgz", - "integrity": "sha512-DRuxD5axfDM1/Ue4VahwSxl1O5rn61hX8/sF0HY8y0iCbpqdxw3rB3QasdHn/LJ6Wb2y5DoWzXcz3L1Cr+Thrw==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "14.1.3", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-14.1.3.tgz", - "integrity": "sha512-uC2DaDoWH7h1P/aJ4Fok3Xiw6P0Lo4ez7NbowW2VGNXw/Xv6tOuLUcxhBYZxsSUJtpeknCi8/fvnSpyCFp4Rcg==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.1.tgz", + "integrity": "sha512-3PMvF2zRJAifcRNni9uMk/gulWfWS+qVI/pagd+4yLF5bcXPZPPH2xlYRYOsUjmCJOXSTAC2PjRzbhsRzR2fDQ==", "cpu": [ "x64" ], @@ -2535,14 +2836,24 @@ "@sinonjs/commons": "^3.0.0" } }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==" + }, "node_modules/@swc/helpers": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", - "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", "dependencies": { - "tslib": "^2.4.0" + "tslib": "^2.8.0" } }, + "node_modules/@swc/helpers/node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==" + }, "node_modules/@t3-oss/env-core": { "version": "0.9.2", "resolved": "https://registry.npmjs.org/@t3-oss/env-core/-/env-core-0.9.2.tgz", @@ -2822,9 +3133,17 @@ "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==" }, "node_modules/@types/prop-types": { - "version": "15.7.11", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.11.tgz", - "integrity": "sha512-ga8y9v9uyeiLdpKddhxYQkxNDrfvuPrlFb0N1qnZZByvcElJaXthF1UhvCh9TLWJBEHeNtdnbysW7Y6Uq8CVng==" + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==" + }, + "node_modules/@types/quill": { + "version": "1.3.10", + "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz", + "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==", + "dependencies": { + "parchment": "^1.1.2" + } }, "node_modules/@types/raf": { "version": "3.4.3", @@ -2921,9 +3240,9 @@ "dev": true }, "node_modules/@types/stylis": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-n4sx2bqL0mW1tvDf/loQ+aMX7GQD3lc3fkCMC55VFNDu/vBOabO+LTIeXKM14xK0ppk5TUGcWRjiSpIlUpghKw==" + "version": "4.2.5", + "resolved": "https://registry.npmjs.org/@types/stylis/-/stylis-4.2.5.tgz", + "integrity": "sha512-1Xve+NMN7FWjY14vLoY5tL3BVEQ/n42YLwaqJIPYhotZ9uBHt87VceMwWQpzmdEt2TNXIorIFG+YeCUUW7RInw==" }, "node_modules/@types/tough-cookie": { "version": "4.0.5", @@ -3578,9 +3897,9 @@ } }, "node_modules/axios": { - "version": "1.6.8", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.8.tgz", - "integrity": "sha512-v/ZHtJDU39mDpyBoFVkETcd/uNdxrWRrg3bKpOKzXFA6Bvqopts6ALSMU3y6ijYxbw2B+wPrIv46egTzJXCLGQ==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.0", @@ -3953,7 +4272,6 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -3968,6 +4286,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.1.tgz", + "integrity": "sha512-BhYE+WDaywFg2TBWYNXAE+8B1ATnThNBqXHP5nQu0jWJdVvY2hvkpyB3qOmtmDePiS5/BDQ8wASEWGMWRG148g==", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.3.tgz", + "integrity": "sha512-YTd+6wGlNlPxSuri7Y6X8tY2dmm12UMH66RpKMhiX6rsk5wXXnYgbUcOt8kiS31/AjfoTOvCsE+w8nZQLQnzHA==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -4229,10 +4574,18 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "engines": { + "node": ">=0.8" + } + }, "node_modules/clsx": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.0.tgz", - "integrity": "sha512-m3iNNWpd9rl3jvvcBnu70ylMdrXt8Vlq4HYadnU5fwcOtvkSQWPmj7amUcDT2qYI7risszBjI5AUIUox9D16pg==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", "engines": { "node": ">=6" } @@ -4253,6 +4606,19 @@ "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -4269,6 +4635,16 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4529,6 +4905,25 @@ } } }, + "node_modules/deep-equal": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.2.tgz", + "integrity": "sha512-5tdhKF6DbU7iIzrIOa1AOUt39ZRm13cmL1cGEh//aqR8x9+tNfbywRf0n5FD/18OKMdo7DNEtrX2t22ZAkI+eg==", + "dependencies": { + "is-arguments": "^1.1.1", + "is-date-object": "^1.0.5", + "is-regex": "^1.1.4", + "object-is": "^1.1.5", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.5.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -4548,7 +4943,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", @@ -4565,7 +4959,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", - "dev": true, "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", @@ -4608,6 +5001,15 @@ "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", @@ -4742,6 +5144,19 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexify": { "version": "4.1.3", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.3.tgz", @@ -4914,13 +5329,9 @@ "dev": true }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "engines": { "node": ">= 0.4" } @@ -4929,7 +5340,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -4960,6 +5370,17 @@ "node": ">= 0.4" } }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-set-tostringtag": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz", @@ -5712,6 +6133,11 @@ "node": ">=6" } }, + "node_modules/eventemitter3": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz", + "integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==" + }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", @@ -5808,6 +6234,11 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "dev": true }, + "node_modules/fast-diff": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.3.0.tgz", + "integrity": "sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==" + }, "node_modules/fast-fifo": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", @@ -6090,7 +6521,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", - "dev": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6152,16 +6582,20 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", - "dev": true, + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.7.tgz", + "integrity": "sha512-VW6Pxhsrk0KAOqs3WEd0klDiF/+V7gQOpAvY1jVU/LHmaD/kQO4523aiJuikX/QAKYiW6x8Jh+RJej1almdtCA==", "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.0", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -6179,6 +6613,18 @@ "node": ">=8.0.0" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -6337,12 +6783,11 @@ } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", - "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -6393,7 +6838,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, "dependencies": { "es-define-property": "^1.0.0" }, @@ -6414,10 +6858,9 @@ } }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "engines": { "node": ">= 0.4" }, @@ -6429,7 +6872,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -6441,9 +6883,9 @@ } }, "node_modules/hasown": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.1.tgz", - "integrity": "sha512-1/th4MHjnwncwXsIW6QMzlvYL9kG5e/CpVvLRZe4XPa8TOUNbCELqmvhDmnkNsAjwaG4+I8gJJL0JBvTTLO9qA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dependencies": { "function-bind": "^1.1.2" }, @@ -6665,6 +7107,21 @@ "node": ">= 12" } }, + "node_modules/is-arguments": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.2.0.tgz", + "integrity": "sha512-7bVbi0huj/wrIAOzb8U1aszg9kdi3KN/CyU19CTI7tAoZYEZoL9yCDXpbXN+uPsuWnP02cyug1gleqq+TU+YCA==", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-array-buffer": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.4.tgz", @@ -6768,7 +7225,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", - "dev": true, "dependencies": { "has-tostringtag": "^1.0.0" }, @@ -6905,7 +7361,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", - "dev": true, "dependencies": { "call-bind": "^1.0.2", "has-tostringtag": "^1.0.0" @@ -8322,12 +8777,44 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash-es": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", + "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==" + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true }, + "node_modules/loglevel": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/loglevel/-/loglevel-1.9.2.tgz", + "integrity": "sha512-HgMmCqIJSAKqo68l0rS2AanEWfkxaZ5wNiEFb5ggm08lDs9Xl2KxBlX3PTcaD2chBM1gXAYf491/M2Rv8Jwayg==", + "engines": { + "node": ">= 0.6.0" + }, + "funding": { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/loglevel" + } + }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", @@ -8380,6 +8867,14 @@ "tmpl": "1.0.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/media-typer": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", @@ -8568,45 +9063,53 @@ } }, "node_modules/next": { - "version": "14.1.3", - "resolved": "https://registry.npmjs.org/next/-/next-14.1.3.tgz", - "integrity": "sha512-oexgMV2MapI0UIWiXKkixF8J8ORxpy64OuJ/J9oVUmIthXOUCcuVEZX+dtpgq7wIfIqtBwQsKEDXejcjTsan9g==", + "version": "15.3.1", + "resolved": "https://registry.npmjs.org/next/-/next-15.3.1.tgz", + "integrity": "sha512-8+dDV0xNLOgHlyBxP1GwHGVaNXsmp+2NhZEYrXr24GWLHtt27YrBPbPuHvzlhi7kZNYjeJNR93IF5zfFu5UL0g==", "dependencies": { - "@next/env": "14.1.3", - "@swc/helpers": "0.5.2", + "@next/env": "15.3.1", + "@swc/counter": "0.1.3", + "@swc/helpers": "0.5.15", "busboy": "1.6.0", "caniuse-lite": "^1.0.30001579", - "graceful-fs": "^4.2.11", "postcss": "8.4.31", - "styled-jsx": "5.1.1" + "styled-jsx": "5.1.6" }, "bin": { "next": "dist/bin/next" }, "engines": { - "node": ">=18.17.0" + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "14.1.3", - "@next/swc-darwin-x64": "14.1.3", - "@next/swc-linux-arm64-gnu": "14.1.3", - "@next/swc-linux-arm64-musl": "14.1.3", - "@next/swc-linux-x64-gnu": "14.1.3", - "@next/swc-linux-x64-musl": "14.1.3", - "@next/swc-win32-arm64-msvc": "14.1.3", - "@next/swc-win32-ia32-msvc": "14.1.3", - "@next/swc-win32-x64-msvc": "14.1.3" + "@next/swc-darwin-arm64": "15.3.1", + "@next/swc-darwin-x64": "15.3.1", + "@next/swc-linux-arm64-gnu": "15.3.1", + "@next/swc-linux-arm64-musl": "15.3.1", + "@next/swc-linux-x64-gnu": "15.3.1", + "@next/swc-linux-x64-musl": "15.3.1", + "@next/swc-win32-arm64-msvc": "15.3.1", + "@next/swc-win32-x64-msvc": "15.3.1", + "sharp": "^0.34.1" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", - "react": "^18.2.0", - "react-dom": "^18.2.0", + "@playwright/test": "^1.41.2", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "peerDependenciesMeta": { "@opentelemetry/api": { "optional": true }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, "sass": { "optional": true } @@ -8795,11 +9298,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object-is": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.6.tgz", + "integrity": "sha512-F8cZ+KfGlSGi09lJT7/Nd6KJZ9ygtvYC0/UYYLI9nmQKLMnydpB9yvbv9K1uSkEu7FU9vYPmVwLg328tX+ot3Q==", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/object-keys": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", - "dev": true, "engines": { "node": ">= 0.4" } @@ -9034,6 +9551,11 @@ "node": ">= 14" } }, + "node_modules/parchment": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz", + "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg==" + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -9270,10 +9792,9 @@ } }, "node_modules/postcss": { - "version": "8.4.35", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.35.tgz", - "integrity": "sha512-u5U8qYpBCpN13BsiEB0CbR1Hhh4Gc0zLFuedrHJKMctHCHAGrMdG0PRM/KErzAL3CU6/eckEtmHNB3x6e3c0vA==", - "dev": true, + "version": "8.4.49", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.49.tgz", + "integrity": "sha512-OCVPnIObs4N29kxTjzLfUryOkvZEq+pf8jTF0lg8E7uETuWHA+v7j3c/xJmiqpX450191LlmZfUKkXxkTry7nA==", "funding": [ { "type": "opencollective", @@ -9290,8 +9811,8 @@ ], "dependencies": { "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -9739,6 +10260,38 @@ "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==" }, + "node_modules/quill": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/quill/-/quill-2.0.3.tgz", + "integrity": "sha512-xEYQBqfYx/sfb33VJiKnSJp8ehloavImQ2A6564GAbqG55PGw1dAWUn1MUbQB62t0azawUS2CZZhWCjO8gRvTw==", + "dependencies": { + "eventemitter3": "^5.0.1", + "lodash-es": "^4.17.21", + "parchment": "^3.0.0", + "quill-delta": "^5.1.0" + }, + "engines": { + "npm": ">=8.2.3" + } + }, + "node_modules/quill-delta": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-5.1.0.tgz", + "integrity": "sha512-X74oCeRI4/p0ucjb5Ma8adTXd9Scumz367kkMK5V/IatcX6A0vlgLgKbzXWy5nZmCGeNJm2oQX0d2Eqj+ZIlCA==", + "dependencies": { + "fast-diff": "^1.3.0", + "lodash.clonedeep": "^4.5.0", + "lodash.isequal": "^4.5.0" + }, + "engines": { + "node": ">= 12.0.0" + } + }, + "node_modules/quill/node_modules/parchment": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/parchment/-/parchment-3.0.0.tgz", + "integrity": "sha512-HUrJFQ/StvgmXRcQ1ftY6VEZUq3jA2t9ncFN4F84J/vN0/FPpQF+8FKXb3l6fLces6q0uOHj6NJn+2xvZnxO6A==" + }, "node_modules/raf": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz", @@ -9749,26 +10302,22 @@ } }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", - "dependencies": { - "loose-envify": "^1.1.0" - }, + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^19.1.0" } }, "node_modules/react-is": { @@ -9776,6 +10325,56 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" }, + "node_modules/react-quill": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-2.0.0.tgz", + "integrity": "sha512-4qQtv1FtCfLgoD3PXAur5RyxuUbPXQGOHgTlFie3jtxp43mXDtzCKaOgQ3mLyZfi1PUlyjycfivKelFhy13QUg==", + "dependencies": { + "@types/quill": "^1.3.10", + "lodash": "^4.17.4", + "quill": "^1.3.7" + }, + "peerDependencies": { + "react": "^16 || ^17 || ^18", + "react-dom": "^16 || ^17 || ^18" + } + }, + "node_modules/react-quill/node_modules/eventemitter3": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz", + "integrity": "sha512-jLN68Dx5kyFHaePoXWPsCGW5qdyZQtLYHkxkg02/Mz6g0kYpDx4FyP6XfArhQdlOC4b8Mv+EMxPo/8La7Tzghg==" + }, + "node_modules/react-quill/node_modules/fast-diff": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz", + "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig==" + }, + "node_modules/react-quill/node_modules/quill": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz", + "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==", + "dependencies": { + "clone": "^2.1.1", + "deep-equal": "^1.0.1", + "eventemitter3": "^2.0.3", + "extend": "^3.0.2", + "parchment": "^1.1.4", + "quill-delta": "^3.6.2" + } + }, + "node_modules/react-quill/node_modules/quill-delta": { + "version": "3.6.3", + "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz", + "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==", + "dependencies": { + "deep-equal": "^1.0.1", + "extend": "^3.0.2", + "fast-diff": "1.1.2" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/react-transition-group": { "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", @@ -9861,7 +10460,6 @@ "version": "1.5.2", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz", "integrity": "sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==", - "dev": true, "dependencies": { "call-bind": "^1.0.6", "define-properties": "^1.2.1", @@ -10115,12 +10713,9 @@ } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", - "dependencies": { - "loose-envify": "^1.1.0" - } + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==" }, "node_modules/semver": { "version": "7.6.0", @@ -10140,7 +10735,6 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -10157,7 +10751,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", - "dev": true, "dependencies": { "define-data-property": "^1.1.4", "es-errors": "^1.3.0", @@ -10173,6 +10766,58 @@ "resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz", "integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ==" }, + "node_modules/sharp": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", + "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", + "hasInstallScript": true, + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.7.1" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.1", + "@img/sharp-darwin-x64": "0.34.1", + "@img/sharp-libvips-darwin-arm64": "1.1.0", + "@img/sharp-libvips-darwin-x64": "1.1.0", + "@img/sharp-libvips-linux-arm": "1.1.0", + "@img/sharp-libvips-linux-arm64": "1.1.0", + "@img/sharp-libvips-linux-ppc64": "1.1.0", + "@img/sharp-libvips-linux-s390x": "1.1.0", + "@img/sharp-libvips-linux-x64": "1.1.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", + "@img/sharp-libvips-linuxmusl-x64": "1.1.0", + "@img/sharp-linux-arm": "0.34.1", + "@img/sharp-linux-arm64": "0.34.1", + "@img/sharp-linux-s390x": "0.34.1", + "@img/sharp-linux-x64": "0.34.1", + "@img/sharp-linuxmusl-arm64": "0.34.1", + "@img/sharp-linuxmusl-x64": "0.34.1", + "@img/sharp-wasm32": "0.34.1", + "@img/sharp-win32-ia32": "0.34.1", + "@img/sharp-win32-x64": "0.34.1" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -10224,6 +10869,21 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "optional": true + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -10283,9 +10943,9 @@ } }, "node_modules/source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "engines": { "node": ">=0.10.0" } @@ -10593,19 +11253,19 @@ "integrity": "sha512-PdHt7hHUJKxvTCgbKX9C1V/ftOcjJQgz8BZwNfV5c4B6dcGqlpelTbJ999jBGZ2jYiPAwcX5dP6oBwVlBlUbxw==" }, "node_modules/styled-components": { - "version": "6.1.8", - "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.8.tgz", - "integrity": "sha512-PQ6Dn+QxlWyEGCKDS71NGsXoVLKfE1c3vApkvDYS5KAK+V8fNWGhbSUEo9Gg2iaID2tjLXegEW3bZDUGpofRWw==", + "version": "6.1.16", + "resolved": "https://registry.npmjs.org/styled-components/-/styled-components-6.1.16.tgz", + "integrity": "sha512-KpWB6ORAWGmbWM10cDJfEV6sXc/uVkkkQV3SLwTNQ/E/PqWgNHIoMSLh1Lnk2FkB9+JHK7uuMq1i+9ArxDD7iQ==", "dependencies": { - "@emotion/is-prop-valid": "1.2.1", - "@emotion/unitless": "0.8.0", - "@types/stylis": "4.2.0", + "@emotion/is-prop-valid": "1.2.2", + "@emotion/unitless": "0.8.1", + "@types/stylis": "4.2.5", "css-to-react-native": "3.2.0", - "csstype": "3.1.2", - "postcss": "8.4.31", + "csstype": "3.1.3", + "postcss": "8.4.49", "shallowequal": "1.1.0", - "stylis": "4.3.1", - "tslib": "2.5.0" + "stylis": "4.3.2", + "tslib": "2.6.2" }, "engines": { "node": ">= 16" @@ -10619,65 +11279,15 @@ "react-dom": ">= 16.8.0" } }, - "node_modules/styled-components/node_modules/@emotion/is-prop-valid": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", - "dependencies": { - "@emotion/memoize": "^0.8.1" - } - }, - "node_modules/styled-components/node_modules/@emotion/unitless": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz", - "integrity": "sha512-VINS5vEYAscRl2ZUDiT3uMPlrFQupiKgHz5AA4bCH1miKBg4qtwkim1qPmJj/4WG6TreYMY111rEFsjupcOKHw==" - }, - "node_modules/styled-components/node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" - }, - "node_modules/styled-components/node_modules/postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", - "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "dependencies": { - "nanoid": "^3.3.6", - "picocolors": "^1.0.0", - "source-map-js": "^1.0.2" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, "node_modules/styled-components/node_modules/stylis": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.1.tgz", - "integrity": "sha512-EQepAV+wMsIaGVGX1RECzgrcqRRU/0sYOHkeLsZ3fzHaHXZy4DaOOX0vOlGQdlsjkh3mFHAIlVimpwAs4dslyQ==" - }, - "node_modules/styled-components/node_modules/tslib": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", - "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.2.tgz", + "integrity": "sha512-bhtUjWd/z6ltJiQwg0dUfxEJ+W+jdqQd8TbWLWyeIJHlnsqmGLRFFd8e5mA0AZi/zx90smXRlN66YMTcaSFifg==" }, "node_modules/styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", "dependencies": { "client-only": "0.0.1" }, @@ -10685,7 +11295,7 @@ "node": ">= 12.0.0" }, "peerDependencies": { - "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0" + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" }, "peerDependenciesMeta": { "@babel/core": { @@ -11266,6 +11876,22 @@ "node": ">=14.17" } }, + "node_modules/typesense": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/typesense/-/typesense-2.0.3.tgz", + "integrity": "sha512-fRJjFdDNZn6qF9XzIk+bB8n8cm0fiAx1SGcpLDfNcsGtp8znITfG+SO+l/qk63GCRXZwJGq7wrMDLFUvblJSHA==", + "dependencies": { + "axios": "^1.7.2", + "loglevel": "^1.8.1", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -11348,11 +11974,11 @@ "integrity": "sha512-H/A06tKD7sS1O1X2SshBVeA5FLycRpjqiBeqGKmBwBDBy28EnRjORxTNe269KSSr5un5qyWi1iL61wLxpd+ZOg==" }, "node_modules/use-sync-external-store": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", - "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/util-deprecate": { diff --git a/package.json b/package.json index 952675f..c836e1d 100644 --- a/package.json +++ b/package.json @@ -22,8 +22,8 @@ "@emotion/styled": "^11.11.0", "@google-cloud/storage": "^7.14.0", "@heroicons/react": "^1.0.6", - "@mui/icons-material": "^5.15.12", - "@mui/material": "^5.15.12", + "@mui/icons-material": "^5.17.1", + "@mui/material": "^5.17.1", "@prisma/client": "^5.10.2", "@t3-oss/env-nextjs": "^0.9.2", "@tanstack/react-query": "^4.36.1", @@ -35,14 +35,17 @@ "cheerio": "^1.0.0-rc.12", "html2pdf.js": "^0.10.1", "multer": "^1.4.5-lts.1", - "next": "^14.1.0", + "next": "^15.3.1", "next-auth": "^4.24.6", "next-connect": "^1.0.0", "puppeteer": "^22.8.0", - "react": "18.2.0", - "react-dom": "18.2.0", - "styled-components": "^6.1.8", + "quill": "^2.0.3", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-quill": "^2.0.0", + "styled-components": "^6.1.16", "superjson": "^2.2.1", + "typesense": "^2.0.3", "zod": "^3.22.4" }, "devDependencies": { diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 89d2f6a..8bf64fc 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -42,6 +42,13 @@ model Session { user User @relation(fields: [userId], references: [id], onDelete: Cascade) } +model ChiAurora { + id String @id @default(cuid()) + crush String @unique + userId String + expires DateTime +} + model User { id String @id @default(cuid()) name String? diff --git a/prisma/seed.ts b/prisma/seed.ts index 98827c4..54924cf 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,4 +1,23 @@ import { PrismaClient } from '@prisma/client'; +const prisma = new PrismaClient(); +async function main() { + await prisma.ChiAurora.create({ + data: { + id: "1fjk3", + crush: "Alex", + userId: "aurora1", + expires: new Date(new Date().getTime() + 1000 * 60 * 60 * 24 * 7), + }, + }); +} + +main() + .catch(e => { + throw e + }) + .finally(async () => { + await prisma.$disconnect() + }) diff --git a/public/swarthmore-talk-logo.webp b/public/swarthmore-talk-logo.webp new file mode 100644 index 0000000..51ffea9 Binary files /dev/null and b/public/swarthmore-talk-logo.webp differ diff --git a/src/components/AdvancedSearch.tsx b/src/components/AdvancedSearch.tsx new file mode 100644 index 0000000..e924438 --- /dev/null +++ b/src/components/AdvancedSearch.tsx @@ -0,0 +1,681 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { useRouter } from 'next/router'; +import { + TextField, + InputAdornment, + IconButton, + List, + ListItem, + ListItemIcon, + ListItemText, + Box, + Typography, + Chip, + CircularProgress, + Divider, + Button, + Tooltip +} from '@mui/material'; +import SearchIcon from '@mui/icons-material/Search'; +import FolderIcon from '@mui/icons-material/Folder'; +import InsertDriveFileIcon from '@mui/icons-material/InsertDriveFile'; +import NoResultsIcon from '@mui/icons-material/SearchOff'; +import ClearIcon from '@mui/icons-material/Clear'; +import FilterListIcon from '@mui/icons-material/FilterList'; +import SchoolIcon from '@mui/icons-material/School'; +import FormatQuoteIcon from '@mui/icons-material/FormatQuote'; +import { api } from "~/utils/api"; + +// Types +interface HighlightItem { + field: string; + snippet: string; +} + +interface DocumentItem { + id: string; + course_id: string; + course_name: string; + semester_code: string; + semester_full: string; + department: string; + folder_name: string; + document_name: string; + document_path: string; + content: string; + url: string; +} + +interface SearchHit { + document: DocumentItem; + highlights: HighlightItem[]; +} + +interface FacetItem { + value: string; + count: number; +} + +interface SearchResults { + hits: SearchHit[]; + facet_counts?: { + semester_code?: FacetItem[]; + department?: FacetItem[]; + }; +} + +interface DocumentMatch { + folder: string; + document: string; + url: string; + highlights: string[]; +} + +interface CourseGroup { + course: { + id: string; + name: string; + semester: string; + department: string; + }; + matchTypes: Set; + courseMatched: boolean; + documentMatches: DocumentMatch[]; +} + +const AdvancedSearch: React.FC = () => { + const [searchQuery, setSearchQuery] = useState(''); + const [results, setResults] = useState([]); + const [showResults, setShowResults] = useState(false); + const [searchField, setSearchField] = useState(''); // '' means all fields + const [filters, setFilters] = useState({ + semester_code: '', + department: '' + }); + const [facets, setFacets] = useState<{ + semester_code: FacetItem[]; + department: FacetItem[]; + }>({ + semester_code: [], + department: [] + }); + const [showFilters, setShowFilters] = useState(false); + const [preventHideResults, setPreventHideResults] = useState(false); + const [activeResultIndex, setActiveResultIndex] = useState(-1); + + const searchInputRef = useRef(null); + const resultsRef = useRef(null); + + const router = useRouter(); + + // Using tRPC mutation + const { mutate: search, isLoading } = api.search.search.useMutation({ + onSuccess: (data: SearchResults) => { + processSearchResults(data); + }, + onError: (error) => { + console.error('Search error:', error); + setResults([]); + } + }); + + // Process and group search results + const processSearchResults = (data: SearchResults) => { + if (!data.hits || data.hits.length === 0) { + setResults([]); + return; + } + + // Group results by course + const courseGroups: Record = {}; + + data.hits.forEach(hit => { + const { document, highlights } = hit; + const courseId = document.course_id; + + if (!courseGroups[courseId]) { + courseGroups[courseId] = { + course: { + id: courseId, + name: document.course_name, + semester: document.semester_full, + department: document.department + }, + matchTypes: new Set(), + courseMatched: false, + documentMatches: [] + }; + } + + // Track what matched in this specific hit + let documentMatched = false; + + if (highlights) { + highlights.forEach(highlight => { + const field = highlight.field; + courseGroups[courseId].matchTypes.add(field); + + // Check if this document actually contains a match + if (field === 'folder_name' || field === 'document_name' || field === 'content') { + documentMatched = true; + } else if (field === 'course_name') { + courseGroups[courseId].courseMatched = true; + } + }); + } + + // Find highlights for content + const contentHighlights: string[] = []; + + if (highlights) { + highlights.forEach(highlight => { + if (highlight.field === 'content') { + contentHighlights.push(highlight.snippet); + } + }); + } + + // Only add this document hit if it actually contains a match + if (documentMatched) { + courseGroups[courseId].documentMatches.push({ + folder: document.folder_name, + document: document.document_name, + url: document.url, + highlights: contentHighlights + }); + } + }); + + // Convert to array for rendering + const groupedResults = Object.values(courseGroups); + setResults(groupedResults); + + // Update facets if available + if (data.facet_counts) { + setFacets({ + semester_code: data.facet_counts.semester_code || [], + department: data.facet_counts.department || [] + }); + } + + // Reset active result index when new results come in + setActiveResultIndex(-1); + }; + + // Handle search on enter or button click + const handleSearch = () => { + if (!searchQuery.trim()) { + setResults([]); + setShowResults(false); + return; + } + + setShowResults(true); + + // Build filter string + const filterClauses: string[] = []; + Object.entries(filters).forEach(([field, value]) => { + if (value) { + filterClauses.push(`${field}:=${value}`); + } + }); + + // Call tRPC mutation + search({ + query: searchQuery, + queryBy: searchField || undefined, + filterBy: filterClauses.length > 0 ? filterClauses.join(' && ') : undefined, + facetBy: 'semester_code,department', + perPage: 20 + }); + }; + + // Handle click on a course + const handleCourseClick = (courseId: string) => { + router.push(`/${courseId}`); + setShowResults(false); + }; + + // Handle click on a document + const handleDocumentClick = (courseId: string, hit: DocumentMatch) => { + router.push(`/viewer/${courseId}/${encodeURIComponent(hit.folder)}/${encodeURIComponent(hit.document)}`); + }; + + // Handle filter change + const handleFilterChange = (filterType: string, value: string) => { + setFilters({ + ...filters, + [filterType]: value + }); + + // Automatically refresh search when filter changes + setTimeout(() => { + if (searchQuery) handleSearch(); + }, 100); + }; + + // Handle clear filters + const handleClearFilters = () => { + setFilters({ + semester_code: '', + department: '' + }); + + // Automatically refresh search when filters cleared + setTimeout(() => { + if (searchQuery) handleSearch(); + }, 100); + }; + + // Handle blur of search field + const handleBlur = (e: React.FocusEvent) => { + // Don't hide results if we're tabbing to the results list + if (e.relatedTarget && resultsRef.current?.contains(e.relatedTarget as Node)) { + return; + } + + setTimeout(() => { + if (!preventHideResults) { + setShowResults(false); + } + }, 150); + }; + + // Handle search field type change + const handleSearchFieldChange = (field: string) => { + setSearchField(field); + + // Automatically refresh search + setTimeout(() => { + if (searchQuery) handleSearch(); + }, 100); + }; + + // Handle clear search + const handleClearSearch = () => { + setSearchQuery(''); + setResults([]); + setShowResults(false); + + // Focus back on the search input after clearing + searchInputRef.current?.focus(); + }; + + // Handle keyboard navigation + const handleKeyDown = (e: React.KeyboardEvent) => { + if (!showResults || results.length === 0) return; + + // Down arrow - move to next result + if (e.key === 'ArrowDown') { + e.preventDefault(); + setActiveResultIndex(prev => (prev < results.length - 1 ? prev + 1 : prev)); + } + // Up arrow - move to previous result + else if (e.key === 'ArrowUp') { + e.preventDefault(); + setActiveResultIndex(prev => (prev > 0 ? prev - 1 : 0)); + } + // Enter - select current result + else if (e.key === 'Enter' && activeResultIndex >= 0) { + e.preventDefault(); + handleCourseClick(results[activeResultIndex].course.id); + } + // Escape - close results + else if (e.key === 'Escape') { + e.preventDefault(); + setShowResults(false); + } + }; + + // Focus on active result when it changes + useEffect(() => { + if (activeResultIndex >= 0 && resultsRef.current) { + const activeElement = resultsRef.current.querySelector(`[data-index="${activeResultIndex}"]`); + if (activeElement) { + (activeElement as HTMLElement).focus(); + } + } + }, [activeResultIndex]); + + // Render filter chip + const renderFilterChip = (label: string, value: string, filterType: string) => ( + handleFilterChange(filterType, '')} + deleteIcon={} + className="m-1 bg-blue-100 hover:bg-blue-200 dark:bg-blue-900 dark:hover:bg-blue-800 text-blue-800 dark:text-blue-200" + size="small" + aria-label={`Filter by ${label}: ${value}. Press delete to remove.`} + /> + ); + + // Determine if we should announce results to screen readers + const getResultsAnnouncement = () => { + if (isLoading) return "Searching..."; + if (results.length === 0 && searchQuery) return `No results found for "${searchQuery}"`; + if (results.length > 0) return `Found ${results.length} results for "${searchQuery}"`; + return ""; + }; + + return ( +
+ + setSearchQuery(e.target.value)} + onKeyDown={(e) => { + if (e.key === 'Enter' && !e.shiftKey) handleSearch(); + handleKeyDown(e); + }} + onBlur={handleBlur} + onFocus={() => searchQuery && setShowResults(true)} + className="rounded-lg shadow w-full" + InputProps={{ + className: "bg-white dark:bg-gray-700 text-gray-900 dark:text-gray-200", + startAdornment: ( + + + ), + endAdornment: ( + + {isLoading ? ( + + ) : searchQuery ? ( + + + + + + ) : null} + + setShowFilters(!showFilters)} + aria-label={showFilters ? "hide filters" : "show filters"} + aria-expanded={showFilters} + aria-controls="filter-panel" + className={`focus:outline focus:outline-2 focus:outline-offset-2 focus:outline-blue-500 dark:focus:outline-blue-400 dark:text-white ml-1 ${showFilters ? 'bg-blue-100 dark:bg-blue-900' : ''}`} + > + + + + + + + + + + ), + }} + inputProps={{ + className: "text-gray-900 dark:text-gray-200 placeholder-gray-500 dark:placeholder-gray-300", + "aria-label": "Search courses and documents", + }} + aria-controls={showResults ? "search-results" : undefined} + aria-expanded={showResults} + /> + + + {/* Screen reader announcement for search results */} +
+ {getResultsAnnouncement()} +
+ + {/* Filter options */} + {showFilters && ( + +
+ + Search Filters + + +
+ +
+ + Search in: + +
+ handleSearchFieldChange('')} + className={`${!searchField ? 'bg-blue-500 text-white' : 'bg-gray-200 dark:bg-gray-600 text-gray-800 dark:text-gray-200'}`} + size="small" + aria-pressed={!searchField} + role="radio" + tabIndex={!searchField ? 0 : -1} + /> + handleSearchFieldChange('course_name')} + className={`${searchField === 'course_name' ? 'bg-blue-500 text-white' : 'bg-gray-200 dark:bg-gray-600 text-gray-800 dark:text-gray-200'}`} + size="small" + aria-pressed={searchField === 'course_name'} + role="radio" + tabIndex={searchField === 'course_name' ? 0 : -1} + /> + handleSearchFieldChange('folder_name')} + className={`${searchField === 'folder_name' ? 'bg-blue-500 text-white' : 'bg-gray-200 dark:bg-gray-600 text-gray-800 dark:text-gray-200'}`} + size="small" + aria-pressed={searchField === 'folder_name'} + role="radio" + tabIndex={searchField === 'folder_name' ? 0 : -1} + /> + handleSearchFieldChange('document_name')} + className={`${searchField === 'document_name' ? 'bg-blue-500 text-white' : 'bg-gray-200 dark:bg-gray-600 text-gray-800 dark:text-gray-200'}`} + size="small" + aria-pressed={searchField === 'document_name'} + role="radio" + tabIndex={searchField === 'document_name' ? 0 : -1} + /> + handleSearchFieldChange('content')} + className={`${searchField === 'content' ? 'bg-blue-500 text-white' : 'bg-gray-200 dark:bg-gray-600 text-gray-800 dark:text-gray-200'}`} + size="small" + aria-pressed={searchField === 'content'} + role="radio" + tabIndex={searchField === 'content' ? 0 : -1} + /> +
+
+ + {facets.semester_code.length > 0 && ( +
+ + Semester: + +
+ {facets.semester_code.map((facet, index) => ( + handleFilterChange('semester_code', facet.value)} + className={`${filters.semester_code === facet.value ? 'bg-blue-500 text-white' : 'bg-gray-200 dark:bg-gray-600 text-gray-800 dark:text-gray-200'}`} + size="small" + aria-pressed={filters.semester_code === facet.value} + aria-label={`Filter by semester: ${facet.value} (${facet.count} results)`} + /> + ))} +
+
+ )} + + {facets.department.length > 0 && ( +
+ + Department: + +
+ {facets.department.map((facet, index) => ( + handleFilterChange('department', facet.value)} + className={`${filters.department === facet.value ? 'bg-blue-500 text-white' : 'bg-gray-200 dark:bg-gray-600 text-gray-800 dark:text-gray-200'}`} + size="small" + aria-pressed={filters.department === facet.value} + aria-label={`Filter by department: ${facet.value} (${facet.count} results)`} + /> + ))} +
+
+ )} +
+ )} + + {/* Active filters display */} + {(filters.semester_code || filters.department) && ( + + + Active filters: + + {filters.semester_code && renderFilterChip('Semester', filters.semester_code, 'semester_code')} + {filters.department && renderFilterChip('Department', filters.department, 'department')} + + )} + + {/* Search results */} + {showResults && ( + setPreventHideResults(true)} + onMouseUp={() => setPreventHideResults(false)} + role="listbox" + aria-label="Search results" + tabIndex={-1} + > + {isLoading ? ( +
+ + Loading search results... +
+ ) : results.length > 0 ? ( + results.map((group, groupIndex) => { + const onlyCourseMatched = group.courseMatched && group.documentMatches.length === 0; + + return ( +
+ handleCourseClick(group.course.id)} + className={`hover:bg-gray-100 dark:hover:bg-gray-700 flex items-start ${activeResultIndex === groupIndex ? 'bg-gray-100 dark:bg-gray-700' : ''}`} + tabIndex={0} + data-index={groupIndex} + role="option" + aria-selected={activeResultIndex === groupIndex} + > + + +
+ + {group.course.name} + + } + secondary={ + + {group.course.semester} · {group.course.department} + + } + aria-label={`Course: ${group.course.name}, ${group.course.semester}, ${group.course.department}`} + /> + + {/* For course-only matches, don't show documents */} + {!onlyCourseMatched && ( + + {group.documentMatches.map((hit, hitIndex) => ( + + { + e.stopPropagation(); + handleDocumentClick(group.course.id, hit); + }} + className="cursor-pointer hover:bg-gray-200 dark:hover:bg-black p-1 rounded transition-colors focus:outline-none focus:ring-2 focus:ring-blue-500 dark:focus:ring-blue-400" + tabIndex={0} + role="link" + aria-label={`Document: ${hit.folder} / ${hit.document}`} + > + + + + {hit.highlights.length > 0 && ( + + + + + )} + + + ))} + + )} +
+
+ {groupIndex < results.length - 1 && } +
+ ); + }) + ) : searchQuery ? ( + + + ) : null} +
+ )} +
+ ); +}; + +export default AdvancedSearch; \ No newline at end of file diff --git a/src/components/AnnotationInputModal.tsx b/src/components/AnnotationInputModal.tsx new file mode 100644 index 0000000..a36553a --- /dev/null +++ b/src/components/AnnotationInputModal.tsx @@ -0,0 +1,83 @@ +import React, { useState, useEffect } from "react"; +import { Box, Modal, Button } from "@mui/material"; +import dynamic from "next/dynamic"; +import "react-quill/dist/quill.snow.css"; + +// Dynamically import ReactQuill to prevent SSR issues +const ReactQuill = dynamic(() => import("react-quill"), { ssr: false }); + +const AnnotationInputModal: React.FC<{ + open: boolean; + selectedText: string | null; + onClose: () => void; + onSave: (annotation: string) => void; +}> = ({ open, selectedText, onClose, onSave }) => { + const [annotation, setAnnotation] = useState(""); + + useEffect(() => { + if (open) setAnnotation(""); // Clear annotation when modal opens + }, [open]); + + return ( + + +

+ Annotate Highlighted Text +

+

+ Highlighted Text: {selectedText} +

+ + + + + + + +
+
+ ); +}; + +export default AnnotationInputModal; diff --git a/src/components/AnnotationViewModal.tsx b/src/components/AnnotationViewModal.tsx new file mode 100644 index 0000000..6539825 --- /dev/null +++ b/src/components/AnnotationViewModal.tsx @@ -0,0 +1,50 @@ +import React from "react"; +import { Box, Modal, Button } from "@mui/material"; + +const AnnotationViewModal: React.FC<{ + open: boolean; + annotation: string | null; + onClose: () => void; + onDelete: () => void; +}> = ({ open, annotation, onClose, onDelete }) => { + return ( + + +

Annotation

+ +
+ + + + + + + + ); +}; + +export default AnnotationViewModal; diff --git a/src/components/CourseContent.tsx b/src/components/CourseContent.tsx new file mode 100644 index 0000000..3b51337 --- /dev/null +++ b/src/components/CourseContent.tsx @@ -0,0 +1,510 @@ +import React, { useState, useEffect } from 'react'; +import { useRouter } from 'next/router'; +import styled from 'styled-components'; +import Star from './Star'; + +// Define styled components that will be used in the component +const Container = styled.div` + padding: 2rem; + max-width: 1200px; + margin: 0 auto; + + @media (max-width: 768px) { + padding: 1rem; + } +`; + +const BackButton = styled.button` + display: inline-flex; + align-items: center; + background: transparent; + border: none; + color: #b43135; + font-weight: 500; + padding: 0.5rem; + margin-bottom: 1.5rem; + border-radius: 0.25rem; + cursor: pointer; + transition: color 0.2s ease; + + &:hover { + color: #9a2a2e; + text-decoration: underline; + } + + &:focus-visible { + outline: 2px solid #FFD700; + outline-offset: 2px; + } + + .dark & { + color: #ff9a9e; + + &:hover { + color: #ff7a7e; + } + } +`; + +const BackArrow = styled.span` + margin-right: 0.5rem; + font-size: 1.2rem; +`; + +const Header = styled.div` + margin-bottom: 2rem; + border-bottom: 1px solid #eaeaea; + padding-bottom: 1.5rem; + + .dark & { + border-bottom-color: #333; + } +`; + +const SemesterTag = styled.div` + display: inline-block; + background-color: #FFD700; + color: #333; + font-weight: bold; + padding: 6px 12px; + border-radius: 20px; + margin-top: 0.5rem; + + .dark & { + background-color: #FFB700; + } +`; + +const FilterBar = styled.div` + display: flex; + gap: 0.5rem; + margin-top: 1.5rem; +`; + +interface FilterButtonProps { + active: boolean; +} + +const FilterButton = styled.button` + padding: 0.5rem 1rem; + border-radius: 6px; + font-weight: ${props => props.active ? 'bold' : 'normal'}; + background-color: ${props => props.active ? '#b43135' : 'transparent'}; + color: ${props => props.active ? 'white' : '#555'}; + border: 1px solid ${props => props.active ? '#b43135' : '#ccc'}; + transition: all 0.3s ease; + font-size: 0.9rem; + + &:hover { + background-color: ${props => props.active ? '#9a2a2e' : '#f0f0f0'}; + } + + &:focus-visible { + outline: 2px solid #FFD700; + outline-offset: 2px; + } + + .dark & { + background-color: ${props => props.active ? '#1a2036' : 'transparent'}; + color: ${props => props.active ? 'white' : '#aaa'}; + border-color: ${props => props.active ? '#1a2036' : '#555'}; + + &:hover { + background-color: ${props => props.active ? '#111827' : '#333'}; + } + } +`; + +const DocumentGrid = styled.div` + display: grid; + grid-template-columns: repeat(auto-fill, minmax(350px, 1fr)); + gap: 1.5rem; + + @media (max-width: 768px) { + grid-template-columns: 1fr; + } +`; + +const DocumentCard = styled.div` + --transition: 350ms; + display: flex; + flex-direction: column; + background: white; + border-radius: 15px; + box-shadow: 0 15px 25px rgba(0, 0, 0, 0.2); + overflow: hidden; + height: 100%; + min-height: 180px; + position: relative; + transition: transform 0.3s ease, box-shadow 0.3s ease; + cursor: pointer; + + /* Dark mode styles */ + .dark & { + background: #1e293b; + box-shadow: 0 15px 25px rgba(0, 0, 0, 0.4); + } + + &:hover { + transform: translateY(-5px); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.25); + + .dark & { + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.5); + } + } + + &:focus-visible { + outline: 3px solid #FFD700; + transform: translateY(-5px); + + .dark & { + outline-color: #FFB700; + } + } +`; + +const StarWrapper = styled.div` + position: absolute; + top: 1.5rem; + right: 1.5rem; + z-index: 10; + width: 30px; + height: 30px; +`; + +const DocumentIconWrapper = styled.div` + position: absolute; + top: 1.5rem; + left: 1.5rem; + width: 40px; + height: 40px; + color: #b43135; + + .dark & { + color: #FFB700; + } + + svg { + width: 100%; + height: 100%; + fill: currentColor; + } +`; + +const DocumentContent = styled.div` + padding: 1.5rem 1.5rem 5rem 5rem; + padding-right: 3.5rem; /* Add extra padding on the right for the star */ + flex: 1; +`; + +const DocumentTitle = styled.h2` + font-size: 1.1rem; + font-weight: 600; + color: #333; + margin-bottom: 0.5rem; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + padding-right: 1rem; /* Add padding to prevent text from going under the star */ + + .dark & { + color: #e5e7eb; + } +`; + +const DocumentSubtitle = styled.p` + font-size: 0.9rem; + color: #666; + display: -webkit-box; + -webkit-line-clamp: 2; + -webkit-box-orient: vertical; + overflow: hidden; + + .dark & { + color: #9ca3af; + } +`; + +const ReadButton = styled.button` + position: absolute; + bottom: 0; + left: 0; + right: 0; + background: rgba(0, 0, 0, 0.05); + padding: 0.75rem; + display: flex; + align-items: center; + justify-content: center; + color: #b43135; + font-weight: 500; + transition: background 0.3s ease, color 0.3s ease; + border: none; + cursor: pointer; + width: 100%; + + &:hover { + background: #b43135; + color: white; + + .dark & { + background: #1a2036; + } + } + + &:focus-visible { + outline: 2px solid #FFD700; + outline-offset: -2px; + z-index: 1; + background: #b43135; + color: white; + + .dark & { + outline-color: #FFB700; + background: #1a2036; + } + } + + .dark & { + background: rgba(255, 255, 255, 0.05); + color: #FFB700; + } + + .icon { + display: inline-block; + width: 14px; + height: 14px; + margin-left: 8px; + + svg { + width: 100%; + height: 100%; + fill: currentColor; + transform: rotate(-45deg); + transition: transform 0.2s ease-out; + } + } + + &:hover .icon svg, + &:focus-visible .icon svg { + transform: rotate(2deg); + } +`; + +// NoResults component +const NoResultsContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2rem; + margin: 2rem auto; + max-width: 500px; + text-align: center; +`; + +const IconWrapper = styled.div` + width: 64px; + height: 64px; + color: #b43135; + margin-bottom: 1rem; + + .dark & { + color: #ff9a9e; + } + + svg { + width: 100%; + height: 100%; + } +`; + +const Title = styled.h2` + font-size: 1.5rem; + font-weight: bold; + margin-bottom: 0.5rem; + color: #333; + + .dark & { + color: #e5e7eb; + } +`; + +const Description = styled.p` + color: #666; + + .dark & { + color: #9ca3af; + } +`; + +// Define the NoResults component +const NoResults = ({ description }) => ( + + + No Documents Found + {description} + +); + +// Component props +interface CourseContentProps { + courseData: any; + courseName: string; +} + +const CourseContent: React.FC = ({ courseData, courseName }) => { + const router = useRouter(); + const [starredDocuments, setStarredDocuments] = useState([]); + const [showStarredOnly, setShowStarredOnly] = useState(false); + + // Load starred documents from localStorage + useEffect(() => { + if (typeof window !== 'undefined') { + const stored = JSON.parse(localStorage.getItem('starredDocuments') || '[]'); + setStarredDocuments(stored); + } + }, []); + + if (!courseData?.children) { + return ( + + + + ); + } + + // Handle starring a document + const handleStarToggle = (docId: string, isStarred: boolean) => { + // Get current starred documents + const newStarredDocuments = isStarred + ? [...starredDocuments, docId] + : starredDocuments.filter(id => id !== docId); + + // Update localStorage + localStorage.setItem('starredDocuments', JSON.stringify(newStarredDocuments)); + + // Update state + setStarredDocuments(newStarredDocuments); + }; + // Navigate to document viewer + const navigateToDocument = (folderName: string, fileName: string) => { + router.push(`/viewer/${courseName}/${encodeURIComponent(folderName)}/${encodeURIComponent(fileName)}`); + }; + + return ( + + router.push('/')}> + Back to All Courses + + +
+

+ {courseData.course?.full || courseName} +

+ + {courseData.semester?.full} + +

+ Browse available documents for this course. Click on a document to view its contents. +

+ + + setShowStarredOnly(false)} + aria-pressed={!showStarredOnly} + aria-label="Show all documents" + > + All Documents + + setShowStarredOnly(true)} + aria-pressed={showStarredOnly} + aria-label="Show starred documents only" + > + Starred + + +
+ + + {courseData.children.map((folder, folderIndex) => { + // Filter only HTML files + const htmlFiles = folder.children?.filter(file => + file.type === 'file' && file.name.endsWith('.html') + ); + + if (!htmlFiles || htmlFiles.length === 0) return null; + + // Apply filter for starred documents if needed + return htmlFiles + .filter(file => { + if (showStarredOnly) { + const docId = `${courseName}-${folder.name}-${file.name}`; + return starredDocuments.includes(docId); + } + return true; + }) + .map((file, fileIndex) => ( + + + + + + + + + {folder.name} + {file.name.replace('.html', '')} + + + navigateToDocument(folder.name, file.name)} + tabIndex={0} + aria-label={`Read ${file.name.replace('.html', '')}`} + > + Read + + + + + + + + )); + })} + + + {courseData.children.every(folder => + !folder.children?.some(file => + file.type === 'file' && file.name.endsWith('.html') + ) + ) && ( + + )} +
+ ); +}; + + +export default CourseContent; \ No newline at end of file diff --git a/src/components/HTMLViewer.tsx b/src/components/HTMLViewer.tsx index f6eb124..3800427 100644 --- a/src/components/HTMLViewer.tsx +++ b/src/components/HTMLViewer.tsx @@ -83,11 +83,11 @@ const HTMLViewer = () => { - + {course} {courseFolder && ( - + {courseFolder} )} diff --git a/src/components/Header.tsx b/src/components/Header.tsx index 313ccc9..337756f 100644 --- a/src/components/Header.tsx +++ b/src/components/Header.tsx @@ -17,6 +17,7 @@ import Image from 'next/image'; import SearchBarWithSuggestions from './SearchBarWithSuggestions'; import ThemeToggle from './ThemeToggle'; import useColorMode from '~/hooks/useColorMode'; +import AdvancedSearch from './AdvancedSearch'; interface CourseItem { name: string; @@ -69,7 +70,7 @@ const AppHeader: React.FC = ({ onSearch, jsonData }) => { {/* Search Bar */}
- +
{/* Theme Toggle */} diff --git a/src/components/Home.tsx b/src/components/Home.tsx index 2d81d4e..7badfd1 100644 --- a/src/components/Home.tsx +++ b/src/components/Home.tsx @@ -1,98 +1,729 @@ -import React, { useEffect, useRef } from 'react'; +import React, { useState, useEffect } from 'react'; import Link from 'next/link'; +import { useRouter } from 'next/router'; +import styled from 'styled-components'; import NoResults from './NoResults'; +import Star from './Star'; + +// Types for our data structure +type SemesterInfo = { + code: string; + season: string; + year: string; + full: string; +}; + +type CourseInfo = { + dept: string[]; + number: string[]; + full: string; +}; type CourseItem = { + type: string; name: string; - type?: 'coursefolder'; + semester?: SemesterInfo; + course?: CourseInfo; children?: CourseItem[]; }; interface HomeProps { - data: CourseItem[]; - setSelectedKey: (key: string) => void; - } - -const Home: React.FC = ({ data, setSelectedKey }) => { -const sectionRefs = useRef<(HTMLDivElement | null)[]>([]); - -useEffect(() => { - const observer = new IntersectionObserver( - (entries) => { - entries.forEach(entry => { - if (entry.isIntersecting) { - setSelectedKey(entry.target.getAttribute("data-index") || ''); - } - }); - }, - { rootMargin: '0px', threshold: 0.1 } - ); - - sectionRefs.current.forEach((section, index) => { - if (section) { - observer.observe(section); + data: CourseItem[]; +} + +// Function to compare semester codes for sorting +// Format is like F22 (Fall 2022), S23 (Spring 2023), etc. +const compareSemesters = (a: string, b: string): number => { + // Extract season (F, S) and year (22, 23, etc.) + const seasonA = a.charAt(0); + const seasonB = b.charAt(0); + const yearA = parseInt(a.substring(1)); + const yearB = parseInt(b.substring(1)); + + // Compare years first (newer years come first) + if (yearA !== yearB) { + return yearB - yearA; + } + + // If same year, sort by season (Spring comes before Fall in same year when sorting newest first) + // Since Spring is earlier in the year than Fall, when sorting newest first, + // Fall should come before Spring for the same year + if (seasonA === seasonB) { + return 0; + } + return seasonA === 'F' ? -1 : 1; // Fall comes before Spring in same year when sorting descending +}; + +const Home: React.FC = ({ data }) => { + const router = useRouter(); + const [starredCourses, setStarredCourses] = useState([]); + const [showStarredOnly, setShowStarredOnly] = useState(false); + const [selectedSemester, setSelectedSemester] = useState('all'); + + // Load starred courses from localStorage on component mount + useEffect(() => { + if (typeof window !== 'undefined') { + const stored = JSON.parse(localStorage.getItem('starredCourses') || '[]'); + setStarredCourses(stored); + } + }, []); + + // Filter only course type items + const courses = data.filter(item => item.type === 'course'); + + // Extract unique semesters for the filter + const semesters = React.useMemo(() => { + const semesterSet = new Set(); + courses.forEach(course => { + if (course.semester?.full) { + semesterSet.add(course.semester.full); } }); + + // Convert to array and sort + return Array.from(semesterSet).sort((a, b) => { + // Extract season and year + const aMatch = a.match(/(Spring|Fall) (\d{4})/); + const bMatch = b.match(/(Spring|Fall) (\d{4})/); + + if (!aMatch || !bMatch) return 0; + + const [, aSeason, aYear] = aMatch; + const [, bSeason, bYear] = bMatch; + + // Compare years first + if (aYear !== bYear) { + return parseInt(bYear) - parseInt(aYear); // Descending + } + + // Same year, compare seasons + if (aSeason === bSeason) return 0; + return aSeason === 'Fall' ? -1 : 1; // Fall comes before Spring + }); + }, [courses]); - return () => { - sectionRefs.current.forEach(section => { - if (section) observer.unobserve(section); - }); - }; - }, [data, setSelectedKey]); - + // Sort courses chronologically (newest first) + const sortedCourses = [...courses].sort((a, b) => { + if (a.semester?.code && b.semester?.code) { + return compareSemesters(a.semester.code, b.semester.code); + } + return 0; + }); + // Apply filters: starred and semester + const displayedCourses = sortedCourses.filter(course => { + // Filter by starred status + if (showStarredOnly && !starredCourses.includes(course.name)) { + return false; + } + + // Filter by semester + if (selectedSemester !== 'all' && course.semester?.full !== selectedSemester) { + return false; + } + + return true; + }); - return ( -
- {data.length !== 0 && ( -

Welcome to the Swarthmore A11yGator!

- )} - - {data.length !== 0 && ( -

Explore accessible course documents and enhance your learning experience.

- )} + // Handle clicking on a course button + const navigateToCourse = (courseName: string) => { + // Find the matching course + const course = courses.find(item => item.name === courseName); + if (course?.children && course.children.length > 0) { + // Navigate to the first document of the first folder in the course + const firstFolder = course.children[0]; + if (firstFolder.children && firstFolder.children.length > 0) { + const firstFile = firstFolder.children.find(file => file.name.endsWith('.html')); + if (firstFile) { + router.push(`/${courseName}`); + } + } + } + }; + + // Handle toggling star for a course + const handleStarToggle = (courseId: string, isStarred: boolean) => { + const newStarredCourses = isStarred + ? [...starredCourses, courseId] + : starredCourses.filter(id => id !== courseId); + + setStarredCourses(newStarredCourses); + localStorage.setItem('starredCourses', JSON.stringify(newStarredCourses)); + }; - {data.length === 0 ? ( -
- + // Handle clearing all filters + const clearFilters = () => { + setShowStarredOnly(false); + setSelectedSemester('all'); + }; + + return ( + +

+ Welcome to the Swarthmore Accessible Docs! +

+ +
+ Your go-to resource for inclusive course materials. Find accessible notes and materials, and enjoy reading in light or dark mode, enhancing your learning experience. +
+ + + + View: + setShowStarredOnly(false)} + aria-pressed={!showStarredOnly} + aria-label="Show all courses" + > + All Courses + + setShowStarredOnly(true)} + aria-pressed={showStarredOnly} + aria-label="Show starred courses only" + > + Starred + + + + + Semester: + + + + + + {(showStarredOnly || selectedSemester !== 'all') && ( + + Clear Filters + + )} + + + {displayedCourses.length === 0 ? ( +
+ {showStarredOnly ? ( + + ) : ( + + )}
) : ( - data.map((course, index) => ( -
sectionRefs.current[index] = el} data-index={(index + 1).toString()} key={index} tabIndex={0} aria-label={`${course.name} course-folder`} className="mt-12 mb-5 p-5 bg-white dark:bg-slate-800 border border-gray-500 dark:border-gray-600 shadow-xl rounded-lg dark:text-gray-100" id={course.name}> -

{course.name}

- - {course.children?.map((item, subIndex) => ( -
- {item.type === 'coursefolder' ? ( -
-

{item.name}

- - {item.children?.map((file, fileIndex) => ( - file.name.endsWith('.html') && ( - - {file.name} - - ) - ))} + + {displayedCourses.map((course, index) => { + // Calculate document count + const documentCount = course.children?.reduce((count, folder) => { + const htmlFiles = folder.children?.filter(file => + file.name?.endsWith('.html') + ).length || 0; + return count + htmlFiles; + }, 0); + + return ( + + + + + + - )) +
+
+
+ {course.semester?.full} +
+
+ {documentCount} {documentCount === 1 ? 'document' : 'documents'} +
+
+ +
+
+ ); + })} +
)} -
+ ); +}; + +const Container = styled.div` + padding: 2rem; + max-width: 1200px; + margin: 0 auto; - } + @media (max-width: 768px) { + padding: 1rem; + } +`; + +const FilterSection = styled.div` + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 1.5rem; + margin-bottom: 2rem; + justify-content: center; + + @media (max-width: 768px) { + flex-direction: column; + align-items: stretch; + gap: 1rem; + } +`; + +const FilterContainer = styled.div` + display: flex; + align-items: center; + gap: 0.5rem; + + @media (max-width: 768px) { + justify-content: center; + } +`; + +const FilterLabel = styled.span` + font-weight: 500; + color: #555; + + .dark & { + color: #aaa; + } +`; + +const SelectWrapper = styled.div` + select { + border-radius: 6px; + border: 1px solid #ccc; + padding: 0.5rem; + background-color: white; + min-width: 200px; + font-size: 1rem; + + &:focus { + outline: 2px solid #FFD700; + border-color: transparent; + } + + .dark & { + background-color: #1e293b; + border-color: #4b5563; + color: white; + } + } +`; + +const ClearFiltersButton = styled.button` + background-color: transparent; + color: #b43135; + border: 1px solid #b43135; + border-radius: 6px; + padding: 0.5rem 1rem; + font-size: 0.9rem; + transition: all 0.3s ease; + + &:hover { + background-color: rgba(180, 49, 53, 0.1); + } + + &:focus-visible { + outline: 2px solid #FFD700; + outline-offset: 2px; + } + + .dark & { + color: #ff9a9e; + border-color: #ff9a9e; + + &:hover { + background-color: rgba(255, 154, 158, 0.1); + } + } +`; + +interface FilterButtonProps { + active: boolean; +} + +const FilterButton = styled.button` + padding: 0.5rem 1rem; + border-radius: 6px; + font-weight: ${props => props.active ? 'bold' : 'normal'}; + background-color: ${props => props.active ? '#b43135' : 'transparent'}; + color: ${props => props.active ? 'white' : '#555'}; + border: 1px solid ${props => props.active ? '#b43135' : '#ccc'}; + transition: all 0.3s ease; + font-size: 0.9rem; + + &:hover { + background-color: ${props => props.active ? '#9a2a2e' : '#f0f0f0'}; + } + + &:focus-visible { + outline: 2px solid #FFD700; + outline-offset: 2px; + } + + .dark & { + background-color: ${props => props.active ? '#1a2036' : 'transparent'}; + color: ${props => props.active ? 'white' : '#aaa'}; + border-color: ${props => props.active ? '#1a2036' : '#555'}; + + &:hover { + background-color: ${props => props.active ? '#111827' : '#333'}; + } + } +`; + +const StarWrapper = styled.div` + position: absolute; + top: 15px; + right: 15px; + z-index: 10; + width: 40px; + height: 40px; +`; -export default Home; +const CourseGrid = styled.div` + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 2rem; + + @media (max-width: 640px) { + grid-template-columns: 1fr; + } +`; + +const CourseCard = styled.div` + --transition: 350ms; + --folder-W: 120px; + --folder-H: 80px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-between; + padding: 20px; + background: linear-gradient(135deg, #b43135, #9a2a2e); + border-radius: 15px; + box-shadow: 0 20px 30px rgba(0, 0, 0, 0.4); + height: 380px; + position: relative; + transition: transform 0.3s ease, box-shadow 0.3s ease; + + /* Dark mode styles */ + .dark & { + background: linear-gradient(135deg, #1a2036, #111827); + box-shadow: 0 20px 30px rgba(0, 0, 0, 0.6); + } + + &:hover { + transform: translateY(-5px); + box-shadow: 0 25px 40px rgba(0, 0, 0, 0.5); + + .dark & { + box-shadow: 0 25px 40px rgba(0, 0, 0, 0.7); + } + } + + &:focus-visible { + outline: 3px solid #FFD700; + transform: translateY(-5px); + + .dark & { + outline-color: #FFB700; + } + } + + .folder { + position: absolute; + top: 20px; + left: calc(50% - 60px); + animation: float 2.5s infinite ease-in-out; + transition: transform var(--transition) ease; + } + + .folder:hover { + transform: scale(1.05); + } + + .folder .front-side, + .folder .back-side { + position: absolute; + transition: transform var(--transition); + transform-origin: bottom center; + } + + .folder .back-side::before, + .folder .back-side::after { + content: ""; + display: block; + background-color: white; + opacity: 0.5; + z-index: 0; + width: var(--folder-W); + height: var(--folder-H); + position: absolute; + transform-origin: bottom center; + border-radius: 15px; + transition: transform 350ms; + z-index: 0; + + .dark & { + background-color: #e5e7eb; + } + } + + &:hover .back-side::before { + transform: rotateX(-5deg) skewX(5deg); + } + + &:hover .back-side::after { + transform: rotateX(-15deg) skewX(12deg); + } + + .folder .front-side { + z-index: 1; + } + + &:hover .front-side { + transform: rotateX(-40deg) skewX(15deg); + } + + .folder .tip { + background: linear-gradient(135deg, #FF9A56, #FF6F56); + width: 80px; + height: 20px; + border-radius: 12px 12px 0 0; + box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2); + position: absolute; + top: -10px; + z-index: 2; + + .dark & { + background: linear-gradient(135deg, #FF7A36, #FF4F36); + } + } + + .folder .cover { + background: linear-gradient(135deg, #FFE563, #FFC663); + width: var(--folder-W); + height: var(--folder-H); + box-shadow: 0 15px 30px rgba(0, 0, 0, 0.3); + border-radius: 10px; + display: flex; + align-items: center; + justify-content: center; + + .dark & { + background: linear-gradient(135deg, #FFD533, #FFB633); + } + } + + .course-code { + font-size: 1.1rem; + font-weight: bold; + color: #333; + text-align: center; + max-width: 90%; + overflow: hidden; + text-overflow: ellipsis; + } + + .semester-tag { + background-color: #FFD700; + color: #333; + font-weight: bold; + padding: 6px 12px; + border-radius: 20px; + position: absolute; + bottom: 120px; + + .dark & { + background-color: #FFB700; + } + } + + .doc-count { + color: #FFFFFF; + font-size: 0.9rem; + position: absolute; + bottom: 90px; + + .dark & { + color: #e5e7eb; + } + } + + .custom-file-upload { + font-size: 1.1em; + color: #FFFFFF; + text-align: center; + background: rgba(0, 0, 0, 0.25); + border: none; + border-radius: 10px; + cursor: pointer; + transition: background var(--transition) ease; + display: inline-block; + width: 100%; + padding: 0; + position: absolute; + bottom: 0; + margin: 0; + overflow: hidden; + + .dark & { + background: rgba(255, 255, 255, 0.1); + } + } + + .custom-file-upload:hover { + background: rgba(0, 0, 0, 0.35); + + .dark & { + background: rgba(255, 255, 255, 0.2); + } + } + + button { + font-size: 18px; + color: white; + font-family: inherit; + font-weight: 400; + cursor: pointer; + position: relative; + border: none; + background: none; + transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1); + transition-duration: 400ms; + transition-property: color; + display: inline-flex; + align-items: center; + width: 100%; + justify-content: center; + padding: 15px; + + .dark & { + color: #e5e7eb; + } + } + + button:focus, + button:hover { + color: #FFD700; + + .dark & { + color: #FFB700; + } + } + + button:focus-visible { + outline: 2px solid #FFD700; + outline-offset: -2px; + border-radius: 8px; + + .dark & { + outline-color: #FFB700; + } + } + + button:focus:after, + button:hover:after { + width: 100%; + } + + button:after { + content: ""; + pointer-events: none; + bottom: -2px; + left: 0; + position: absolute; + width: 0%; + height: 2px; + background-color: #FFD700; + transition-timing-function: cubic-bezier(0.25, 0.8, 0.25, 1); + transition-duration: 500ms; + transition-property: width; + + .dark & { + background-color: #FFB700; + } + } + + .svg-icon { + width: 0.9em; + height: 0.8em; + margin-left: 10px; + fill: white; + transform: rotate(-45deg); + transition: transform 0.2s ease-out; + + .dark & { + fill: #e5e7eb; + } + } + + button:hover .svg-icon { + transform: rotate(0deg); + fill: #FFD700; + + .dark & { + fill: #FFB700; + } + } + + @keyframes float { + 0% { + transform: translateY(0px); + } + 50% { + transform: translateY(-3px); + } + 100% { + transform: translateY(0px); + } + } +`; +export default Home; \ No newline at end of file diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx index e51c971..f1ef9bd 100644 --- a/src/components/Layout.tsx +++ b/src/components/Layout.tsx @@ -1,9 +1,11 @@ // components/layout.tsx import { useSession } from 'next-auth/react'; -import { Box, CircularProgress } from '@mui/material'; +import { Box } from '@mui/material'; import AppHeader from './Header'; -import Sidebar from './Sidebar'; import AppFooter from './Footer'; +import Loader from './Loader'; +import { useState, useEffect } from 'react'; +import Router from 'next/router'; interface LayoutProps { children: React.ReactNode; @@ -11,47 +13,156 @@ interface LayoutProps { selectedKey: string; setSelectedKey: (key: string) => void; setCollapsed: (collapsed: boolean) => void; - collapsed: boolean; + collapsed: boolean; +} + +// This script will run on the client-side immediately when included +if (typeof window !== 'undefined') { + // Create a global loading state in localStorage + localStorage.setItem('isLoading', 'true'); + + // Set a timeout to clear the loading state (ensures it doesn't get stuck) + setTimeout(() => { + localStorage.setItem('isLoading', 'false'); + }, 3000); } const Layout = ({ children, data, selectedKey, setSelectedKey, setCollapsed, collapsed }: LayoutProps) => { const { data: session, status } = useSession(); - const loading = status === "loading"; + const [isLoading, setIsLoading] = useState(true); + + // Initialize state from localStorage on first render + useEffect(() => { + if (typeof window !== 'undefined') { + // Force the loader to show immediately on page load + document.body.style.visibility = 'hidden'; + + // Set timer for minimum display time + const initialLoadTimer = setTimeout(() => { + setIsLoading(false); + document.body.style.visibility = 'visible'; + localStorage.setItem('isLoading', 'false'); + }, 1500); + + return () => { + clearTimeout(initialLoadTimer); + document.body.style.visibility = 'visible'; + }; + } + }, []); + + // Set up router events to guarantee loader display on navigation + useEffect(() => { + const showLoader = () => { + setIsLoading(true); + if (typeof window !== 'undefined') { + localStorage.setItem('isLoading', 'true'); + } + }; + + const hideLoader = () => { + // Add delay to ensure loader shows for at least 1.5 seconds + setTimeout(() => { + setIsLoading(false); + if (typeof window !== 'undefined') { + localStorage.setItem('isLoading', 'false'); + } + }, 700); + }; + + // Set up router event listeners for page navigation + Router.events.on('routeChangeStart', showLoader); + Router.events.on('routeChangeComplete', hideLoader); + Router.events.on('routeChangeError', hideLoader); + + // Handle page refresh with beforeunload + const handleBeforeUnload = () => { + if (typeof window !== 'undefined') { + localStorage.setItem('isLoading', 'true'); + } + }; + + window.addEventListener('beforeunload', handleBeforeUnload); + + // Clean up event listeners + return () => { + Router.events.off('routeChangeStart', showLoader); + Router.events.off('routeChangeComplete', hideLoader); + Router.events.off('routeChangeError', hideLoader); + window.removeEventListener('beforeunload', handleBeforeUnload); + }; + }, []); + + // Force loader to show when NextAuth is loading + useEffect(() => { + if (status === "loading") { + setIsLoading(true); + } + }, [status]); - if (loading) { + // Always show loader when loading state is true + if (isLoading || status === "loading" || (typeof window !== 'undefined' && localStorage.getItem('isLoading') === 'true')) { return ( - -
+ + ); } + // Layout for non-authenticated users if (!session) { - return <> - {children} - - ; // Render only the children when not logged in + return ( + + + {children} + + + + ); } + // Layout for authenticated users return ( - - - setCollapsed((prev) => !prev)} - drawerWidth={240} - /> - - - {}} jsonData={data} /> + + {}} jsonData={data} /> + {children} - + ); }; -export default Layout; +export default Layout; \ No newline at end of file diff --git a/src/components/Star.tsx b/src/components/Star.tsx new file mode 100644 index 0000000..a3fa7c8 --- /dev/null +++ b/src/components/Star.tsx @@ -0,0 +1,326 @@ +import React, { useState, useEffect } from 'react'; +import styled from 'styled-components'; + +interface StarProps { + courseId: string; + onToggle: (courseId: string, isStarred: boolean) => void; +} + +const Star: React.FC = ({ courseId, onToggle }) => { + const [isStarred, setIsStarred] = useState(false); + + // Check localStorage on component mount + useEffect(() => { + const starredCourses = JSON.parse(localStorage.getItem('starredCourses') || '[]'); + setIsStarred(starredCourses.includes(courseId)); + }, [courseId]); + + const handleToggle = (e: React.ChangeEvent) => { + const newValue = e.target.checked; + setIsStarred(newValue); + onToggle(courseId, newValue); + + // Update localStorage + const starredCourses = JSON.parse(localStorage.getItem('starredCourses') || '[]'); + let updatedStarredCourses; + + if (newValue) { + updatedStarredCourses = [...starredCourses, courseId]; + } else { + updatedStarredCourses = starredCourses.filter((id: string) => id !== courseId); + } + + localStorage.setItem('starredCourses', JSON.stringify(updatedStarredCourses)); + }; + + return ( + + + + ); +}; + +const StyledWrapper = styled.div` + width: 100%; + height: 100%; + display: block; + + .star { + --star-color: rgb(250, 190, 21); + position: relative; + width: 40px; /* Reduced size to fit nicely in the corner */ + height: 40px; /* Reduced size to fit nicely in the corner */ + transition: transform 0.3s ease; + cursor: pointer; + } + + .dark .star { + --star-color: rgb(255, 215, 0); + } + + .star .checkbox { + position: absolute; + width: 100%; + height: 100%; + opacity: 0; + z-index: 20; + cursor: pointer; + } + + .star .svg-container { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + } + + .star .svg-outline, + .star .svg-filled { + fill: var(--star-color); + position: absolute; + width: 100%; + height: 100%; + transition: all 0.3s ease; + } + + .star .svg-filled { + animation: keyframes-svg-filled 1s; + opacity: 0; + transform: scale(0); + } + + .star .svg-celebrate { + position: absolute; + display: none; + stroke: var(--star-color); + fill: var(--star-color); + stroke-width: 2px; + } + + .star .particle { + position: absolute; + animation-fill-mode: forwards; + display: none; + } + + .star .checkbox:checked ~ .svg-container .svg-outline { + opacity: 0; + } + + .star .checkbox:checked ~ .svg-container .svg-filled { + opacity: 1; + transform: scale(1); + } + + .star .checkbox:not(:checked) ~ .svg-container .svg-filled { + animation: keyframes-svg-unfilled 0.3s forwards; + } + + .star .checkbox:checked ~ .svg-container .svg-celebrate { + display: block; + } + + .star .checkbox:checked ~ .svg-container .particle { + display: block; + } + + .star .particle:nth-child(1) { + animation: particle-1 1s cubic-bezier(0.25, 0.1, 0.25, 1); + } + .star .particle:nth-child(2) { + animation: particle-2 1s ease-out; + } + .star .particle:nth-child(3) { + animation: particle-3 1s ease-out; + } + .star .particle:nth-child(4) { + animation: particle-4 1s ease-out; + } + .star .particle:nth-child(5) { + animation: particle-5 1s ease-out; + } + .star .particle:nth-child(6) { + animation: particle-6 1s ease-out; + } + .star .particle:nth-child(7) { + animation: particle-7 1s ease-out; + } + .star .particle:nth-child(8) { + animation: particle-8 1s ease-out; + } + + @keyframes keyframes-svg-filled { + 0% { + transform: scale(0); + opacity: 0; + } + 25% { + transform: scale(1.2); + opacity: 1; + } + 50% { + transform: scale(1); + filter: brightness(1.5); + } + } + + @keyframes keyframes-svg-unfilled { + 0% { + transform: scale(1); + opacity: 1; + } + 100% { + transform: scale(0); + opacity: 0; + } + } + + @keyframes particle-1 { + 0% { + transform: translate(0, 0) scale(1); + opacity: 1; + } + 40% { + transform: translate(-20px, -25px) scale(0.6); + opacity: 0.6; + } + 100% { + transform: translate(-40px, 40px) scale(0); + opacity: 0; + } + } + + @keyframes particle-2 { + 0% { + transform: translate(0, 0) scale(1); + opacity: 1; + } + 40% { + transform: translate(20px, -25px) scale(0.6); + opacity: 0.6; + } + 100% { + transform: translate(40px, 40px) scale(0); + opacity: 0; + } + } + + @keyframes particle-3 { + 0% { + transform: translate(0, 0) scale(1); + opacity: 1; + } + 40% { + transform: translate(-30px, -20px) scale(0.6); + opacity: 0.6; + } + 100% { + transform: translate(-50px, 45px) scale(0); + opacity: 0; + } + } + + @keyframes particle-4 { + 0% { + transform: translate(0, 0) scale(1); + opacity: 1; + } + 40% { + transform: translate(30px, -20px) scale(0.6); + opacity: 0.6; + } + 100% { + transform: translate(50px, 45px) scale(0); + opacity: 0; + } + } + + @keyframes particle-5 { + 0% { + transform: translate(0, 0) scale(1); + opacity: 1; + } + 45% { + transform: translate(0, -30px) scale(0.6); + opacity: 0.6; + } + 100% { + transform: translate(0, 40px) scale(0); + opacity: 0; + } + } + + @keyframes particle-6 { + 0% { + transform: translate(0, 0) scale(1); + opacity: 1; + } + 35% { + transform: translate(-35px, -15px) scale(0.6); + opacity: 0.6; + } + 100% { + transform: translate(-60px, 50px) scale(0); + opacity: 0; + } + } + + @keyframes particle-7 { + 0% { + transform: translate(0, 0) scale(1); + opacity: 1; + } + 35% { + transform: translate(35px, -15px) scale(0.6); + opacity: 0.6; + } + 100% { + transform: translate(60px, 50px) scale(0); + opacity: 0; + } + } + + @keyframes particle-8 { + 0% { + transform: translate(0, 0) scale(1); + opacity: 1; + } + 45% { + transform: translate(0, -35px) scale(0.6); + opacity: 0.6; + } + 100% { + transform: translate(0, 45px) scale(0); + opacity: 0; + } + } +`; + +export default Star; \ No newline at end of file diff --git a/src/components/ThemeToggle.tsx b/src/components/ThemeToggle.tsx index 000d33a..76f7bbc 100644 --- a/src/components/ThemeToggle.tsx +++ b/src/components/ThemeToggle.tsx @@ -1,78 +1,382 @@ import React from 'react'; import { Tooltip } from '@mui/material'; +import styled from 'styled-components'; interface DarkModeToggleProps { - colorMode: string; - setColorMode: (colorMode: string) => void; - } + colorMode: string; + setColorMode: (colorMode: string) => void; +} - - const DarkModeToggle: React.FC = ({ colorMode, setColorMode }) => { +const DarkModeToggle: React.FC = ({ colorMode, setColorMode }) => { + const handleToggle = () => { + setColorMode(colorMode === "light" ? "dark" : "light"); + }; - const handleToggle = () => { - setColorMode(colorMode === "light" ? "dark" : "light"); - }; + const handleKeyPress = (event: React.KeyboardEvent) => { + if (event.key === 'Enter') { + handleToggle(); + } + }; - const handleKeyPress = (event: React.KeyboardEvent) => { - if (event.key === 'Enter') { - handleToggle(); - } - }; - return ( -
- - -
+ title={`Switch to ${colorMode === 'light' ? 'dark' : 'light'} mode`} + placement="bottom" + className='z-50' + arrow + > + + +
); }; -export default DarkModeToggle; +const StyledWrapper = styled.div` + .switch { + position: relative; + display: inline-block; + width: 80px; + height: 44px; + } + + .switch input { + opacity: 0; + width: 0; + height: 0; + } + + .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0; + background-color: #2196f3; + -webkit-transition: 0.4s; + transition: 0.4s; + z-index: 0; + overflow: hidden; + } + + .sun-moon { + position: absolute; + content: ""; + height: 36px; + width: 36px; + left: 4px; + bottom: 4px; + background-color: yellow; + -webkit-transition: 0.4s; + transition: 0.4s; + } + + input:checked + .slider { + background-color: black; + } + + input:focus + .slider { + box-shadow: 0 0 1px #2196f3; + } + + input:checked + .slider .sun-moon { + -webkit-transform: translateX(40px); + -ms-transform: translateX(40px); + transform: translateX(40px); + background-color: white; + -webkit-animation: rotate-center 0.6s ease-in-out both; + animation: rotate-center 0.6s ease-in-out both; + } + + .moon-dot { + opacity: 0; + transition: 0.4s; + fill: gray; + } + + input:checked + .slider .sun-moon .moon-dot { + opacity: 1; + } + + .slider.round { + border-radius: 34px; + } + + .slider.round .sun-moon { + border-radius: 50%; + } + + #moon-dot-1 { + left: 10px; + top: 3px; + position: absolute; + width: 6px; + height: 6px; + z-index: 4; + } + + #moon-dot-2 { + left: 2px; + top: 10px; + position: absolute; + width: 10px; + height: 10px; + z-index: 4; + } + + #moon-dot-3 { + left: 16px; + top: 18px; + position: absolute; + width: 3px; + height: 3px; + z-index: 4; + } + + #light-ray-1 { + left: -8px; + top: -8px; + position: absolute; + width: 43px; + height: 43px; + z-index: -1; + fill: white; + opacity: 10%; + } + + #light-ray-2 { + left: -50%; + top: -50%; + position: absolute; + width: 55px; + height: 55px; + z-index: -1; + fill: white; + opacity: 10%; + } + + #light-ray-3 { + left: -18px; + top: -18px; + position: absolute; + width: 60px; + height: 60px; + z-index: -1; + fill: white; + opacity: 10%; + } + + .cloud-light { + position: absolute; + fill: #eee; + animation-name: cloud-move; + animation-duration: 6s; + animation-iteration-count: infinite; + } + + .cloud-dark { + position: absolute; + fill: #ccc; + animation-name: cloud-move; + animation-duration: 6s; + animation-iteration-count: infinite; + animation-delay: 1s; + } + + #cloud-1 { + left: 30px; + top: 15px; + width: 40px; + } + + #cloud-2 { + left: 44px; + top: 10px; + width: 20px; + } + + #cloud-3 { + left: 18px; + top: 24px; + width: 30px; + } + + #cloud-4 { + left: 36px; + top: 18px; + width: 40px; + } + + #cloud-5 { + left: 48px; + top: 14px; + width: 20px; + } + + #cloud-6 { + left: 22px; + top: 26px; + width: 30px; + } + + @keyframes cloud-move { + 0% { + transform: translateX(0px); + } + + 40% { + transform: translateX(4px); + } + + 80% { + transform: translateX(-4px); + } + + 100% { + transform: translateX(0px); + } + } + + .stars { + transform: translateY(-32px); + opacity: 0; + transition: 0.4s; + } + + .star { + fill: white; + position: absolute; + -webkit-transition: 0.4s; + transition: 0.4s; + animation-name: star-twinkle; + animation-duration: 2s; + animation-iteration-count: infinite; + } + + input:checked + .slider .stars { + -webkit-transform: translateY(0); + -ms-transform: translateY(0); + transform: translateY(0); + opacity: 1; + } + + #star-1 { + width: 20px; + top: 2px; + left: 3px; + animation-delay: 0.3s; + } + + #star-2 { + width: 6px; + top: 16px; + left: 3px; + } + + #star-3 { + width: 12px; + top: 20px; + left: 10px; + animation-delay: 0.6s; + } + + #star-4 { + width: 18px; + top: 0px; + left: 18px; + animation-delay: 1.3s; + } + + @keyframes star-twinkle { + 0% { + transform: scale(1); + } + + 40% { + transform: scale(1.2); + } + + 80% { + transform: scale(0.8); + } + + 100% { + transform: scale(1); + } + } + + @keyframes rotate-center { + 0% { + transform: translateX(40px) rotate(0); + } + 100% { + transform: translateX(40px) rotate(360deg); + } + } +`; + +export default DarkModeToggle; \ No newline at end of file diff --git a/src/components/loader.tsx b/src/components/loader.tsx new file mode 100644 index 0000000..e3ef6ce --- /dev/null +++ b/src/components/loader.tsx @@ -0,0 +1,273 @@ +import React from 'react'; +import styled from 'styled-components'; + +const Loader = () => { + return ( + +
+
+
+
+
+
+
X
+
+
+
*
+
+
+
+
+ + ); +} + +const StyledWrapper = styled.div` + .loading { + display: flex; + justify-content: center; + align-items: center; + } + + .loading-wide { + width: 150px; + height: 150px; + display: flex; + justify-content: center; + align-items: center; + position: relative; + } + + .color { + background-color: #4285f4; /* Google Blue */ + } + + .l1 { + width: 15px; + height: 65px; + position: absolute; + animation: move-h 1.2s infinite cubic-bezier(0.65, 0.05, 0.36, 1); + background-color: #f4b400; /* Google Yellow */ + } + + .l2 { + width: 15px; + height: 60px; + position: absolute; + transform: rotate(90deg); + animation: move-v 1.2s infinite cubic-bezier(0.65, 0.05, 0.36, 1); + background-color: #db4437; /* Google Red */ + } + + @keyframes move-h { + 0% { + top: 0; + opacity: 0; + } + 25% { + opacity: 1; + } + 50% { + top: 30%; + opacity: 1; + } + 75% { + opacity: 1; + } + 100% { + top: 100%; + opacity: 0; + } + } + + @keyframes move-v { + 0% { + left: 0; + opacity: 0; + } + 25% { + opacity: 1; + } + 50% { + left: 45%; + opacity: 1; + } + 75% { + opacity: 1; + } + 100% { + left: 100%; + opacity: 0; + } + } + + .animation-effect-light { + animation: effect 0.2s 0.1s infinite linear; + } + .animation-effect-light-d { + animation: effect 0.3s 0.2s infinite linear; + } + .animation-effect-rot { + animation: rot 0.8s infinite cubic-bezier(0.65, 0.05, 0.36, 1); + } + .animation-effect-scale { + animation: scale 0.8s infinite cubic-bezier(0.65, 0.05, 0.36, 1); + } + + @keyframes effect { + 0% { + opacity: 0; + } + 50% { + opacity: 1; + } + 100% { + opacity: 0; + } + } + + @keyframes rot { + 0% { + transform: rotate(0deg); + } + 50% { + transform: rotate(180deg); + } + 100% { + transform: rotate(360deg); + } + } + + @keyframes scale { + 0% { + scale: 1; + } + 50% { + scale: 1.9; + } + 100% { + scale: 1; + } + } + + .e1 { + width: 1px; + height: 40px; + opacity: 0.3; + position: absolute; + top: 0; + left: 8%; + background-color: #0f9d58; /* Google Green */ + } + + .e2 { + width: 60px; + height: 1px; + opacity: 0.8; + position: absolute; + top: 8%; + left: 0; + background-color: #4285f4; /* Google Blue */ + } + + .e3 { + position: absolute; + top: 10%; + left: 12%; + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + font-weight: 900; + font-size: 18px; + color: #4285f4; /* Google Blue */ + } + + .e4 { + width: 1px; + height: 40px; + opacity: 0.3; + position: absolute; + top: 90%; + right: 10%; + background-color: #db4437; /* Google Red */ + } + + .e5 { + width: 40px; + height: 1px; + opacity: 0.3; + position: absolute; + top: 100%; + right: 0; + background-color: #f4b400; /* Google Yellow */ + } + + .e6 { + position: absolute; + top: 100%; + right: 0; + font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; + font-size: 32px; + color: #0f9d58; /* Google Green */ + } + + .e7 { + width: 1px; + height: 20px; + position: absolute; + bottom: 0; + left: 0; + transform: rotate(45deg); + animation: height 1s infinite cubic-bezier(0.65, 0.05, 0.36, 1); + background-color: #f4b400; /* Google Yellow */ + } + + @keyframes height { + 0% { + bottom: 0%; + left: 0%; + height: 0px; + } + 25% { + height: 90px; + } + 50% { + bottom: 100%; + left: 100%; + height: 90px; + } + 75% { + height: 0px; + } + 100% { + bottom: 0%; + left: 0%; + height: 0px; + } + } + + .e8 { + width: 20px; + height: 1px; + position: absolute; + bottom: 50%; + left: 0; + animation: width 1.5s infinite cubic-bezier(0.65, 0.05, 0.36, 1); + background-color: #0f9d58; /* Google Green */ + } + + @keyframes width { + 0% { + left: 0%; + width: 0px; + } + 50% { + left: 100%; + width: 90px; + } + 100% { + left: 0%; + width: 0px; + } + } +`; + +export default Loader; \ No newline at end of file diff --git a/src/env.js b/src/env.js index 605fbdd..5dcd177 100644 --- a/src/env.js +++ b/src/env.js @@ -30,6 +30,11 @@ export const env = createEnv({ ), GOOGLE_CLIENT_ID: z.string(), GOOGLE_CLIENT_SECRET: z.string(), + // Typesense environment variables + TYPESENSE_HOST: z.string(), + TYPESENSE_PORT: z.coerce.number(), + TYPESENSE_PROTOCOL: z.string(), + TYPESENSE_API_KEY: z.string(), }, /** @@ -52,6 +57,10 @@ export const env = createEnv({ NEXTAUTH_URL: process.env.NEXTAUTH_URL, GOOGLE_CLIENT_ID: process.env.GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET: process.env.GOOGLE_CLIENT_SECRET, + TYPESENSE_HOST: process.env.TYPESENSE_HOST, + TYPESENSE_PORT: process.env.TYPESENSE_PORT, + TYPESENSE_PROTOCOL: process.env.TYPESENSE_PROTOCOL, + TYPESENSE_API_KEY: process.env.TYPESENSE_API_KEY, }, /** * Run `build` or `dev` with `SKIP_ENV_VALIDATION` to skip env validation. This is especially @@ -63,4 +72,4 @@ export const env = createEnv({ * `SOME_VAR=''` will throw an error. */ emptyStringAsUndefined: true, -}); +}); \ No newline at end of file diff --git a/src/pages/[course].tsx b/src/pages/[course].tsx new file mode 100644 index 0000000..4c41bdb --- /dev/null +++ b/src/pages/[course].tsx @@ -0,0 +1,72 @@ +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; +import CourseContent from '~/components/CourseContent'; +import { Box, Typography, CircularProgress } from '@mui/material'; +import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline'; +import ScrollToTopButton from '~/components/ScrollToTopButton'; +import { useSession } from 'next-auth/react'; + +const CoursePage = ({ data }) => { + const { data: session, status } = useSession(); + const router = useRouter(); + const { course } = router.query; + if (status === "unauthenticated") { + router.push("/"); + } + const [courseData, setCourseData] = useState(null); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + if (data && course) { + // Find the course in the data array + const foundCourse = data.find(item => item.name === course); + + if (foundCourse) { + setCourseData(foundCourse); + setError(null); + } else { + setError('Course not found'); + } + setLoading(false); + } + }, [data, course]); + + if (loading) { + return ( + + + + ); + } + + if (error) { + return ( + + + + {error} + + + The requested course could not be found. Please check the URL and try again. + + + ); + } + + return ( + + + + + ); +}; + +export default CoursePage; \ No newline at end of file diff --git a/src/pages/_app.tsx b/src/pages/_app.tsx index 6f47c2c..09787c9 100644 --- a/src/pages/_app.tsx +++ b/src/pages/_app.tsx @@ -44,7 +44,7 @@ const MyApp = ({ Component, pageProps: { session, ...pageProps } }: AppProps) => collapsed={collapsed} > - Swarthmore A11yGator + AccessibleDocs :: Swarthmore College diff --git a/src/pages/access-denied.tsx b/src/pages/access-denied.tsx index 245eaf2..6f163c4 100644 --- a/src/pages/access-denied.tsx +++ b/src/pages/access-denied.tsx @@ -4,9 +4,9 @@ import Link from 'next/link'; export default function AccessDenied() { return (
-
+
Swarthmore College { } return ( -
+
-
+
{
- Swarthmore Logo -
+ Swarthmore Logo + {/*
{data.length === 0 && (

No course documents available at the moment.

)} -
+
*/}
); diff --git a/src/pages/viewer/[...params].tsx b/src/pages/viewer/[...params].tsx new file mode 100644 index 0000000..34f8532 --- /dev/null +++ b/src/pages/viewer/[...params].tsx @@ -0,0 +1,521 @@ +import React, { useEffect, useState, useRef } from 'react'; +import { useRouter } from 'next/router'; +import axios from 'axios'; +import cheerio from 'cheerio'; +import Head from 'next/head'; +import { Typography, Breadcrumbs, Button, Box } from '@mui/material'; +import HomeIcon from '@mui/icons-material/Home'; +import ArrowBackIcon from '@mui/icons-material/ArrowBack'; +import GetAppIcon from '@mui/icons-material/GetApp'; +import Link from 'next/link'; +import { api } from "~/utils/api"; +import styled from 'styled-components'; +import { useSession } from 'next-auth/react'; + + +const DocumentViewer = ({ data }) => { + const { data: session, status } = useSession(); + const router = useRouter(); + if (status === "unauthenticated") { + router.push("/"); + } + const { mutateAsync: generatePdf, isLoading } = api.pdf.generatePdf.useMutation(); + const [content, setContent] = useState(''); + const [pageTitle, setPageTitle] = useState(''); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const contentRef = useRef(null); + const { params } = router.query; + + useEffect(() => { + if (!router.isReady || !params || params.length < 3) return; + + setLoading(true); + setError(null); + + const [courseName, encodedFolderName, encodedFileName] = params; + const folderName = decodeURIComponent(encodedFolderName); + const fileName = decodeURIComponent(encodedFileName); + + const storageUrl = 'https://storage.googleapis.com/a11y'; + const url = `${storageUrl}/${courseName}/${folderName}/${fileName}`; + + axios.get(url) + .then((res) => { + const fetchedContent = res.data; + const $ = cheerio.load(fetchedContent); + + // Adjust image paths + $('img').each(function () { + const oldSrc = $(this).attr('src'); + if (oldSrc && !oldSrc.startsWith('http')) { + const newSrc = `${storageUrl}/${courseName}/${folderName}/${oldSrc}`; + $(this).attr('src', newSrc); + } + }); + + // Extract and set page title + const title = $('title').text() || fileName.replace('.html', ''); + setPageTitle(title); + $('title').remove(); + + // Set content + setContent($.html()); + }) + .catch((err) => { + console.error('Error loading document:', err); + setError('Failed to load document. Please try again later.'); + }) + .finally(() => { + setLoading(false); + }); + }, [router.isReady, params]); + + const handleDownloadPDF = async () => { + try { + const base64pdf = await generatePdf({ htmlContent: content }); + const link = document.createElement('a'); + link.href = `data:application/pdf;base64,${base64pdf}`; + link.download = `${params?.[2] || 'document'}.pdf`; + document.body.appendChild(link); + link.click(); + document.body.removeChild(link); + } catch (error) { + console.error('Error generating PDF:', error); + } + }; + + // Go back to the course page + const goBackToCourse = () => { + if (params && params.length > 0) { + router.push(`/${params[0]}`); + } else { + router.push('/'); + } + }; + + if (loading) { + return ( + + + Loading document... + + ); + } + + if (error) { + return ( + + + + + + + + + + Error Loading Document + + + {error} + + + + ); + } + + return ( + + + {`${pageTitle} :: AccessibleDocs :: Swarthmore College` || 'Document Viewer'} + + + + + + + + + + + {params && params.length > 0 && ( + + + {params[0]} + + + )} + {params && params.length > 1 && ( + + {decodeURIComponent(params[2]).replace('.html', '')} + + )} + + + {/* + {pageTitle || (params && params.length > 2 ? decodeURIComponent(params[2]).replace('.html', '') : 'Document')} + */} + + + + + + } + onClick={goBackToCourse} + aria-label="Go back to course" + className="back-button" + > + Back to Course + + } + onClick={handleDownloadPDF} + disabled={isLoading} + aria-label="Download as PDF" + className="download-button" + > + Download PDF + + + + + ); +}; + +// Styled Components +const Container = styled.div` + padding: 2rem; + max-width: 1200px; + margin: 0 auto; + min-height: 100vh; + + @media (max-width: 768px) { + padding: 1rem; + } +`; + +const ContentWrapper = styled.div` + background-color: white; + border-radius: 12px; + box-shadow: 0 15px 35px rgba(0, 0, 0, 0.25), 0 5px 15px rgba(0, 0, 0, 0.15); + overflow: hidden; + + .dark & { + background-color: #1e293b; + box-shadow: 0 15px 35px rgba(0, 0, 0, 0.5), 0 5px 15px rgba(0, 0, 0, 0.3); + } +`; + +const NavigationHeader = styled.div` + padding: 1.5rem 2rem; + border-bottom: 1px solid rgba(0, 0, 0, 0.1); + + .dark & { + border-bottom-color: rgba(255, 255, 255, 0.1); + } + + @media (max-width: 768px) { + padding: 1rem; + } +`; + +const StyledBreadcrumbs = styled(Breadcrumbs)` + .MuiBreadcrumbs-separator { + color: #666; + + .dark & { + color: #00FFFF; + } + } +`; + +const DocumentTitle = styled(Typography)` + margin-top: 0.75rem; + font-weight: 600; + color: #333; + + .dark & { + color: #f3f4f6; + } +`; + +const BreadcrumbLink = styled.a` + display: flex; + align-items: center; + color: #3b82f6; /* Changed from pink to blue */ + text-decoration: none; + + &:hover { + text-decoration: underline; + color: #2563eb; /* Darker blue on hover */ + } + + .dark & { + color: #00FFFF; /* Lighter blue for dark mode */ + + &:hover { + color: #93c5fd; /* Even lighter blue on hover in dark mode */ + } + } +`; + +const DocumentContent = styled.div` + padding: 2rem; + overflow-x: auto; + color: #333; + line-height: 1.6; + + /* Basic HTML content styling */ + h1, h2, h3, h4, h5, h6 { + margin-top: 1.5rem; + margin-bottom: 1rem; + font-weight: 600; + color: #111; + } + + p { + margin-bottom: 1.25rem; + } + + a { + color: #3b82f6; /* Updated from pink to blue */ + text-decoration: underline; + } + + ul, ol { + margin-bottom: 1.25rem; + padding-left: 1.5rem; + } + + img { + max-width: 100%; + height: auto; + margin: 1.5rem 0; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + } + + blockquote { + margin: 1.5rem 0; + padding: 1rem 1.5rem; + border-left: 4px solid #3b82f6; /* Changed from pink to blue */ + background-color: #f8f8f8; + color: #555; + font-style: italic; + } + + code { + background-color: #f1f1f1; + padding: 0.2rem 0.4rem; + border-radius: 4px; + font-family: monospace; + font-size: 0.9em; + } + + pre { + background-color: #f1f1f1; + padding: 1rem; + border-radius: 4px; + overflow-x: auto; + margin: 1.5rem 0; + } + + table { + width: 100%; + border-collapse: collapse; + margin: 1.5rem 0; + } + + th, td { + border: 1px solid #e0e0e0; + padding: 0.75rem; + text-align: left; + } + + th { + background-color: #f5f5f5; + font-weight: bold; + } + + /* Dark mode styling */ + .dark & { + color: #e5e7eb; + + h1, h2, h3, h4, h5, h6 { + color: #f3f4f6; + } + + a { + color: #60a5fa; /* Lighter blue for dark mode */ + } + + blockquote { + background-color: #2a3546; + border-left-color: #60a5fa; /* Lighter blue for dark mode */ + color: #d1d5db; + } + + code, pre { + background-color: #2d3748; + } + + table, th, td { + border-color: #4b5563; + } + + th { + background-color: #374151; + } + + img { + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3); + } + } + + @media (max-width: 768px) { + padding: 1.5rem 1rem; + } +`; + +const ActionButtonsContainer = styled.div` + display: flex; + padding: 1.5rem 2rem; + border-top: 1px solid rgba(0, 0, 0, 0.1); + gap: 1rem; + flex-wrap: wrap; + + .dark & { + border-top-color: rgba(255, 255, 255, 0.1); + } + + @media (max-width: 768px) { + padding: 1rem; + flex-direction: column; + } +`; + +const ActionButton = styled(Button)` + &.back-button { + background-color: #3b82f6; /* Changed from red to blue */ + + &:hover { + background-color: #2563eb; /* Darker blue on hover */ + } + + .dark & { + background-color: #1d4ed8; + + &:hover { + background-color: #1e40af; + } + } + } + + &.download-button { + background-color: #2c7a7b; + + &:hover { + background-color: #24676a; + } + + .dark & { + background-color: #065f60; + + &:hover { + background-color: #044e4f; + } + } + } + + &:focus-visible { + outline: 2px solid #FFD700; + outline-offset: 2px; + + .dark & { + outline-color: #FFB700; + } + } +`; + +const LoadingContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-height: 50vh; + padding: 2rem; +`; + +const ErrorContainer = styled.div` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + min-height: 50vh; + max-width: 600px; + margin: 0 auto; + padding: 2rem; +`; + +const ErrorIcon = styled.div` + width: 64px; + height: 64px; + color: #ef4444; /* Changed from pink to red */ + margin-bottom: 1.5rem; + + .dark & { + color: #f87171; /* Lighter red for dark mode */ + } + + svg { + width: 100%; + height: 100%; + } +`; + +const Spinner = styled.div` + width: 50px; + height: 50px; + border: 5px solid rgba(0, 0, 0, 0.1); + border-top-color: #3b82f6; /* Changed from pink to blue */ + border-radius: 50%; + animation: spin 1s linear infinite; + margin-bottom: 1rem; + + @keyframes spin { + to { + transform: rotate(360deg); + } + } + + .dark & { + border-color: rgba(255, 255, 255, 0.1); + border-top-color: #60a5fa; /* Lighter blue for dark mode */ + } +`; + +const LoadingText = styled.p` + color: #666; + font-size: 1.1rem; + + .dark & { + color: #9ca3af; + } +`; + +export default DocumentViewer; \ No newline at end of file diff --git a/src/server/api/root.ts b/src/server/api/root.ts index ef74ee0..3470e48 100644 --- a/src/server/api/root.ts +++ b/src/server/api/root.ts @@ -1,8 +1,10 @@ +// src/server/api/root.ts import { postRouter } from "~/server/api/routers/post"; import { createTRPCRouter } from "~/server/api/trpc"; import { pdfRouter } from "./routers/pdfRouter"; import { fetchDataRouter } from "./routers/dataFetchRouter"; -import { storageRouter } from "./routers/storageRouter"; +import { searchRouter } from "./routers/searchRouter"; + /** * This is the primary router for your server. * @@ -12,9 +14,8 @@ export const appRouter = createTRPCRouter({ post: postRouter, pdf: pdfRouter, fetchData: fetchDataRouter, - storage: storageRouter, - + search: searchRouter, }); // export type definition of API -export type AppRouter = typeof appRouter; +export type AppRouter = typeof appRouter; \ No newline at end of file diff --git a/src/server/api/routers/searchRouter.ts b/src/server/api/routers/searchRouter.ts new file mode 100644 index 0000000..10eac3f --- /dev/null +++ b/src/server/api/routers/searchRouter.ts @@ -0,0 +1,53 @@ +// src/server/api/routers/searchRouter.ts +import { z } from "zod"; +import { createTRPCRouter, publicProcedure } from "~/server/api/trpc"; +import { Client } from "typesense"; +import { env } from "~/env"; + +// Typesense client +const typesense = new Client({ + nodes: [{ + host: env.TYPESENSE_HOST, + port: env.TYPESENSE_PORT, + protocol: env.TYPESENSE_PROTOCOL + }], + apiKey: env.TYPESENSE_API_KEY, + connectionTimeoutSeconds: 5 +}); + +export const searchRouter = createTRPCRouter({ + search: publicProcedure + .input( + z.object({ + query: z.string(), + queryBy: z.string().optional(), + filterBy: z.string().optional(), + perPage: z.number().optional(), + facetBy: z.string().optional(), + }) + ) + .mutation(async ({ input }) => { + try { + const searchParams = { + q: input.query, + query_by: input.queryBy || 'course_name,folder_name,document_name,content', + highlight_full_fields: 'content,course_name,folder_name,document_name', + highlight_affix_num_tokens: 10, + highlight_start_tag: '', + highlight_end_tag: '', + facet_by: input.facetBy || 'semester_code,department', + filter_by: input.filterBy || '', + per_page: input.perPage || 20 + }; + + const results = await typesense + .collections('course_documents') + .documents() + .search(searchParams); + + return results; + } catch (error) { + throw new Error(`Search failed: ${error.message}`); + } + }), +}); \ No newline at end of file diff --git a/src/server/auth.ts b/src/server/auth.ts index 8c9466e..4c53d5a 100644 --- a/src/server/auth.ts +++ b/src/server/auth.ts @@ -27,10 +27,10 @@ declare module "next-auth" { }; } - // interface User { - // // ...other properties - // // role: UserRole; - // } + interface User { + // ...other properties + role: string; + } } /** @@ -41,33 +41,22 @@ declare module "next-auth" { export const authOptions: NextAuthOptions = { callbacks: { async signIn({ account, profile }) { - if (account.provider === "google") { - if (profile.email_verified && profile.email.endsWith("@swarthmore.edu")) { - return true; // Allow login - } else { - return '/access-denied'; - } + if (account?.provider === "google") { + return profile?.email_verified && + profile?.email?.endsWith("@swarthmore.edu") ? + true : '/access-denied'; } - return true; + return true; }, - session: ({ session, user }) => ({ - ...session, - user: { - ...session.user, - id: user.id, - }, - }), - }, - adapter: PrismaAdapter(db) as Adapter, - callbacks: { async session({ session, user }) { - // Include the user's role in the session if (session.user) { - session.user.role = user.role; + session.user.id = user.id; + session.user.role = user.role || "user"; // Default to "user" if role is undefined } return session; }, }, + adapter: PrismaAdapter(db) as Adapter, providers: [ GoogleProvider({ clientId: env.GOOGLE_CLIENT_ID, @@ -76,10 +65,7 @@ export const authOptions: NextAuthOptions = { /** * ...add more providers here. * - * Most other providers require a bit more work than the GOOGLE provider. For example, the - * GOOGLE provider requires you to add the `refresh_token_expires_in` field to the Account - * model. Refer to the NextAuth.js docs for the provider you want to use. Example: - * + * Most other providers require a bit more work than the Google provider. * @see https://next-auth.js.org/providers/google */ ], @@ -95,4 +81,4 @@ export const getServerAuthSession = (ctx: { res: GetServerSidePropsContext["res"]; }) => { return getServerSession(ctx.req, ctx.res, authOptions); -}; +}; \ No newline at end of file