diff --git a/package.json b/package.json index 4a952280a..ca4649f50 100644 --- a/package.json +++ b/package.json @@ -18,6 +18,7 @@ "db:seed": "NODE_ENV='test' tsx prisma/seeds/index.ts", "dev": "next dev", "build": "next build", + "build:icons": "tsx scripts/build-icons.ts", "start": "next start", "postinstall": "prisma generate", "prepare": "husky", @@ -59,7 +60,6 @@ "@radix-ui/react-tooltip": "^1.1.2", "@react-email/components": "0.0.22", "@react-pdf/renderer": "^3.4.4", - "@remixicon/react": "^4.0.1", "@scalar/nextjs-api-reference": "^0.4.18", "@sentry/nextjs": "^8.19.0", "@simplewebauthn/browser": "^10.0.0", @@ -129,6 +129,7 @@ "@simplewebauthn/types": "^10.0.0", "@tailwindcss/typography": "^0.5.12", "@types/cookie": "^0.6.0", + "@types/fs-extra": "^11.0.4", "@types/inquirer": "^9.0.7", "@types/lodash-es": "^4.17.12", "@types/node": "^20.12.12", @@ -138,12 +139,17 @@ "@types/ua-parser-js": "^0.7.39", "autoprefixer": "^10.4.19", "colors": "^1.4.0", + "execa": "^9.3.0", + "fast-glob": "^3.3.2", + "fs-extra": "^11.2.0", "husky": "^9.1.3", "inquirer": "^9.2.22", "knip": "^5.17.2", "lint-staged": "^15.2.2", + "node-html-parser": "^6.1.13", "postcss": "^8.4.40", "prisma": "^5.13.0", + "remixicon": "^4.3.0", "tailwindcss": "^3.4.3", "tsx": "^4.7.0", "typescript": "^5.4.5", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c6e578d3b..36455c9e5 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -95,9 +95,6 @@ importers: '@react-pdf/renderer': specifier: ^3.4.4 version: 3.4.4(react@18.3.1) - '@remixicon/react': - specifier: ^4.0.1 - version: 4.2.0(react@18.3.1) '@scalar/nextjs-api-reference': specifier: ^0.4.18 version: 0.4.18(@babel/core@7.24.5)(@opentelemetry/api@1.9.0)(postcss@8.4.40)(react-dom@18.2.0(react@18.3.1))(tailwindcss@3.4.3)(typescript@5.4.5)(vitest@1.6.0(@types/node@20.12.12)(terser@5.31.1)) @@ -300,6 +297,9 @@ importers: '@types/cookie': specifier: ^0.6.0 version: 0.6.0 + '@types/fs-extra': + specifier: ^11.0.4 + version: 11.0.4 '@types/inquirer': specifier: ^9.0.7 version: 9.0.7 @@ -327,6 +327,15 @@ importers: colors: specifier: ^1.4.0 version: 1.4.0 + execa: + specifier: ^9.3.0 + version: 9.3.0 + fast-glob: + specifier: ^3.3.2 + version: 3.3.2 + fs-extra: + specifier: ^11.2.0 + version: 11.2.0 husky: specifier: ^9.1.3 version: 9.1.4 @@ -339,12 +348,18 @@ importers: lint-staged: specifier: ^15.2.2 version: 15.2.2 + node-html-parser: + specifier: ^6.1.13 + version: 6.1.13 postcss: specifier: ^8.4.40 version: 8.4.40 prisma: specifier: ^5.13.0 version: 5.14.0 + remixicon: + specifier: ^4.3.0 + version: 4.3.0 tailwindcss: specifier: ^3.4.3 version: 3.4.3 @@ -2921,11 +2936,6 @@ packages: '@remirror/core-constants@2.0.2': resolution: {integrity: sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==} - '@remixicon/react@4.2.0': - resolution: {integrity: sha512-eGhKpZ88OU0qkcY9pJu6khBmItDV82nU130E6C68yc+FbljueHlUYy/4CrJsmf860RIDMay2Rpzl27OSJ81miw==} - peerDependencies: - react: '>=18.2.0' - '@replit/codemirror-css-color-picker@6.1.1': resolution: {integrity: sha512-e/wYHcgt3HRDpvYuwqXyjv3LEY6VyFjJeDQK1UtFmaykp86R6Cbw3ULH9pvuJuelaW6nS4CVtIRHuOfbFLlqwQ==} peerDependencies: @@ -3112,6 +3122,9 @@ packages: resolution: {integrity: sha512-OccT/4vWnz3MFoPRIRlFDFWFBca3GCxXQ9LjFaOTzb47W9Fb8l4mwsonZ/h8pCYNq0tZqaRul6G5J8AFzAgKrg==} engines: {node: '>=18'} + '@sec-ant/readable-stream@0.4.1': + resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} + '@selderee/plugin-htmlparser2@0.11.0': resolution: {integrity: sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ==} @@ -3254,6 +3267,10 @@ packages: '@sinclair/typebox@0.27.8': resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@sindresorhus/merge-streams@4.0.0': + resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} + engines: {node: '>=18'} + '@sindresorhus/slugify@2.2.1': resolution: {integrity: sha512-MkngSCRZ8JdSOCHRaYd+D01XhvU3Hjy6MGl06zhOk614hp9EOAp5gIkBeQg7wtmxpitU6eAL4kdiRMcJa2dlrw==} engines: {node: '>=12'} @@ -3911,6 +3928,9 @@ packages: '@types/extend@3.0.4': resolution: {integrity: sha512-ArMouDUTJEz1SQRpFsT2rIw7DeqICFv5aaVzLSIYMYQSLcwcGOfT3VyglQs/p7K3F7fT4zxr0NWxYZIdifD6dA==} + '@types/fs-extra@11.0.4': + resolution: {integrity: sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==} + '@types/har-format@1.2.15': resolution: {integrity: sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==} @@ -3929,6 +3949,9 @@ packages: '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + '@types/jsonfile@6.1.4': + resolution: {integrity: sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==} + '@types/lodash-es@4.17.12': resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==} @@ -4571,6 +4594,9 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + bowser@2.11.0: resolution: {integrity: sha512-AlcaJBi/pqqJBIQ8U9Mcpc9i8Aqxn88Skv5d+xBX006BY5u8N3mGLHa5Lgppa7L/HfwgwLgZ6NYs+Ag6uUmJRA==} @@ -4873,6 +4899,13 @@ packages: crypto-js@4.2.0: resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + css-select@5.1.0: + resolution: {integrity: sha512-nwoRF1rvRRnnCqqY7updORDsuqKzqYJ28+oSMaJMMgOauh3fvwHqMS7EZpIPqK8GL+g9mKxF1vP/ZjSeNjEVHg==} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + css.escape@1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} @@ -5247,6 +5280,10 @@ packages: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} + execa@9.3.0: + resolution: {integrity: sha512-l6JFbqnHEadBoVAVpN5dl2yCyfX28WoBAGaoQcNmLLSedOxTxcn2Qa83s8I/PA5i56vWru2OHOtrwF7Om2vqlg==} + engines: {node: ^18.19.0 || >=20.5.0} + extend@3.0.2: resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} @@ -5291,6 +5328,10 @@ packages: fastq@1.17.1: resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + figures@6.1.0: + resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} + engines: {node: '>=18'} + file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} @@ -5375,6 +5416,10 @@ packages: resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} engines: {node: '>=14.14'} + fs-extra@11.2.0: + resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==} + engines: {node: '>=14.14'} + fs-minipass@2.1.0: resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} engines: {node: '>= 8'} @@ -5429,6 +5474,10 @@ packages: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} + get-stream@9.0.1: + resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} + engines: {node: '>=18'} + get-tsconfig@4.7.5: resolution: {integrity: sha512-ZCuZCnlqNzjb4QprAzXKdpp/gh6KTxSJuw3IBsPnV/7fV4NxC9ckB+vPTt8w7fJA0TaSD7c55BR47JD6MEDyDw==} @@ -5609,6 +5658,10 @@ packages: hastscript@8.0.0: resolution: {integrity: sha512-dMOtzCEd3ABUeSIISmrETiKuyydk1w0pa+gE/uormcTpSYuaNJPbX1NU3JLyscSLjwAQM8bWMhhIlnCqnRvDTw==} + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + help-me@5.0.0: resolution: {integrity: sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==} @@ -5672,6 +5725,10 @@ packages: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} + human-signals@7.0.0: + resolution: {integrity: sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q==} + engines: {node: '>=18.18.0'} + husky@9.1.4: resolution: {integrity: sha512-bho94YyReb4JV7LYWRWxZ/xr6TtOTt8cMfmQ39MQYJ7f/YE268s3GdghGwi+y4zAeqewE5zYLvuhV0M0ijsDEA==} engines: {node: '>=18'} @@ -5836,6 +5893,10 @@ packages: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + is-stream@4.0.1: + resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} + engines: {node: '>=18'} + is-typed-array@1.1.13: resolution: {integrity: sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw==} engines: {node: '>= 0.4'} @@ -5844,6 +5905,10 @@ packages: resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} engines: {node: '>=10'} + is-unicode-supported@2.0.0: + resolution: {integrity: sha512-FRdAyx5lusK1iHG0TWpVtk9+1i+GjrzRffhDg4ovQ7mcidMQ6mj+MhKPmvh7Xwyv5gIS06ns49CA7Sqg7lC22Q==} + engines: {node: '>=18'} + is-url@1.2.4: resolution: {integrity: sha512-ITvGim8FhRiYe4IQ5uHSkj7pVaPDrCTkNd3yq3cV7iZAcJdHTUMPMEHcqSOy9xZ9qFenQCvi+2wjH9a1nXqHww==} @@ -6591,6 +6656,9 @@ packages: encoding: optional: true + node-html-parser@6.1.13: + resolution: {integrity: sha512-qIsTMOY4C/dAa5Q5vsobRpOOvPfC4pB61UVW2uSwZNUp0QU/jCekTal1vMmbO0DgdHeLUJpv/ARmDqErVxA3Sg==} + node-releases@2.0.14: resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} @@ -6630,6 +6698,9 @@ packages: nprogress@0.2.0: resolution: {integrity: sha512-I19aIingLgR1fmhftnbWWO3dXc0hSxqHQHQb3H8m+K3TnEn/iSeTZZOyvKXWqQESMwuUVnatlCnZdLBZZt2VSA==} + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + oauth@0.9.15: resolution: {integrity: sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==} @@ -7385,6 +7456,9 @@ packages: remark-stringify@11.0.0: resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + remixicon@4.3.0: + resolution: {integrity: sha512-jRYQ37dTFSkJtvcxwTUAkIiXkYRvA9EDvVuXPNrmt2xf/VS//CRgFtsX2TAFBoQOhh9SDh7l6La4Xu12snEyxg==} + require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} @@ -7677,6 +7751,10 @@ packages: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} + strip-final-newline@4.0.0: + resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} + engines: {node: '>=18'} + strip-indent@3.0.0: resolution: {integrity: sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==} engines: {node: '>=8'} @@ -8401,6 +8479,10 @@ packages: resolution: {integrity: sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==} engines: {node: '>=12.20'} + yoctocolors@2.1.1: + resolution: {integrity: sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==} + engines: {node: '>=18'} + yoga-layout@2.0.1: resolution: {integrity: sha512-tT/oChyDXelLo2A+UVnlW9GU7CsvFMaEnd9kVFsaiCQonFAXd3xrHhkLYu+suwwosrAEQ746xBU+HvYtm1Zs2Q==} @@ -11717,10 +11799,6 @@ snapshots: '@remirror/core-constants@2.0.2': {} - '@remixicon/react@4.2.0(react@18.3.1)': - dependencies: - react: 18.3.1 - '@replit/codemirror-css-color-picker@6.1.1(@codemirror/language@6.10.2)(@codemirror/state@6.4.1)(@codemirror/view@6.28.2)': dependencies: '@codemirror/language': 6.10.2 @@ -12090,6 +12168,8 @@ snapshots: - '@vue/composition-api' - typescript + '@sec-ant/readable-stream@0.4.1': {} + '@selderee/plugin-htmlparser2@0.11.0': dependencies: domhandler: 5.0.3 @@ -12319,6 +12399,8 @@ snapshots: '@sinclair/typebox@0.27.8': {} + '@sindresorhus/merge-streams@4.0.0': {} + '@sindresorhus/slugify@2.2.1': dependencies: '@sindresorhus/transliterate': 1.6.0 @@ -13131,6 +13213,11 @@ snapshots: '@types/extend@3.0.4': {} + '@types/fs-extra@11.0.4': + dependencies: + '@types/jsonfile': 6.1.4 + '@types/node': 20.12.12 + '@types/har-format@1.2.15': {} '@types/hast@2.3.10': @@ -13150,6 +13237,10 @@ snapshots: '@types/json-schema@7.0.15': {} + '@types/jsonfile@6.1.4': + dependencies: + '@types/node': 20.12.12 + '@types/lodash-es@4.17.12': dependencies: '@types/lodash': 4.17.4 @@ -14134,6 +14225,8 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 + boolbase@1.0.0: {} + bowser@2.11.0: {} brace-expansion@1.1.11: @@ -14450,6 +14543,16 @@ snapshots: crypto-js@4.2.0: {} + css-select@5.1.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 5.0.3 + domutils: 3.1.0 + nth-check: 2.1.1 + + css-what@6.1.0: {} + css.escape@1.5.1: {} cssesc@3.0.0: {} @@ -14883,6 +14986,21 @@ snapshots: signal-exit: 4.1.0 strip-final-newline: 3.0.0 + execa@9.3.0: + dependencies: + '@sindresorhus/merge-streams': 4.0.0 + cross-spawn: 7.0.3 + figures: 6.1.0 + get-stream: 9.0.1 + human-signals: 7.0.0 + is-plain-obj: 4.1.0 + is-stream: 4.0.1 + npm-run-path: 5.3.0 + pretty-ms: 9.0.0 + signal-exit: 4.1.0 + strip-final-newline: 4.0.0 + yoctocolors: 2.1.1 + extend@3.0.2: {} external-editor@3.1.0: @@ -14923,6 +15041,10 @@ snapshots: dependencies: reusify: 1.0.4 + figures@6.1.0: + dependencies: + is-unicode-supported: 2.0.0 + file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 @@ -15014,6 +15136,12 @@ snapshots: jsonfile: 6.1.0 universalify: 2.0.1 + fs-extra@11.2.0: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + fs-minipass@2.1.0: dependencies: minipass: 3.3.6 @@ -15063,6 +15191,11 @@ snapshots: get-stream@8.0.1: {} + get-stream@9.0.1: + dependencies: + '@sec-ant/readable-stream': 0.4.1 + is-stream: 4.0.1 + get-tsconfig@4.7.5: dependencies: resolve-pkg-maps: 1.0.0 @@ -15391,6 +15524,8 @@ snapshots: property-information: 6.5.0 space-separated-tokens: 2.0.2 + he@1.2.0: {} + help-me@5.0.0: {} highlight.js@11.9.0: {} @@ -15453,6 +15588,8 @@ snapshots: human-signals@5.0.0: {} + human-signals@7.0.0: {} + husky@9.1.4: {} hyphen@1.10.4: {} @@ -15595,12 +15732,16 @@ snapshots: is-stream@3.0.0: {} + is-stream@4.0.1: {} + is-typed-array@1.1.13: dependencies: which-typed-array: 1.1.15 is-unicode-supported@0.1.0: {} + is-unicode-supported@2.0.0: {} + is-url@1.2.4: {} is-what@4.1.16: {} @@ -16683,6 +16824,11 @@ snapshots: dependencies: whatwg-url: 5.0.0 + node-html-parser@6.1.13: + dependencies: + css-select: 5.1.0 + he: 1.2.0 + node-releases@2.0.14: {} nodemailer@6.9.14: {} @@ -16718,6 +16864,10 @@ snapshots: nprogress@0.2.0: {} + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + oauth@0.9.15: {} object-assign@4.1.1: {} @@ -17714,6 +17864,8 @@ snapshots: mdast-util-to-markdown: 2.1.0 unified: 11.0.4 + remixicon@4.3.0: {} + require-directory@2.1.1: {} require-from-string@2.0.2: {} @@ -18062,6 +18214,8 @@ snapshots: strip-final-newline@3.0.0: {} + strip-final-newline@4.0.0: {} + strip-indent@3.0.0: dependencies: min-indent: 1.0.1 @@ -18860,6 +19014,8 @@ snapshots: yocto-queue@1.1.1: {} + yoctocolors@2.1.1: {} + yoga-layout@2.0.1: {} zhead@2.2.4: {} diff --git a/public/icons/sprite.svg b/public/icons/sprite.svg new file mode 100644 index 000000000..5b7d3b501 --- /dev/null +++ b/public/icons/sprite.svg @@ -0,0 +1,103 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/build-icons.ts b/scripts/build-icons.ts new file mode 100644 index 000000000..0a77ba1e5 --- /dev/null +++ b/scripts/build-icons.ts @@ -0,0 +1,147 @@ +import * as path from "node:path"; +// this script is based on https://github.com/epicweb-dev/epic-stack/blob/main/other/build-icons.ts +// +import { $ } from "execa"; +import glob from "fast-glob"; +import fsExtra from "fs-extra"; +import { parse } from "node-html-parser"; + +import { copyIcons, removeTempDir } from "./generate-icons"; + +const cwd = process.cwd(); +const inputDir = path.join(cwd, "temp-svg-icons"); +const inputDirRelative = path.relative(cwd, inputDir); +const outputDir = path.join(cwd, "public", "icons"); +const typesDir = path.join(cwd, "src", "components", "ui", "icon"); + +const shouldVerboseLog = process.argv.includes("--log=verbose"); +const logVerbose = shouldVerboseLog ? console.log : () => {}; + +async function generateIconFiles() { + const files = glob + .sync("**/*.svg", { + cwd: inputDir, + }) + .sort((a, b) => a.localeCompare(b)); + + await fsExtra.ensureDir(outputDir); + + const spriteFilepath = path.join(outputDir, "sprite.svg"); + const typeOutputFilepath = path.join(typesDir, "icon-names.ts"); + const currentSprite = await fsExtra + .readFile(spriteFilepath, "utf8") + .catch(() => ""); + const currentTypes = await fsExtra + .readFile(typeOutputFilepath, "utf8") + .catch(() => ""); + + const iconNames = files.map((file) => iconName(file)); + + const spriteUpToDate = iconNames.every((name) => + currentSprite.includes(`id=${name}`), + ); + const typesUpToDate = iconNames.every((name) => + currentTypes.includes(`"${name}"`), + ); + + if (spriteUpToDate && typesUpToDate) { + logVerbose("Icons are up to date"); + return; + } + + logVerbose(`Generating sprite for ${inputDirRelative}`); + + const spriteChanged = await generateSvgSprite({ + files, + inputDir, + outputPath: spriteFilepath, + }); + + for (const file of files) { + logVerbose("✅", file); + } + logVerbose(`Saved to ${path.relative(cwd, spriteFilepath)}`); + + const stringifiedIconNames = iconNames.map((name) => JSON.stringify(name)); + + const typeOutputContent = `// This file is generated by pnpm run build:icons + +export type IconName = +\t| ${stringifiedIconNames.join("\n\t| ")}; +`; + const typesChanged = await writeIfChanged( + typeOutputFilepath, + typeOutputContent, + ); + + logVerbose(`Manifest saved to ${path.relative(cwd, typeOutputFilepath)}`); + + if (spriteChanged || typesChanged) { + console.log(`Generated ${files.length} icons`); + } +} + +function iconName(file: string) { + return file.replace(/\.svg$/, ""); +} + +/** + * Creates a single SVG file that contains all the icons + */ +async function generateSvgSprite({ + files, + inputDir, + outputPath, +}: { + files: string[]; + inputDir: string; + outputPath: string; +}) { + // Each SVG becomes a symbol and we wrap them all in a single SVG + const symbols = await Promise.all( + files.map(async (file) => { + const input = await fsExtra.readFile(path.join(inputDir, file), "utf8"); + const root = parse(input); + + const svg = root.querySelector("svg"); + if (!svg) throw new Error("No SVG element found"); + + svg.tagName = "symbol"; + svg.setAttribute("id", iconName(file)); + svg.removeAttribute("xmlns"); + svg.removeAttribute("xmlns:xlink"); + svg.removeAttribute("version"); + svg.removeAttribute("width"); + svg.removeAttribute("height"); + + return svg.toString().trim(); + }), + ); + + const output = [ + ``, + "", + ``, + "", // for semantics: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/defs + ...symbols, + "", + "", + "", // trailing newline + ].join("\n"); + + return writeIfChanged(outputPath, output); +} + +async function writeIfChanged(filepath: string, newContent: string) { + const currentContent = await fsExtra + .readFile(filepath, "utf8") + .catch(() => ""); + if (currentContent === newContent) return false; + await fsExtra.writeFile(filepath, newContent, "utf8"); + await $`biome check --apply --no-errors-on-unmatched ${filepath}`; + return true; +} + +copyIcons(); +await generateIconFiles(); +await removeTempDir(); diff --git a/scripts/generate-icons.ts b/scripts/generate-icons.ts new file mode 100644 index 000000000..789d6c0f1 --- /dev/null +++ b/scripts/generate-icons.ts @@ -0,0 +1,35 @@ +import path from "node:path"; +import { iconList } from "@/components/ui/icon/icon-list"; +import glob from "fast-glob"; +import fs from "fs-extra"; + +const cwd = process.cwd(); +const inputDir = path.join(cwd, "node_modules", "remixicon", "icons"); +const tempDir = path.join(cwd, "temp-svg-icons"); + +export function copyIcons() { + fs.ensureDirSync(tempDir); + + const svgIcons = glob.sync("**/*.svg", { + cwd: inputDir, + }); + + for (const icon of svgIcons) { + const iconName = path.basename(icon); + + if (iconList.has(iconName.replace(".svg", ""))) { + const destinationFile = path.join(tempDir, iconName); + const file = path.join(inputDir, icon); + try { + fs.copyFileSync(file, destinationFile); + console.log(`Copied icon: ${iconName}`); + } catch (err) { + console.error(`Error copying file: ${iconName}`, err); + } + } + } +} + +export const removeTempDir = async () => { + await fs.remove(tempDir); +}; diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/captable/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/captable/page.tsx index 249dae9bc..c6f1daea4 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/captable/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/captable/page.tsx @@ -1,6 +1,6 @@ import EmptyState from "@/components/common/empty-state"; import { Button } from "@/components/ui/button"; -import { RiPieChartFill } from "@remixicon/react"; +import { Icon } from "@/components/ui/icon"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -10,7 +10,7 @@ export const metadata: Metadata = { const CaptablePage = () => { return ( } + icon={} title="Work in progress." subtitle="This page is not yet available." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/documents/[bucketId]/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/documents/[bucketId]/page.tsx index 31feddcfe..785b74c16 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/documents/[bucketId]/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/documents/[bucketId]/page.tsx @@ -2,10 +2,10 @@ import FileIcon from "@/components/common/file-icon"; import FilePreview from "@/components/file/preview"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { withServerComponentSession } from "@/server/auth"; import { db } from "@/server/db"; import { getPresignedGetUrl } from "@/server/file-uploads"; -import { RiArrowLeftSLine } from "@remixicon/react"; import Link from "next/link"; import { notFound } from "next/navigation"; import { Fragment } from "react"; @@ -42,7 +42,7 @@ const DocumentPreview = async ({ size="icon" className="-mt-1 mr-3 flex items-center rounded-full" > - + diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/documents/components/table.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/documents/components/table.tsx index fb7a6a524..84ead14d7 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/documents/components/table.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/documents/components/table.tsx @@ -3,8 +3,8 @@ import { dayjsExt } from "@/common/dayjs"; import FileIcon from "@/components/common/file-icon"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { getPresignedGetUrl } from "@/server/file-uploads"; -import { RiMore2Fill } from "@remixicon/react"; import { useRouter } from "next/navigation"; import { @@ -77,7 +77,10 @@ const DocumentsTable = ({ documents, companyPublicId }: DocumentTableProps) => {
- + Options diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/documents/data-rooms/components/data-room-files.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/documents/data-rooms/components/data-room-files.tsx index b61f7770d..43801c3e9 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/documents/data-rooms/components/data-room-files.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/documents/data-rooms/components/data-room-files.tsx @@ -8,16 +8,12 @@ import { Card } from "@/components/ui/card"; import type { ShareContactType } from "@/schema/contacts"; import { api } from "@/trpc/react"; +import { Icon } from "@/components/ui/icon"; import type { Bucket, DataRoom } from "@prisma/client"; -import { RiShareLine } from "@remixicon/react"; import { useDebounceCallback } from "usehooks-ts"; import { pushModal } from "@/components/modals"; -import { - RiFolder3Fill as FolderIcon, - RiAddFill, - RiUploadCloudLine, -} from "@remixicon/react"; + import Link from "next/link"; import DataRoomUploader from "./data-room-uploader"; @@ -50,7 +46,8 @@ const DataRoomFiles = ({
-
diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/documents/data-rooms/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/documents/data-rooms/page.tsx index 22602b8b5..ec100285b 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/documents/data-rooms/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/documents/data-rooms/page.tsx @@ -2,9 +2,9 @@ import EmptyState from "@/components/common/empty-state"; import { Button } from "@/components/ui/button"; +import { Icon } from "@/components/ui/icon"; import { getServerComponentAuthSession } from "@/server/auth"; import { db } from "@/server/db"; -import { RiAddFill, RiFolderCheckFill } from "@remixicon/react"; import { Fragment } from "react"; import DataRoomPopover from "./components/data-room-popover"; import Folders from "./components/dataroom-folders"; @@ -44,14 +44,14 @@ const DataRoomPage = async () => { ) : ( } + icon={} title="You don't have any data rooms yet." subtitle="A secure spaces to share multiple documents with investors, stakeholders and external parties." > - + Create a data room } diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/documents/document-upload-button.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/documents/document-upload-button.tsx index fdea11ae4..d7b09bee8 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/documents/document-upload-button.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/documents/document-upload-button.tsx @@ -2,7 +2,7 @@ import { pushModal } from "@/components/modals"; import { Button } from "@/components/ui/button"; -import { RiAddFill } from "@remixicon/react"; +import { Icon } from "@/components/ui/icon"; import React from "react"; type DocumentUploadButtonProps = { @@ -22,7 +22,7 @@ export const DocumentUploadButton = ({ }); }} > - + {buttonDisplayName} ); diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/components/add-esign-doc-button.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/components/add-esign-doc-button.tsx index 80d07c950..f58eebe80 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/components/add-esign-doc-button.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/components/add-esign-doc-button.tsx @@ -2,7 +2,7 @@ import { pushModal } from "@/components/modals"; import { Button } from "@/components/ui/button"; -import { RiAddFill } from "@remixicon/react"; +import { Icon } from "@/components/ui/icon"; import type React from "react"; type AddEsignDocumentButtonProps = { @@ -26,7 +26,7 @@ export const AddEsignDocumentButton = ({ }); }} > - + Upload a document ); diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/page.tsx index 2a423f3a5..b0cc67c1d 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/page.tsx @@ -1,9 +1,9 @@ import EmptyState from "@/components/common/empty-state"; import { PageLayout } from "@/components/dashboard/page-layout"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { withServerComponentSession } from "@/server/auth"; import { api } from "@/trpc/server"; -import { RiUploadCloudLine } from "@remixicon/react"; import type { Metadata } from "next"; import { AddEsignDocumentButton } from "./components/add-esign-doc-button"; import { ESignTable } from "./components/table"; @@ -19,7 +19,7 @@ const EsignDocumentPage = async () => { if (documents.length === 0) { return ( } + icon={} title="You do not have any documents!" subtitle="Click the button below to upload a new document for electronic signature." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/v/[templatePublicId]/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/v/[templatePublicId]/page.tsx index 648983984..b227e674d 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/v/[templatePublicId]/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/documents/esign/v/[templatePublicId]/page.tsx @@ -2,10 +2,10 @@ import { PdfCanvas } from "@/components/template/pdf-canvas"; import { Alert, AlertDescription } from "@/components/ui/alert"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { serverAccessControl } from "@/lib/rbac/access-control"; import { TemplateSigningFieldProvider } from "@/providers/template-signing-field-provider"; import { api } from "@/trpc/server"; -import { RiCheckFill } from "@remixicon/react"; type BadgeVariant = | "warning" @@ -87,7 +87,10 @@ export default async function TemplateDetailViewPage({
- +

diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/documents/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/documents/page.tsx index 1306b2bcc..f55e2e75b 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/documents/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/documents/page.tsx @@ -1,11 +1,11 @@ import EmptyState from "@/components/common/empty-state"; import { PageLayout } from "@/components/dashboard/page-layout"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { UnAuthorizedState } from "@/components/ui/un-authorized-state"; import { serverAccessControl } from "@/lib/rbac/access-control"; import { withServerComponentSession } from "@/server/auth"; import { api } from "@/trpc/server"; -import { RiUploadCloudLine } from "@remixicon/react"; import type { Metadata } from "next"; import DocumentsTable from "./components/table"; import { DocumentUploadButton } from "./document-upload-button"; @@ -32,7 +32,7 @@ const DocumentsPage = async () => { if (documents.length === 0) { return ( } + icon={} title="You do not have any documents!" subtitle="Please click the button below to upload a new document." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/documents/share/_page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/documents/share/_page.tsx index fb635f74e..fab64bf9e 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/documents/share/_page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/documents/share/_page.tsx @@ -2,9 +2,9 @@ import EmptyState from "@/components/common/empty-state"; import { PageLayout } from "@/components/dashboard/page-layout"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { withServerComponentSession } from "@/server/auth"; import { api } from "@/trpc/server"; -import { RiAddFill, RiUploadCloudLine } from "@remixicon/react"; import type { Metadata } from "next"; import DocumentUploadModal from "../components/modal"; import DocumentsTable from "../components/table"; @@ -20,7 +20,7 @@ const DocumentsPage = async () => { if (documents.length === 0) { return ( } + icon={} title="You do not have any documents!" subtitle="Please click the button below to upload a new document." > @@ -28,7 +28,7 @@ const DocumentsPage = async () => { companyPublicId={session.user.companyPublicId} trigger={ } @@ -47,7 +47,7 @@ const DocumentsPage = async () => { companyPublicId={session.user.companyPublicId} trigger={ } diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/create-equity-plan-button.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/create-equity-plan-button.tsx index 4c1ae7ca2..b2b64de27 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/create-equity-plan-button.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/create-equity-plan-button.tsx @@ -2,9 +2,9 @@ import { pushModal } from "@/components/modals"; import { Button } from "@/components/ui/button"; +import { Icon } from "@/components/ui/icon"; import { EquityPlanMutationType } from "@/trpc/routers/equity-plan/schema"; import type { ShareClassMutationType } from "@/trpc/routers/share-class/schema"; -import { RiAddFill } from "@remixicon/react"; import type React from "react"; type CreateEquityPlanButtonProps = { @@ -31,7 +31,7 @@ export const CreateEquityPlanButton = ({ }); }} > - + Create an equity plan ); diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/form.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/form.tsx index 39ef7394d..aef87775b 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/form.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/form.tsx @@ -3,12 +3,12 @@ import Tldr from "@/components/common/tldr"; import { pushModal } from "@/components/modals"; import { Button } from "@/components/ui/button"; +import { Icon } from "@/components/ui/icon"; import { Input } from "@/components/ui/input"; import { Textarea } from "@/components/ui/textarea"; import { api } from "@/trpc/react"; import type { ShareClassMutationType } from "@/trpc/routers/share-class/schema"; import { zodResolver } from "@hookform/resolvers/zod"; -import { RiAddFill } from "@remixicon/react"; import { useRouter } from "next/navigation"; import { useForm } from "react-hook-form"; import { NumericFormat } from "react-number-format"; @@ -242,7 +242,11 @@ const EquityPlanForm = ({ >

- +
Create new share class
@@ -273,7 +277,7 @@ const EquityPlanForm = ({ }); }} > - + Create a share class )} diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/page.tsx index 1ca14c625..10fa85563 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/page.tsx @@ -1,11 +1,11 @@ import EmptyState from "@/components/common/empty-state"; import Tldr from "@/components/common/tldr"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { withServerComponentSession } from "@/server/auth"; import { db } from "@/server/db"; import type { EquityPlanMutationType } from "@/trpc/routers/equity-plan/schema"; import type { ShareClassMutationType } from "@/trpc/routers/share-class/schema"; -import { RiAddFill, RiPieChart2Line } from "@remixicon/react"; import type { Metadata } from "next"; import { CreateEquityPlanButton } from "./create-equity-plan-button"; import EquityPlanTable from "./table"; @@ -44,7 +44,7 @@ const EquityPlanPage = async () => { if (equityPlans.length === 0) { return ( } + icon={} title="You do not have any equity plans!" subtitle="Please click the button below to create a new equity plan." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/table.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/table.tsx index 60a5caa80..e99e73030 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/table.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/equity-plans/table.tsx @@ -9,9 +9,9 @@ import { import Tldr from "@/components/common/tldr"; import { Card } from "@/components/ui/card"; -import { type EquityPlanMutationType } from "@/trpc/routers/equity-plan/schema"; -import { type ShareClassMutationType } from "@/trpc/routers/share-class/schema"; -import { RiEqualizer2Line } from "@remixicon/react"; +import { Icon } from "@/components/ui/icon"; +import type { EquityPlanMutationType } from "@/trpc/routers/equity-plan/schema"; +import type { ShareClassMutationType } from "@/trpc/routers/share-class/schema"; import EquityPlanModal from "./modal"; const formatter = new Intl.NumberFormat("en-US"); @@ -49,7 +49,9 @@ const EquityPlanTable = ({ Cancellation behavior Board approval date Plan effective date - + + Actions + @@ -90,7 +92,12 @@ const EquityPlanTable = ({ /> } trigger={ - + } /> diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/convertible-notes/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/convertible-notes/page.tsx index 3690e43e0..836504246 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/convertible-notes/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/convertible-notes/page.tsx @@ -1,6 +1,6 @@ import EmptyState from "@/components/common/empty-state"; import { Button } from "@/components/ui/button"; -import { RiPieChartFill } from "@remixicon/react"; +import { Icon } from "@/components/ui/icon"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -10,7 +10,7 @@ export const metadata: Metadata = { const ConvertibleNotesPage = () => { return ( } + icon={} title="Work in progress." subtitle="This page is not yet available." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/investments/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/investments/page.tsx index e8497aba5..b3df48e1b 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/investments/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/investments/page.tsx @@ -1,6 +1,6 @@ import EmptyState from "@/components/common/empty-state"; import { Button } from "@/components/ui/button"; -import { RiPieChartFill } from "@remixicon/react"; +import { Icon } from "@/components/ui/icon"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -10,7 +10,7 @@ export const metadata: Metadata = { const InvestmentsPage = () => { return ( } + icon={} title="Work in progress." subtitle="This page is not yet available." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/safes/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/safes/page.tsx index d81acf197..00eeee5e4 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/safes/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/fundraise/safes/page.tsx @@ -3,8 +3,8 @@ import { PageLayout } from "@/components/dashboard/page-layout"; import { SafeActions } from "@/components/safe/safe-actions"; import { SafeTable } from "@/components/safe/safe-table"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { api } from "@/trpc/server"; -import { RiSafeFill } from "@remixicon/react"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -17,7 +17,7 @@ const SafePage = async () => { if (!safes?.data?.length) { return ( } + icon={} title="Create and manage SAFE agreements." subtitle="Please click the button for creating agreements." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/reports/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/reports/page.tsx index f45fa9c03..a1ea1b213 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/reports/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/reports/page.tsx @@ -1,6 +1,6 @@ import EmptyState from "@/components/common/empty-state"; import { Button } from "@/components/ui/button"; -import { RiFilePdf2Fill } from "@remixicon/react"; +import { Icon } from "@/components/ui/icon"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -10,7 +10,7 @@ export const metadata: Metadata = { const ReportsPage = () => { return ( } + icon={} title="No reports available." subtitle="Please click the button below to generate a report" > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/securities/options/issue-stock-option-button.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/securities/options/issue-stock-option-button.tsx index b7dc8631c..783b51a61 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/securities/options/issue-stock-option-button.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/securities/options/issue-stock-option-button.tsx @@ -4,7 +4,7 @@ import { pushModal } from "@/components/modals"; import type { TStakeholders } from "@/components/modals/issue-share-modal"; import type { TEquityPlans } from "@/components/modals/issue-stock-option-modal"; import { Button } from "@/components/ui/button"; -import { RiAddFill } from "@remixicon/react"; +import { Icon } from "@/components/ui/icon"; import type React from "react"; type CreateShareClassButtonProps = { @@ -37,7 +37,7 @@ export const IssueStockOptionButton = ({ }); }} > - {showButtonIcon && } + {showButtonIcon && } {buttonDisplayName} ); diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/securities/options/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/securities/options/page.tsx index 03e7cadcc..91a4f44ea 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/securities/options/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/securities/options/page.tsx @@ -2,8 +2,8 @@ import EmptyState from "@/components/common/empty-state"; import Tldr from "@/components/common/tldr"; import OptionTable from "@/components/securities/options/option-table"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { api } from "@/trpc/server"; -import { RiGroup2Fill } from "@remixicon/react"; import type { Metadata } from "next"; import { IssueStockOptionButton } from "./issue-stock-option-button"; @@ -19,7 +19,7 @@ const OptionsPage = async () => { if (options?.data?.length === 0) { return ( } + icon={} title="You have not issued any stock options yet." subtitle="Please click the button below to start issueing options." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/securities/shares/issue-share-button.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/securities/shares/issue-share-button.tsx index 1183ecec1..1b5eb62de 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/securities/shares/issue-share-button.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/securities/shares/issue-share-button.tsx @@ -6,7 +6,7 @@ import type { TStakeholders, } from "@/components/modals/issue-share-modal"; import { Button } from "@/components/ui/button"; -import { RiAddFill } from "@remixicon/react"; +import { Icon } from "@/components/ui/icon"; import type React from "react"; type IssueShareButtonProps = { @@ -34,7 +34,7 @@ export const IssueShareButton = ({ }); }} > - + Create a share ); diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/securities/shares/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/securities/shares/page.tsx index fbd24edb0..c67abfcdf 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/securities/shares/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/securities/shares/page.tsx @@ -3,8 +3,8 @@ import { ShareModal } from "@/components/securities/shares/share-modal"; import ShareTable from "@/components/securities/shares/share-table"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { api } from "@/trpc/server"; -import { RiAddFill, RiPieChartFill } from "@remixicon/react"; import type { Metadata } from "next"; import { IssueShareButton } from "./issue-share-button"; @@ -20,7 +20,7 @@ const SharesPage = async () => { if (shares?.data?.length === 0) { return ( } + icon={} title="You have not issued any shares" subtitle="Please click the button below to start issuing shares." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/securities/transactions/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/securities/transactions/page.tsx index 3b5bd19dd..40594b091 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/securities/transactions/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/securities/transactions/page.tsx @@ -1,6 +1,6 @@ import EmptyState from "@/components/common/empty-state"; import { Button } from "@/components/ui/button"; -import { RiPieChartFill } from "@remixicon/react"; +import { Icon } from "@/components/ui/icon"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -10,7 +10,7 @@ export const metadata: Metadata = { const TransactionsPage = () => { return ( } + icon={} title="Work in progress." subtitle="This page is not yet available." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/settings/bank-accounts/components/cta-button.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/settings/bank-accounts/components/cta-button.tsx index 5ec8bb910..cf3b32e6a 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/settings/bank-accounts/components/cta-button.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/settings/bank-accounts/components/cta-button.tsx @@ -2,7 +2,7 @@ import { pushModal } from "@/components/modals"; import { Button } from "@/components/ui/button"; -import { RiAddLine } from "@remixicon/react"; +import { Icon } from "@/components/ui/icon"; const CtaButton = () => { return ( @@ -14,7 +14,7 @@ const CtaButton = () => { }); }} > - + Add a bank account ); diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/settings/bank-accounts/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/settings/bank-accounts/page.tsx index 357d32436..8c227d18c 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/settings/bank-accounts/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/settings/bank-accounts/page.tsx @@ -1,8 +1,8 @@ import EmptyState from "@/components/common/empty-state"; +import { Icon } from "@/components/ui/icon"; import { UnAuthorizedState } from "@/components/ui/un-authorized-state"; import { serverAccessControl } from "@/lib/rbac/access-control"; import { api } from "@/trpc/server"; -import { RiBankFill } from "@remixicon/react"; import type { Metadata } from "next"; import { Fragment } from "react"; import CtaButton from "./components/cta-button"; @@ -28,7 +28,7 @@ const ApiSettingsPage = async () => { {data.bankAccounts.length === 0 ? ( } + icon={} title="Bank accounts" subtitle="Add a bank account to receive funds" > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/components/create-access-token.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/components/create-access-token.tsx index e2837997a..8800f8f14 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/components/create-access-token.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/components/create-access-token.tsx @@ -4,8 +4,8 @@ import Modal from "@/components/common/modal"; import Tldr from "@/components/common/tldr"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { api } from "@/trpc/react"; -import { RiAddLine } from "@remixicon/react"; import { useRouter } from "next/navigation"; import { Fragment, useState } from "react"; import { toast } from "sonner"; @@ -49,7 +49,7 @@ const CreateAccessToken = () => { }} loading={loading} > - + Create an access token diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/components/table.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/components/table.tsx index efeddc9f4..5a9eb3fe8 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/components/table.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/components/table.tsx @@ -22,6 +22,7 @@ import { DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { Icon } from "@/components/ui/icon"; import { Table, TableBody, @@ -32,7 +33,6 @@ import { } from "@/components/ui/table"; import { api } from "@/trpc/react"; import type { RouterOutputs } from "@/trpc/shared"; -import { RiMore2Fill } from "@remixicon/react"; import { useRouter } from "next/navigation"; import { useState } from "react"; import { toast } from "sonner"; @@ -122,7 +122,10 @@ const AccessTokenTable = ({ tokens }: { tokens: AccessTokens }) => {
- + Options diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/page.tsx index c5a338ce6..ff1be8da3 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/settings/developer/page.tsx @@ -1,6 +1,6 @@ import EmptyState from "@/components/common/empty-state"; +import { Icon } from "@/components/ui/icon"; import { api } from "@/trpc/server"; -import { RiTerminalBoxFill } from "@remixicon/react"; import type { Metadata } from "next"; import Link from "next/link"; import { Fragment } from "react"; @@ -20,7 +20,7 @@ const AccessTokenPage = async () => { {data.accessTokens.length === 0 ? ( } + icon={} title="Access tokens" subtitle={

diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/settings/team/add-team-member-dropdown-menu.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/settings/team/add-team-member-dropdown-menu.tsx index c6fdd653b..413e61907 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/settings/team/add-team-member-dropdown-menu.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/settings/team/add-team-member-dropdown-menu.tsx @@ -8,8 +8,8 @@ import { DropdownMenuContent, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; +import { Icon } from "@/components/ui/icon"; import type { RouterOutputs } from "@/trpc/shared"; -import { RiAccountCircleFill, RiAddLine } from "@remixicon/react"; type Roles = RouterOutputs["rbac"]["listRoles"]["rolesList"]; @@ -24,7 +24,7 @@ export const AddTeamMemberDropdownMenu = ({ @@ -51,7 +51,7 @@ export const AddTeamMemberDropdownMenu = ({ }} > <> - + Invite a team member diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/create-share-class-button.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/create-share-class-button.tsx index 0aa333903..9afa093ed 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/create-share-class-button.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/create-share-class-button.tsx @@ -3,8 +3,8 @@ import Tldr from "@/components/common/tldr"; import { pushModal } from "@/components/modals"; import { Button } from "@/components/ui/button"; +import { Icon } from "@/components/ui/icon"; import type { ShareClassMutationType } from "@/trpc/routers/share-class/schema"; -import { RiAddFill } from "@remixicon/react"; type CreateShareClassButtonProps = { shareClasses: ShareClassMutationType[]; @@ -34,7 +34,7 @@ export const CreateShareButton = ({ }); }} > - + Create a share class ); diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/edit-share-class-button.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/edit-share-class-button.tsx index 597166dbc..2f417e539 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/edit-share-class-button.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/edit-share-class-button.tsx @@ -2,8 +2,8 @@ import Tldr from "@/components/common/tldr"; import { pushModal } from "@/components/modals"; +import { Icon } from "@/components/ui/icon"; import type { ShareClassMutationType } from "@/trpc/routers/share-class/schema"; -import { RiEqualizer2Line } from "@remixicon/react"; type EditShareClassButtonProps = { shareClasses?: ShareClassMutationType[]; @@ -15,26 +15,29 @@ export const EditShareClassButton = ({ shareClass, }: EditShareClassButtonProps) => { return ( - { - pushModal("ShareClassModal", { - shouldClientFetch: false, - type: "update", - title: "Update share class", - shareClass, - shareClasses, - subtitle: ( - - ), - }); - }} - /> + ); }; diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/page.tsx index 31af92980..4fd149ec6 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/share-classes/page.tsx @@ -1,9 +1,9 @@ import EmptyState from "@/components/common/empty-state"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { withServerComponentSession } from "@/server/auth"; import { db } from "@/server/db"; import type { ShareClassMutationType } from "@/trpc/routers/share-class/schema"; -import { RiPieChart2Line } from "@remixicon/react"; import type { Metadata } from "next"; import { CreateShareButton } from "./create-share-class-button"; import ShareClassTable from "./table"; @@ -32,7 +32,7 @@ const SharesPage = async () => { if (shareClasses.length === 0) { return ( } + icon={} title="You do not have any share classes!" subtitle="Please click the button below to create a new share class." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/stakeholders/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/stakeholders/page.tsx index e2d358d57..867e814ee 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/stakeholders/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/stakeholders/page.tsx @@ -2,10 +2,10 @@ import EmptyState from "@/components/common/empty-state"; import StakeholderDropdown from "@/components/stakeholder/stakeholder-dropdown"; import StakeholderTable from "@/components/stakeholder/stakeholder-table"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import { UnAuthorizedState } from "@/components/ui/un-authorized-state"; import { serverAccessControl } from "@/lib/rbac/access-control"; import { api } from "@/trpc/server"; -import { RiGroup2Fill } from "@remixicon/react"; import type { Metadata } from "next"; export const metadata: Metadata = { @@ -32,7 +32,11 @@ const StakeholdersPage = async () => { if (stakeholders.length === 0) { return ( } + icon={ +

+ +
+ } title="You do not have any stakeholders!" subtitle="Please click the button below to add or import stakeholders." > diff --git a/src/app/(authenticated)/(dashboard)/[publicId]/updates/page.tsx b/src/app/(authenticated)/(dashboard)/[publicId]/updates/page.tsx index 982e28c3e..36bc07ee3 100644 --- a/src/app/(authenticated)/(dashboard)/[publicId]/updates/page.tsx +++ b/src/app/(authenticated)/(dashboard)/[publicId]/updates/page.tsx @@ -1,9 +1,9 @@ import EmptyState from "@/components/common/empty-state"; import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; +import { Icon } from "@/components/ui/icon"; import UpdateTable from "@/components/update/update-table"; import { api } from "@/trpc/server"; -import { RiAddFill, RiMailSendLine } from "@remixicon/react"; import type { Metadata } from "next"; import Link from "next/link"; @@ -21,13 +21,13 @@ const UpdatesPage = async ({ if (updates.data.length === 0) { return ( } + icon={} title="You have not sent any updates." subtitle="Please click the button below to send an update to your stakeholders." > @@ -48,7 +48,7 @@ const UpdatesPage = async ({
diff --git a/src/app/(documents)/data-rooms/[publicId]/[bucketId]/page.tsx b/src/app/(documents)/data-rooms/[publicId]/[bucketId]/page.tsx index be48f2381..4b4094fa7 100644 --- a/src/app/(documents)/data-rooms/[publicId]/[bucketId]/page.tsx +++ b/src/app/(documents)/data-rooms/[publicId]/[bucketId]/page.tsx @@ -2,10 +2,10 @@ import FilePreview from "@/components/file/preview"; import { SharePageLayout } from "@/components/share/page-layout"; +import { Icon } from "@/components/ui/icon"; import { type JWTVerifyResult, decode } from "@/lib/jwt"; import { db } from "@/server/db"; import { getPresignedGetUrl } from "@/server/file-uploads"; -import { RiFolder3Fill as FolderIcon } from "@remixicon/react"; import Link from "next/link"; import { notFound } from "next/navigation"; @@ -24,7 +24,11 @@ const DataRoomPage = async ({ return notFound(); } - const { companyId, dataRoomId, recipientId } = decodedToken?.payload; + if (!decodedToken?.payload) { + return notFound(); + } + + const { companyId, dataRoomId, recipientId } = decodedToken.payload; if (!companyId || !recipientId || !dataRoomId) { return notFound(); } @@ -93,7 +97,8 @@ const DataRoomPage = async ({ }} title={
-