diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 535334f2..6003fc56 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -65,10 +65,10 @@ importers: version: 4.1.0 drizzle-orm: specifier: ^0.37.0 - version: 0.37.0(@aws-sdk/client-rds-data@3.556.0(aws-crt@1.21.2))(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@18.3.13)(kysely@0.25.0)(pg@8.13.1)(react@18.3.1) + version: 0.37.0(@aws-sdk/client-rds-data@3.556.0)(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@18.3.14)(kysely@0.25.0)(pg@8.13.1)(react@18.3.1) express: specifier: ^4.21.1 - version: 4.21.1 + version: 4.21.2 express-session: specifier: ^1.18.1 version: 1.18.1 @@ -156,7 +156,7 @@ importers: version: 0.88.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@reduxjs/toolkit': specifier: ^2.4.0 - version: 2.4.0(react-redux@9.1.2(@types/react@18.3.13)(react@18.3.1)(redux@5.0.1))(react@18.3.1) + version: 2.4.0(react-redux@9.1.2(@types/react@18.3.14)(react@18.3.1)(redux@5.0.1))(react@18.3.1) '@trpc/client': specifier: ^10.45.2 version: 10.45.2(@trpc/server@10.45.2) @@ -169,9 +169,6 @@ importers: react: specifier: ^18.3.1 version: 18.3.1 - react-beautiful-dnd: - specifier: ^13.1.1 - version: 13.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) react-bootstrap: specifier: ^1.6.8 version: 1.6.8(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -195,13 +192,16 @@ importers: version: 2.8.5 react-redux: specifier: ^9.1.2 - version: 9.1.2(@types/react@18.3.13)(react@18.3.1)(redux@5.0.1) + version: 9.1.2(@types/react@18.3.14)(react@18.3.1)(redux@5.0.1) react-responsive: specifier: ^10.0.0 version: 10.0.0(react@18.3.1) react-router-dom: specifier: ^7.0.2 version: 7.0.2(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react-sortablejs: + specifier: ^6.1.4 + version: 6.1.4(@types/sortablejs@1.15.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sortablejs@1.15.6) react-transition-group: specifier: ^4.4.5 version: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -223,13 +223,10 @@ importers: version: link:../types '@types/react': specifier: ^18.3.12 - version: 18.3.13 - '@types/react-beautiful-dnd': - specifier: ^13.1.8 - version: 13.1.8 + version: 18.3.14 '@types/react-dom': specifier: ^18.3.1 - version: 18.3.1 + version: 18.3.2 '@types/react-google-recaptcha': specifier: ^2.1.9 version: 2.1.9 @@ -250,7 +247,7 @@ importers: version: 7.18.0(eslint@8.57.1)(typescript@5.7.2) '@vitejs/plugin-react': specifier: ^4.3.4 - version: 4.3.4(vite@6.0.2(@types/node@20.12.8)(sass@1.82.0)(tsx@4.19.2)(yaml@2.5.1)) + version: 4.3.4(vite@6.0.3(@types/node@20.12.8)(sass@1.82.0)(tsx@4.19.2)(yaml@2.5.1)) eslint: specifier: ^8.57.1 version: 8.57.1 @@ -277,7 +274,7 @@ importers: version: 5.7.2 vite: specifier: ^6.0.2 - version: 6.0.2(@types/node@20.12.8)(sass@1.82.0)(tsx@4.19.2)(yaml@2.5.1) + version: 6.0.3(@types/node@20.12.8)(sass@1.82.0)(tsx@4.19.2)(yaml@2.5.1) types: dependencies: @@ -517,8 +514,8 @@ packages: resolution: {integrity: sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.26.3': - resolution: {integrity: sha512-yTmc8J+Sj8yLzwr4PD5Xb/WF3bOYu2C2OoSZPzbuqRm4n98XirsbzaX+GloeO376UnSYIYJ4NCanwV5/ugZkwA==} + '@babel/traverse@7.26.4': + resolution: {integrity: sha512-fH+b7Y4p3yqvApJALCPJcwb0/XaOSgtK4pzV6WVjPR5GLFQBRI7pfoX2V2iM48NXvX07NUxxm1Vw98YjqTcU5w==} engines: {node: '>=6.9.0'} '@babel/types@7.24.5': @@ -1136,9 +1133,6 @@ packages: react: ^16.8.0 || ^17 || ^18 react-dom: ^16.8.0 || ^17 || ^18 - '@httptoolkit/websocket-stream@6.0.1': - resolution: {integrity: sha512-A0NOZI+Glp3Xgcz6Na7i7o09+/+xm2m0UCU8gdtM2nIv6/cjLmhMZMqehSpTlgbx9omtLmV8LVqOskPEyWnmZQ==} - '@humanwhocodes/config-array@0.11.14': resolution: {integrity: sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==} engines: {node: '>=10.10.0'} @@ -1765,26 +1759,20 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@types/react-beautiful-dnd@13.1.8': - resolution: {integrity: sha512-E3TyFsro9pQuK4r8S/OL6G99eq7p8v29sX0PM7oT8Z+PJfZvSQTx4zTQbUJ+QZXioAF0e7TGBEcA1XhYhCweyQ==} - - '@types/react-dom@18.3.1': - resolution: {integrity: sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==} + '@types/react-dom@18.3.2': + resolution: {integrity: sha512-Fqp+rcvem9wEnGr3RY8dYNvSQ8PoLqjZ9HLgaPUOjJJD120uDyOxOjc/39M4Kddp9JQCxpGQbnhVQF0C0ncYVg==} '@types/react-google-recaptcha@2.1.9': resolution: {integrity: sha512-nT31LrBDuoSZJN4QuwtQSF3O89FVHC4jLhM+NtKEmVF5R1e8OY0Jo4//x2Yapn2aNHguwgX5doAq8Zo+Ehd0ug==} - '@types/react-redux@7.1.33': - resolution: {integrity: sha512-NF8m5AjWCkert+fosDsN3hAlHzpjSiXlVy9EgQEmLoBhaNXbmyeGs/aj5dQzKuF+/q+S7JQagorGDW8pJ28Hmg==} - '@types/react-transition-group@4.4.11': resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} '@types/react-twemoji@0.4.3': resolution: {integrity: sha512-VzWkjmO8yJ31JSWgQhe0LCLU3FxcurTOklECm9p8v1xI/WfUBOGbSrqPjQPL46LE4oOqfrYIQ+0LM/8I6A8EDQ==} - '@types/react@18.3.13': - resolution: {integrity: sha512-ii/gswMmOievxAJed4PAHT949bpYjPKXvXo1v6cRB/kqc2ZR4n+SgyCyvyc5Fec5ez8VnUumI1Vk7j6fRyRogg==} + '@types/react@18.3.14': + resolution: {integrity: sha512-NzahNKvjNhVjuPBQ+2G7WlxstQ+47kXZNHlUvFakDViuIEfGY926GqhMueQFZ7woG+sPiQKlF36XfrIUVSUfFg==} '@types/semver@7.5.8': resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==} @@ -1795,6 +1783,9 @@ packages: '@types/serve-static@1.15.7': resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} + '@types/sortablejs@1.15.8': + resolution: {integrity: sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg==} + '@types/toastify-js@1.12.3': resolution: {integrity: sha512-9RjLlbAHMSaae/KZNHGv19VG4gcLIm3YjvacCXBtfMfYn26h76YP5oxXI8k26q4iKXCB9LNfv18lsoS0JnFPTg==} @@ -1804,9 +1795,6 @@ packages: '@types/warning@3.0.3': resolution: {integrity: sha512-D1XC7WK8K+zZEveUPY+cf4+kgauk8N4eHr/XIHXGlGYkHLud6hK9lYfZk1ry1TNh798cZUCgb6MqGEG8DkJt6Q==} - '@types/ws@8.5.10': - resolution: {integrity: sha512-vmQSUcfalpIq0R9q7uTo2lXs6eGIpt9wtnLdMv9LVpIjCA/+ufZRozlVoVelIYixx1ugCBKDhn89vnsEGOCx9A==} - '@typescript-eslint/eslint-plugin@7.18.0': resolution: {integrity: sha512-94EQTWZ40mzBc42ATNIBimBEDltSJ9RQHCC8vc/PDbxi4k8dVwUAv4o98dk50M1zB+JGFxp43FP7f8+FP8R6Sw==} engines: {node: ^18.18.0 || >=20.0.0} @@ -2034,16 +2022,10 @@ packages: ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - aws-crt@1.21.2: - resolution: {integrity: sha512-/jq5yJwdethIaC+HyqCrgW2ZqxjUURBo/6CIfnD0HbpIE+vLIPQxENW/pp3Atu20Hm2WkjM9diJD4zJycK+olA==} - aws4fetch@1.0.20: resolution: {integrity: sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g==} @@ -2051,9 +2033,6 @@ packages: resolution: {integrity: sha512-RE3mdQ7P3FRSe7eqCWoeQ/Z9QXrtniSjp1wUjt5nRC3WIpz5rSCve6o3fsZ2aCpJtrZjSZgjwXAoTO5k4tEI0w==} engines: {node: '>=4'} - axios@1.7.9: - resolution: {integrity: sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==} - axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} @@ -2061,9 +2040,6 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - base64url@3.0.1: resolution: {integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==} engines: {node: '>=6.0.0'} @@ -2076,9 +2052,6 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - body-parser@1.20.3: resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} @@ -2117,12 +2090,6 @@ packages: buffer-from@1.1.2: resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - buffer@6.0.3: - resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} - bytes@3.1.2: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} @@ -2161,6 +2128,9 @@ packages: resolution: {integrity: sha512-n8enUVCED/KVRQlab1hr3MVpcVMvxtZjmEa956u+4YijlmQED223XMSYj2tLuKvr4jcCTzNNMpQDUer72MMmzA==} engines: {node: '>= 14.16.0'} + classnames@2.3.1: + resolution: {integrity: sha512-OlQdbZ7gLfGarSqxesMesDa5uz7KFbID8Kpq/SxIoNGDqY8lSYs0D+hhtBXhcdB3rcbXArFr7vlHheLk1voeNA==} + classnames@2.5.1: resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==} @@ -2199,24 +2169,13 @@ packages: colorette@2.0.20: resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - commander@12.1.0: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} - commist@1.1.0: - resolution: {integrity: sha512-rraC8NXWOEjhADbZe9QBNzLAN5Q3fsTPQtBV+fEVj6xKIgDgNiEVE6ZNfHpZOqfQ21YUzfVNUXLOEZquYvQPPg==} - concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - concat-stream@2.0.0: - resolution: {integrity: sha512-MWufYdFw53ccGjCA+Ol7XJYpAlW6/prSMzuPOTRnJGcGzuhLn4Scrz7qf6o8bROZ514ltazcIFJZevcfbo0x7A==} - engines: {'0': node >= 6.0} - concurrently@9.1.0: resolution: {integrity: sha512-VxkzwMAn4LP7WyMnJNbHN5mKV9L2IbyDjpzemKr99sXNR3GqRNMMHdm7prV1ws9wg7ETj6WUkNOigZVsptwbgg==} engines: {node: '>=18'} @@ -2259,19 +2218,10 @@ packages: resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} engines: {node: '>=18'} - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} - crypto-js@4.2.0: - resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} - - css-box-model@1.2.1: - resolution: {integrity: sha512-a7Vr4Q/kd/aw96bnJG332W9V9LkJO69JRcaCYDUqjp6/z0w6VcZjgAcTbgFxEPfBgdnAwlh3iwu+hLopa+flJw==} - css-mediaquery@0.1.2: resolution: {integrity: sha512-COtn4EROW5dBGlE/4PiKnh6rZpAPxDeFLaEEwt4i10jpDMFt2EhQGS79QmmrO+iKCHv0PU/HrOWEhijFd1x99Q==} @@ -2379,10 +2329,6 @@ packages: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - depd@2.0.0: resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} engines: {node: '>= 0.8'} @@ -2532,17 +2478,11 @@ packages: sqlite3: optional: true - duplexify@3.7.1: - resolution: {integrity: sha512-07z8uv2wMyS51kKhD1KsdXJg5WQ6t93RneqRxUHnskXVtlYYkLqM0gqStQZ3pj073g687jPCHrqNfCzawLYh5g==} - - duplexify@4.1.3: - resolution: {integrity: sha512-M3BmBhwJRZsSx38lZyhE53Csddgzl5R7xGJNk7CVddZD6CcmwMCH8J+7AprIrQKH7TonKxaCjcv27Qmf+sQ+oA==} - ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.68: - resolution: {integrity: sha512-FgMdJlma0OzUYlbrtZ4AeXjKxKPk6KT8WOP8BjcqxWtlg8qyJQjRzPJzUtUn5GBg1oQ26hFs7HOOHJMYiJRnvQ==} + electron-to-chromium@1.5.71: + resolution: {integrity: sha512-dB68l59BI75W1BUGVTAEJy45CEVuEGy9qPVVQ8pnHyHMn36PLPPoE1mjLH+lo9rKulO3HC2OhbACI/8tCqJBcA==} emoji-regex@10.3.0: resolution: {integrity: sha512-QpLs9D9v9kArv4lfDEgg1X/gN5XLnf/A6l9cs8SPZLRZR3ZkY9+kwIQTxm+fsSej5UMYGE8fdoaZVIBlqG0XTw==} @@ -2564,9 +2504,6 @@ packages: encoding@0.1.13: resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} @@ -2631,6 +2568,10 @@ packages: engines: {node: '>=18'} hasBin: true + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -2733,8 +2674,8 @@ packages: resolution: {integrity: sha512-a5mtTqEaZvBCL9A9aqkrtfz+3SMDhOVUnjafjo+s7A9Txkq+SVX2DLvSp1Zrv4uCXa3lMSK3viWnh9Gg07PBUA==} engines: {node: '>= 0.8.0'} - express@4.21.1: - resolution: {integrity: sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==} + express@4.21.2: + resolution: {integrity: sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==} engines: {node: '>= 0.10.0'} fast-deep-equal@3.1.3: @@ -2784,22 +2725,9 @@ packages: flatted@3.3.1: resolution: {integrity: sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==} - follow-redirects@1.15.6: - resolution: {integrity: sha512-wWN62YITEaOpSK584EZXJafH1AGpO8RVgElfkuXbTOrPX4fIfOyEpW/CsiNd8JdYrAoOvafRTOEnvsO++qCqFA==} - engines: {node: '>=4.0'} - peerDependencies: - debug: '*' - peerDependenciesMeta: - debug: - optional: true - for-each@0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} - form-data@4.0.0: - resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==} - engines: {node: '>= 6'} - forwarded@0.2.0: resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} engines: {node: '>= 0.6'} @@ -2928,9 +2856,6 @@ packages: resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} hasBin: true - help-me@3.0.0: - resolution: {integrity: sha512-hx73jClhyk910sidBB7ERlnhMlFsJJIBqSVMFDwPN8o2v9nmp5KgLq1Xz1Bf1fCMMZ6mPrX159iG0VLy/fPMtQ==} - hoist-non-react-statics@3.3.2: resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} @@ -2962,9 +2887,6 @@ packages: resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} engines: {node: '>=0.10.0'} - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - ignore-by-default@1.0.1: resolution: {integrity: sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==} @@ -3131,20 +3053,12 @@ packages: resolution: {integrity: sha512-LvIm3/KWzS9oRFHugab7d+M/GcBXuXX5xZkzPmN+NxihdQlZUQ4dWuSV1xR/sq6upL1TJEDrfBgRepHFdBtSNQ==} engines: {node: '>= 0.4'} - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - isomorphic-ws@4.0.1: - resolution: {integrity: sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w==} - peerDependencies: - ws: '*' - iterator.prototype@1.1.3: resolution: {integrity: sha512-FW5iMbeQ6rBGm/oKgzq2aW4KvAGpxPzYES8N4g4xNXUKpL1mclMvOe+76AcLDTvD+Ze+sOpVhgdAQEKF4L9iGQ==} engines: {node: '>= 0.4'} @@ -3165,9 +3079,6 @@ packages: resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} engines: {node: '>=0.10.0'} - js-sdsl@4.3.0: - resolution: {integrity: sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==} - js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -3224,10 +3135,6 @@ packages: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} - leven@2.1.0: - resolution: {integrity: sha512-nvVPLpIHUxCUoRLrFqTgSxXJ614d8AgQoWl7zPe/2VadE8+1dpU3LBhowRuBAcuwruWtOdD8oYC9jDNJjXDPyA==} - engines: {node: '>=0.10.0'} - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -3283,9 +3190,6 @@ packages: resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} engines: {node: '>= 0.6'} - memoize-one@5.2.1: - resolution: {integrity: sha512-zYiwtZUcYyXKo/np96AGZAckk+FWWsUdJ3cHGGmld7+AhvcWmQyGCYUh1hc4Q/pkOhb65dQR/pqCyK0cOaHz4Q==} - merge-descriptors@1.0.3: resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} @@ -3340,21 +3244,10 @@ packages: resolution: {integrity: sha512-KqWh+VchfxcMNRAJjj2tnsSJdNbHsVgnkBhTNrW7AjVo6OvLtxw8zfT9oLw1JSohlFzJ8jCoTgaoXvJ+kHt6fw==} engines: {node: '>=16 || 14 >=14.17'} - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - morgan@1.10.0: resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==} engines: {node: '>= 0.8.0'} - mqtt-packet@6.10.0: - resolution: {integrity: sha512-ja8+mFKIHdB1Tpl6vac+sktqy3gA8t9Mduom1BA75cI+R9AHnZOiaBQwpGiWnaVJLDGRdNhQmFaAqd7tkKSMGA==} - - mqtt@4.3.8: - resolution: {integrity: sha512-2xT75uYa0kiPEF/PE0VPdavmEkoBzMT/UL9moid0rAvlCtV48qBwxD62m7Ld/4j8tSkIO1E/iqRl/S72SEOhOw==} - engines: {node: '>=10.0.0'} - hasBin: true - ms@2.0.0: resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} @@ -3410,9 +3303,6 @@ packages: nth-check@2.1.1: resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} - number-allocator@1.0.14: - resolution: {integrity: sha512-OrL44UTVAvkKdOdRQZIJpLkAdjXGTRda052sN4sO77bKEzYYqWKMBjQvrJFzqygI99gL6Z4u2xctPW1tB8ErvA==} - oauth@0.10.0: resolution: {integrity: sha512-1orQ9MT1vHFGQxhuy7E/0gECD3fd2fCC+PIX+/jgmU/gI3EpRocXtmtvxCO5x3WZ443FLTLFWNDjl5MPJf9u+Q==} @@ -3559,8 +3449,8 @@ packages: path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-to-regexp@0.1.10: - resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} + path-to-regexp@0.1.12: + resolution: {integrity: sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} @@ -3686,13 +3576,6 @@ packages: engines: {node: '>=14'} hasBin: true - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - process@0.11.10: - resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} - engines: {node: '>= 0.6.0'} - prop-types-extra@1.1.1: resolution: {integrity: sha512-59+AHNnHYCdiC+vMwY52WmvP5dM3QLeoumYuEyceQDi9aEhtwN9zIQ2ZNo25sMyXnbh32h+P1ezDsUpUH3JAew==} peerDependencies: @@ -3705,15 +3588,9 @@ packages: resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} engines: {node: '>= 0.10'} - proxy-from-env@1.1.0: - resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} - pstree.remy@1.1.8: resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - pump@3.0.0: - resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} - punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} @@ -3725,9 +3602,6 @@ packages: queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} - raf-schd@4.0.3: - resolution: {integrity: sha512-tQkJl2GRWh83ui2DiPTJz9wEiMN20syf+5oKfB03yYP7ioZcJwsIK8FjrtLwH1m7C7e+Tt2yYBlrOpdT+dyeIQ==} - random-bytes@1.0.0: resolution: {integrity: sha512-iv7LhNVO047HzYR3InF6pUcUsPQiHTM1Qal51DcGSuZFBil1aBBWG5eHPNek7bvILMaYJ/8RU1e8w1AMdHmLQQ==} engines: {node: '>= 0.8'} @@ -3745,13 +3619,6 @@ packages: peerDependencies: react: '>=16.4.1' - react-beautiful-dnd@13.1.1: - resolution: {integrity: sha512-0Lvs4tq2VcrEjEgDXHjT98r+63drkKEgqyxdA7qD3mvKwga6a5SscbdLPO2IExotU1jW8L0Ksdl0Cj2AF67nPQ==} - deprecated: 'react-beautiful-dnd is now deprecated. Context and options: https://github.com/atlassian/react-beautiful-dnd/issues/2672' - peerDependencies: - react: ^16.8.5 || ^17.0.0 || ^18.0.0 - react-dom: ^16.8.5 || ^17.0.0 || ^18.0.0 - react-bootstrap-icons@1.11.4: resolution: {integrity: sha512-lnkOpNEZ/Zr7mNxvjA9efuarCPSgtOuGA55XiRj7ASJnBjb1wEAdtJOd2Aiv9t07r7FLI1IgyZPg9P6jqWD/IA==} peerDependencies: @@ -3791,9 +3658,6 @@ packages: react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} - react-is@17.0.2: - resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} - react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} @@ -3817,18 +3681,6 @@ packages: react: ^16.8.0 || ^17 || ^18 react-dom: ^16.8.0 || ^17 || ^18 - react-redux@7.2.9: - resolution: {integrity: sha512-Gx4L3uM182jEEayZfRbI/G11ZpYdNAnBs70lFVMNdHJI76XYtR+7m0MN+eAs7UHBPhWXcnFPaS+9owSCJQHNpQ==} - peerDependencies: - react: ^16.8.3 || ^17 || ^18 - react-dom: '*' - react-native: '*' - peerDependenciesMeta: - react-dom: - optional: true - react-native: - optional: true - react-redux@9.1.2: resolution: {integrity: sha512-0OA4dhM1W48l3uzmv6B7TXPCGmokUU4p1M44DGN2/D9a1FjVPukVjER1PcPX97jIg6aUeLq1XJo1IpfbgULn0w==} peerDependencies: @@ -3868,6 +3720,14 @@ packages: react-dom: optional: true + react-sortablejs@6.1.4: + resolution: {integrity: sha512-fc7cBosfhnbh53Mbm6a45W+F735jwZ1UFIYSrIqcO/gRIFoDyZeMtgKlpV4DdyQfbCzdh5LoALLTDRxhMpTyXQ==} + peerDependencies: + '@types/sortablejs': '1' + react: '>=16.9.0' + react-dom: '>=16.9.0' + sortablejs: '1' + react-transition-group@4.4.5: resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: @@ -3884,13 +3744,6 @@ packages: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - readdirp@3.6.0: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} @@ -3904,9 +3757,6 @@ packages: peerDependencies: redux: ^5.0.0 - redux@4.2.1: - resolution: {integrity: sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==} - redux@5.0.1: resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} @@ -3921,9 +3771,6 @@ packages: resolution: {integrity: sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw==} engines: {node: '>= 0.4'} - reinterval@1.1.0: - resolution: {integrity: sha512-QIRet3SYrGp0HUHO88jVskiG6seqUGC5iAG7AwI/BV4ypGcuqk9Du6YQBUOUqm9c8pw1eyLoIaONifRua1lsEQ==} - require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -4079,6 +3926,9 @@ packages: resolution: {integrity: sha512-bSiSngZ/jWeX93BqeIAbImyTbEihizcwNjFoRUIY/T1wWQsfsm2Vw1agPKylXvQTU7iASGdHhyqRlqQzfz+Htg==} engines: {node: '>=18'} + sortablejs@1.15.6: + resolution: {integrity: sha512-aNfiuwMEpfBM/CN6LY0ibyhxPfPbyFeBTYJKCvzkJ2GkUpazIt3H+QIPAMHwqQ7tMKaHz1Qj+rJJCqljnf4p3A==} + source-map-js@1.2.0: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} @@ -4094,9 +3944,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - split2@3.2.2: - resolution: {integrity: sha512-9NThjpgZnifTkJpzTZ7Eue85S49QwpNhZTq6GRJwObb6jnLFNGB7Qm73V5HewTROPyxD0C29xqmaI68bQtV+hg==} - split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -4142,9 +3989,6 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} - stream-shift@1.0.3: - resolution: {integrity: sha512-76ORR0DO1o1hlKwTbi/DM3EXWGf3ZJYO8cXX5RJwnul2DEg2oyoZyjLNoQM8WsvZiFKCRfC1O0J7iCvie3RZmQ==} - string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} @@ -4179,12 +4023,6 @@ packages: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} @@ -4227,8 +4065,8 @@ packages: text-table@0.2.0: resolution: {integrity: sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==} - tiny-invariant@1.3.3: - resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tiny-invariant@1.2.0: + resolution: {integrity: sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==} to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} @@ -4308,9 +4146,6 @@ packages: resolution: {integrity: sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g==} engines: {node: '>= 0.4'} - typedarray@0.0.6: - resolution: {integrity: sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==} - typescript@5.7.2: resolution: {integrity: sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==} engines: {node: '>=14.17'} @@ -4360,19 +4195,11 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - use-memo-one@1.1.3: - resolution: {integrity: sha512-g66/K7ZQGYrI6dy8GLpVcMsBp4s17xNkYJVSMvTEevGy3nDxHOfE6z8BVE22+5G5x7t3+bhzrlTDB7ObrEE0cQ==} - peerDependencies: - react: ^16.8.0 || ^17.0.0 || ^18.0.0 - use-sync-external-store@1.2.2: resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -4385,8 +4212,8 @@ packages: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} - vite@6.0.2: - resolution: {integrity: sha512-XdQ+VsY2tJpBsKGs0wf3U/+azx8BBpYRHFAyKm5VeEZNOJZRB63q7Sc8Iup3k0TrN3KO6QgyzFf+opSbfY1y0g==} + vite@6.0.3: + resolution: {integrity: sha512-Cmuo5P0ENTN6HxLSo6IHsjCLn/81Vgrp81oaiFFMRa8gGDj5xEjIcEpf2ZymZtZR8oU0P2JX5WuUp/rlXcHkAw==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -4469,30 +4296,6 @@ packages: wrappy@1.0.2: resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - ws@7.5.9: - resolution: {integrity: sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==} - engines: {node: '>=8.3.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - - ws@8.16.0: - resolution: {integrity: sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==} - engines: {node: '>=10.0.0'} - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: '>=5.0.2' - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} @@ -4573,13 +4376,13 @@ snapshots: tslib: 1.14.1 optional: true - '@aws-sdk/client-rds-data@3.556.0(aws-crt@1.21.2)': + '@aws-sdk/client-rds-data@3.556.0': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2) + '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) '@aws-sdk/core': 3.556.0 - '@aws-sdk/credential-provider-node': 3.556.0(aws-crt@1.21.2) + '@aws-sdk/credential-provider-node': 3.556.0 '@aws-sdk/middleware-host-header': 3.535.0 '@aws-sdk/middleware-logger': 3.535.0 '@aws-sdk/middleware-recursion-detection': 3.535.0 @@ -4588,7 +4391,7 @@ snapshots: '@aws-sdk/types': 3.535.0 '@aws-sdk/util-endpoints': 3.540.0 '@aws-sdk/util-user-agent-browser': 3.535.0 - '@aws-sdk/util-user-agent-node': 3.535.0(aws-crt@1.21.2) + '@aws-sdk/util-user-agent-node': 3.535.0 '@smithy/config-resolver': 2.2.0 '@smithy/core': 1.4.2 '@smithy/fetch-http-handler': 2.5.0 @@ -4619,13 +4422,13 @@ snapshots: - aws-crt optional: true - '@aws-sdk/client-sso-oidc@3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2)': + '@aws-sdk/client-sso-oidc@3.556.0(@aws-sdk/credential-provider-node@3.556.0)': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 - '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2) + '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) '@aws-sdk/core': 3.556.0 - '@aws-sdk/credential-provider-node': 3.556.0(aws-crt@1.21.2) + '@aws-sdk/credential-provider-node': 3.556.0 '@aws-sdk/middleware-host-header': 3.535.0 '@aws-sdk/middleware-logger': 3.535.0 '@aws-sdk/middleware-recursion-detection': 3.535.0 @@ -4634,7 +4437,7 @@ snapshots: '@aws-sdk/types': 3.535.0 '@aws-sdk/util-endpoints': 3.540.0 '@aws-sdk/util-user-agent-browser': 3.535.0 - '@aws-sdk/util-user-agent-node': 3.535.0(aws-crt@1.21.2) + '@aws-sdk/util-user-agent-node': 3.535.0 '@smithy/config-resolver': 2.2.0 '@smithy/core': 1.4.2 '@smithy/fetch-http-handler': 2.5.0 @@ -4665,7 +4468,7 @@ snapshots: - aws-crt optional: true - '@aws-sdk/client-sso@3.556.0(aws-crt@1.21.2)': + '@aws-sdk/client-sso@3.556.0': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 @@ -4678,7 +4481,7 @@ snapshots: '@aws-sdk/types': 3.535.0 '@aws-sdk/util-endpoints': 3.540.0 '@aws-sdk/util-user-agent-browser': 3.535.0 - '@aws-sdk/util-user-agent-node': 3.535.0(aws-crt@1.21.2) + '@aws-sdk/util-user-agent-node': 3.535.0 '@smithy/config-resolver': 2.2.0 '@smithy/core': 1.4.2 '@smithy/fetch-http-handler': 2.5.0 @@ -4709,12 +4512,12 @@ snapshots: - aws-crt optional: true - '@aws-sdk/client-sts@3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2)': + '@aws-sdk/client-sts@3.556.0(@aws-sdk/credential-provider-node@3.556.0)': dependencies: '@aws-crypto/sha256-browser': 3.0.0 '@aws-crypto/sha256-js': 3.0.0 '@aws-sdk/core': 3.556.0 - '@aws-sdk/credential-provider-node': 3.556.0(aws-crt@1.21.2) + '@aws-sdk/credential-provider-node': 3.556.0 '@aws-sdk/middleware-host-header': 3.535.0 '@aws-sdk/middleware-logger': 3.535.0 '@aws-sdk/middleware-recursion-detection': 3.535.0 @@ -4723,7 +4526,7 @@ snapshots: '@aws-sdk/types': 3.535.0 '@aws-sdk/util-endpoints': 3.540.0 '@aws-sdk/util-user-agent-browser': 3.535.0 - '@aws-sdk/util-user-agent-node': 3.535.0(aws-crt@1.21.2) + '@aws-sdk/util-user-agent-node': 3.535.0 '@smithy/config-resolver': 2.2.0 '@smithy/core': 1.4.2 '@smithy/fetch-http-handler': 2.5.0 @@ -4786,13 +4589,13 @@ snapshots: tslib: 2.6.2 optional: true - '@aws-sdk/credential-provider-ini@3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2)': + '@aws-sdk/credential-provider-ini@3.556.0(@aws-sdk/credential-provider-node@3.556.0)': dependencies: - '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2) + '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) '@aws-sdk/credential-provider-env': 3.535.0 '@aws-sdk/credential-provider-process': 3.535.0 - '@aws-sdk/credential-provider-sso': 3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2) - '@aws-sdk/credential-provider-web-identity': 3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2) + '@aws-sdk/credential-provider-sso': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/credential-provider-web-identity': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) '@aws-sdk/types': 3.535.0 '@smithy/credential-provider-imds': 2.3.0 '@smithy/property-provider': 2.2.0 @@ -4804,14 +4607,14 @@ snapshots: - aws-crt optional: true - '@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2)': + '@aws-sdk/credential-provider-node@3.556.0': dependencies: '@aws-sdk/credential-provider-env': 3.535.0 '@aws-sdk/credential-provider-http': 3.552.0 - '@aws-sdk/credential-provider-ini': 3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2) + '@aws-sdk/credential-provider-ini': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) '@aws-sdk/credential-provider-process': 3.535.0 - '@aws-sdk/credential-provider-sso': 3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2) - '@aws-sdk/credential-provider-web-identity': 3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2) + '@aws-sdk/credential-provider-sso': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) + '@aws-sdk/credential-provider-web-identity': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) '@aws-sdk/types': 3.535.0 '@smithy/credential-provider-imds': 2.3.0 '@smithy/property-provider': 2.2.0 @@ -4831,10 +4634,10 @@ snapshots: tslib: 2.6.2 optional: true - '@aws-sdk/credential-provider-sso@3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2)': + '@aws-sdk/credential-provider-sso@3.556.0(@aws-sdk/credential-provider-node@3.556.0)': dependencies: - '@aws-sdk/client-sso': 3.556.0(aws-crt@1.21.2) - '@aws-sdk/token-providers': 3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2) + '@aws-sdk/client-sso': 3.556.0 + '@aws-sdk/token-providers': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) '@aws-sdk/types': 3.535.0 '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 @@ -4845,9 +4648,9 @@ snapshots: - aws-crt optional: true - '@aws-sdk/credential-provider-web-identity@3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2)': + '@aws-sdk/credential-provider-web-identity@3.556.0(@aws-sdk/credential-provider-node@3.556.0)': dependencies: - '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2) + '@aws-sdk/client-sts': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) '@aws-sdk/types': 3.535.0 '@smithy/property-provider': 2.2.0 '@smithy/types': 2.12.0 @@ -4899,9 +4702,9 @@ snapshots: tslib: 2.6.2 optional: true - '@aws-sdk/token-providers@3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2)': + '@aws-sdk/token-providers@3.556.0(@aws-sdk/credential-provider-node@3.556.0)': dependencies: - '@aws-sdk/client-sso-oidc': 3.556.0(@aws-sdk/credential-provider-node@3.556.0(aws-crt@1.21.2))(aws-crt@1.21.2) + '@aws-sdk/client-sso-oidc': 3.556.0(@aws-sdk/credential-provider-node@3.556.0) '@aws-sdk/types': 3.535.0 '@smithy/property-provider': 2.2.0 '@smithy/shared-ini-file-loader': 2.4.0 @@ -4945,14 +4748,12 @@ snapshots: tslib: 2.6.2 optional: true - '@aws-sdk/util-user-agent-node@3.535.0(aws-crt@1.21.2)': + '@aws-sdk/util-user-agent-node@3.535.0': dependencies: '@aws-sdk/types': 3.535.0 '@smithy/node-config-provider': 2.3.0 '@smithy/types': 2.12.0 tslib: 2.6.2 - optionalDependencies: - aws-crt: 1.21.2 optional: true '@aws-sdk/util-utf8-browser@3.259.0': @@ -4983,7 +4784,7 @@ snapshots: '@babel/helpers': 7.26.0 '@babel/parser': 7.26.3 '@babel/template': 7.25.9 - '@babel/traverse': 7.26.3 + '@babel/traverse': 7.26.4 '@babel/types': 7.26.3 convert-source-map: 2.0.0 debug: 4.3.4 @@ -5011,7 +4812,7 @@ snapshots: '@babel/helper-module-imports@7.25.9': dependencies: - '@babel/traverse': 7.26.3 + '@babel/traverse': 7.26.4 '@babel/types': 7.26.3 transitivePeerDependencies: - supports-color @@ -5021,7 +4822,7 @@ snapshots: '@babel/core': 7.26.0 '@babel/helper-module-imports': 7.25.9 '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.3 + '@babel/traverse': 7.26.4 transitivePeerDependencies: - supports-color @@ -5079,7 +4880,7 @@ snapshots: '@babel/parser': 7.26.3 '@babel/types': 7.26.3 - '@babel/traverse@7.26.3': + '@babel/traverse@7.26.4': dependencies: '@babel/code-frame': 7.26.2 '@babel/generator': 7.26.3 @@ -5438,21 +5239,6 @@ snapshots: react-dom: 18.3.1(react@18.3.1) react-is: 16.13.1 - '@httptoolkit/websocket-stream@6.0.1': - dependencies: - '@types/ws': 8.5.10 - duplexify: 3.7.1 - inherits: 2.0.4 - isomorphic-ws: 4.0.1(ws@8.16.0) - readable-stream: 2.3.8 - safe-buffer: 5.2.1 - ws: 8.16.0 - xtend: 4.0.2 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - optional: true - '@humanwhocodes/config-array@0.11.14': dependencies: '@humanwhocodes/object-schema': 2.0.3 @@ -5762,7 +5548,7 @@ snapshots: - encoding - supports-color - '@reduxjs/toolkit@2.4.0(react-redux@9.1.2(@types/react@18.3.13)(react@18.3.1)(redux@5.0.1))(react@18.3.1)': + '@reduxjs/toolkit@2.4.0(react-redux@9.1.2(@types/react@18.3.14)(react@18.3.1)(redux@5.0.1))(react@18.3.1)': dependencies: immer: 10.1.1 redux: 5.0.1 @@ -5770,7 +5556,7 @@ snapshots: reselect: 5.1.0 optionalDependencies: react: 18.3.1 - react-redux: 9.1.2(@types/react@18.3.13)(react@18.3.1)(redux@5.0.1) + react-redux: 9.1.2(@types/react@18.3.14)(react@18.3.1)(redux@5.0.1) '@restart/context@2.1.4(react@18.3.1)': dependencies: @@ -6238,7 +6024,7 @@ snapshots: '@types/hoist-non-react-statics@3.3.5': dependencies: - '@types/react': 18.3.13 + '@types/react': 18.3.14 hoist-non-react-statics: 3.3.2 '@types/http-errors@2.0.4': {} @@ -6278,34 +6064,23 @@ snapshots: '@types/range-parser@1.2.7': {} - '@types/react-beautiful-dnd@13.1.8': - dependencies: - '@types/react': 18.3.13 - - '@types/react-dom@18.3.1': + '@types/react-dom@18.3.2': dependencies: - '@types/react': 18.3.13 + '@types/react': 18.3.14 '@types/react-google-recaptcha@2.1.9': dependencies: - '@types/react': 18.3.13 - - '@types/react-redux@7.1.33': - dependencies: - '@types/hoist-non-react-statics': 3.3.5 - '@types/react': 18.3.13 - hoist-non-react-statics: 3.3.2 - redux: 4.2.1 + '@types/react': 18.3.14 '@types/react-transition-group@4.4.11': dependencies: - '@types/react': 18.3.13 + '@types/react': 18.3.14 '@types/react-twemoji@0.4.3': dependencies: - '@types/react': 18.3.13 + '@types/react': 18.3.14 - '@types/react@18.3.13': + '@types/react@18.3.14': dependencies: '@types/prop-types': 15.7.12 csstype: 3.1.3 @@ -6323,17 +6098,14 @@ snapshots: '@types/node': 20.12.8 '@types/send': 0.17.4 + '@types/sortablejs@1.15.8': {} + '@types/toastify-js@1.12.3': {} '@types/use-sync-external-store@0.0.3': {} '@types/warning@3.0.3': {} - '@types/ws@8.5.10': - dependencies: - '@types/node': 20.12.8 - optional: true - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.7.2))(eslint@8.57.1)(typescript@5.7.2)': dependencies: '@eslint-community/regexpp': 4.10.0 @@ -6507,14 +6279,14 @@ snapshots: dependencies: '@codegenie/serverless-express': 4.14.1 - '@vitejs/plugin-react@4.3.4(vite@6.0.2(@types/node@20.12.8)(sass@1.82.0)(tsx@4.19.2)(yaml@2.5.1))': + '@vitejs/plugin-react@4.3.4(vite@6.0.3(@types/node@20.12.8)(sass@1.82.0)(tsx@4.19.2)(yaml@2.5.1))': dependencies: '@babel/core': 7.26.0 '@babel/plugin-transform-react-jsx-self': 7.25.9(@babel/core@7.26.0) '@babel/plugin-transform-react-jsx-source': 7.25.9(@babel/core@7.26.0) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 6.0.2(@types/node@20.12.8)(sass@1.82.0)(tsx@4.19.2)(yaml@2.5.1) + vite: 6.0.3(@types/node@20.12.8)(sass@1.82.0)(tsx@4.19.2)(yaml@2.5.1) transitivePeerDependencies: - supports-color @@ -6633,49 +6405,18 @@ snapshots: ast-types-flow@0.0.8: {} - asynckit@0.4.0: - optional: true - available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.0.0 - aws-crt@1.21.2: - dependencies: - '@aws-sdk/util-utf8-browser': 3.259.0 - '@httptoolkit/websocket-stream': 6.0.1 - axios: 1.7.9 - buffer: 6.0.3 - crypto-js: 4.2.0 - mqtt: 4.3.8 - process: 0.11.10 - transitivePeerDependencies: - - bufferutil - - debug - - supports-color - - utf-8-validate - optional: true - aws4fetch@1.0.20: {} axe-core@4.10.2: {} - axios@1.7.9: - dependencies: - follow-redirects: 1.15.6 - form-data: 4.0.0 - proxy-from-env: 1.1.0 - transitivePeerDependencies: - - debug - optional: true - axobject-query@4.1.0: {} balanced-match@1.0.2: {} - base64-js@1.5.1: - optional: true - base64url@3.0.1: {} basic-auth@2.0.1: @@ -6684,13 +6425,6 @@ snapshots: binary-extensions@2.3.0: {} - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - optional: true - body-parser@1.20.3: dependencies: bytes: 3.1.2 @@ -6738,24 +6472,12 @@ snapshots: browserslist@4.24.2: dependencies: caniuse-lite: 1.0.30001686 - electron-to-chromium: 1.5.68 + electron-to-chromium: 1.5.71 node-releases: 2.0.18 update-browserslist-db: 1.1.1(browserslist@4.24.2) buffer-from@1.1.2: {} - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - optional: true - - buffer@6.0.3: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - optional: true - bytes@3.1.2: {} call-bind@1.0.7: @@ -6801,6 +6523,8 @@ snapshots: dependencies: readdirp: 4.0.2 + classnames@2.3.1: {} + classnames@2.5.1: {} cli-cursor@5.0.0: @@ -6836,29 +6560,10 @@ snapshots: colorette@2.0.20: {} - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - optional: true - commander@12.1.0: {} - commist@1.1.0: - dependencies: - leven: 2.1.0 - minimist: 1.2.8 - optional: true - concat-map@0.0.1: {} - concat-stream@2.0.0: - dependencies: - buffer-from: 1.1.2 - inherits: 2.0.4 - readable-stream: 3.6.2 - typedarray: 0.0.6 - optional: true - concurrently@9.1.0: dependencies: chalk: 4.1.2 @@ -6898,22 +6603,12 @@ snapshots: cookie@1.0.2: {} - core-util-is@1.0.3: - optional: true - cross-spawn@7.0.3: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 - crypto-js@4.2.0: - optional: true - - css-box-model@1.2.1: - dependencies: - tiny-invariant: 1.3.3 - css-mediaquery@0.1.2: {} css-select@5.1.0: @@ -7029,9 +6724,6 @@ snapshots: has-property-descriptors: 1.0.2 object-keys: 1.1.1 - delayed-stream@1.0.0: - optional: true - depd@2.0.0: {} dequal@2.0.3: {} @@ -7091,36 +6783,20 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.37.0(@aws-sdk/client-rds-data@3.556.0(aws-crt@1.21.2))(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@18.3.13)(kysely@0.25.0)(pg@8.13.1)(react@18.3.1): + drizzle-orm@0.37.0(@aws-sdk/client-rds-data@3.556.0)(@libsql/client-wasm@0.14.0)(@opentelemetry/api@1.9.0)(@types/pg@8.11.10)(@types/react@18.3.14)(kysely@0.25.0)(pg@8.13.1)(react@18.3.1): optionalDependencies: - '@aws-sdk/client-rds-data': 3.556.0(aws-crt@1.21.2) + '@aws-sdk/client-rds-data': 3.556.0 '@libsql/client-wasm': 0.14.0 '@opentelemetry/api': 1.9.0 '@types/pg': 8.11.10 - '@types/react': 18.3.13 + '@types/react': 18.3.14 kysely: 0.25.0 pg: 8.13.1 react: 18.3.1 - duplexify@3.7.1: - dependencies: - end-of-stream: 1.4.4 - inherits: 2.0.4 - readable-stream: 2.3.8 - stream-shift: 1.0.3 - optional: true - - duplexify@4.1.3: - dependencies: - end-of-stream: 1.4.4 - inherits: 2.0.4 - readable-stream: 3.6.2 - stream-shift: 1.0.3 - optional: true - ee-first@1.1.1: {} - electron-to-chromium@1.5.68: {} + electron-to-chromium@1.5.71: {} emoji-regex@10.3.0: {} @@ -7137,11 +6813,6 @@ snapshots: iconv-lite: 0.6.3 optional: true - end-of-stream@1.4.4: - dependencies: - once: 1.4.0 - optional: true - entities@4.5.0: {} environment@1.1.0: {} @@ -7351,6 +7022,8 @@ snapshots: '@esbuild/win32-ia32': 0.24.0 '@esbuild/win32-x64': 0.24.0 + escalade@3.1.2: {} + escalade@3.2.0: {} escape-html@1.0.3: {} @@ -7558,7 +7231,7 @@ snapshots: transitivePeerDependencies: - supports-color - express@4.21.1: + express@4.21.2: dependencies: accepts: 1.3.8 array-flatten: 1.1.1 @@ -7579,7 +7252,7 @@ snapshots: methods: 1.1.2 on-finished: 2.4.1 parseurl: 1.3.3 - path-to-regexp: 0.1.10 + path-to-regexp: 0.1.12 proxy-addr: 2.0.7 qs: 6.13.0 range-parser: 1.2.1 @@ -7654,20 +7327,10 @@ snapshots: flatted@3.3.1: {} - follow-redirects@1.15.6: - optional: true - for-each@0.3.3: dependencies: is-callable: 1.2.7 - form-data@4.0.0: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - mime-types: 2.1.35 - optional: true - forwarded@0.2.0: {} fresh@0.5.2: {} @@ -7789,12 +7452,6 @@ snapshots: he@1.2.0: {} - help-me@3.0.0: - dependencies: - glob: 7.2.3 - readable-stream: 3.6.2 - optional: true - hoist-non-react-statics@3.3.2: dependencies: react-is: 16.13.1 @@ -7829,9 +7486,6 @@ snapshots: safer-buffer: 2.1.2 optional: true - ieee754@1.2.1: - optional: true - ignore-by-default@1.0.1: {} ignore@5.3.1: {} @@ -7976,18 +7630,10 @@ snapshots: call-bind: 1.0.7 get-intrinsic: 1.2.4 - isarray@1.0.0: - optional: true - isarray@2.0.5: {} isexe@2.0.0: {} - isomorphic-ws@4.0.1(ws@8.16.0): - dependencies: - ws: 8.16.0 - optional: true - iterator.prototype@1.1.3: dependencies: define-properties: 1.2.1 @@ -8007,9 +7653,6 @@ snapshots: js-levenshtein@1.1.6: {} - js-sdsl@4.3.0: - optional: true - js-tokens@4.0.0: {} js-yaml@4.1.0: @@ -8060,9 +7703,6 @@ snapshots: dependencies: language-subtag-registry: 0.3.22 - leven@2.1.0: - optional: true - levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -8132,8 +7772,6 @@ snapshots: media-typer@0.3.0: {} - memoize-one@5.2.1: {} - merge-descriptors@1.0.3: {} merge-stream@2.0.0: {} @@ -8176,9 +7814,6 @@ snapshots: dependencies: brace-expansion: 2.0.1 - minimist@1.2.8: - optional: true - morgan@1.10.0: dependencies: basic-auth: 2.0.1 @@ -8189,40 +7824,6 @@ snapshots: transitivePeerDependencies: - supports-color - mqtt-packet@6.10.0: - dependencies: - bl: 4.1.0 - debug: 4.3.7 - process-nextick-args: 2.0.1 - transitivePeerDependencies: - - supports-color - optional: true - - mqtt@4.3.8: - dependencies: - commist: 1.1.0 - concat-stream: 2.0.0 - debug: 4.3.7 - duplexify: 4.1.3 - help-me: 3.0.0 - inherits: 2.0.4 - lru-cache: 6.0.0 - minimist: 1.2.8 - mqtt-packet: 6.10.0 - number-allocator: 1.0.14 - pump: 3.0.0 - readable-stream: 3.6.2 - reinterval: 1.1.0 - rfdc: 1.4.1 - split2: 3.2.2 - ws: 7.5.9 - xtend: 4.0.2 - transitivePeerDependencies: - - bufferutil - - supports-color - - utf-8-validate - optional: true - ms@2.0.0: {} ms@2.1.2: {} @@ -8274,14 +7875,6 @@ snapshots: dependencies: boolbase: 1.0.0 - number-allocator@1.0.14: - dependencies: - debug: 4.3.7 - js-sdsl: 4.3.0 - transitivePeerDependencies: - - supports-color - optional: true - oauth@0.10.0: {} oauth@0.9.15: {} @@ -8439,7 +8032,7 @@ snapshots: path-parse@1.0.7: {} - path-to-regexp@0.1.10: {} + path-to-regexp@0.1.12: {} path-type@4.0.0: {} @@ -8538,12 +8131,6 @@ snapshots: prettier@3.4.2: {} - process-nextick-args@2.0.1: - optional: true - - process@0.11.10: - optional: true - prop-types-extra@1.1.1(react@18.3.1): dependencies: react: 18.3.1 @@ -8561,17 +8148,8 @@ snapshots: forwarded: 0.2.0 ipaddr.js: 1.9.1 - proxy-from-env@1.1.0: - optional: true - pstree.remy@1.1.8: {} - pump@3.0.0: - dependencies: - end-of-stream: 1.4.4 - once: 1.4.0 - optional: true - punycode@2.3.1: {} qs@6.13.0: @@ -8580,8 +8158,6 @@ snapshots: queue-microtask@1.2.3: {} - raf-schd@4.0.3: {} - random-bytes@1.0.0: {} range-parser@1.2.1: {} @@ -8599,20 +8175,6 @@ snapshots: prop-types: 15.8.1 react: 18.3.1 - react-beautiful-dnd@13.1.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.24.5 - css-box-model: 1.2.1 - memoize-one: 5.2.1 - raf-schd: 4.0.3 - react: 18.3.1 - react-dom: 18.3.1(react@18.3.1) - react-redux: 7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1) - redux: 4.2.1 - use-memo-one: 1.1.3(react@18.3.1) - transitivePeerDependencies: - - react-native - react-bootstrap-icons@1.11.4(react@18.3.1): dependencies: prop-types: 15.8.1 @@ -8633,7 +8195,7 @@ snapshots: '@restart/hooks': 0.4.16(react@18.3.1) '@types/invariant': 2.2.37 '@types/prop-types': 15.7.12 - '@types/react': 18.3.13 + '@types/react': 18.3.14 '@types/react-transition-group': 4.4.11 '@types/warning': 3.0.3 classnames: 2.5.1 @@ -8671,8 +8233,6 @@ snapshots: react-is@16.13.1: {} - react-is@17.0.2: {} - react-is@18.3.1: {} react-lifecycles-compat@3.0.4: {} @@ -8700,25 +8260,13 @@ snapshots: react-fast-compare: 3.2.2 warning: 4.0.3 - react-redux@7.2.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1): - dependencies: - '@babel/runtime': 7.24.5 - '@types/react-redux': 7.1.33 - hoist-non-react-statics: 3.3.2 - loose-envify: 1.4.0 - prop-types: 15.8.1 - react: 18.3.1 - react-is: 17.0.2 - optionalDependencies: - react-dom: 18.3.1(react@18.3.1) - - react-redux@9.1.2(@types/react@18.3.13)(react@18.3.1)(redux@5.0.1): + react-redux@9.1.2(@types/react@18.3.14)(react@18.3.1)(redux@5.0.1): dependencies: '@types/use-sync-external-store': 0.0.3 react: 18.3.1 use-sync-external-store: 1.2.2(react@18.3.1) optionalDependencies: - '@types/react': 18.3.13 + '@types/react': 18.3.14 redux: 5.0.1 react-refresh@0.14.2: {} @@ -8747,6 +8295,15 @@ snapshots: optionalDependencies: react-dom: 18.3.1(react@18.3.1) + react-sortablejs@6.1.4(@types/sortablejs@1.15.8)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(sortablejs@1.15.6): + dependencies: + '@types/sortablejs': 1.15.8 + classnames: 2.3.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + sortablejs: 1.15.6 + tiny-invariant: 1.2.0 + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.24.5 @@ -8767,24 +8324,6 @@ snapshots: dependencies: loose-envify: 1.4.0 - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - optional: true - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - optional: true - readdirp@3.6.0: dependencies: picomatch: 2.3.1 @@ -8795,10 +8334,6 @@ snapshots: dependencies: redux: 5.0.1 - redux@4.2.1: - dependencies: - '@babel/runtime': 7.24.5 - redux@5.0.1: {} reflect.getprototypeof@1.0.6: @@ -8820,9 +8355,6 @@ snapshots: es-errors: 1.3.0 set-function-name: 2.0.2 - reinterval@1.1.0: - optional: true - require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -9027,6 +8559,8 @@ snapshots: ansi-styles: 6.2.1 is-fullwidth-code-point: 5.0.0 + sortablejs@1.15.6: {} + source-map-js@1.2.0: {} source-map-js@1.2.1: {} @@ -9038,11 +8572,6 @@ snapshots: source-map@0.6.1: {} - split2@3.2.2: - dependencies: - readable-stream: 3.6.2 - optional: true - split2@4.2.0: {} sst-darwin-arm64@3.3.47: @@ -9074,9 +8603,6 @@ snapshots: statuses@2.0.1: {} - stream-shift@1.0.3: - optional: true - string-argv@0.3.2: {} string-width@4.2.3: @@ -9136,16 +8662,6 @@ snapshots: define-properties: 1.2.1 es-object-atoms: 1.0.0 - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - optional: true - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - optional: true - strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 @@ -9179,7 +8695,7 @@ snapshots: text-table@0.2.0: {} - tiny-invariant@1.3.3: {} + tiny-invariant@1.2.0: {} to-fast-properties@2.0.0: {} @@ -9260,9 +8776,6 @@ snapshots: is-typed-array: 1.1.13 possible-typed-array-names: 1.0.0 - typedarray@0.0.6: - optional: true - typescript@5.7.2: {} uid-safe@2.1.5: @@ -9281,7 +8794,7 @@ snapshots: uncontrollable@7.2.1(react@18.3.1): dependencies: '@babel/runtime': 7.24.5 - '@types/react': 18.3.13 + '@types/react': 18.3.14 invariant: 2.2.4 react: 18.3.1 react-lifecycles-compat: 3.0.4 @@ -9311,17 +8824,10 @@ snapshots: dependencies: punycode: 2.3.1 - use-memo-one@1.1.3(react@18.3.1): - dependencies: - react: 18.3.1 - use-sync-external-store@1.2.2(react@18.3.1): dependencies: react: 18.3.1 - util-deprecate@1.0.2: - optional: true - utils-merge@1.0.1: {} uuid@9.0.1: @@ -9329,7 +8835,7 @@ snapshots: vary@1.1.2: {} - vite@6.0.2(@types/node@20.12.8)(sass@1.82.0)(tsx@4.19.2)(yaml@2.5.1): + vite@6.0.3(@types/node@20.12.8)(sass@1.82.0)(tsx@4.19.2)(yaml@2.5.1): dependencies: esbuild: 0.24.0 postcss: 8.4.49 @@ -9410,12 +8916,6 @@ snapshots: wrappy@1.0.2: {} - ws@7.5.9: - optional: true - - ws@8.16.0: - optional: true - xtend@4.0.2: {} y18n@5.0.8: {} @@ -9433,7 +8933,7 @@ snapshots: yargs@17.7.2: dependencies: cliui: 8.0.1 - escalade: 3.2.0 + escalade: 3.1.2 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 diff --git a/site/package.json b/site/package.json index b2125fcf..066478b2 100644 --- a/site/package.json +++ b/site/package.json @@ -12,7 +12,6 @@ "bootstrap": "^4.6.2", "node-html-parser": "^6.1.13", "react": "^18.3.1", - "react-beautiful-dnd": "^13.1.1", "react-bootstrap": "^1.6.8", "react-bootstrap-icons": "^1.11.4", "react-bootstrap-range-slider": "^3.0.8", @@ -23,6 +22,7 @@ "react-redux": "^9.1.2", "react-responsive": "^10.0.0", "react-router-dom": "^7.0.2", + "react-sortablejs": "^6.1.4", "react-transition-group": "^4.4.5", "react-twemoji": "^0.6.0", "semantic-ui-css": "^2.5.0", @@ -51,7 +51,6 @@ "devDependencies": { "@peterportal/types": "workspace:*", "@types/react": "^18.3.12", - "@types/react-beautiful-dnd": "^13.1.8", "@types/react-dom": "^18.3.1", "@types/react-google-recaptcha": "^2.1.9", "@types/react-transition-group": "^4.4.11", diff --git a/site/src/App.scss b/site/src/App.scss index bd2a836e..e961da41 100644 --- a/site/src/App.scss +++ b/site/src/App.scss @@ -156,3 +156,23 @@ button { } } } + +.ui-overlay { + position: fixed; + inset: 0; + background-color: #6664; + opacity: 0; + transition: opacity 0.5s; + cursor: default; + pointer-events: none; + + &.enter-active, + &.enter-done { + opacity: 1; + pointer-events: all; + } + &.exit { + pointer-events: none; + opacity: 0; + } +} diff --git a/site/src/component/SearchHitContainer/SearchHitContainer.scss b/site/src/component/SearchHitContainer/SearchHitContainer.scss index 2da81473..7442c818 100644 --- a/site/src/component/SearchHitContainer/SearchHitContainer.scss +++ b/site/src/component/SearchHitContainer/SearchHitContainer.scss @@ -1,6 +1,10 @@ .search-hit-container { padding-top: 2vh; overflow-y: auto; + + > *:not(:last-child) { + margin-bottom: 20px; + } } .no-results { diff --git a/site/src/component/SearchModule/SearchModule.scss b/site/src/component/SearchModule/SearchModule.scss index 130e0dad..ae9a3735 100644 --- a/site/src/component/SearchModule/SearchModule.scss +++ b/site/src/component/SearchModule/SearchModule.scss @@ -1,6 +1,19 @@ +.search-module { + button { + font: inherit; + padding: 0; + appearance: none; + -webkit-appearance: none; + width: 40px; + text-align: center; + justify-content: center; + } +} + .search-bar { border-color: #80bdff; - padding: 2vh 2vh; + padding: 8px 14px; + font-size: 16px; } [data-theme='dark'] { diff --git a/site/src/component/SearchModule/SearchModule.tsx b/site/src/component/SearchModule/SearchModule.tsx index 1330462d..bc57449e 100644 --- a/site/src/component/SearchModule/SearchModule.tsx +++ b/site/src/component/SearchModule/SearchModule.tsx @@ -2,7 +2,7 @@ import { useState, useEffect, FC, useCallback } from 'react'; import './SearchModule.scss'; import Form from 'react-bootstrap/Form'; import InputGroup from 'react-bootstrap/InputGroup'; -import { Bag, Search } from 'react-bootstrap-icons'; +import { Search } from 'react-bootstrap-icons'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; import { CourseGQLData, ProfessorGQLData, SearchIndex } from '../../types/types'; @@ -21,7 +21,7 @@ interface SearchModuleProps { const SearchModule: FC = ({ index }) => { const dispatch = useAppDispatch(); const search = useAppSelector((state) => state.search[index]); - const showCourseBag = useAppSelector((state) => state.roadmap.showCourseBag); + const [searchQuery, setSearchQuery] = useState(''); const [pendingRequest, setPendingRequest] = useState(null); const fuzzySearch = useCallback( @@ -48,18 +48,24 @@ const SearchModule: FC = ({ index }) => { fuzzySearch(search.query); }, [search.query, fuzzySearch]); - const searchAfterTimeout = (query: string) => { - if (pendingRequest) { - clearTimeout(pendingRequest); + const searchImmediately = (query: string) => { + if (pendingRequest) clearTimeout(pendingRequest); + if (location.pathname === '/roadmap') { + dispatch(setShowCourseBag(!query)); } - const timeout = window.setTimeout(() => { + if (query && query !== search.query) { dispatch(setQuery({ index, query })); setPendingRequest(null); - }, SEARCH_TIMEOUT_MS); + } + }; + const searchAfterTimeout = (query: string) => { + setSearchQuery(query); + if (pendingRequest) clearTimeout(pendingRequest); + const timeout = window.setTimeout(() => searchImmediately(query), SEARCH_TIMEOUT_MS); setPendingRequest(timeout); }; - const coursePlaceholder = 'Search a course number or department'; + const coursePlaceholder = 'Search for a course...'; const professorPlaceholder = 'Search a professor'; const placeholder = index === 'courses' ? coursePlaceholder : professorPlaceholder; @@ -67,29 +73,19 @@ const SearchModule: FC = ({ index }) => {
- - - - - searchAfterTimeout(e.target.value)} defaultValue={search.query} /> - { - // only show course bag icon on roadmap page - location.pathname === '/roadmap' && ( - - dispatch(setShowCourseBag(!showCourseBag))}> - - - - ) - } + + +
diff --git a/site/src/component/SideBar/SideBar.tsx b/site/src/component/SideBar/SideBar.tsx index f84ea98f..173117fb 100644 --- a/site/src/component/SideBar/SideBar.tsx +++ b/site/src/component/SideBar/SideBar.tsx @@ -11,6 +11,7 @@ import { setSidebarStatus } from '../../store/slices/uiSlice'; import Footer from '../Footer/Footer'; import trpc from '../../trpc'; import { useIsLoggedIn } from '../../hooks/isLoggedIn'; +import UIOverlay from '../UIOverlay/UIOverlay'; const SideBar = () => { const dispatch = useAppDispatch(); @@ -111,9 +112,7 @@ const SideBar = () => { <>
{links}
- {/* Clicking this is only an alternative action to something that is already accessible */} - {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions,jsx-a11y/click-events-have-key-events */} -
+
diff --git a/site/src/component/SideBar/Sidebar.scss b/site/src/component/SideBar/Sidebar.scss index d8b32ee0..dda56139 100644 --- a/site/src/component/SideBar/Sidebar.scss +++ b/site/src/component/SideBar/Sidebar.scss @@ -112,24 +112,6 @@ display: none; } } -.sidebar-overlay { - position: fixed; - inset: 0; - background-color: #6664; - z-index: 399; - opacity: 0; - transition: opacity 0.5s; - cursor: default; - - &.enter-active, - &.enter-done { - opacity: 1; - } - &.exit { - pointer-events: none; - opacity: 0; - } -} [data-theme='dark'] { .sidebar:not(.mini) { diff --git a/site/src/component/UIOverlay/UIOverlay.tsx b/site/src/component/UIOverlay/UIOverlay.tsx new file mode 100644 index 00000000..96eb53c9 --- /dev/null +++ b/site/src/component/UIOverlay/UIOverlay.tsx @@ -0,0 +1,11 @@ +const UIOverlay = ({ + zIndex, + ...props +}: { zIndex: number; passedRef?: React.RefObject } & JSX.IntrinsicElements['div']) => { + const passedRef = props.passedRef; + delete props.passedRef; + // Clicking this is only an alternative action to something that is already accessible + return
; +}; + +export default UIOverlay; diff --git a/site/src/globals.scss b/site/src/globals.scss index 030a2308..b9e82c09 100644 --- a/site/src/globals.scss +++ b/site/src/globals.scss @@ -6,3 +6,47 @@ $mobile-cutoff: 800px; padding: 16px 20px; } } + +@mixin clickable($hoverVal: 0.8, $activeVal: 0.5) { + transition: opacity 0.2s; + cursor: pointer; + &:hover { + opacity: $hoverVal; + } + &:active { + opacity: $activeVal; + } +} + +@mixin bottom-overlay($zIndex: 400) { + position: fixed; + top: unset; + left: 0; + width: 100%; + bottom: 0; + z-index: $zIndex; + max-height: calc(100% - 120px); + padding-bottom: 40px; + transform: translateY(100%); + transition: transform 0.3s; + &.enter, + &.enter-done { + transform: translateY(0); + } +} + +@mixin bottom-button() { + position: fixed; + bottom: 0; + left: 0; + height: 40px; + background-color: var(--zot-blue); + width: 100%; + border: none; + appearance: none; + color: white; + font: inherit; + font-weight: 600; + font-size: 14px; + @include clickable(0.9, 0.7); +} diff --git a/site/src/helpers/sortable.ts b/site/src/helpers/sortable.ts new file mode 100644 index 00000000..d1e03972 --- /dev/null +++ b/site/src/helpers/sortable.ts @@ -0,0 +1,23 @@ +import { ReactSortableProps, SortableOptions } from 'react-sortablejs'; +import { CourseGQLData } from '../types/types'; + +const baseSortable: SortableOptions = { + animation: 150, + forceFallback: true, + fallbackOnBody: true, + filter: '.btn', +}; + +export const quarterSortable: SortableOptions & Partial> = { + ...baseSortable, + setList: () => {}, + group: { name: 'courses' }, +}; + +export const courseSearchSortable: SortableOptions & Partial> = { + ...baseSortable, + setList: () => {}, + sort: false, + revertOnSpill: true, + group: { name: 'courses', pull: 'clone', put: false }, +}; diff --git a/site/src/helpers/util.tsx b/site/src/helpers/util.tsx index 954b80d4..29932e0c 100644 --- a/site/src/helpers/util.tsx +++ b/site/src/helpers/util.tsx @@ -139,3 +139,7 @@ export const unionTerms = (courseHistory: CourseWithTermsLookup) => { return sortTerms(union); }; + +export function deepCopy(obj: T): T { + return JSON.parse(JSON.stringify(obj)); +} diff --git a/site/src/hooks/namedAcademicTerm.ts b/site/src/hooks/namedAcademicTerm.ts new file mode 100644 index 00000000..a41508b1 --- /dev/null +++ b/site/src/hooks/namedAcademicTerm.ts @@ -0,0 +1,13 @@ +import { quarterDisplayNames } from '../helpers/planner'; +import { useAppSelector } from '../store/hooks'; + +export const useNamedAcademicTerm = () => { + const planner = useAppSelector((state) => state.roadmap.plans[state.roadmap.currentPlanIndex].content.yearPlans); + const { year, quarter } = useAppSelector((state) => state.roadmap.currentYearAndQuarter) || {}; + + if (year == null || quarter == null) return { year: null, quarter: null }; + + const quarterName = quarterDisplayNames[planner[year].quarters[quarter].name]; + const yearName = planner[year].startYear + Number(quarterName === quarterDisplayNames.Fall); + return { year: yearName, quarter: quarterName }; +}; diff --git a/site/src/pages/RoadmapPage/AddCoursePopup.scss b/site/src/pages/RoadmapPage/AddCoursePopup.scss index 14812da3..a4d06e2a 100644 --- a/site/src/pages/RoadmapPage/AddCoursePopup.scss +++ b/site/src/pages/RoadmapPage/AddCoursePopup.scss @@ -1,4 +1,71 @@ +@use '../../globals.scss'; + +.add-course-modal { + @include globals.bottom-overlay(500); + background-color: var(--overlay1); + + &:is(.enter, .enter-done) + .ui-overlay { + opacity: 1; + pointer-events: all; + } + + button.fixed { + @include globals.bottom-button(); + } + + .modal-dialog { + position: fixed; + bottom: 0; + left: 0; + width: 100%; + height: auto; + margin: 0; + max-width: 100%; + .modal-content { + border-radius: 8px 8px 0 0; + } + } + + .modal-header { + gap: 8px; + .spacer { + margin-left: auto; + } + } + + h2, + .unit-count { + font-size: 20px; + } + + button:not(.fixed), + button.close { + background: none; + font: inherit; + border: none; + width: 20px; + height: 20px; + display: flex; + align-items: center; + padding: 0; + + @include globals.clickable(0.8, 0.5); + } + + button.close-button { + width: 32px; + } + + .quarter-offerings-section { + display: flex; + gap: 6px; + color: var(--text-secondary); + .quarter-indicator-container { + margin-left: 0; + } + } +} + .add-course-form { - border-radius: var(--border-radius); padding: 2rem; } diff --git a/site/src/pages/RoadmapPage/AddCoursePopup.tsx b/site/src/pages/RoadmapPage/AddCoursePopup.tsx index d37fc4f3..50f2d3d6 100644 --- a/site/src/pages/RoadmapPage/AddCoursePopup.tsx +++ b/site/src/pages/RoadmapPage/AddCoursePopup.tsx @@ -1,155 +1,98 @@ -import React, { FC, useEffect, useState } from 'react'; -import Button from 'react-bootstrap/Button'; -import Form from 'react-bootstrap/Form'; +import { FC } from 'react'; import Modal from 'react-bootstrap/Modal'; -import { quarterDisplayNames } from '../../helpers/planner'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; import { moveCourse, setShowAddCourse, setShowSearch } from '../../store/slices/roadmapSlice'; import './AddCoursePopup.scss'; +import { useCoursebag } from '../../hooks/coursebag'; +import { Bookmark, BookmarkFill, X } from 'react-bootstrap-icons'; +import UIOverlay from '../../component/UIOverlay/UIOverlay'; +import { useNamedAcademicTerm } from '../../hooks/namedAcademicTerm'; +import CourseQuarterIndicator from '../../component/QuarterTooltip/CourseQuarterIndicator'; interface AddCoursePopupProps {} const AddCoursePopup: FC = () => { - const dispatch = useAppDispatch(); - const planner = useAppSelector((state) => state.roadmap.plans[state.roadmap.currentPlanIndex].content.yearPlans); - const showForm = useAppSelector((state) => state.roadmap.showAddCourse); const currentYearAndQuarter = useAppSelector((state) => state.roadmap.currentYearAndQuarter); - const [year, setYear] = useState(currentYearAndQuarter?.year ?? -1); - const [quarter, setQuarter] = useState(currentYearAndQuarter?.quarter ?? -1); - const [validated, setValidated] = useState(false); + const { coursebag, addCourseToBag, removeCourseFromBag } = useCoursebag(); + const showAddCourse = useAppSelector((state) => state.roadmap.showAddCourse); const activeCourse = useAppSelector((state) => state.roadmap.activeCourse); + const term = useNamedAcademicTerm(); - useEffect(() => { - setYear(currentYearAndQuarter?.year ?? -1); - setQuarter(currentYearAndQuarter?.quarter ?? -1); - }, [currentYearAndQuarter]); - - const closeForm = () => { - // close form - dispatch(setShowAddCourse(false)); - }; - - const submit = (event: React.FormEvent) => { - // validate form - const form = event.currentTarget; - const valid = form.checkValidity(); - event.preventDefault(); - event.stopPropagation(); + const dispatch = useAppDispatch(); - // validated - setValidated(true); + const quarter = currentYearAndQuarter?.quarter ?? -1; + const year = currentYearAndQuarter?.year ?? -1; - // do not proceed if not valid - if (valid === false) { - return; - } + const closePopup = () => dispatch(setShowAddCourse(false)); + const contentClassName = 'ppc-modal add-course-modal ' + (showAddCourse ? 'enter' : 'exit'); + const overlay = ; - // add course to roadmap + const addToRoadmap = () => { dispatch( moveCourse({ - from: { - yearIndex: -1, - quarterIndex: -1, - courseIndex: -1, - }, - to: { - yearIndex: year, - quarterIndex: quarter, - courseIndex: 0, - }, + from: { yearIndex: -1, quarterIndex: -1, courseIndex: -1 }, + to: { yearIndex: year, quarterIndex: quarter, courseIndex: 0 }, }), ); // hide the search bar to view the roadmap dispatch(setShowSearch({ show: false })); - closeForm(); + closePopup(); }; - const addCourseForm = ( -
-

Add Course

-

- Where do you want to add {activeCourse ? activeCourse.department + ' ' + activeCourse.courseNumber : 'a course'} - ? -

- - School Year - { - const parsed = parseInt(e.target.value); - if (isNaN(parsed)) { - setYear(-1); - } else { - setYear(parsed); - } - }} - > - - {planner.map((plannerYear, i) => { - const value = plannerYear.startYear; - return ( - - ); - })} - - Missing year - - {year != -1 && ( - - Quarter - { - const parsed = parseInt(e.target.value); - if (!isNaN(parsed)) { - setQuarter(parsed); - } - }} - > - - {planner[year].quarters.map((plannerQuarter, i) => { - const value = quarterDisplayNames[plannerQuarter.name]; - return ( - - ); - })} - - Missing quarter - - )} -
- - -
-
- ); + if (!activeCourse) + return ( + <> +
+ {overlay} + + ); + + const inCourseBag = coursebag.some((course) => course.id === activeCourse.id); + + const toggleSaved = () => { + if (inCourseBag) { + removeCourseFromBag(activeCourse); + } else { + addCourseToBag(activeCourse); + } + }; + + const { minUnits, maxUnits, department, courseNumber, title, description } = activeCourse; return ( - -
{addCourseForm}
-
+ <> +
+ +

+ {department} {courseNumber} +

+ + ({minUnits === maxUnits ? minUnits : `${minUnits}-${maxUnits}`} unit{maxUnits === 1 ? '' : 's'}) + + +
+ +
+ +

+ {title}: {description} +

+

+ Previous Offerings: + +

+ {/** @todo Add UnmetPrerequisiteText for prerequisites that don't exist in the planner */} +
+ +
+ {overlay} + ); }; diff --git a/site/src/pages/RoadmapPage/Course.scss b/site/src/pages/RoadmapPage/Course.scss index 831de12c..45f3814e 100644 --- a/site/src/pages/RoadmapPage/Course.scss +++ b/site/src/pages/RoadmapPage/Course.scss @@ -6,6 +6,7 @@ font-size: 16px; padding: 12px; position: relative; + margin: 4px; .course-and-info { color: var(--zot-blue); @@ -39,8 +40,14 @@ .course-and-info { display: flex; - gap: 5px; + gap: 6px; + margin-right: 4px; align-items: center; + width: 100%; + > .name { + display: block; + margin-right: auto; + } } .course-delete-btn { diff --git a/site/src/pages/RoadmapPage/Course.tsx b/site/src/pages/RoadmapPage/Course.tsx index 81bf12dd..c1c84c75 100644 --- a/site/src/pages/RoadmapPage/Course.tsx +++ b/site/src/pages/RoadmapPage/Course.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react'; +import React, { FC } from 'react'; import './Course.scss'; import { Button } from 'react-bootstrap'; import { InfoCircle, ExclamationTriangle, Trash, BagPlus, BagFill } from 'react-bootstrap-icons'; @@ -8,6 +8,17 @@ import Popover from 'react-bootstrap/Popover'; import { CourseGQLData } from '../../types/types'; import ThemeContext from '../../style/theme-context'; +import { setActiveCourse, setShowAddCourse } from '../../store/slices/roadmapSlice'; +import { useAppDispatch } from '../../store/hooks'; + +export const UnmetPrerequisiteText: React.FC<{ requiredCourses?: string[] }> = ({ requiredCourses }) => ( + <> + Prerequisite(s) not met! Missing: {requiredCourses?.join(', ')} +
+ Already completed prerequisite(s) at another institution? Click 'Transfer Credits' at the top of the planner to + clear the prerequisite(s). + +); interface CourseProps extends CourseGQLData { requiredCourses?: string[]; @@ -16,6 +27,7 @@ interface CourseProps extends CourseGQLData { onAddToBag?: () => void; isInBag?: boolean; removeFromBag?: () => void; + addMode?: 'tap' | 'drag'; } const Course: FC = (props) => { @@ -63,38 +75,46 @@ const Course: FC = (props) => { ); - const WarningPopover = ( + const dispatch = useAppDispatch(); + + const warningPopover = ( - Prerequisite(s) not met! Missing: {requiredCourses?.join(', ')} -
- Already completed prerequisite(s) at another institution? Click 'Transfer Credits' at the top of the planner to - clear the prerequisite(s). +
); const courseRoute = '/course/' + props.department.replace(/\s+/g, '') + props.courseNumber.replace(/\s+/g, ''); + const insertCourseOnClick = () => { + dispatch(setActiveCourse(props)); + dispatch(setShowAddCourse(true)); + }; + + const tapProps = { onClick: insertCourseOnClick, role: 'button', tabIndex: 0 }; + const tappableCourseProps = props.addMode === 'tap' ? tapProps : {}; + return ( -
+
- - - {department + ' ' + courseNumber} - - , {minUnits === maxUnits ? minUnits : `${minUnits}-${maxUnits}`} units + + {department + ' ' + courseNumber} + + + {minUnits === maxUnits ? minUnits : `${minUnits}-${maxUnits}`} unit{maxUnits === 1 ? '' : 's'} {requiredCourses && ( - + )}
+
{onDelete ? ( {({ darkMode }) => ( diff --git a/site/src/pages/RoadmapPage/CourseBag.tsx b/site/src/pages/RoadmapPage/CourseBag.tsx deleted file mode 100644 index ed5e876b..00000000 --- a/site/src/pages/RoadmapPage/CourseBag.tsx +++ /dev/null @@ -1,43 +0,0 @@ -import { useCoursebag } from '../../hooks/coursebag'; -import Course from './Course'; -import './Coursebag.scss'; -import { Draggable } from 'react-beautiful-dnd'; -const CourseBag = () => { - const { coursebag, removeCourseFromBag } = useCoursebag(); - - return ( -
-

Course Bag

-
- {coursebag.map((course, index) => { - return ( - - {(provided) => ( -
- { - removeCourseFromBag(course); - }} - /> -
- )} -
- ); - })} -
-
- ); -}; - -export default CourseBag; diff --git a/site/src/pages/RoadmapPage/CourseHitItem.tsx b/site/src/pages/RoadmapPage/CourseHitItem.tsx deleted file mode 100644 index c79d42ad..00000000 --- a/site/src/pages/RoadmapPage/CourseHitItem.tsx +++ /dev/null @@ -1,85 +0,0 @@ -import { FC } from 'react'; -import { Draggable } from 'react-beautiful-dnd'; -import { useAppDispatch } from '../../store/hooks'; -import { setActiveCourse, setShowAddCourse } from '../../store/slices/roadmapSlice'; -import Course from './Course'; - -import { useIsMobile } from '../../helpers/util'; -import { CourseGQLData } from '../../types/types'; -import { useCoursebag } from '../../hooks/coursebag'; - -interface CourseHitItemProps extends CourseGQLData { - index: number; -} - -const CourseHitItem: FC = (props: CourseHitItemProps) => { - const dispatch = useAppDispatch(); - const isMobile = useIsMobile(); - const { coursebag, addCourseToBag, removeCourseFromBag } = useCoursebag(); - const isInBag = coursebag.some((course) => course.id === props.id); - // do not make course draggable on mobile - const onMobileMouseDown = () => { - dispatch(setActiveCourse(props)); - dispatch(setShowAddCourse(true)); - // also hide the search bar to view the roadmap - }; - const onMobileKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'Enter') { - onMobileMouseDown(); - } - }; - const onAddToBag = () => { - if (!props) return; - if (props.id === undefined) return; - if (coursebag.some((course) => course.id === props.id)) return; - addCourseToBag(props); - }; - const removeFromBag = () => { - removeCourseFromBag(props); - }; - - if (isMobile) { - return ( -
- -
- ); - } - // course is draggable on desktop - else { - return ( - - {(provided) => { - return ( -
- -
- ); - }} -
- ); - } -}; - -export default CourseHitItem; diff --git a/site/src/pages/RoadmapPage/Coursebag.scss b/site/src/pages/RoadmapPage/Coursebag.scss deleted file mode 100644 index 7737f66e..00000000 --- a/site/src/pages/RoadmapPage/Coursebag.scss +++ /dev/null @@ -1,29 +0,0 @@ -.coursebag-container { - padding-top: 2vh; - overflow-y: auto; - height: 100%; -} -.coursebag-title { - font-size: 2rem; - font-weight: 500; - padding: 0 1.8rem; -} -.no-results { - display: flex; - flex-direction: column; - align-items: center; - gap: 2rem; - font-size: 1.5rem; - padding: 2rem; - text-align: center; - - img { - width: 400px; - max-width: 100%; - } -} - -.search-pagination { - display: flex; - justify-content: center; -} diff --git a/site/src/pages/RoadmapPage/Header.scss b/site/src/pages/RoadmapPage/Header.scss index edd0474f..d9916766 100644 --- a/site/src/pages/RoadmapPage/Header.scss +++ b/site/src/pages/RoadmapPage/Header.scss @@ -28,6 +28,13 @@ white-space: nowrap; } } + .btn-group { + flex-wrap: wrap; + justify-content: end; + } + .btn-group button { + flex-grow: 0; + } } } diff --git a/site/src/pages/RoadmapPage/Quarter.scss b/site/src/pages/RoadmapPage/Quarter.scss index d9e9cccb..70f8dfb7 100644 --- a/site/src/pages/RoadmapPage/Quarter.scss +++ b/site/src/pages/RoadmapPage/Quarter.scss @@ -82,17 +82,24 @@ } } - > .quarter-course .course { + .course { box-shadow: none; border: 1px solid #268cd366; border-radius: 0; &.invalid .course-and-info { color: orange; } + .quarter-indicator-container { + display: none; + } + &.sortable-ghost { + opacity: 0.35; + } } } .course { + margin-block: 0; margin-inline: -1px; } } @@ -104,6 +111,10 @@ body:has(> #profile-popover) { transition: background-color 0s; } } +body:has(> .course.sortable-fallback) * { + // when a course is dragged, show grabbing cursor no matter what + cursor: grabbing !important; +} .quarter-menu-btn { display: block; diff --git a/site/src/pages/RoadmapPage/Quarter.tsx b/site/src/pages/RoadmapPage/Quarter.tsx index 694a19bc..7c7ba1b2 100644 --- a/site/src/pages/RoadmapPage/Quarter.tsx +++ b/site/src/pages/RoadmapPage/Quarter.tsx @@ -1,17 +1,24 @@ import { FC, useContext, useRef, useState } from 'react'; -import { Draggable } from 'react-beautiful-dnd'; import { Button, OverlayTrigger, Popover } from 'react-bootstrap'; import { Plus, ThreeDots } from 'react-bootstrap-icons'; import { quarterDisplayNames } from '../../helpers/planner'; -import { useIsMobile } from '../../helpers/util'; +import { deepCopy, useIsMobile } from '../../helpers/util'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; -import { clearQuarter, deleteCourse, deleteQuarter, setShowSearch } from '../../store/slices/roadmapSlice'; +import { + clearQuarter, + deleteCourse, + deleteQuarter, + moveCourse, + setActiveCourse, + setShowSearch, +} from '../../store/slices/roadmapSlice'; import ThemeContext from '../../style/theme-context'; import { PlannerQuarterData } from '../../types/types'; import './Quarter.scss'; -import { StrictModeDroppable } from './StrictModeDroppable'; import Course from './Course'; +import { ReactSortable, SortableEvent } from 'react-sortablejs'; +import { quarterSortable } from '../../helpers/sortable'; interface QuarterProps { year: number; @@ -49,52 +56,26 @@ const Quarter: FC = ({ year, yearIndex, quarterIndex, data }) => { const unitCount = calculateQuarterStats()[0]; - const renderCourses = () => { - return data.courses.map((course, index) => { - return ( - - {(provided) => { - let requiredCourses: string[] = null!; - // if this is an invalid course, set the required courses - invalidCourses.forEach((ic) => { - const loc = ic.location; - if (loc.courseIndex == index && loc.quarterIndex == quarterIndex && loc.yearIndex == yearIndex) { - requiredCourses = ic.required; - } - }); - - const onDelete = () => { - dispatch( - deleteCourse({ - yearIndex, - quarterIndex, - courseIndex: index, - }), - ); - }; + const coursesCopy = deepCopy(data.courses); // Sortable requires data to be extensible (non read-only) - return ( -
- -
- ); - }} -
- ); - }); + const removeCourseAt = (index: number) => { + dispatch(deleteCourse({ courseIndex: index, quarterIndex, yearIndex })); + }; + const removeCourse = (event: SortableEvent) => removeCourseAt(event.oldIndex!); + const addCourse = (event: SortableEvent) => { + const movePayload = { + from: { yearIndex: -1, quarterIndex: -1, courseIndex: -1 }, + to: { yearIndex, quarterIndex, courseIndex: event.newIndex! }, + }; + dispatch(moveCourse(movePayload)); + }; + const sortCourse = (event: SortableEvent) => { + if (event.from !== event.to) return; + const movePayload = { + from: { yearIndex, quarterIndex, courseIndex: event.oldDraggableIndex! }, + to: { yearIndex, quarterIndex, courseIndex: event.newDraggableIndex! }, + }; + dispatch(moveCourse(movePayload)); }; const popover = ( @@ -126,6 +107,11 @@ const Quarter: FC = ({ year, yearIndex, quarterIndex, data }) => { ); + const setDraggedItem = (event: SortableEvent) => { + const course = data.courses[event.oldIndex!]; + dispatch(setActiveCourse(course)); + }; + return (
@@ -151,16 +137,37 @@ const Quarter: FC = ({ year, yearIndex, quarterIndex, data }) => { )}
- - {(provided) => { + + {data.courses.map((course, index) => { + let requiredCourses: string[] = null!; + // if this is an invalid course, set the required courses + invalidCourses.forEach((ic) => { + const loc = ic.location; + if (loc.courseIndex == index && loc.quarterIndex == quarterIndex && loc.yearIndex == yearIndex) { + requiredCourses = ic.required; + } + }); + return ( -
- {renderCourses()} - {provided.placeholder} -
+ // addMode="drag" somehow fixes the issue with tapping a course after adding on mobile + removeCourseAt(index)} + addMode="drag" + /> ); - }} -
+ })} + {isMobile && ( <> diff --git a/site/src/pages/RoadmapPage/SearchSidebar.scss b/site/src/pages/RoadmapPage/SearchSidebar.scss index fee7b5c8..b861bc91 100644 --- a/site/src/pages/RoadmapPage/SearchSidebar.scss +++ b/site/src/pages/RoadmapPage/SearchSidebar.scss @@ -1,22 +1,39 @@ +@use '../../globals'; + .search-sidebar { position: sticky; right: 0; top: 0; overflow: auto; + width: 368px; height: 100%; + background-color: var(--overlay1); display: flex; flex-direction: column; - background-color: var(--overlay1); + flex-shrink: 0; + padding: 16px 20px; + border-radius: 8px; - .close-icon { - width: 5vh; - height: 5vh; - float: right; + .search-body { + display: flex; + flex-direction: column; + height: 100%; + overflow: auto; + gap: 8px; + margin-top: 8px; } - .search-body { - flex-grow: 1; - overflow: hidden; + &.mobile { + @include globals.bottom-overlay(450); + border-radius: 8px 8px 0 0; + } + + button.fixed { + @include globals.bottom-button(); + } + + .coursebag-title { + margin: 12px 6px 0; } } @@ -36,5 +53,36 @@ } .search-sidebar-search-module { - margin: 2rem 2rem 0 2rem; + .form-group { + margin-bottom: 0; + } +} + +.coursebag-title { + font-size: 1.25rem; + font-weight: 500; +} + +h3.coursebag-title { + font-size: 2rem; +} + +.no-results { + display: flex; + flex-direction: column; + align-items: center; + gap: 2rem; + font-size: 1.5rem; + padding: 2rem; + text-align: center; + + img { + width: 400px; + max-width: 100%; + } +} + +.search-pagination { + display: flex; + justify-content: center; } diff --git a/site/src/pages/RoadmapPage/SearchSidebar.tsx b/site/src/pages/RoadmapPage/SearchSidebar.tsx index 54e3ef7f..bbf04115 100644 --- a/site/src/pages/RoadmapPage/SearchSidebar.tsx +++ b/site/src/pages/RoadmapPage/SearchSidebar.tsx @@ -1,65 +1,113 @@ import './SearchSidebar.scss'; -import CloseButton from 'react-bootstrap/CloseButton'; -import SearchHitContainer from '../../component/SearchHitContainer/SearchHitContainer'; import SearchModule from '../../component/SearchModule/SearchModule'; -import CourseHitItem from './CourseHitItem'; -import { useIsMobile } from '../../helpers/util'; +import { deepCopy, useIsMobile } from '../../helpers/util'; import { useAppDispatch, useAppSelector } from '../../store/hooks'; -import { setShowSearch } from '../../store/slices/roadmapSlice'; -import { StrictModeDroppable } from './StrictModeDroppable'; -import CourseBag from './CourseBag'; +import { setActiveCourse, setShowSearch } from '../../store/slices/roadmapSlice'; +import { useEffect, useRef } from 'react'; +import UIOverlay from '../../component/UIOverlay/UIOverlay'; -const SearchSidebar = () => { +import { ReactSortable, SortableEvent } from 'react-sortablejs'; +import { useCoursebag } from '../../hooks/coursebag'; +import { CourseGQLData } from '../../types/types'; +import Course from './Course'; +import { courseSearchSortable } from '../../helpers/sortable'; +import { Spinner } from 'react-bootstrap'; +import { useNamedAcademicTerm } from '../../hooks/namedAcademicTerm'; +import noResultsImg from '../../asset/no-results-crop.webp'; + +const CloseRoadmapSearchButton = () => { + const isMobile = useIsMobile(); const dispatch = useAppDispatch(); + const { year, quarter } = useNamedAcademicTerm(); + + if (!isMobile) return <>; + + const closeSearch = () => dispatch(setShowSearch({ show: false })); + + return ( + + ); +}; + +interface SearchPlaceholderProps { + searchInProgress: boolean; + showCourseBag: boolean; +} +const SearchPlaceholder = ({ searchInProgress, showCourseBag }: SearchPlaceholderProps) => { + if (searchInProgress) return ; + + const placeholderText = showCourseBag + ? 'No courses saved. Try searching for something!' + : "Sorry, we couldn't find any results for that search!"; + + return ( + <> + No results found + {placeholderText} + + ); +}; + +const SearchSidebar = () => { const isMobile = useIsMobile(); + const showSearch = useAppSelector((state) => state.roadmap.showSearch); const { showCourseBag } = useAppSelector((state) => state.roadmap); + const overlayRef = useRef(null); + const sidebarRef = useRef(null); + const dispatch = useAppDispatch(); + + const { coursebag } = useCoursebag(); + const { results, searchInProgress } = useAppSelector((state) => state.search.courses); + + // Deep copy because Sortable requires data to be extensible (non read-only) + const shownCourses = deepCopy(showCourseBag ? coursebag : results) as CourseGQLData[]; + + const closeSearch = () => dispatch(setShowSearch({ show: false })); + + useEffect(() => { + if (!isMobile) return; + sidebarRef.current?.classList.toggle('enter-done', showSearch); + overlayRef.current?.classList.toggle('enter-done', showSearch); + }, [isMobile, showSearch]); + + const setDraggedItem = (event: SortableEvent) => { + const course = shownCourses[event.oldIndex!]; + dispatch(setActiveCourse(course)); + }; + return ( -
- {isMobile && ( -
- { - dispatch(setShowSearch({ show: false })); - }} - /> -
- )} -
+ <> + {isMobile && showSearch && } +
- {!showCourseBag ? ( - - {(provided) => { - return ( -
-
- -
- {provided.placeholder} -
- ); - }} -
+ {showCourseBag &&

Saved Courses

} + + {!searchInProgress && shownCourses.length ? ( + + {shownCourses.map((course, i) => ( + + ))} + ) : ( - - {(provided) => { - return ( -
-
- -
- {provided.placeholder} -
- ); - }} -
+
+ +
)} +
-
+ ); }; diff --git a/site/src/pages/RoadmapPage/StrictModeDroppable.tsx b/site/src/pages/RoadmapPage/StrictModeDroppable.tsx deleted file mode 100644 index 5a4939cf..00000000 --- a/site/src/pages/RoadmapPage/StrictModeDroppable.tsx +++ /dev/null @@ -1,26 +0,0 @@ -import { useEffect, useState } from 'react'; -import { Droppable, DroppableProps } from 'react-beautiful-dnd'; - -/** - * react-beautitful-dnd does not work in strict mode in React v18 - * this wrapper fixes that - * from: https://github.com/atlassian/react-beautiful-dnd/issues/2399#issuecomment-1175638194 - */ -export const StrictModeDroppable = ({ children, ...props }: DroppableProps) => { - const [enabled, setEnabled] = useState(false); - - useEffect(() => { - const animation = requestAnimationFrame(() => setEnabled(true)); - - return () => { - cancelAnimationFrame(animation); - setEnabled(false); - }; - }, []); - - if (!enabled) { - return null; - } - - return {children}; -}; diff --git a/site/src/pages/RoadmapPage/Year.scss b/site/src/pages/RoadmapPage/Year.scss index 1a895dbc..388ecb5c 100644 --- a/site/src/pages/RoadmapPage/Year.scss +++ b/site/src/pages/RoadmapPage/Year.scss @@ -44,7 +44,7 @@ .year-accordion-content { border-radius: var(--border-radius); display: grid; - grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 20px; margin-block: 16px 4px; } diff --git a/site/src/pages/RoadmapPage/index.scss b/site/src/pages/RoadmapPage/index.scss index 93af9c4e..1c091ed5 100644 --- a/site/src/pages/RoadmapPage/index.scss +++ b/site/src/pages/RoadmapPage/index.scss @@ -4,6 +4,7 @@ overflow: hidden; display: flex; width: 100%; + gap: 36px; .hide { display: none; @@ -32,3 +33,21 @@ margin: 0; } } + +.search-body, +.quarter { + &:not(.disabled) .course { + cursor: grab; + user-select: none; + -webkit-user-select: none; + &:active { + cursor: grabbing; + } + } + &.disabled .course { + cursor: pointer; + } + .course-and-info svg { + cursor: help; + } +} diff --git a/site/src/pages/RoadmapPage/index.tsx b/site/src/pages/RoadmapPage/index.tsx index 49fc1db3..072310c8 100644 --- a/site/src/pages/RoadmapPage/index.tsx +++ b/site/src/pages/RoadmapPage/index.tsx @@ -1,146 +1,26 @@ -import { FC, useCallback } from 'react'; +import { FC } from 'react'; import './index.scss'; import Planner from './Planner'; import SearchSidebar from './SearchSidebar'; -import { DragDropContext, DropResult, DragStart } from 'react-beautiful-dnd'; -import { useAppDispatch, useAppSelector } from '../../store/hooks'; -import { moveCourse, deleteCourse, setActiveCourse } from '../../store/slices/roadmapSlice'; +import { useAppSelector } from '../../store/hooks'; import AddCoursePopup from './AddCoursePopup'; -import { CourseGQLData } from '../../types/types'; import { useIsMobile } from '../../helpers/util'; -import { useCoursebag } from '../../hooks/coursebag'; +import { CSSTransition } from 'react-transition-group'; const RoadmapPage: FC = () => { - const dispatch = useAppDispatch(); const showSearch = useAppSelector((state) => state.roadmap.showSearch); - const searchResults = useAppSelector((state) => state.search.courses.results) as CourseGQLData[]; - const { coursebag, addCourseToBag, removeCourseFromBag } = useCoursebag(); const isMobile = useIsMobile(); - const roadmaps = useAppSelector((state) => state.roadmap.plans); - const roadmap = roadmaps[useAppSelector((state) => state.roadmap.currentPlanIndex)].content.yearPlans; - const onDragEnd = useCallback( - (result: DropResult) => { - if (result.reason === 'DROP') { - // no destination - if (!result.destination) { - return; - } - - // dragging to search bar - if (result.destination.droppableId === 'search' && result.source.droppableId != 'search') { - // removing from quarter - - const [yearIndex, quarterIndex] = result.source.droppableId.split('-'); - dispatch( - deleteCourse({ - yearIndex: parseInt(yearIndex), - quarterIndex: parseInt(quarterIndex), - courseIndex: result.source.index, - }), - ); - return; - } - //move from planner to coursebag - if (result.destination.droppableId === 'coursebag' && result.source.droppableId != 'coursebag') { - const [yearIndex, quarterIndex]: string[] = result.source.droppableId.split('-'); - const course = roadmap[parseInt(yearIndex)].quarters[parseInt(quarterIndex)].courses[result.source.index]; - addCourseToBag(course); - dispatch( - deleteCourse({ - yearIndex: parseInt(yearIndex), - quarterIndex: parseInt(quarterIndex), - courseIndex: result.source.index, - }), - ); - - return; - } - - if (result.source.droppableId === 'coursebag' && result.destination.droppableId != 'coursebag') { - const course = coursebag[result.source.index]; - - removeCourseFromBag(course); - } - - const movePayload = { - from: { - yearIndex: -1, - quarterIndex: -1, - courseIndex: -1, - }, - to: { - yearIndex: -1, - quarterIndex: -1, - courseIndex: -1, - }, - }; - - // roadmap to roadmap has source - if (result.source.droppableId != 'search' && result.source.droppableId != 'coursebag') { - const [yearIndex, quarterIndex] = result.source.droppableId.split('-'); - movePayload.from.yearIndex = parseInt(yearIndex); - movePayload.from.quarterIndex = parseInt(quarterIndex); - movePayload.from.courseIndex = result.source.index; - } - // search to roadmap has no source (use activeCourse in global state) - - // both have destination - const [yearIndex, quarterIndex] = result.destination.droppableId.split('-'); - movePayload.to.yearIndex = parseInt(yearIndex); - movePayload.to.quarterIndex = parseInt(quarterIndex); - movePayload.to.courseIndex = result.destination.index; - - dispatch(moveCourse(movePayload)); - } - }, - [coursebag, dispatch, roadmap, addCourseToBag, removeCourseFromBag], - ); - - const onDragStart = useCallback( - (start: DragStart) => { - if (start.source.droppableId === 'search') { - const activeCourse = searchResults[start.source.index]; - dispatch(setActiveCourse(activeCourse)); - } - if (start.source.droppableId === 'coursebag') { - const activeCourse = coursebag[start.source.index]; - dispatch(setActiveCourse(activeCourse)); - } - }, - [dispatch, searchResults, coursebag], - ); - - // do not conditionally renderer because it would remount planner which would discard unsaved changes - const mobileVersion = ( - <> -
- -
-
- -
- - ); - - const desktopVersion = ( - <> -
- -
-
- -
- - ); return ( <>
- - {isMobile && mobileVersion} - {!isMobile && desktopVersion} - +
+ +
+ + +
); diff --git a/site/src/pages/SearchPage/HitItem.scss b/site/src/pages/SearchPage/HitItem.scss index 21994387..08998ce8 100644 --- a/site/src/pages/SearchPage/HitItem.scss +++ b/site/src/pages/SearchPage/HitItem.scss @@ -1,7 +1,7 @@ .hit-item { border-radius: var(--border-radius); background-color: var(--overlay1); - padding: 2vh; + padding: 20px 24px; margin-bottom: 2vh; cursor: pointer; diff --git a/site/src/pages/SearchPage/SearchPage.scss b/site/src/pages/SearchPage/SearchPage.scss index f2196542..c5c76e2e 100644 --- a/site/src/pages/SearchPage/SearchPage.scss +++ b/site/src/pages/SearchPage/SearchPage.scss @@ -3,7 +3,9 @@ #content-container { display: flex; flex-grow: 1; + max-width: 100%; height: 100%; + margin-inline: auto; #search-list { width: 50vw; diff --git a/site/src/store/slices/roadmapSlice.ts b/site/src/store/slices/roadmapSlice.ts index 55937e54..c6bdf0d4 100644 --- a/site/src/store/slices/roadmapSlice.ts +++ b/site/src/store/slices/roadmapSlice.ts @@ -81,7 +81,7 @@ const initialSliceState: RoadmapSliceState = { showAddCourse: false, showTransfer: false, transfers: [], - showCourseBag: false, + showCourseBag: true, }; /** added for multiple planner */ diff --git a/site/src/style/theme.scss b/site/src/style/theme.scss index def312fa..d1ce1ceb 100644 --- a/site/src/style/theme.scss +++ b/site/src/style/theme.scss @@ -6,6 +6,7 @@ --overlay3: #fff; --overlay4: var(--peterportal-gray-blue); --text: #212529; // default bootstrap color + --text-secondary: #606166; --text-dark: #000; --text-light: #495057; --hover: #cacbcd; // semantic-ui default @@ -22,6 +23,7 @@ // jumps to 10% brighter --overlay4: #4c4c4c; --text: #fff; + --text-secondary: #99999f; --text-dark: #fff; --text-light: #fff; --hover: #cacbcd; diff --git a/site/vite.config.ts b/site/vite.config.ts index f75dd7de..315ac7ad 100644 --- a/site/vite.config.ts +++ b/site/vite.config.ts @@ -20,7 +20,6 @@ export default defineConfig({ rollupOptions: { output: { manualChunks: { - dnd: ['react-beautiful-dnd'], nivo: ['@nivo/core', '@nivo/bar', '@nivo/pie'], miscComponentLibraries: ['semantic-ui-react', 'react-bootstrap', 'react-bootstrap-icons'], },