├── backend ├── .env.example ├── .gitignore ├── README.md ├── package.json ├── pnpm-lock.yaml ├── src │ ├── bootstrap │ │ ├── database.ts │ │ └── seed-database.ts │ ├── controllers │ │ └── projects.ts │ ├── entity │ │ ├── index.ts │ │ ├── project_managers.ts │ │ └── projects.ts │ ├── enum │ │ └── index.ts │ ├── index.ts │ ├── interfaces │ │ ├── IBase.ts │ │ ├── IProject.ts │ │ ├── IProjectManager.ts │ │ └── index.ts │ ├── middlewares │ │ └── index.ts │ └── routes │ │ ├── index.ts │ │ └── projects.ts └── tsconfig.json └── frontend ├── .eslintrc.json ├── .gitignore ├── README.md ├── app ├── columns.tsx ├── data.tsx ├── favicon.ico ├── fonts │ ├── GeistMonoVF.woff │ └── GeistVF.woff ├── globals.css ├── layout.tsx └── page.tsx ├── components.json ├── components ├── data-table │ ├── Footer │ │ ├── Pagination.tsx │ │ └── index.tsx │ ├── Header │ │ ├── SearchBar.tsx │ │ └── index.tsx │ ├── ProjectActions.tsx │ ├── ProjectLastUpdate.tsx │ ├── ProjectManager.tsx │ ├── ProjectName.tsx │ ├── ProjectResources.tsx │ ├── ProjectStatus.tsx │ ├── ProjectTimeLine.tsx │ ├── SortableHeader.tsx │ ├── TableHeader.tsx │ ├── Tabs │ │ ├── TabItem.tsx │ │ └── index.tsx │ └── index.tsx └── ui │ ├── avatar.tsx │ ├── button.tsx │ ├── checkbox.tsx │ ├── dropdown-menu.tsx │ ├── select.tsx │ ├── separator.tsx │ ├── table.tsx │ └── tooltip.tsx ├── enum └── index.ts ├── lib └── utils.ts ├── next.config.ts ├── package-lock.json ├── package.json ├── pnpm-lock.yaml ├── postcss.config.mjs ├── public ├── file.svg ├── globe.svg ├── next.svg ├── vercel.svg └── window.svg ├── tailwind.config.ts └── tsconfig.json /backend/.env.example: -------------------------------------------------------------------------------- 1 | DATABASE_URL="" -------------------------------------------------------------------------------- /backend/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vscode/ 3 | node_modules/ 4 | build/ 5 | tmp/ 6 | temp/ 7 | 8 | .env -------------------------------------------------------------------------------- /backend/README.md: -------------------------------------------------------------------------------- 1 | # Awesome Project Build with TypeORM 2 | 3 | Steps to run this project: 4 | 5 | 1. Run `npm i` command 6 | 2. Setup database settings inside `data-source.ts` file 7 | 3. Run `npm start` command 8 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "backend", 3 | "version": "0.0.1", 4 | "description": "Awesome project developed with TypeORM.", 5 | "type": "commonjs", 6 | "devDependencies": { 7 | "@types/cookie-parser": "^1.4.8", 8 | "@types/cors": "^2.8.17", 9 | "@types/morgan": "^1.9.9", 10 | "@types/node": "^16.11.10", 11 | "ts-node": "10.9.1", 12 | "typescript": "5.6.3" 13 | }, 14 | "dependencies": { 15 | "@faker-js/faker": "^9.2.0", 16 | "@types/express": "^5.0.0", 17 | "cookie-parser": "^1.4.7", 18 | "cors": "^2.8.5", 19 | "express": "^4.21.1", 20 | "express-rate-limit": "^7.4.1", 21 | "http-status-codes": "^2.3.0", 22 | "morgan": "^1.10.0", 23 | "pg": "^8.4.0", 24 | "reflect-metadata": "^0.1.13", 25 | "typeorm": "0.3.20", 26 | "yup": "^1.4.0" 27 | }, 28 | "scripts": { 29 | "dev": "nodemon --exec ts-node src/index.ts", 30 | "start": "ts-node src/index.ts", 31 | "typeorm": "typeorm-ts-node-commonjs" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /backend/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@faker-js/faker': 12 | specifier: ^9.2.0 13 | version: 9.2.0 14 | '@types/express': 15 | specifier: ^5.0.0 16 | version: 5.0.0 17 | cookie-parser: 18 | specifier: ^1.4.7 19 | version: 1.4.7 20 | cors: 21 | specifier: ^2.8.5 22 | version: 2.8.5 23 | express: 24 | specifier: ^4.21.1 25 | version: 4.21.1 26 | express-rate-limit: 27 | specifier: ^7.4.1 28 | version: 7.4.1(express@4.21.1) 29 | http-status-codes: 30 | specifier: ^2.3.0 31 | version: 2.3.0 32 | morgan: 33 | specifier: ^1.10.0 34 | version: 1.10.0 35 | pg: 36 | specifier: ^8.4.0 37 | version: 8.13.1 38 | reflect-metadata: 39 | specifier: ^0.1.13 40 | version: 0.1.14 41 | typeorm: 42 | specifier: 0.3.20 43 | version: 0.3.20(pg@8.13.1)(ts-node@10.9.1(@types/node@16.18.121)(typescript@5.6.3)) 44 | yup: 45 | specifier: ^1.4.0 46 | version: 1.4.0 47 | devDependencies: 48 | '@types/cookie-parser': 49 | specifier: ^1.4.8 50 | version: 1.4.8(@types/express@5.0.0) 51 | '@types/cors': 52 | specifier: ^2.8.17 53 | version: 2.8.17 54 | '@types/morgan': 55 | specifier: ^1.9.9 56 | version: 1.9.9 57 | '@types/node': 58 | specifier: ^16.11.10 59 | version: 16.18.121 60 | ts-node: 61 | specifier: 10.9.1 62 | version: 10.9.1(@types/node@16.18.121)(typescript@5.6.3) 63 | typescript: 64 | specifier: 5.6.3 65 | version: 5.6.3 66 | 67 | packages: 68 | 69 | '@cspotcode/source-map-support@0.8.1': 70 | resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} 71 | engines: {node: '>=12'} 72 | 73 | '@faker-js/faker@9.2.0': 74 | resolution: {integrity: sha512-ulqQu4KMr1/sTFIYvqSdegHT8NIkt66tFAkugGnHA+1WAfEn6hMzNR+svjXGFRVLnapxvej67Z/LwchFrnLBUg==} 75 | engines: {node: '>=18.0.0', npm: '>=9.0.0'} 76 | 77 | '@isaacs/cliui@8.0.2': 78 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 79 | engines: {node: '>=12'} 80 | 81 | '@jridgewell/resolve-uri@3.1.2': 82 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 83 | engines: {node: '>=6.0.0'} 84 | 85 | '@jridgewell/sourcemap-codec@1.5.0': 86 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 87 | 88 | '@jridgewell/trace-mapping@0.3.9': 89 | resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} 90 | 91 | '@pkgjs/parseargs@0.11.0': 92 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 93 | engines: {node: '>=14'} 94 | 95 | '@sqltools/formatter@1.2.5': 96 | resolution: {integrity: sha512-Uy0+khmZqUrUGm5dmMqVlnvufZRSK0FbYzVgp0UMstm+F5+W2/jnEEQyc9vo1ZR/E5ZI/B1WjjoTqBqwJL6Krw==} 97 | 98 | '@tsconfig/node10@1.0.11': 99 | resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} 100 | 101 | '@tsconfig/node12@1.0.11': 102 | resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} 103 | 104 | '@tsconfig/node14@1.0.3': 105 | resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} 106 | 107 | '@tsconfig/node16@1.0.4': 108 | resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} 109 | 110 | '@types/body-parser@1.19.5': 111 | resolution: {integrity: sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==} 112 | 113 | '@types/connect@3.4.38': 114 | resolution: {integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==} 115 | 116 | '@types/cookie-parser@1.4.8': 117 | resolution: {integrity: sha512-l37JqFrOJ9yQfRQkljb41l0xVphc7kg5JTjjr+pLRZ0IyZ49V4BQ8vbF4Ut2C2e+WH4al3xD3ZwYwIUfnbT4NQ==} 118 | peerDependencies: 119 | '@types/express': '*' 120 | 121 | '@types/cors@2.8.17': 122 | resolution: {integrity: sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA==} 123 | 124 | '@types/express-serve-static-core@5.0.2': 125 | resolution: {integrity: sha512-vluaspfvWEtE4vcSDlKRNer52DvOGrB2xv6diXy6UKyKW0lqZiWHGNApSyxOv+8DE5Z27IzVvE7hNkxg7EXIcg==} 126 | 127 | '@types/express@5.0.0': 128 | resolution: {integrity: sha512-DvZriSMehGHL1ZNLzi6MidnsDhUZM/x2pRdDIKdwbUNqqwHxMlRdkxtn6/EPKyqKpHqTl/4nRZsRNLpZxZRpPQ==} 129 | 130 | '@types/http-errors@2.0.4': 131 | resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==} 132 | 133 | '@types/mime@1.3.5': 134 | resolution: {integrity: sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==} 135 | 136 | '@types/morgan@1.9.9': 137 | resolution: {integrity: sha512-iRYSDKVaC6FkGSpEVVIvrRGw0DfJMiQzIn3qr2G5B3C//AWkulhXgaBd7tS9/J79GWSYMTHGs7PfI5b3Y8m+RQ==} 138 | 139 | '@types/node@16.18.121': 140 | resolution: {integrity: sha512-Gk/pOy8H0cvX8qNrwzElYIECpcUn87w4EAEFXFvPJ8qsP9QR/YqukUORSy0zmyDyvdo149idPpy4W6iC5aSbQA==} 141 | 142 | '@types/qs@6.9.17': 143 | resolution: {integrity: sha512-rX4/bPcfmvxHDv0XjfJELTTr+iB+tn032nPILqHm5wbthUUUuVtNGGqzhya9XUxjTP8Fpr0qYgSZZKxGY++svQ==} 144 | 145 | '@types/range-parser@1.2.7': 146 | resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} 147 | 148 | '@types/send@0.17.4': 149 | resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} 150 | 151 | '@types/serve-static@1.15.7': 152 | resolution: {integrity: sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==} 153 | 154 | accepts@1.3.8: 155 | resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==} 156 | engines: {node: '>= 0.6'} 157 | 158 | acorn-walk@8.3.4: 159 | resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} 160 | engines: {node: '>=0.4.0'} 161 | 162 | acorn@8.14.0: 163 | resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} 164 | engines: {node: '>=0.4.0'} 165 | hasBin: true 166 | 167 | ansi-regex@5.0.1: 168 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 169 | engines: {node: '>=8'} 170 | 171 | ansi-regex@6.1.0: 172 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 173 | engines: {node: '>=12'} 174 | 175 | ansi-styles@4.3.0: 176 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 177 | engines: {node: '>=8'} 178 | 179 | ansi-styles@6.2.1: 180 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 181 | engines: {node: '>=12'} 182 | 183 | any-promise@1.3.0: 184 | resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} 185 | 186 | app-root-path@3.1.0: 187 | resolution: {integrity: sha512-biN3PwB2gUtjaYy/isrU3aNWI5w+fAfvHkSvCKeQGxhmYpwKFUxudR3Yya+KqVRHBmEDYh+/lTozYCFbmzX4nA==} 188 | engines: {node: '>= 6.0.0'} 189 | 190 | arg@4.1.3: 191 | resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} 192 | 193 | array-flatten@1.1.1: 194 | resolution: {integrity: sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==} 195 | 196 | balanced-match@1.0.2: 197 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 198 | 199 | base64-js@1.5.1: 200 | resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} 201 | 202 | basic-auth@2.0.1: 203 | resolution: {integrity: sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==} 204 | engines: {node: '>= 0.8'} 205 | 206 | body-parser@1.20.3: 207 | resolution: {integrity: sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==} 208 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 209 | 210 | brace-expansion@2.0.1: 211 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 212 | 213 | buffer@6.0.3: 214 | resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} 215 | 216 | bytes@3.1.2: 217 | resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} 218 | engines: {node: '>= 0.8'} 219 | 220 | call-bind@1.0.7: 221 | resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} 222 | engines: {node: '>= 0.4'} 223 | 224 | chalk@4.1.2: 225 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 226 | engines: {node: '>=10'} 227 | 228 | cli-highlight@2.1.11: 229 | resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} 230 | engines: {node: '>=8.0.0', npm: '>=5.0.0'} 231 | hasBin: true 232 | 233 | cliui@7.0.4: 234 | resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} 235 | 236 | cliui@8.0.1: 237 | resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} 238 | engines: {node: '>=12'} 239 | 240 | color-convert@2.0.1: 241 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 242 | engines: {node: '>=7.0.0'} 243 | 244 | color-name@1.1.4: 245 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 246 | 247 | content-disposition@0.5.4: 248 | resolution: {integrity: sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==} 249 | engines: {node: '>= 0.6'} 250 | 251 | content-type@1.0.5: 252 | resolution: {integrity: sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==} 253 | engines: {node: '>= 0.6'} 254 | 255 | cookie-parser@1.4.7: 256 | resolution: {integrity: sha512-nGUvgXnotP3BsjiLX2ypbQnWoGUPIIfHQNZkkC668ntrzGWEZVW70HDEB1qnNGMicPje6EttlIgzo51YSwNQGw==} 257 | engines: {node: '>= 0.8.0'} 258 | 259 | cookie-signature@1.0.6: 260 | resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} 261 | 262 | cookie@0.7.1: 263 | resolution: {integrity: sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==} 264 | engines: {node: '>= 0.6'} 265 | 266 | cookie@0.7.2: 267 | resolution: {integrity: sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==} 268 | engines: {node: '>= 0.6'} 269 | 270 | cors@2.8.5: 271 | resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==} 272 | engines: {node: '>= 0.10'} 273 | 274 | create-require@1.1.1: 275 | resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} 276 | 277 | cross-spawn@7.0.6: 278 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 279 | engines: {node: '>= 8'} 280 | 281 | dayjs@1.11.13: 282 | resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} 283 | 284 | debug@2.6.9: 285 | resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} 286 | peerDependencies: 287 | supports-color: '*' 288 | peerDependenciesMeta: 289 | supports-color: 290 | optional: true 291 | 292 | debug@4.3.7: 293 | resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==} 294 | engines: {node: '>=6.0'} 295 | peerDependencies: 296 | supports-color: '*' 297 | peerDependenciesMeta: 298 | supports-color: 299 | optional: true 300 | 301 | define-data-property@1.1.4: 302 | resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} 303 | engines: {node: '>= 0.4'} 304 | 305 | depd@2.0.0: 306 | resolution: {integrity: sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==} 307 | engines: {node: '>= 0.8'} 308 | 309 | destroy@1.2.0: 310 | resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} 311 | engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} 312 | 313 | diff@4.0.2: 314 | resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} 315 | engines: {node: '>=0.3.1'} 316 | 317 | dotenv@16.4.5: 318 | resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} 319 | engines: {node: '>=12'} 320 | 321 | eastasianwidth@0.2.0: 322 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 323 | 324 | ee-first@1.1.1: 325 | resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} 326 | 327 | emoji-regex@8.0.0: 328 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 329 | 330 | emoji-regex@9.2.2: 331 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 332 | 333 | encodeurl@1.0.2: 334 | resolution: {integrity: sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==} 335 | engines: {node: '>= 0.8'} 336 | 337 | encodeurl@2.0.0: 338 | resolution: {integrity: sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==} 339 | engines: {node: '>= 0.8'} 340 | 341 | es-define-property@1.0.0: 342 | resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} 343 | engines: {node: '>= 0.4'} 344 | 345 | es-errors@1.3.0: 346 | resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} 347 | engines: {node: '>= 0.4'} 348 | 349 | escalade@3.2.0: 350 | resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} 351 | engines: {node: '>=6'} 352 | 353 | escape-html@1.0.3: 354 | resolution: {integrity: sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==} 355 | 356 | etag@1.8.1: 357 | resolution: {integrity: sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==} 358 | engines: {node: '>= 0.6'} 359 | 360 | express-rate-limit@7.4.1: 361 | resolution: {integrity: sha512-KS3efpnpIDVIXopMc65EMbWbUht7qvTCdtCR2dD/IZmi9MIkopYESwyRqLgv8Pfu589+KqDqOdzJWW7AHoACeg==} 362 | engines: {node: '>= 16'} 363 | peerDependencies: 364 | express: 4 || 5 || ^5.0.0-beta.1 365 | 366 | express@4.21.1: 367 | resolution: {integrity: sha512-YSFlK1Ee0/GC8QaO91tHcDxJiE/X4FbpAyQWkxAvG6AXCuR65YzK8ua6D9hvi/TzUfZMpc+BwuM1IPw8fmQBiQ==} 368 | engines: {node: '>= 0.10.0'} 369 | 370 | finalhandler@1.3.1: 371 | resolution: {integrity: sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==} 372 | engines: {node: '>= 0.8'} 373 | 374 | foreground-child@3.3.0: 375 | resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} 376 | engines: {node: '>=14'} 377 | 378 | forwarded@0.2.0: 379 | resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==} 380 | engines: {node: '>= 0.6'} 381 | 382 | fresh@0.5.2: 383 | resolution: {integrity: sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==} 384 | engines: {node: '>= 0.6'} 385 | 386 | function-bind@1.1.2: 387 | resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} 388 | 389 | get-caller-file@2.0.5: 390 | resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} 391 | engines: {node: 6.* || 8.* || >= 10.*} 392 | 393 | get-intrinsic@1.2.4: 394 | resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} 395 | engines: {node: '>= 0.4'} 396 | 397 | glob@10.4.5: 398 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 399 | hasBin: true 400 | 401 | gopd@1.1.0: 402 | resolution: {integrity: sha512-FQoVQnqcdk4hVM4JN1eromaun4iuS34oStkdlLENLdpULsuQcTyXj8w7ayhuUfPwEYZ1ZOooOTT6fdA9Vmx/RA==} 403 | engines: {node: '>= 0.4'} 404 | 405 | has-flag@4.0.0: 406 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 407 | engines: {node: '>=8'} 408 | 409 | has-property-descriptors@1.0.2: 410 | resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} 411 | 412 | has-proto@1.1.0: 413 | resolution: {integrity: sha512-QLdzI9IIO1Jg7f9GT1gXpPpXArAn6cS31R1eEZqz08Gc+uQ8/XiqHWt17Fiw+2p6oTTIq5GXEpQkAlA88YRl/Q==} 414 | engines: {node: '>= 0.4'} 415 | 416 | has-symbols@1.0.3: 417 | resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} 418 | engines: {node: '>= 0.4'} 419 | 420 | hasown@2.0.2: 421 | resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} 422 | engines: {node: '>= 0.4'} 423 | 424 | highlight.js@10.7.3: 425 | resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} 426 | 427 | http-errors@2.0.0: 428 | resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} 429 | engines: {node: '>= 0.8'} 430 | 431 | http-status-codes@2.3.0: 432 | resolution: {integrity: sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==} 433 | 434 | iconv-lite@0.4.24: 435 | resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} 436 | engines: {node: '>=0.10.0'} 437 | 438 | ieee754@1.2.1: 439 | resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} 440 | 441 | inherits@2.0.4: 442 | resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} 443 | 444 | ipaddr.js@1.9.1: 445 | resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} 446 | engines: {node: '>= 0.10'} 447 | 448 | is-fullwidth-code-point@3.0.0: 449 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 450 | engines: {node: '>=8'} 451 | 452 | isexe@2.0.0: 453 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 454 | 455 | jackspeak@3.4.3: 456 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 457 | 458 | lru-cache@10.4.3: 459 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 460 | 461 | make-error@1.3.6: 462 | resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} 463 | 464 | media-typer@0.3.0: 465 | resolution: {integrity: sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==} 466 | engines: {node: '>= 0.6'} 467 | 468 | merge-descriptors@1.0.3: 469 | resolution: {integrity: sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==} 470 | 471 | methods@1.1.2: 472 | resolution: {integrity: sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==} 473 | engines: {node: '>= 0.6'} 474 | 475 | mime-db@1.52.0: 476 | resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} 477 | engines: {node: '>= 0.6'} 478 | 479 | mime-types@2.1.35: 480 | resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} 481 | engines: {node: '>= 0.6'} 482 | 483 | mime@1.6.0: 484 | resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} 485 | engines: {node: '>=4'} 486 | hasBin: true 487 | 488 | minimatch@9.0.5: 489 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 490 | engines: {node: '>=16 || 14 >=14.17'} 491 | 492 | minipass@7.1.2: 493 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 494 | engines: {node: '>=16 || 14 >=14.17'} 495 | 496 | mkdirp@2.1.6: 497 | resolution: {integrity: sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==} 498 | engines: {node: '>=10'} 499 | hasBin: true 500 | 501 | morgan@1.10.0: 502 | resolution: {integrity: sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==} 503 | engines: {node: '>= 0.8.0'} 504 | 505 | ms@2.0.0: 506 | resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==} 507 | 508 | ms@2.1.3: 509 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 510 | 511 | mz@2.7.0: 512 | resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} 513 | 514 | negotiator@0.6.3: 515 | resolution: {integrity: sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==} 516 | engines: {node: '>= 0.6'} 517 | 518 | object-assign@4.1.1: 519 | resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} 520 | engines: {node: '>=0.10.0'} 521 | 522 | object-inspect@1.13.3: 523 | resolution: {integrity: sha512-kDCGIbxkDSXE3euJZZXzc6to7fCrKHNI/hSRQnRuQ+BWjFNzZwiFF8fj/6o2t2G9/jTj8PSIYTfCLelLZEeRpA==} 524 | engines: {node: '>= 0.4'} 525 | 526 | on-finished@2.3.0: 527 | resolution: {integrity: sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==} 528 | engines: {node: '>= 0.8'} 529 | 530 | on-finished@2.4.1: 531 | resolution: {integrity: sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==} 532 | engines: {node: '>= 0.8'} 533 | 534 | on-headers@1.0.2: 535 | resolution: {integrity: sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==} 536 | engines: {node: '>= 0.8'} 537 | 538 | package-json-from-dist@1.0.1: 539 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 540 | 541 | parse5-htmlparser2-tree-adapter@6.0.1: 542 | resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} 543 | 544 | parse5@5.1.1: 545 | resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} 546 | 547 | parse5@6.0.1: 548 | resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} 549 | 550 | parseurl@1.3.3: 551 | resolution: {integrity: sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==} 552 | engines: {node: '>= 0.8'} 553 | 554 | path-key@3.1.1: 555 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 556 | engines: {node: '>=8'} 557 | 558 | path-scurry@1.11.1: 559 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 560 | engines: {node: '>=16 || 14 >=14.18'} 561 | 562 | path-to-regexp@0.1.10: 563 | resolution: {integrity: sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==} 564 | 565 | pg-cloudflare@1.1.1: 566 | resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} 567 | 568 | pg-connection-string@2.7.0: 569 | resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} 570 | 571 | pg-int8@1.0.1: 572 | resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} 573 | engines: {node: '>=4.0.0'} 574 | 575 | pg-pool@3.7.0: 576 | resolution: {integrity: sha512-ZOBQForurqh4zZWjrgSwwAtzJ7QiRX0ovFkZr2klsen3Nm0aoh33Ls0fzfv3imeH/nw/O27cjdz5kzYJfeGp/g==} 577 | peerDependencies: 578 | pg: '>=8.0' 579 | 580 | pg-protocol@1.7.0: 581 | resolution: {integrity: sha512-hTK/mE36i8fDDhgDFjy6xNOG+LCorxLG3WO17tku+ij6sVHXh1jQUJ8hYAnRhNla4QVD2H8er/FOjc/+EgC6yQ==} 582 | 583 | pg-types@2.2.0: 584 | resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} 585 | engines: {node: '>=4'} 586 | 587 | pg@8.13.1: 588 | resolution: {integrity: sha512-OUir1A0rPNZlX//c7ksiu7crsGZTKSOXJPgtNiHGIlC9H0lO+NC6ZDYksSgBYY/thSWhnSRBv8w1lieNNGATNQ==} 589 | engines: {node: '>= 8.0.0'} 590 | peerDependencies: 591 | pg-native: '>=3.0.1' 592 | peerDependenciesMeta: 593 | pg-native: 594 | optional: true 595 | 596 | pgpass@1.0.5: 597 | resolution: {integrity: sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==} 598 | 599 | postgres-array@2.0.0: 600 | resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} 601 | engines: {node: '>=4'} 602 | 603 | postgres-bytea@1.0.0: 604 | resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} 605 | engines: {node: '>=0.10.0'} 606 | 607 | postgres-date@1.0.7: 608 | resolution: {integrity: sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==} 609 | engines: {node: '>=0.10.0'} 610 | 611 | postgres-interval@1.2.0: 612 | resolution: {integrity: sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==} 613 | engines: {node: '>=0.10.0'} 614 | 615 | property-expr@2.0.6: 616 | resolution: {integrity: sha512-SVtmxhRE/CGkn3eZY1T6pC8Nln6Fr/lu1mKSgRud0eC73whjGfoAogbn78LkD8aFL0zz3bAFerKSnOl7NlErBA==} 617 | 618 | proxy-addr@2.0.7: 619 | resolution: {integrity: sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==} 620 | engines: {node: '>= 0.10'} 621 | 622 | qs@6.13.0: 623 | resolution: {integrity: sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==} 624 | engines: {node: '>=0.6'} 625 | 626 | range-parser@1.2.1: 627 | resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} 628 | engines: {node: '>= 0.6'} 629 | 630 | raw-body@2.5.2: 631 | resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} 632 | engines: {node: '>= 0.8'} 633 | 634 | reflect-metadata@0.1.14: 635 | resolution: {integrity: sha512-ZhYeb6nRaXCfhnndflDK8qI6ZQ/YcWZCISRAWICW9XYqMUwjZM9Z0DveWX/ABN01oxSHwVxKQmxeYZSsm0jh5A==} 636 | 637 | reflect-metadata@0.2.2: 638 | resolution: {integrity: sha512-urBwgfrvVP/eAyXx4hluJivBKzuEbSQs9rKWCrCkbSxNv8mxPcUZKeuoF3Uy4mJl3Lwprp6yy5/39VWigZ4K6Q==} 639 | 640 | require-directory@2.1.1: 641 | resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} 642 | engines: {node: '>=0.10.0'} 643 | 644 | safe-buffer@5.1.2: 645 | resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} 646 | 647 | safe-buffer@5.2.1: 648 | resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} 649 | 650 | safer-buffer@2.1.2: 651 | resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} 652 | 653 | send@0.19.0: 654 | resolution: {integrity: sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==} 655 | engines: {node: '>= 0.8.0'} 656 | 657 | serve-static@1.16.2: 658 | resolution: {integrity: sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==} 659 | engines: {node: '>= 0.8.0'} 660 | 661 | set-function-length@1.2.2: 662 | resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} 663 | engines: {node: '>= 0.4'} 664 | 665 | setprototypeof@1.2.0: 666 | resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} 667 | 668 | sha.js@2.4.11: 669 | resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} 670 | hasBin: true 671 | 672 | shebang-command@2.0.0: 673 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 674 | engines: {node: '>=8'} 675 | 676 | shebang-regex@3.0.0: 677 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 678 | engines: {node: '>=8'} 679 | 680 | side-channel@1.0.6: 681 | resolution: {integrity: sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==} 682 | engines: {node: '>= 0.4'} 683 | 684 | signal-exit@4.1.0: 685 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 686 | engines: {node: '>=14'} 687 | 688 | split2@4.2.0: 689 | resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} 690 | engines: {node: '>= 10.x'} 691 | 692 | statuses@2.0.1: 693 | resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} 694 | engines: {node: '>= 0.8'} 695 | 696 | string-width@4.2.3: 697 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 698 | engines: {node: '>=8'} 699 | 700 | string-width@5.1.2: 701 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 702 | engines: {node: '>=12'} 703 | 704 | strip-ansi@6.0.1: 705 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 706 | engines: {node: '>=8'} 707 | 708 | strip-ansi@7.1.0: 709 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 710 | engines: {node: '>=12'} 711 | 712 | supports-color@7.2.0: 713 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 714 | engines: {node: '>=8'} 715 | 716 | thenify-all@1.6.0: 717 | resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} 718 | engines: {node: '>=0.8'} 719 | 720 | thenify@3.3.1: 721 | resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} 722 | 723 | tiny-case@1.0.3: 724 | resolution: {integrity: sha512-Eet/eeMhkO6TX8mnUteS9zgPbUMQa4I6Kkp5ORiBD5476/m+PIRiumP5tmh5ioJpH7k51Kehawy2UDfsnxxY8Q==} 725 | 726 | toidentifier@1.0.1: 727 | resolution: {integrity: sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==} 728 | engines: {node: '>=0.6'} 729 | 730 | toposort@2.0.2: 731 | resolution: {integrity: sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg==} 732 | 733 | ts-node@10.9.1: 734 | resolution: {integrity: sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==} 735 | hasBin: true 736 | peerDependencies: 737 | '@swc/core': '>=1.2.50' 738 | '@swc/wasm': '>=1.2.50' 739 | '@types/node': '*' 740 | typescript: '>=2.7' 741 | peerDependenciesMeta: 742 | '@swc/core': 743 | optional: true 744 | '@swc/wasm': 745 | optional: true 746 | 747 | tslib@2.8.1: 748 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 749 | 750 | type-fest@2.19.0: 751 | resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} 752 | engines: {node: '>=12.20'} 753 | 754 | type-is@1.6.18: 755 | resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==} 756 | engines: {node: '>= 0.6'} 757 | 758 | typeorm@0.3.20: 759 | resolution: {integrity: sha512-sJ0T08dV5eoZroaq9uPKBoNcGslHBR4E4y+EBHs//SiGbblGe7IeduP/IH4ddCcj0qp3PHwDwGnuvqEAnKlq/Q==} 760 | engines: {node: '>=16.13.0'} 761 | hasBin: true 762 | peerDependencies: 763 | '@google-cloud/spanner': ^5.18.0 764 | '@sap/hana-client': ^2.12.25 765 | better-sqlite3: ^7.1.2 || ^8.0.0 || ^9.0.0 766 | hdb-pool: ^0.1.6 767 | ioredis: ^5.0.4 768 | mongodb: ^5.8.0 769 | mssql: ^9.1.1 || ^10.0.1 770 | mysql2: ^2.2.5 || ^3.0.1 771 | oracledb: ^6.3.0 772 | pg: ^8.5.1 773 | pg-native: ^3.0.0 774 | pg-query-stream: ^4.0.0 775 | redis: ^3.1.1 || ^4.0.0 776 | sql.js: ^1.4.0 777 | sqlite3: ^5.0.3 778 | ts-node: ^10.7.0 779 | typeorm-aurora-data-api-driver: ^2.0.0 780 | peerDependenciesMeta: 781 | '@google-cloud/spanner': 782 | optional: true 783 | '@sap/hana-client': 784 | optional: true 785 | better-sqlite3: 786 | optional: true 787 | hdb-pool: 788 | optional: true 789 | ioredis: 790 | optional: true 791 | mongodb: 792 | optional: true 793 | mssql: 794 | optional: true 795 | mysql2: 796 | optional: true 797 | oracledb: 798 | optional: true 799 | pg: 800 | optional: true 801 | pg-native: 802 | optional: true 803 | pg-query-stream: 804 | optional: true 805 | redis: 806 | optional: true 807 | sql.js: 808 | optional: true 809 | sqlite3: 810 | optional: true 811 | ts-node: 812 | optional: true 813 | typeorm-aurora-data-api-driver: 814 | optional: true 815 | 816 | typescript@5.6.3: 817 | resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} 818 | engines: {node: '>=14.17'} 819 | hasBin: true 820 | 821 | unpipe@1.0.0: 822 | resolution: {integrity: sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==} 823 | engines: {node: '>= 0.8'} 824 | 825 | utils-merge@1.0.1: 826 | resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} 827 | engines: {node: '>= 0.4.0'} 828 | 829 | uuid@9.0.1: 830 | resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} 831 | hasBin: true 832 | 833 | v8-compile-cache-lib@3.0.1: 834 | resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} 835 | 836 | vary@1.1.2: 837 | resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} 838 | engines: {node: '>= 0.8'} 839 | 840 | which@2.0.2: 841 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 842 | engines: {node: '>= 8'} 843 | hasBin: true 844 | 845 | wrap-ansi@7.0.0: 846 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 847 | engines: {node: '>=10'} 848 | 849 | wrap-ansi@8.1.0: 850 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 851 | engines: {node: '>=12'} 852 | 853 | xtend@4.0.2: 854 | resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} 855 | engines: {node: '>=0.4'} 856 | 857 | y18n@5.0.8: 858 | resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} 859 | engines: {node: '>=10'} 860 | 861 | yargs-parser@20.2.9: 862 | resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} 863 | engines: {node: '>=10'} 864 | 865 | yargs-parser@21.1.1: 866 | resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} 867 | engines: {node: '>=12'} 868 | 869 | yargs@16.2.0: 870 | resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} 871 | engines: {node: '>=10'} 872 | 873 | yargs@17.7.2: 874 | resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} 875 | engines: {node: '>=12'} 876 | 877 | yn@3.1.1: 878 | resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} 879 | engines: {node: '>=6'} 880 | 881 | yup@1.4.0: 882 | resolution: {integrity: sha512-wPbgkJRCqIf+OHyiTBQoJiP5PFuAXaWiJK6AmYkzQAh5/c2K9hzSApBZG5wV9KoKSePF7sAxmNSvh/13YHkFDg==} 883 | 884 | snapshots: 885 | 886 | '@cspotcode/source-map-support@0.8.1': 887 | dependencies: 888 | '@jridgewell/trace-mapping': 0.3.9 889 | 890 | '@faker-js/faker@9.2.0': {} 891 | 892 | '@isaacs/cliui@8.0.2': 893 | dependencies: 894 | string-width: 5.1.2 895 | string-width-cjs: string-width@4.2.3 896 | strip-ansi: 7.1.0 897 | strip-ansi-cjs: strip-ansi@6.0.1 898 | wrap-ansi: 8.1.0 899 | wrap-ansi-cjs: wrap-ansi@7.0.0 900 | 901 | '@jridgewell/resolve-uri@3.1.2': {} 902 | 903 | '@jridgewell/sourcemap-codec@1.5.0': {} 904 | 905 | '@jridgewell/trace-mapping@0.3.9': 906 | dependencies: 907 | '@jridgewell/resolve-uri': 3.1.2 908 | '@jridgewell/sourcemap-codec': 1.5.0 909 | 910 | '@pkgjs/parseargs@0.11.0': 911 | optional: true 912 | 913 | '@sqltools/formatter@1.2.5': {} 914 | 915 | '@tsconfig/node10@1.0.11': {} 916 | 917 | '@tsconfig/node12@1.0.11': {} 918 | 919 | '@tsconfig/node14@1.0.3': {} 920 | 921 | '@tsconfig/node16@1.0.4': {} 922 | 923 | '@types/body-parser@1.19.5': 924 | dependencies: 925 | '@types/connect': 3.4.38 926 | '@types/node': 16.18.121 927 | 928 | '@types/connect@3.4.38': 929 | dependencies: 930 | '@types/node': 16.18.121 931 | 932 | '@types/cookie-parser@1.4.8(@types/express@5.0.0)': 933 | dependencies: 934 | '@types/express': 5.0.0 935 | 936 | '@types/cors@2.8.17': 937 | dependencies: 938 | '@types/node': 16.18.121 939 | 940 | '@types/express-serve-static-core@5.0.2': 941 | dependencies: 942 | '@types/node': 16.18.121 943 | '@types/qs': 6.9.17 944 | '@types/range-parser': 1.2.7 945 | '@types/send': 0.17.4 946 | 947 | '@types/express@5.0.0': 948 | dependencies: 949 | '@types/body-parser': 1.19.5 950 | '@types/express-serve-static-core': 5.0.2 951 | '@types/qs': 6.9.17 952 | '@types/serve-static': 1.15.7 953 | 954 | '@types/http-errors@2.0.4': {} 955 | 956 | '@types/mime@1.3.5': {} 957 | 958 | '@types/morgan@1.9.9': 959 | dependencies: 960 | '@types/node': 16.18.121 961 | 962 | '@types/node@16.18.121': {} 963 | 964 | '@types/qs@6.9.17': {} 965 | 966 | '@types/range-parser@1.2.7': {} 967 | 968 | '@types/send@0.17.4': 969 | dependencies: 970 | '@types/mime': 1.3.5 971 | '@types/node': 16.18.121 972 | 973 | '@types/serve-static@1.15.7': 974 | dependencies: 975 | '@types/http-errors': 2.0.4 976 | '@types/node': 16.18.121 977 | '@types/send': 0.17.4 978 | 979 | accepts@1.3.8: 980 | dependencies: 981 | mime-types: 2.1.35 982 | negotiator: 0.6.3 983 | 984 | acorn-walk@8.3.4: 985 | dependencies: 986 | acorn: 8.14.0 987 | 988 | acorn@8.14.0: {} 989 | 990 | ansi-regex@5.0.1: {} 991 | 992 | ansi-regex@6.1.0: {} 993 | 994 | ansi-styles@4.3.0: 995 | dependencies: 996 | color-convert: 2.0.1 997 | 998 | ansi-styles@6.2.1: {} 999 | 1000 | any-promise@1.3.0: {} 1001 | 1002 | app-root-path@3.1.0: {} 1003 | 1004 | arg@4.1.3: {} 1005 | 1006 | array-flatten@1.1.1: {} 1007 | 1008 | balanced-match@1.0.2: {} 1009 | 1010 | base64-js@1.5.1: {} 1011 | 1012 | basic-auth@2.0.1: 1013 | dependencies: 1014 | safe-buffer: 5.1.2 1015 | 1016 | body-parser@1.20.3: 1017 | dependencies: 1018 | bytes: 3.1.2 1019 | content-type: 1.0.5 1020 | debug: 2.6.9 1021 | depd: 2.0.0 1022 | destroy: 1.2.0 1023 | http-errors: 2.0.0 1024 | iconv-lite: 0.4.24 1025 | on-finished: 2.4.1 1026 | qs: 6.13.0 1027 | raw-body: 2.5.2 1028 | type-is: 1.6.18 1029 | unpipe: 1.0.0 1030 | transitivePeerDependencies: 1031 | - supports-color 1032 | 1033 | brace-expansion@2.0.1: 1034 | dependencies: 1035 | balanced-match: 1.0.2 1036 | 1037 | buffer@6.0.3: 1038 | dependencies: 1039 | base64-js: 1.5.1 1040 | ieee754: 1.2.1 1041 | 1042 | bytes@3.1.2: {} 1043 | 1044 | call-bind@1.0.7: 1045 | dependencies: 1046 | es-define-property: 1.0.0 1047 | es-errors: 1.3.0 1048 | function-bind: 1.1.2 1049 | get-intrinsic: 1.2.4 1050 | set-function-length: 1.2.2 1051 | 1052 | chalk@4.1.2: 1053 | dependencies: 1054 | ansi-styles: 4.3.0 1055 | supports-color: 7.2.0 1056 | 1057 | cli-highlight@2.1.11: 1058 | dependencies: 1059 | chalk: 4.1.2 1060 | highlight.js: 10.7.3 1061 | mz: 2.7.0 1062 | parse5: 5.1.1 1063 | parse5-htmlparser2-tree-adapter: 6.0.1 1064 | yargs: 16.2.0 1065 | 1066 | cliui@7.0.4: 1067 | dependencies: 1068 | string-width: 4.2.3 1069 | strip-ansi: 6.0.1 1070 | wrap-ansi: 7.0.0 1071 | 1072 | cliui@8.0.1: 1073 | dependencies: 1074 | string-width: 4.2.3 1075 | strip-ansi: 6.0.1 1076 | wrap-ansi: 7.0.0 1077 | 1078 | color-convert@2.0.1: 1079 | dependencies: 1080 | color-name: 1.1.4 1081 | 1082 | color-name@1.1.4: {} 1083 | 1084 | content-disposition@0.5.4: 1085 | dependencies: 1086 | safe-buffer: 5.2.1 1087 | 1088 | content-type@1.0.5: {} 1089 | 1090 | cookie-parser@1.4.7: 1091 | dependencies: 1092 | cookie: 0.7.2 1093 | cookie-signature: 1.0.6 1094 | 1095 | cookie-signature@1.0.6: {} 1096 | 1097 | cookie@0.7.1: {} 1098 | 1099 | cookie@0.7.2: {} 1100 | 1101 | cors@2.8.5: 1102 | dependencies: 1103 | object-assign: 4.1.1 1104 | vary: 1.1.2 1105 | 1106 | create-require@1.1.1: {} 1107 | 1108 | cross-spawn@7.0.6: 1109 | dependencies: 1110 | path-key: 3.1.1 1111 | shebang-command: 2.0.0 1112 | which: 2.0.2 1113 | 1114 | dayjs@1.11.13: {} 1115 | 1116 | debug@2.6.9: 1117 | dependencies: 1118 | ms: 2.0.0 1119 | 1120 | debug@4.3.7: 1121 | dependencies: 1122 | ms: 2.1.3 1123 | 1124 | define-data-property@1.1.4: 1125 | dependencies: 1126 | es-define-property: 1.0.0 1127 | es-errors: 1.3.0 1128 | gopd: 1.1.0 1129 | 1130 | depd@2.0.0: {} 1131 | 1132 | destroy@1.2.0: {} 1133 | 1134 | diff@4.0.2: {} 1135 | 1136 | dotenv@16.4.5: {} 1137 | 1138 | eastasianwidth@0.2.0: {} 1139 | 1140 | ee-first@1.1.1: {} 1141 | 1142 | emoji-regex@8.0.0: {} 1143 | 1144 | emoji-regex@9.2.2: {} 1145 | 1146 | encodeurl@1.0.2: {} 1147 | 1148 | encodeurl@2.0.0: {} 1149 | 1150 | es-define-property@1.0.0: 1151 | dependencies: 1152 | get-intrinsic: 1.2.4 1153 | 1154 | es-errors@1.3.0: {} 1155 | 1156 | escalade@3.2.0: {} 1157 | 1158 | escape-html@1.0.3: {} 1159 | 1160 | etag@1.8.1: {} 1161 | 1162 | express-rate-limit@7.4.1(express@4.21.1): 1163 | dependencies: 1164 | express: 4.21.1 1165 | 1166 | express@4.21.1: 1167 | dependencies: 1168 | accepts: 1.3.8 1169 | array-flatten: 1.1.1 1170 | body-parser: 1.20.3 1171 | content-disposition: 0.5.4 1172 | content-type: 1.0.5 1173 | cookie: 0.7.1 1174 | cookie-signature: 1.0.6 1175 | debug: 2.6.9 1176 | depd: 2.0.0 1177 | encodeurl: 2.0.0 1178 | escape-html: 1.0.3 1179 | etag: 1.8.1 1180 | finalhandler: 1.3.1 1181 | fresh: 0.5.2 1182 | http-errors: 2.0.0 1183 | merge-descriptors: 1.0.3 1184 | methods: 1.1.2 1185 | on-finished: 2.4.1 1186 | parseurl: 1.3.3 1187 | path-to-regexp: 0.1.10 1188 | proxy-addr: 2.0.7 1189 | qs: 6.13.0 1190 | range-parser: 1.2.1 1191 | safe-buffer: 5.2.1 1192 | send: 0.19.0 1193 | serve-static: 1.16.2 1194 | setprototypeof: 1.2.0 1195 | statuses: 2.0.1 1196 | type-is: 1.6.18 1197 | utils-merge: 1.0.1 1198 | vary: 1.1.2 1199 | transitivePeerDependencies: 1200 | - supports-color 1201 | 1202 | finalhandler@1.3.1: 1203 | dependencies: 1204 | debug: 2.6.9 1205 | encodeurl: 2.0.0 1206 | escape-html: 1.0.3 1207 | on-finished: 2.4.1 1208 | parseurl: 1.3.3 1209 | statuses: 2.0.1 1210 | unpipe: 1.0.0 1211 | transitivePeerDependencies: 1212 | - supports-color 1213 | 1214 | foreground-child@3.3.0: 1215 | dependencies: 1216 | cross-spawn: 7.0.6 1217 | signal-exit: 4.1.0 1218 | 1219 | forwarded@0.2.0: {} 1220 | 1221 | fresh@0.5.2: {} 1222 | 1223 | function-bind@1.1.2: {} 1224 | 1225 | get-caller-file@2.0.5: {} 1226 | 1227 | get-intrinsic@1.2.4: 1228 | dependencies: 1229 | es-errors: 1.3.0 1230 | function-bind: 1.1.2 1231 | has-proto: 1.1.0 1232 | has-symbols: 1.0.3 1233 | hasown: 2.0.2 1234 | 1235 | glob@10.4.5: 1236 | dependencies: 1237 | foreground-child: 3.3.0 1238 | jackspeak: 3.4.3 1239 | minimatch: 9.0.5 1240 | minipass: 7.1.2 1241 | package-json-from-dist: 1.0.1 1242 | path-scurry: 1.11.1 1243 | 1244 | gopd@1.1.0: 1245 | dependencies: 1246 | get-intrinsic: 1.2.4 1247 | 1248 | has-flag@4.0.0: {} 1249 | 1250 | has-property-descriptors@1.0.2: 1251 | dependencies: 1252 | es-define-property: 1.0.0 1253 | 1254 | has-proto@1.1.0: 1255 | dependencies: 1256 | call-bind: 1.0.7 1257 | 1258 | has-symbols@1.0.3: {} 1259 | 1260 | hasown@2.0.2: 1261 | dependencies: 1262 | function-bind: 1.1.2 1263 | 1264 | highlight.js@10.7.3: {} 1265 | 1266 | http-errors@2.0.0: 1267 | dependencies: 1268 | depd: 2.0.0 1269 | inherits: 2.0.4 1270 | setprototypeof: 1.2.0 1271 | statuses: 2.0.1 1272 | toidentifier: 1.0.1 1273 | 1274 | http-status-codes@2.3.0: {} 1275 | 1276 | iconv-lite@0.4.24: 1277 | dependencies: 1278 | safer-buffer: 2.1.2 1279 | 1280 | ieee754@1.2.1: {} 1281 | 1282 | inherits@2.0.4: {} 1283 | 1284 | ipaddr.js@1.9.1: {} 1285 | 1286 | is-fullwidth-code-point@3.0.0: {} 1287 | 1288 | isexe@2.0.0: {} 1289 | 1290 | jackspeak@3.4.3: 1291 | dependencies: 1292 | '@isaacs/cliui': 8.0.2 1293 | optionalDependencies: 1294 | '@pkgjs/parseargs': 0.11.0 1295 | 1296 | lru-cache@10.4.3: {} 1297 | 1298 | make-error@1.3.6: {} 1299 | 1300 | media-typer@0.3.0: {} 1301 | 1302 | merge-descriptors@1.0.3: {} 1303 | 1304 | methods@1.1.2: {} 1305 | 1306 | mime-db@1.52.0: {} 1307 | 1308 | mime-types@2.1.35: 1309 | dependencies: 1310 | mime-db: 1.52.0 1311 | 1312 | mime@1.6.0: {} 1313 | 1314 | minimatch@9.0.5: 1315 | dependencies: 1316 | brace-expansion: 2.0.1 1317 | 1318 | minipass@7.1.2: {} 1319 | 1320 | mkdirp@2.1.6: {} 1321 | 1322 | morgan@1.10.0: 1323 | dependencies: 1324 | basic-auth: 2.0.1 1325 | debug: 2.6.9 1326 | depd: 2.0.0 1327 | on-finished: 2.3.0 1328 | on-headers: 1.0.2 1329 | transitivePeerDependencies: 1330 | - supports-color 1331 | 1332 | ms@2.0.0: {} 1333 | 1334 | ms@2.1.3: {} 1335 | 1336 | mz@2.7.0: 1337 | dependencies: 1338 | any-promise: 1.3.0 1339 | object-assign: 4.1.1 1340 | thenify-all: 1.6.0 1341 | 1342 | negotiator@0.6.3: {} 1343 | 1344 | object-assign@4.1.1: {} 1345 | 1346 | object-inspect@1.13.3: {} 1347 | 1348 | on-finished@2.3.0: 1349 | dependencies: 1350 | ee-first: 1.1.1 1351 | 1352 | on-finished@2.4.1: 1353 | dependencies: 1354 | ee-first: 1.1.1 1355 | 1356 | on-headers@1.0.2: {} 1357 | 1358 | package-json-from-dist@1.0.1: {} 1359 | 1360 | parse5-htmlparser2-tree-adapter@6.0.1: 1361 | dependencies: 1362 | parse5: 6.0.1 1363 | 1364 | parse5@5.1.1: {} 1365 | 1366 | parse5@6.0.1: {} 1367 | 1368 | parseurl@1.3.3: {} 1369 | 1370 | path-key@3.1.1: {} 1371 | 1372 | path-scurry@1.11.1: 1373 | dependencies: 1374 | lru-cache: 10.4.3 1375 | minipass: 7.1.2 1376 | 1377 | path-to-regexp@0.1.10: {} 1378 | 1379 | pg-cloudflare@1.1.1: 1380 | optional: true 1381 | 1382 | pg-connection-string@2.7.0: {} 1383 | 1384 | pg-int8@1.0.1: {} 1385 | 1386 | pg-pool@3.7.0(pg@8.13.1): 1387 | dependencies: 1388 | pg: 8.13.1 1389 | 1390 | pg-protocol@1.7.0: {} 1391 | 1392 | pg-types@2.2.0: 1393 | dependencies: 1394 | pg-int8: 1.0.1 1395 | postgres-array: 2.0.0 1396 | postgres-bytea: 1.0.0 1397 | postgres-date: 1.0.7 1398 | postgres-interval: 1.2.0 1399 | 1400 | pg@8.13.1: 1401 | dependencies: 1402 | pg-connection-string: 2.7.0 1403 | pg-pool: 3.7.0(pg@8.13.1) 1404 | pg-protocol: 1.7.0 1405 | pg-types: 2.2.0 1406 | pgpass: 1.0.5 1407 | optionalDependencies: 1408 | pg-cloudflare: 1.1.1 1409 | 1410 | pgpass@1.0.5: 1411 | dependencies: 1412 | split2: 4.2.0 1413 | 1414 | postgres-array@2.0.0: {} 1415 | 1416 | postgres-bytea@1.0.0: {} 1417 | 1418 | postgres-date@1.0.7: {} 1419 | 1420 | postgres-interval@1.2.0: 1421 | dependencies: 1422 | xtend: 4.0.2 1423 | 1424 | property-expr@2.0.6: {} 1425 | 1426 | proxy-addr@2.0.7: 1427 | dependencies: 1428 | forwarded: 0.2.0 1429 | ipaddr.js: 1.9.1 1430 | 1431 | qs@6.13.0: 1432 | dependencies: 1433 | side-channel: 1.0.6 1434 | 1435 | range-parser@1.2.1: {} 1436 | 1437 | raw-body@2.5.2: 1438 | dependencies: 1439 | bytes: 3.1.2 1440 | http-errors: 2.0.0 1441 | iconv-lite: 0.4.24 1442 | unpipe: 1.0.0 1443 | 1444 | reflect-metadata@0.1.14: {} 1445 | 1446 | reflect-metadata@0.2.2: {} 1447 | 1448 | require-directory@2.1.1: {} 1449 | 1450 | safe-buffer@5.1.2: {} 1451 | 1452 | safe-buffer@5.2.1: {} 1453 | 1454 | safer-buffer@2.1.2: {} 1455 | 1456 | send@0.19.0: 1457 | dependencies: 1458 | debug: 2.6.9 1459 | depd: 2.0.0 1460 | destroy: 1.2.0 1461 | encodeurl: 1.0.2 1462 | escape-html: 1.0.3 1463 | etag: 1.8.1 1464 | fresh: 0.5.2 1465 | http-errors: 2.0.0 1466 | mime: 1.6.0 1467 | ms: 2.1.3 1468 | on-finished: 2.4.1 1469 | range-parser: 1.2.1 1470 | statuses: 2.0.1 1471 | transitivePeerDependencies: 1472 | - supports-color 1473 | 1474 | serve-static@1.16.2: 1475 | dependencies: 1476 | encodeurl: 2.0.0 1477 | escape-html: 1.0.3 1478 | parseurl: 1.3.3 1479 | send: 0.19.0 1480 | transitivePeerDependencies: 1481 | - supports-color 1482 | 1483 | set-function-length@1.2.2: 1484 | dependencies: 1485 | define-data-property: 1.1.4 1486 | es-errors: 1.3.0 1487 | function-bind: 1.1.2 1488 | get-intrinsic: 1.2.4 1489 | gopd: 1.1.0 1490 | has-property-descriptors: 1.0.2 1491 | 1492 | setprototypeof@1.2.0: {} 1493 | 1494 | sha.js@2.4.11: 1495 | dependencies: 1496 | inherits: 2.0.4 1497 | safe-buffer: 5.2.1 1498 | 1499 | shebang-command@2.0.0: 1500 | dependencies: 1501 | shebang-regex: 3.0.0 1502 | 1503 | shebang-regex@3.0.0: {} 1504 | 1505 | side-channel@1.0.6: 1506 | dependencies: 1507 | call-bind: 1.0.7 1508 | es-errors: 1.3.0 1509 | get-intrinsic: 1.2.4 1510 | object-inspect: 1.13.3 1511 | 1512 | signal-exit@4.1.0: {} 1513 | 1514 | split2@4.2.0: {} 1515 | 1516 | statuses@2.0.1: {} 1517 | 1518 | string-width@4.2.3: 1519 | dependencies: 1520 | emoji-regex: 8.0.0 1521 | is-fullwidth-code-point: 3.0.0 1522 | strip-ansi: 6.0.1 1523 | 1524 | string-width@5.1.2: 1525 | dependencies: 1526 | eastasianwidth: 0.2.0 1527 | emoji-regex: 9.2.2 1528 | strip-ansi: 7.1.0 1529 | 1530 | strip-ansi@6.0.1: 1531 | dependencies: 1532 | ansi-regex: 5.0.1 1533 | 1534 | strip-ansi@7.1.0: 1535 | dependencies: 1536 | ansi-regex: 6.1.0 1537 | 1538 | supports-color@7.2.0: 1539 | dependencies: 1540 | has-flag: 4.0.0 1541 | 1542 | thenify-all@1.6.0: 1543 | dependencies: 1544 | thenify: 3.3.1 1545 | 1546 | thenify@3.3.1: 1547 | dependencies: 1548 | any-promise: 1.3.0 1549 | 1550 | tiny-case@1.0.3: {} 1551 | 1552 | toidentifier@1.0.1: {} 1553 | 1554 | toposort@2.0.2: {} 1555 | 1556 | ts-node@10.9.1(@types/node@16.18.121)(typescript@5.6.3): 1557 | dependencies: 1558 | '@cspotcode/source-map-support': 0.8.1 1559 | '@tsconfig/node10': 1.0.11 1560 | '@tsconfig/node12': 1.0.11 1561 | '@tsconfig/node14': 1.0.3 1562 | '@tsconfig/node16': 1.0.4 1563 | '@types/node': 16.18.121 1564 | acorn: 8.14.0 1565 | acorn-walk: 8.3.4 1566 | arg: 4.1.3 1567 | create-require: 1.1.1 1568 | diff: 4.0.2 1569 | make-error: 1.3.6 1570 | typescript: 5.6.3 1571 | v8-compile-cache-lib: 3.0.1 1572 | yn: 3.1.1 1573 | 1574 | tslib@2.8.1: {} 1575 | 1576 | type-fest@2.19.0: {} 1577 | 1578 | type-is@1.6.18: 1579 | dependencies: 1580 | media-typer: 0.3.0 1581 | mime-types: 2.1.35 1582 | 1583 | typeorm@0.3.20(pg@8.13.1)(ts-node@10.9.1(@types/node@16.18.121)(typescript@5.6.3)): 1584 | dependencies: 1585 | '@sqltools/formatter': 1.2.5 1586 | app-root-path: 3.1.0 1587 | buffer: 6.0.3 1588 | chalk: 4.1.2 1589 | cli-highlight: 2.1.11 1590 | dayjs: 1.11.13 1591 | debug: 4.3.7 1592 | dotenv: 16.4.5 1593 | glob: 10.4.5 1594 | mkdirp: 2.1.6 1595 | reflect-metadata: 0.2.2 1596 | sha.js: 2.4.11 1597 | tslib: 2.8.1 1598 | uuid: 9.0.1 1599 | yargs: 17.7.2 1600 | optionalDependencies: 1601 | pg: 8.13.1 1602 | ts-node: 10.9.1(@types/node@16.18.121)(typescript@5.6.3) 1603 | transitivePeerDependencies: 1604 | - supports-color 1605 | 1606 | typescript@5.6.3: {} 1607 | 1608 | unpipe@1.0.0: {} 1609 | 1610 | utils-merge@1.0.1: {} 1611 | 1612 | uuid@9.0.1: {} 1613 | 1614 | v8-compile-cache-lib@3.0.1: {} 1615 | 1616 | vary@1.1.2: {} 1617 | 1618 | which@2.0.2: 1619 | dependencies: 1620 | isexe: 2.0.0 1621 | 1622 | wrap-ansi@7.0.0: 1623 | dependencies: 1624 | ansi-styles: 4.3.0 1625 | string-width: 4.2.3 1626 | strip-ansi: 6.0.1 1627 | 1628 | wrap-ansi@8.1.0: 1629 | dependencies: 1630 | ansi-styles: 6.2.1 1631 | string-width: 5.1.2 1632 | strip-ansi: 7.1.0 1633 | 1634 | xtend@4.0.2: {} 1635 | 1636 | y18n@5.0.8: {} 1637 | 1638 | yargs-parser@20.2.9: {} 1639 | 1640 | yargs-parser@21.1.1: {} 1641 | 1642 | yargs@16.2.0: 1643 | dependencies: 1644 | cliui: 7.0.4 1645 | escalade: 3.2.0 1646 | get-caller-file: 2.0.5 1647 | require-directory: 2.1.1 1648 | string-width: 4.2.3 1649 | y18n: 5.0.8 1650 | yargs-parser: 20.2.9 1651 | 1652 | yargs@17.7.2: 1653 | dependencies: 1654 | cliui: 8.0.1 1655 | escalade: 3.2.0 1656 | get-caller-file: 2.0.5 1657 | require-directory: 2.1.1 1658 | string-width: 4.2.3 1659 | y18n: 5.0.8 1660 | yargs-parser: 21.1.1 1661 | 1662 | yn@3.1.1: {} 1663 | 1664 | yup@1.4.0: 1665 | dependencies: 1666 | property-expr: 2.0.6 1667 | tiny-case: 1.0.3 1668 | toposort: 2.0.2 1669 | type-fest: 2.19.0 1670 | -------------------------------------------------------------------------------- /backend/src/bootstrap/database.ts: -------------------------------------------------------------------------------- 1 | import "reflect-metadata"; 2 | import { DataSource } from "typeorm"; 3 | 4 | // entity imports 5 | import Entities from "../entity"; 6 | import seedDatabase from "./seed-database"; 7 | 8 | export const AppDataSource = new DataSource({ 9 | type: "postgres", 10 | url: process.env.DATABASE_URL, 11 | synchronize: true, 12 | logging: false, 13 | entities: [...Object.values(Entities)], 14 | }); 15 | 16 | const connectDatabase = async () => { 17 | try { 18 | const response = await AppDataSource.initialize(); 19 | 20 | console.log("Database connected successfully"); 21 | 22 | seedDatabase(); 23 | 24 | return response; 25 | } catch (error: any) { 26 | console.error(`Error in database connection: ${error.message}`); 27 | process.exit(1); 28 | } 29 | }; 30 | 31 | export default connectDatabase; 32 | -------------------------------------------------------------------------------- /backend/src/bootstrap/seed-database.ts: -------------------------------------------------------------------------------- 1 | import { faker, SimpleFaker } from "@faker-js/faker"; 2 | 3 | // entity imports 4 | import { Project, ProjectManager } from "../entity"; 5 | import { PROJECT_STATUS } from "../enum"; 6 | 7 | const randomResources = [ 8 | "UI/UX Design", 9 | "Frontend Development", 10 | "Backend Development", 11 | "Quality Assurance", 12 | "Project Management", 13 | "DevOps", 14 | "Database Management", 15 | "Business Analysis", 16 | "Data Science", 17 | "Machine Learning", 18 | "Artificial Intelligence", 19 | "Cybersecurity", 20 | "Cloud Computing", 21 | "Network Security", 22 | "Technical Support", 23 | "Technical Writing", 24 | "Content Writing", 25 | "Digital Marketing", 26 | "SEO", 27 | "Social Media Marketing", 28 | "Email Marketing", 29 | "Affiliate Marketing", 30 | "Influencer Marketing", 31 | ]; 32 | 33 | // create a SimpleFaker without any locale data 34 | const simpleFaker = new SimpleFaker(); 35 | 36 | const seedDatabase = async () => { 37 | try { 38 | const projectManagers = await ProjectManager.find(); 39 | 40 | if (projectManagers.length === 0) { 41 | // Generate 50 fake project managers 42 | for (let i = 0; i < 50; i++) { 43 | const projectManager = ProjectManager.create({ 44 | name: faker.person.fullName(), 45 | email: faker.internet.email(), 46 | phone: faker.phone.number(), 47 | }); 48 | 49 | await projectManager.save(); 50 | } 51 | } 52 | 53 | const projects = await Project.find(); 54 | 55 | if (projects.length === 0) { 56 | // Generate 100 fake projects 57 | for (let i = 0; i < 100; i++) { 58 | const project = Project.create({ 59 | name: faker.company.name(), 60 | project_manager_id: simpleFaker.number.int(50), 61 | status: simpleFaker.helpers.arrayElement( 62 | Object.values(PROJECT_STATUS) 63 | ), 64 | start_date: faker.date.past(), 65 | end_date: faker.date.future(), 66 | resources: simpleFaker.helpers.arrayElements( 67 | randomResources, 68 | simpleFaker.number.int(15) 69 | ), 70 | estimated_cost: simpleFaker.number.int(1000000), 71 | last_updated: faker.date.recent(), 72 | last_updated_note: faker.hacker.phrase(), 73 | }); 74 | 75 | await project.save(); 76 | } 77 | } 78 | 79 | console.log("Database seeded successfully"); 80 | return; 81 | } catch (error: any) { 82 | console.error(`Error in database connection: ${error.message}`); 83 | } 84 | }; 85 | 86 | export default seedDatabase; 87 | -------------------------------------------------------------------------------- /backend/src/controllers/projects.ts: -------------------------------------------------------------------------------- 1 | import { Request, Response } from "express"; 2 | import * as Yup from "yup"; 3 | import { StatusCodes } from "http-status-codes"; 4 | 5 | import { Project, ProjectManager } from "../entity"; 6 | 7 | const { BAD_REQUEST, OK } = StatusCodes; 8 | 9 | const returnSortingKey = (key: string) => { 10 | if (key === "project_manager_name") { 11 | return "pm.name"; 12 | } 13 | 14 | return `project.${key}`; 15 | }; 16 | 17 | //@desc Get projects list 18 | //@route POST /api/projects 19 | //@access Public 20 | export const list = async (req: Request, res: Response) => { 21 | try { 22 | const { 23 | searchQuery = "", 24 | pagination = { 25 | pageIndex: 0, 26 | pageSize: 10, 27 | }, 28 | sorting = [], 29 | } = req.body; 30 | 31 | const schema = Yup.object().shape({ 32 | pagination: Yup.object() 33 | .shape({ 34 | pageIndex: Yup.number().required(), 35 | pageSize: Yup.number().required(), 36 | }) 37 | .required(), 38 | sorting: Yup.array() 39 | .of( 40 | Yup.object() 41 | .shape({ 42 | id: Yup.string() 43 | .required() 44 | .oneOf([ 45 | "id", 46 | "name", 47 | "status", 48 | "estimated_cost", 49 | "project_manager_name", 50 | "last_updated", 51 | ]), 52 | desc: Yup.boolean().required(), 53 | }) 54 | .required() 55 | ) 56 | .required() 57 | .min(1, "At least one sorting field is required"), 58 | }); 59 | 60 | await schema.validate({ pagination, sorting }); 61 | 62 | const projectQuery = Project.createQueryBuilder( 63 | "project" 64 | ).leftJoinAndMapOne( 65 | "project.project_manager", 66 | ProjectManager, 67 | "pm", 68 | "pm.id = project.project_manager_id" 69 | ); 70 | 71 | if (searchQuery && searchQuery !== "") { 72 | projectQuery.where("project.name ILIKE :searchQuery", { 73 | searchQuery: `%${searchQuery}%`, 74 | }); 75 | 76 | projectQuery.orWhere("project.status ILIKE :searchQuery", { 77 | searchQuery: `%${searchQuery}%`, 78 | }); 79 | 80 | projectQuery.orWhere("pm.name ILIKE :searchQuery", { 81 | searchQuery: `%${searchQuery}%`, 82 | }); 83 | 84 | projectQuery.orWhere("pm.email ILIKE :searchQuery", { 85 | searchQuery: `%${searchQuery}%`, 86 | }); 87 | } 88 | 89 | if (sorting.length > 0) { 90 | projectQuery.orderBy( 91 | returnSortingKey(sorting[0].id), 92 | sorting[0].desc ? "DESC" : "ASC" 93 | ); 94 | 95 | for (let i = 1; i < sorting.length; i++) { 96 | projectQuery.addOrderBy( 97 | returnSortingKey(sorting[i].id), 98 | sorting[i].desc ? "DESC" : "ASC" 99 | ); 100 | } 101 | } 102 | 103 | const projects = await projectQuery 104 | .skip(pagination.pageIndex * pagination.pageSize) 105 | .take(pagination.pageSize) 106 | .getMany(); 107 | 108 | const totalCount = await projectQuery.getCount(); 109 | 110 | res.status(OK).json({ 111 | status: "success", 112 | message: "Projects list", 113 | data: { 114 | totalCount, 115 | projects, 116 | }, 117 | }); 118 | } catch (error) { 119 | res.status(BAD_REQUEST).json({ 120 | status: "error", 121 | message: error.message, 122 | }); 123 | } 124 | }; 125 | -------------------------------------------------------------------------------- /backend/src/entity/index.ts: -------------------------------------------------------------------------------- 1 | import Project from "./projects"; 2 | import ProjectManager from "./project_managers"; 3 | 4 | export default { 5 | Project, 6 | ProjectManager, 7 | }; 8 | 9 | export { Project, ProjectManager }; 10 | -------------------------------------------------------------------------------- /backend/src/entity/project_managers.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | BaseEntity, 6 | CreateDateColumn, 7 | UpdateDateColumn, 8 | DeleteDateColumn, 9 | } from "typeorm"; 10 | 11 | import { IProjectManager } from "../interfaces/index"; 12 | 13 | @Entity({ 14 | name: "project_managers", 15 | orderBy: { 16 | created_at: "ASC", 17 | }, 18 | }) 19 | export default class ProjectManager 20 | extends BaseEntity 21 | implements IProjectManager 22 | { 23 | @PrimaryGeneratedColumn() 24 | id!: number; 25 | 26 | @Column({ type: "text", default: "" }) 27 | name!: string; 28 | 29 | @Column({ type: "text", default: "" }) 30 | email!: string; 31 | 32 | @Column({ type: "text", default: "" }) 33 | phone!: string; 34 | 35 | @CreateDateColumn({ select: false }) 36 | created_at!: Date; 37 | 38 | @UpdateDateColumn({ select: false }) 39 | updated_at!: Date; 40 | 41 | @DeleteDateColumn({ select: false }) 42 | deleted_at!: Date; 43 | } 44 | -------------------------------------------------------------------------------- /backend/src/entity/projects.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Entity, 3 | PrimaryGeneratedColumn, 4 | Column, 5 | BaseEntity, 6 | CreateDateColumn, 7 | UpdateDateColumn, 8 | DeleteDateColumn, 9 | } from "typeorm"; 10 | 11 | import { IProject } from "../interfaces/index"; 12 | 13 | @Entity({ 14 | name: "projects", 15 | orderBy: { 16 | created_at: "ASC", 17 | }, 18 | }) 19 | export default class Project extends BaseEntity implements IProject { 20 | @PrimaryGeneratedColumn() 21 | id!: number; 22 | 23 | @Column({ type: "text", default: "" }) 24 | name!: string; 25 | 26 | @Column({ type: "int" }) 27 | project_manager_id!: number; 28 | 29 | @Column({ type: "text", default: "" }) 30 | status!: string; 31 | 32 | @Column({ type: "date" }) 33 | start_date!: Date; 34 | 35 | @Column({ type: "date" }) 36 | end_date!: Date; 37 | 38 | @Column({ type: "text", default: "" }) 39 | resources!: string[]; 40 | 41 | @Column({ type: "int" }) 42 | estimated_cost!: number; 43 | 44 | @Column({ type: "date" }) 45 | last_updated!: Date; 46 | 47 | @Column({ type: "text", default: "" }) 48 | last_updated_note!: string; 49 | 50 | @CreateDateColumn({ select: false }) 51 | created_at!: Date; 52 | 53 | @UpdateDateColumn({ select: false }) 54 | updated_at!: Date; 55 | 56 | @DeleteDateColumn({ select: false }) 57 | deleted_at!: Date; 58 | } 59 | -------------------------------------------------------------------------------- /backend/src/enum/index.ts: -------------------------------------------------------------------------------- 1 | export enum PROJECT_STATUS { 2 | ALL = "all", 3 | RISK = "risk", 4 | ON_TRACK = "onTrack", 5 | ON_HOLD = "onHold", 6 | POTENTIAL_RISK = "potentialRisk", 7 | ARCHIVED = "archived", 8 | } 9 | -------------------------------------------------------------------------------- /backend/src/index.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | import "dotenv/config"; 3 | 4 | import connectDB from "./bootstrap/database"; 5 | import initialMiddlewares from "./middlewares"; 6 | 7 | import baseRouter from "./routes"; 8 | 9 | const port = process.env.PORT || 8000; 10 | 11 | const getApp = async () => { 12 | const app = express(); 13 | 14 | await initialMiddlewares(app); 15 | await connectDB(); 16 | app.use("/api", baseRouter()); 17 | 18 | return app; 19 | }; 20 | 21 | async function startApp() { 22 | const app = await getApp(); 23 | 24 | app.listen(port, () => { 25 | console.log( 26 | `API SERVER RUNNING ON PORT: ${port} and worker id at ${process.pid}` 27 | ); 28 | }); 29 | } 30 | 31 | startApp(); 32 | -------------------------------------------------------------------------------- /backend/src/interfaces/IBase.ts: -------------------------------------------------------------------------------- 1 | import { BaseEntity } from "typeorm"; 2 | 3 | export default interface IBase extends BaseEntity { 4 | id: number | string; 5 | created_at: Date; 6 | updated_at?: Date; 7 | } 8 | -------------------------------------------------------------------------------- /backend/src/interfaces/IProject.ts: -------------------------------------------------------------------------------- 1 | import IBase from "./IBase"; 2 | 3 | export default interface IProject extends IBase { 4 | name: string; 5 | project_manager_id: number; 6 | status: string; 7 | start_date: Date; 8 | end_date: Date; 9 | resources: string[]; 10 | estimated_cost: number; 11 | last_updated: Date; 12 | last_updated_note: string; 13 | } 14 | -------------------------------------------------------------------------------- /backend/src/interfaces/IProjectManager.ts: -------------------------------------------------------------------------------- 1 | import IBase from "./IBase"; 2 | 3 | export default interface IProjectManager extends IBase { 4 | name: string; 5 | email: string; 6 | phone: string; 7 | } 8 | -------------------------------------------------------------------------------- /backend/src/interfaces/index.ts: -------------------------------------------------------------------------------- 1 | import IProject from "./IProject"; 2 | import IProjectManager from "./IProjectManager"; 3 | 4 | export type { IProject, IProjectManager }; 5 | -------------------------------------------------------------------------------- /backend/src/middlewares/index.ts: -------------------------------------------------------------------------------- 1 | import express, { Express } from "express"; 2 | import morgan from "morgan"; 3 | import cors from "cors"; 4 | import cookieParser from "cookie-parser"; 5 | import { rateLimit } from "express-rate-limit"; 6 | 7 | const limiter = rateLimit({ 8 | windowMs: 15 * 60 * 1000, // 15 minutes 9 | limit: 5000, // Limit each IP to 100 requests per `window` (here, per 15 minutes). 10 | standardHeaders: "draft-7", // draft-6: `RateLimit-*` headers; draft-7: combined `RateLimit` header 11 | legacyHeaders: false, // Disable the `X-RateLimit-*` headers. 12 | // store: ... , // Use an external store for consistency across multiple server instances. 13 | }); 14 | 15 | const initialMiddlewares = (app: Express) => { 16 | if (process.env.NODE_ENV == "production") { 17 | app.use(morgan("combined")); 18 | } else { 19 | app.use(morgan("dev")); 20 | } 21 | 22 | app.set("trust proxy", 1); 23 | app.use(cookieParser()); 24 | app.use(express.static("uploads")); 25 | 26 | app.use(express.json({ limit: "1mb" })); 27 | app.use(express.urlencoded({ extended: true, limit: "1mb" })); 28 | 29 | app.use(limiter); 30 | 31 | const allowedOrigins = ["http://localhost:3000"]; 32 | 33 | app.use( 34 | cors({ 35 | origin: (origin, callback) => { 36 | if (!origin || allowedOrigins.includes(origin)) { 37 | callback(null, true); 38 | } else { 39 | console.log("origin not allowed:", origin); 40 | callback(new Error("Not allowed by CORS")); 41 | } 42 | }, 43 | credentials: true, 44 | }) 45 | ); 46 | }; 47 | 48 | export default initialMiddlewares; 49 | -------------------------------------------------------------------------------- /backend/src/routes/index.ts: -------------------------------------------------------------------------------- 1 | import { Router } from "express"; 2 | import { StatusCodes } from "http-status-codes"; 3 | 4 | import projects from "./projects"; 5 | 6 | const baseRouter = () => { 7 | const router = Router(); 8 | 9 | router.use("/projects", projects); 10 | 11 | router.use("*", (req, res) => { 12 | res.status(StatusCodes.NOT_FOUND).json({ 13 | status: "error", 14 | message: "Not Found", 15 | }); 16 | }); 17 | 18 | return router; 19 | }; 20 | 21 | export default baseRouter; 22 | -------------------------------------------------------------------------------- /backend/src/routes/projects.ts: -------------------------------------------------------------------------------- 1 | import express from "express"; 2 | 3 | import { list } from "../controllers/projects"; 4 | 5 | const router = express.Router(); 6 | 7 | // public routes 8 | router.route("/list").post(list); 9 | 10 | export default router; 11 | -------------------------------------------------------------------------------- /backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "lib": ["es5", "es6", "ES2021.String", "DOM"], 4 | "target": "es6", 5 | "module": "commonjs", 6 | "moduleResolution": "node", 7 | "emitDecoratorMetadata": true, 8 | "experimentalDecorators": true, 9 | "esModuleInterop": true, 10 | "sourceMap": true, 11 | "noImplicitAny": true, 12 | "noImplicitReturns": true, 13 | "skipLibCheck": true, 14 | "outDir": "./build", 15 | "jsx": "react-jsx" 16 | }, 17 | "include": ["src/**/*", "index.ts"], 18 | "exclude": ["node_modules", "build"] 19 | } 20 | -------------------------------------------------------------------------------- /frontend/.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": ["next/core-web-vitals", "next/typescript"] 3 | } 4 | -------------------------------------------------------------------------------- /frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.* 7 | .yarn/* 8 | !.yarn/patches 9 | !.yarn/plugins 10 | !.yarn/releases 11 | !.yarn/versions 12 | 13 | # testing 14 | /coverage 15 | 16 | # next.js 17 | /.next/ 18 | /out/ 19 | 20 | # production 21 | /build 22 | 23 | # misc 24 | .DS_Store 25 | *.pem 26 | 27 | # debug 28 | npm-debug.log* 29 | yarn-debug.log* 30 | yarn-error.log* 31 | 32 | # env files (can opt-in for committing if needed) 33 | .env* 34 | 35 | # vercel 36 | .vercel 37 | 38 | # typescript 39 | *.tsbuildinfo 40 | next-env.d.ts 41 | -------------------------------------------------------------------------------- /frontend/README.md: -------------------------------------------------------------------------------- 1 | This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). 2 | 3 | ## Getting Started 4 | 5 | First, run the development server: 6 | 7 | ```bash 8 | npm run dev 9 | # or 10 | yarn dev 11 | # or 12 | pnpm dev 13 | # or 14 | bun dev 15 | ``` 16 | 17 | Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. 18 | 19 | You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. 20 | 21 | This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. 22 | 23 | ## Learn More 24 | 25 | To learn more about Next.js, take a look at the following resources: 26 | 27 | - [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. 28 | - [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. 29 | 30 | You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! 31 | 32 | ## Deploy on Vercel 33 | 34 | The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. 35 | 36 | Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. 37 | -------------------------------------------------------------------------------- /frontend/app/columns.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { ColumnDef } from "@tanstack/react-table"; 4 | import { Checkbox } from "@/components/ui/checkbox"; 5 | import { SortableHeader } from "@/components/data-table/SortableHeader"; 6 | import { Project } from "./data"; 7 | import ProjectName from "@/components/data-table/ProjectName"; 8 | import ProjectManager from "@/components/data-table/ProjectManager"; 9 | import ProjectStatus from "@/components/data-table/ProjectStatus"; 10 | import ProjectLastUpdate from "@/components/data-table/ProjectLastUpdate"; 11 | import ProjectResources from "@/components/data-table/ProjectResources"; 12 | import ProjectTimeLine from "@/components/data-table/ProjectTimeLine"; 13 | import { ProjectActions } from "@/components/data-table/ProjectActions"; 14 | 15 | function formatCurrency(amount: number) { 16 | if (amount >= 1000) { 17 | return `US$ ${(amount / 1000).toFixed(1)}k`; 18 | } 19 | return `US$ ${amount}`; 20 | } 21 | 22 | export const columns: ColumnDef[] = [ 23 | { 24 | id: "select", 25 | header: ({ table }) => ( 26 | table.toggleAllPageRowsSelected(!!value)} 32 | aria-label="Select all" 33 | className="border-border bg-white shadow-lg border data-[state=checked]:border-0" 34 | /> 35 | ), 36 | cell: ({ row }) => ( 37 | row.toggleSelected(!!value)} 40 | aria-label="Select row" 41 | className="border-border bg-white shadow-lg border data-[state=checked]:border-0" 42 | /> 43 | ), 44 | enableSorting: false, 45 | enableHiding: false, 46 | }, 47 | { 48 | accessorKey: "id", 49 | header: ({ column }) => ( 50 | 51 | ), 52 | cell: ({ row }) =>
{row.getValue("id")}
, 53 | enableSorting: true, 54 | }, 55 | { 56 | accessorKey: "name", 57 | header: ({ column }) => ( 58 | 59 | ), 60 | cell: ({ row }) => , 61 | enableSorting: true, 62 | }, 63 | { 64 | accessorKey: "project_manager", 65 | header: "PM", 66 | cell: ({ row }) => ( 67 | 68 | ), 69 | }, 70 | { 71 | accessorKey: "status", 72 | header: "Status", 73 | cell: ({ row }) => , 74 | }, 75 | { 76 | accessorKey: "last_updated", 77 | header: ({ column }) => ( 78 | 79 | ), 80 | cell: ({ row }) => ( 81 | 82 | ), 83 | enableSorting: true, 84 | }, 85 | { 86 | accessorKey: "resources", 87 | header: "Resources", 88 | cell: ({ row }) => ( 89 | 90 | ), 91 | }, 92 | { 93 | accessorKey: "start_date", 94 | header: "Project timeline", 95 | cell: ({ row }) => ( 96 | 100 | ), 101 | }, 102 | { 103 | accessorKey: "estimated_cost", 104 | header: ({ column }) => ( 105 | 106 | ), 107 | cell: ({ row }) => ( 108 |

109 | {formatCurrency(row.getValue("estimated_cost"))} 110 |

111 | ), 112 | enableSorting: true, 113 | }, 114 | { 115 | accessorKey: "actions", 116 | header: "Actions", 117 | cell: ({ row }) => , 118 | }, 119 | ]; 120 | -------------------------------------------------------------------------------- /frontend/app/data.tsx: -------------------------------------------------------------------------------- 1 | import { STATUS } from "@/enum"; 2 | 3 | export type Project = { 4 | id: string; 5 | name: string; 6 | project_manager: string; 7 | status: STATUS; 8 | last_updated: string; 9 | resources: string[]; 10 | start_date: string; 11 | end_date: string; 12 | estimated_cost: number; 13 | }; 14 | 15 | export const projects: Project[] = [ 16 | { 17 | id: "1", 18 | name: "Nice web app", 19 | project_manager: "James Turner", 20 | status: STATUS.ON_TRACK, 21 | last_updated: "31 Oct 2024, 06:00 PM", 22 | resources: [ 23 | "Research & Development", 24 | "Marketing", 25 | "Backend Development", 26 | "Frontend Development", 27 | "Data Analysis", 28 | "Project Management", 29 | ], 30 | start_date: "04 Jul 2024", 31 | end_date: "20 Jan 2024", 32 | estimated_cost: 6641, 33 | }, 34 | { 35 | id: "2", 36 | name: "Begin web app", 37 | project_manager: "Kaitlyn Williams", 38 | status: STATUS.ON_HOLD, 39 | last_updated: "13 Jun 2024, 08:18 AM", 40 | resources: ["UX/UI Design", "Data Analysis", "Frontend Development"], 41 | start_date: "31 Jan 2024", 42 | end_date: "02 Jan 2024", 43 | estimated_cost: 19906, 44 | }, 45 | { 46 | id: "3", 47 | name: "Important web app", 48 | project_manager: "Emily Blair", 49 | status: STATUS.ON_TRACK, 50 | last_updated: "27 Nov 2024, 09:56 AM", 51 | resources: [ 52 | "Frontend Development", 53 | "QA", 54 | "Research & Development", 55 | "Infrastructure", 56 | "Project Management", 57 | ], 58 | start_date: "04 Aug 2024", 59 | end_date: "08 Jun 2024", 60 | estimated_cost: 6115, 61 | }, 62 | { 63 | id: "4", 64 | name: "Federal web app", 65 | project_manager: "Anthony Saunders", 66 | status: STATUS.RISK, 67 | last_updated: "04 Nov 2024, 12:28 AM", 68 | resources: [ 69 | "Testing", 70 | "Project Management", 71 | "Backend Development", 72 | "Frontend Development", 73 | "Infrastructure", 74 | "Marketing", 75 | ], 76 | start_date: "01 Jun 2024", 77 | end_date: "14 Feb 2024", 78 | estimated_cost: 17790, 79 | }, 80 | { 81 | id: "5", 82 | name: "Mrs web app", 83 | project_manager: "Patrick Stuart", 84 | status: STATUS.POTENTIAL_RISK, 85 | last_updated: "24 Nov 2024, 09:05 AM", 86 | resources: [ 87 | "Data Analysis", 88 | "Project Management", 89 | "UX/UI Design", 90 | "Infrastructure", 91 | ], 92 | start_date: "19 May 2024", 93 | end_date: "19 Feb 2024", 94 | estimated_cost: 10015, 95 | }, 96 | { 97 | id: "6", 98 | name: "Building web app", 99 | project_manager: "John West", 100 | status: STATUS.ON_TRACK, 101 | last_updated: "12 Mar 2024, 05:52 AM", 102 | resources: ["Data Analysis", "QA", "Infrastructure"], 103 | start_date: "09 Mar 2024", 104 | end_date: "09 Jun 2024", 105 | estimated_cost: 12500, 106 | }, 107 | { 108 | id: "7", 109 | name: "Creative web app", 110 | project_manager: "Sophia Harris", 111 | status: STATUS.ON_HOLD, 112 | last_updated: "20 Nov 2024, 04:27 PM", 113 | resources: ["UX/UI Design", "Project Management", "Data Analysis"], 114 | start_date: "14 May 2024", 115 | end_date: "18 Oct 2024", 116 | estimated_cost: 10908, 117 | }, 118 | { 119 | id: "8", 120 | name: "Future web app", 121 | project_manager: "William Wright", 122 | status: STATUS.POTENTIAL_RISK, 123 | last_updated: "03 Nov 2024, 10:32 AM", 124 | resources: [ 125 | "Backend Development", 126 | "Frontend Development", 127 | "Research & Development", 128 | "Testing", 129 | ], 130 | start_date: "25 Apr 2024", 131 | end_date: "05 Dec 2024", 132 | estimated_cost: 13472, 133 | }, 134 | { 135 | id: "9", 136 | name: "Velocity web app", 137 | project_manager: "David Johnson", 138 | status: STATUS.ON_TRACK, 139 | last_updated: "17 Oct 2024, 02:18 PM", 140 | resources: ["QA", "Project Management", "Frontend Development"], 141 | start_date: "02 Mar 2024", 142 | end_date: "13 Jul 2024", 143 | estimated_cost: 8880, 144 | }, 145 | { 146 | id: "10", 147 | name: "Everest web app", 148 | project_manager: "Isabella Green", 149 | status: STATUS.RISK, 150 | last_updated: "08 Aug 2024, 09:47 PM", 151 | resources: [ 152 | "Marketing", 153 | "Infrastructure", 154 | "Backend Development", 155 | "Testing", 156 | ], 157 | start_date: "12 Feb 2024", 158 | end_date: "20 May 2024", 159 | estimated_cost: 17990, 160 | }, 161 | { 162 | id: "11", 163 | name: "Super web app", 164 | project_manager: "Paul Foster", 165 | status: STATUS.POTENTIAL_RISK, 166 | last_updated: "12 Jun 2024, 10:15 AM", 167 | resources: [ 168 | "QA", 169 | "Backend Development", 170 | "Research & Development", 171 | "Marketing", 172 | "Frontend Development", 173 | "Infrastructure", 174 | ], 175 | start_date: "13 Aug 2024", 176 | end_date: "17 Jan 2024", 177 | estimated_cost: 8423, 178 | }, 179 | { 180 | id: "12", 181 | name: "Amount web app", 182 | project_manager: "Olivia Hubbard", 183 | status: STATUS.ON_HOLD, 184 | last_updated: "06 Oct 2024, 12:45 AM", 185 | resources: [ 186 | "UX/UI Design", 187 | "Testing", 188 | "Data Analysis", 189 | "Research & Development", 190 | "Backend Development", 191 | ], 192 | start_date: "27 Oct 2024", 193 | end_date: "28 Oct 2024", 194 | estimated_cost: 6053, 195 | }, 196 | { 197 | id: "13", 198 | name: "Single web app", 199 | project_manager: "Gordon Vasquez", 200 | status: STATUS.RISK, 201 | last_updated: "10 Jul 2024, 12:37 AM", 202 | resources: [ 203 | "UX/UI Design", 204 | "Research & Development", 205 | "Backend Development", 206 | ], 207 | start_date: "16 Oct 2024", 208 | end_date: "17 Sep 2024", 209 | estimated_cost: 7271, 210 | }, 211 | { 212 | id: "14", 213 | name: "Tree web app", 214 | project_manager: "Scott Henry", 215 | status: STATUS.ON_HOLD, 216 | last_updated: "28 Nov 2024, 08:34 AM", 217 | resources: [ 218 | "Marketing", 219 | "Research & Development", 220 | "Frontend Development", 221 | "Project Management", 222 | ], 223 | start_date: "17 May 2024", 224 | end_date: "16 Nov 2024", 225 | estimated_cost: 5063, 226 | }, 227 | { 228 | id: "15", 229 | name: "Production web app", 230 | project_manager: "Daniel Fleming", 231 | status: STATUS.ON_TRACK, 232 | last_updated: "02 Jan 2024, 01:52 AM", 233 | resources: [ 234 | "Marketing", 235 | "Project Management", 236 | "Research & Development", 237 | "Testing", 238 | "UX/UI Design", 239 | ], 240 | start_date: "12 Jun 2024", 241 | end_date: "03 Sep 2024", 242 | estimated_cost: 13639, 243 | }, 244 | { 245 | id: "16", 246 | name: "Mother web app", 247 | project_manager: "Cody Paul", 248 | status: STATUS.ON_HOLD, 249 | last_updated: "04 May 2024, 10:30 PM", 250 | resources: [ 251 | "Marketing", 252 | "Backend Development", 253 | "Testing", 254 | "Research & Development", 255 | "UX/UI Design", 256 | ], 257 | start_date: "07 Oct 2024", 258 | end_date: "25 Mar 2024", 259 | estimated_cost: 16905, 260 | }, 261 | { 262 | id: "17", 263 | name: "Prime web app", 264 | project_manager: "Ethan Moore", 265 | status: STATUS.POTENTIAL_RISK, 266 | last_updated: "02 Nov 2024, 08:12 AM", 267 | resources: [ 268 | "Frontend Development", 269 | "Backend Development", 270 | "QA", 271 | "Project Management", 272 | "Research & Development", 273 | ], 274 | start_date: "20 Jul 2024", 275 | end_date: "25 Nov 2024", 276 | estimated_cost: 11234, 277 | }, 278 | { 279 | id: "18", 280 | name: "Tech web app", 281 | project_manager: "Madeline Davis", 282 | status: STATUS.RISK, 283 | last_updated: "15 Aug 2024, 06:23 AM", 284 | resources: [ 285 | "Marketing", 286 | "Backend Development", 287 | "Data Analysis", 288 | "Research & Development", 289 | ], 290 | start_date: "01 Apr 2024", 291 | end_date: "03 Jul 2024", 292 | estimated_cost: 9463, 293 | }, 294 | { 295 | id: "19", 296 | name: "Bold web app", 297 | project_manager: "Lucas Martin", 298 | status: STATUS.ON_TRACK, 299 | last_updated: "07 Sep 2024, 02:57 PM", 300 | resources: ["Frontend Development", "Project Management", "QA"], 301 | start_date: "11 Dec 2023", 302 | end_date: "15 Jun 2024", 303 | estimated_cost: 13021, 304 | }, 305 | { 306 | id: "20", 307 | name: "Space web app", 308 | project_manager: "Ava Wilson", 309 | status: STATUS.ON_HOLD, 310 | last_updated: "18 Apr 2024, 09:18 AM", 311 | resources: [ 312 | "Backend Development", 313 | "Research & Development", 314 | "Marketing", 315 | "UX/UI Design", 316 | ], 317 | start_date: "17 Jan 2024", 318 | end_date: "30 Aug 2024", 319 | estimated_cost: 10891, 320 | }, 321 | { 322 | id: "21", 323 | name: "Galaxy web app", 324 | project_manager: "Benjamin Thomas", 325 | status: STATUS.ON_TRACK, 326 | last_updated: "23 Nov 2024, 11:30 AM", 327 | resources: [ 328 | "Infrastructure", 329 | "Frontend Development", 330 | "QA", 331 | "Project Management", 332 | "Research & Development", 333 | ], 334 | start_date: "22 Jun 2024", 335 | end_date: "14 Dec 2024", 336 | estimated_cost: 9250, 337 | }, 338 | { 339 | id: "22", 340 | name: "Dragon web app", 341 | project_manager: "Olivia Young", 342 | status: STATUS.POTENTIAL_RISK, 343 | last_updated: "27 Sep 2024, 05:10 PM", 344 | resources: [ 345 | "Backend Development", 346 | "Testing", 347 | "Project Management", 348 | "UX/UI Design", 349 | ], 350 | start_date: "03 Apr 2024", 351 | end_date: "28 Jan 2024", 352 | estimated_cost: 13765, 353 | }, 354 | { 355 | id: "23", 356 | name: "Titan web app", 357 | project_manager: "Henry Clark", 358 | status: STATUS.RISK, 359 | last_updated: "13 May 2024, 07:45 AM", 360 | resources: [ 361 | "Project Management", 362 | "Marketing", 363 | "Frontend Development", 364 | "QA", 365 | ], 366 | start_date: "08 Jan 2024", 367 | end_date: "18 Mar 2024", 368 | estimated_cost: 15632, 369 | }, 370 | { 371 | id: "24", 372 | name: "Ocean web app", 373 | project_manager: "Elijah Wright", 374 | status: STATUS.ON_HOLD, 375 | last_updated: "19 Nov 2024, 12:14 PM", 376 | resources: ["Frontend Development", "Data Analysis", "Infrastructure"], 377 | start_date: "15 Feb 2024", 378 | end_date: "22 Jul 2024", 379 | estimated_cost: 12345, 380 | }, 381 | { 382 | id: "25", 383 | name: "Sun web app", 384 | project_manager: "Mason Lee", 385 | status: STATUS.POTENTIAL_RISK, 386 | last_updated: "01 Sep 2024, 11:01 AM", 387 | resources: ["QA", "Backend Development", "Research & Development"], 388 | start_date: "12 May 2024", 389 | end_date: "06 Dec 2024", 390 | estimated_cost: 12456, 391 | }, 392 | { 393 | id: "26", 394 | name: "Cosmos web app", 395 | project_manager: "James Lee", 396 | status: STATUS.RISK, 397 | last_updated: "05 Aug 2024, 03:12 PM", 398 | resources: [ 399 | "Marketing", 400 | "Backend Development", 401 | "Frontend Development", 402 | "Project Management", 403 | ], 404 | start_date: "29 Mar 2024", 405 | end_date: "21 Dec 2024", 406 | estimated_cost: 14234, 407 | }, 408 | { 409 | id: "27", 410 | name: "Matrix web app", 411 | project_manager: "Charlotte Harris", 412 | status: STATUS.ON_TRACK, 413 | last_updated: "11 Nov 2024, 09:45 AM", 414 | resources: ["Research & Development", "Project Management", "QA"], 415 | start_date: "19 Mar 2024", 416 | end_date: "30 Oct 2024", 417 | estimated_cost: 11987, 418 | }, 419 | { 420 | id: "28", 421 | name: "Phoenix web app", 422 | project_manager: "Liam Martin", 423 | status: STATUS.ON_HOLD, 424 | last_updated: "17 Dec 2024, 04:34 AM", 425 | resources: [ 426 | "Data Analysis", 427 | "Frontend Development", 428 | "Infrastructure", 429 | "Project Management", 430 | ], 431 | start_date: "09 Apr 2024", 432 | end_date: "22 Feb 2024", 433 | estimated_cost: 9732, 434 | }, 435 | { 436 | id: "29", 437 | name: "Nova web app", 438 | project_manager: "Emma Taylor", 439 | status: STATUS.POTENTIAL_RISK, 440 | last_updated: "16 May 2024, 08:15 AM", 441 | resources: ["Testing", "Research & Development", "Frontend Development"], 442 | start_date: "24 Jan 2024", 443 | end_date: "27 Oct 2024", 444 | estimated_cost: 13243, 445 | }, 446 | { 447 | id: "30", 448 | name: "Apollo web app", 449 | project_manager: "Aiden Clark", 450 | status: STATUS.ON_TRACK, 451 | last_updated: "30 Jul 2024, 12:50 PM", 452 | resources: [ 453 | "Frontend Development", 454 | "Backend Development", 455 | "Project Management", 456 | "Testing", 457 | ], 458 | start_date: "10 Jun 2024", 459 | end_date: "19 Jan 2024", 460 | estimated_cost: 14278, 461 | }, 462 | { 463 | id: "31", 464 | name: "Quantum web app", 465 | project_manager: "Lucas Taylor", 466 | status: STATUS.ON_HOLD, 467 | last_updated: "06 Jul 2024, 02:04 PM", 468 | resources: ["Backend Development", "Marketing", "Research & Development"], 469 | start_date: "28 Apr 2024", 470 | end_date: "16 Feb 2024", 471 | estimated_cost: 11687, 472 | }, 473 | { 474 | id: "32", 475 | name: "Star web app", 476 | project_manager: "Jack Robinson", 477 | status: STATUS.POTENTIAL_RISK, 478 | last_updated: "18 Oct 2024, 07:45 AM", 479 | resources: [ 480 | "Infrastructure", 481 | "Testing", 482 | "Frontend Development", 483 | "Data Analysis", 484 | ], 485 | start_date: "07 Mar 2024", 486 | end_date: "29 Oct 2024", 487 | estimated_cost: 13234, 488 | }, 489 | { 490 | id: "33", 491 | name: "Solar web app", 492 | project_manager: "Amelia King", 493 | status: STATUS.ON_TRACK, 494 | last_updated: "14 Oct 2024, 09:10 PM", 495 | resources: ["Frontend Development", "Project Management", "Data Analysis"], 496 | start_date: "03 Jan 2024", 497 | end_date: "30 Mar 2024", 498 | estimated_cost: 14980, 499 | }, 500 | { 501 | id: "34", 502 | name: "Sky web app", 503 | project_manager: "Charlotte Young", 504 | status: STATUS.ON_HOLD, 505 | last_updated: "25 Jul 2024, 05:45 AM", 506 | resources: ["UX/UI Design", "Testing", "Project Management", "Marketing"], 507 | start_date: "27 Apr 2024", 508 | end_date: "12 Jan 2024", 509 | estimated_cost: 11853, 510 | }, 511 | { 512 | id: "35", 513 | name: "Mars web app", 514 | project_manager: "Oliver Green", 515 | status: STATUS.POTENTIAL_RISK, 516 | last_updated: "21 Jun 2024, 10:20 AM", 517 | resources: [ 518 | "Backend Development", 519 | "Frontend Development", 520 | "Data Analysis", 521 | "Research & Development", 522 | ], 523 | start_date: "14 Apr 2024", 524 | end_date: "08 Feb 2024", 525 | estimated_cost: 15432, 526 | }, 527 | { 528 | id: "36", 529 | name: "Venus web app", 530 | project_manager: "Mia Anderson", 531 | status: STATUS.RISK, 532 | last_updated: "28 Jan 2024, 01:50 AM", 533 | resources: [ 534 | "QA", 535 | "Frontend Development", 536 | "Project Management", 537 | "Marketing", 538 | ], 539 | start_date: "19 Feb 2024", 540 | end_date: "26 Dec 2024", 541 | estimated_cost: 10239, 542 | }, 543 | { 544 | id: "37", 545 | name: "Neptune web app", 546 | project_manager: "Zoe Carter", 547 | status: STATUS.ON_HOLD, 548 | last_updated: "23 Oct 2024, 03:15 AM", 549 | resources: ["Backend Development", "Marketing", "Project Management"], 550 | start_date: "09 Feb 2024", 551 | end_date: "25 Jun 2024", 552 | estimated_cost: 9465, 553 | }, 554 | { 555 | id: "38", 556 | name: "Jupiter web app", 557 | project_manager: "Samuel Bell", 558 | status: STATUS.POTENTIAL_RISK, 559 | last_updated: "09 Sep 2024, 07:10 PM", 560 | resources: [ 561 | "Project Management", 562 | "Backend Development", 563 | "Frontend Development", 564 | ], 565 | start_date: "18 May 2024", 566 | end_date: "07 Dec 2024", 567 | estimated_cost: 12367, 568 | }, 569 | { 570 | id: "39", 571 | name: "Pluto web app", 572 | project_manager: "Grace Scott", 573 | status: STATUS.RISK, 574 | last_updated: "15 Jul 2024, 11:12 PM", 575 | resources: [ 576 | "UX/UI Design", 577 | "Data Analysis", 578 | "Testing", 579 | "Frontend Development", 580 | ], 581 | start_date: "06 Apr 2024", 582 | end_date: "11 Mar 2024", 583 | estimated_cost: 11634, 584 | }, 585 | { 586 | id: "40", 587 | name: "Eclipse web app", 588 | project_manager: "Joshua Martin", 589 | status: STATUS.ON_TRACK, 590 | last_updated: "27 Sep 2024, 08:25 PM", 591 | resources: ["Testing", "Project Management", "Backend Development"], 592 | start_date: "12 Feb 2024", 593 | end_date: "17 Dec 2024", 594 | estimated_cost: 10560, 595 | }, 596 | { 597 | id: "41", 598 | name: "Meteor web app", 599 | project_manager: "Lily King", 600 | status: STATUS.POTENTIAL_RISK, 601 | last_updated: "06 Dec 2024, 10:13 PM", 602 | resources: [ 603 | "Infrastructure", 604 | "Data Analysis", 605 | "Project Management", 606 | "Frontend Development", 607 | ], 608 | start_date: "22 Mar 2024", 609 | end_date: "20 Nov 2024", 610 | estimated_cost: 12745, 611 | }, 612 | { 613 | id: "42", 614 | name: "Planet web app", 615 | project_manager: "Benjamin Lewis", 616 | status: STATUS.RISK, 617 | last_updated: "15 May 2024, 01:43 PM", 618 | resources: ["Marketing", "Project Management", "Frontend Development"], 619 | start_date: "04 Oct 2024", 620 | end_date: "16 Feb 2024", 621 | estimated_cost: 9932, 622 | }, 623 | { 624 | id: "43", 625 | name: "Sunset web app", 626 | project_manager: "Chloe Johnson", 627 | status: STATUS.ON_HOLD, 628 | last_updated: "02 Oct 2024, 02:30 PM", 629 | resources: [ 630 | "Backend Development", 631 | "Frontend Development", 632 | "Research & Development", 633 | ], 634 | start_date: "14 Mar 2024", 635 | end_date: "02 Dec 2024", 636 | estimated_cost: 11944, 637 | }, 638 | { 639 | id: "44", 640 | name: "Light web app", 641 | project_manager: "Oliver Harris", 642 | status: STATUS.POTENTIAL_RISK, 643 | last_updated: "21 Apr 2024, 04:03 PM", 644 | resources: [ 645 | "Data Analysis", 646 | "Testing", 647 | "Frontend Development", 648 | "Project Management", 649 | ], 650 | start_date: "23 May 2024", 651 | end_date: "08 Sep 2024", 652 | estimated_cost: 10432, 653 | }, 654 | { 655 | id: "45", 656 | name: "Starship web app", 657 | project_manager: "Harper Wilson", 658 | status: STATUS.RISK, 659 | last_updated: "26 May 2024, 12:14 AM", 660 | resources: ["Testing", "Backend Development", "Project Management"], 661 | start_date: "18 Nov 2024", 662 | end_date: "21 Jan 2024", 663 | estimated_cost: 13890, 664 | }, 665 | { 666 | id: "46", 667 | name: "Echo web app", 668 | project_manager: "Jackie Thomas", 669 | status: STATUS.POTENTIAL_RISK, 670 | last_updated: "15 Nov 2024, 01:20 AM", 671 | resources: [ 672 | "Project Management", 673 | "Frontend Development", 674 | "Backend Development", 675 | "Research & Development", 676 | ], 677 | start_date: "13 Dec 2024", 678 | end_date: "10 Apr 2024", 679 | estimated_cost: 14870, 680 | }, 681 | { 682 | id: "47", 683 | name: "Cloud web app", 684 | project_manager: "Ruby Green", 685 | status: STATUS.ON_HOLD, 686 | last_updated: "30 Jul 2024, 03:50 PM", 687 | resources: ["Marketing", "Project Management", "Infrastructure"], 688 | start_date: "19 Jan 2024", 689 | end_date: "29 Aug 2024", 690 | estimated_cost: 12367, 691 | }, 692 | { 693 | id: "48", 694 | name: "Solaris web app", 695 | project_manager: "Matthew Bell", 696 | status: STATUS.RISK, 697 | last_updated: "08 Dec 2024, 07:15 AM", 698 | resources: ["Frontend Development", "Backend Development"], 699 | start_date: "25 Mar 2024", 700 | end_date: "22 May 2024", 701 | estimated_cost: 14870, 702 | }, 703 | { 704 | id: "49", 705 | name: "Nova web app", 706 | project_manager: "Caden Wilson", 707 | status: STATUS.ON_TRACK, 708 | last_updated: "10 Feb 2024, 05:10 PM", 709 | resources: [ 710 | "Testing", 711 | "Project Management", 712 | "Backend Development", 713 | "Frontend Development", 714 | ], 715 | start_date: "17 Jan 2024", 716 | end_date: "04 Aug 2024", 717 | estimated_cost: 15642, 718 | }, 719 | { 720 | id: "50", 721 | name: "Matrix web app", 722 | project_manager: "Ella Young", 723 | status: STATUS.POTENTIAL_RISK, 724 | last_updated: "23 Nov 2024, 12:36 AM", 725 | resources: [ 726 | "Frontend Development", 727 | "Backend Development", 728 | "Data Analysis", 729 | "QA", 730 | ], 731 | start_date: "14 Feb 2024", 732 | end_date: "20 Oct 2024", 733 | estimated_cost: 13972, 734 | }, 735 | ]; 736 | -------------------------------------------------------------------------------- /frontend/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bipulpoudel/data-table-v3/6b1a4eaa996e69771774695dc59e3c89224465dc/frontend/app/favicon.ico -------------------------------------------------------------------------------- /frontend/app/fonts/GeistMonoVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bipulpoudel/data-table-v3/6b1a4eaa996e69771774695dc59e3c89224465dc/frontend/app/fonts/GeistMonoVF.woff -------------------------------------------------------------------------------- /frontend/app/fonts/GeistVF.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bipulpoudel/data-table-v3/6b1a4eaa996e69771774695dc59e3c89224465dc/frontend/app/fonts/GeistVF.woff -------------------------------------------------------------------------------- /frontend/app/globals.css: -------------------------------------------------------------------------------- 1 | @tailwind base; 2 | @tailwind components; 3 | @tailwind utilities; 4 | 5 | @layer base { 6 | :root { 7 | --background: 0 0% 100%; 8 | --foreground: 224 71.4% 4.1%; 9 | --card: 0 0% 100%; 10 | --card-foreground: 224 71.4% 4.1%; 11 | --popover: 0 0% 100%; 12 | --popover-foreground: 224 71.4% 4.1%; 13 | --primary: 242 64% 61%; 14 | --primary-foreground: 210 20% 98%; 15 | --secondary: 220 14.3% 95.9%; 16 | --secondary-foreground: 220.9 39.3% 11%; 17 | --muted: 220 14.3% 95.9%; 18 | --muted-foreground: 220 8.9% 46.1%; 19 | --accent: 220 14.3% 95.9%; 20 | --accent-foreground: 220.9 39.3% 11%; 21 | --destructive: 0 84.2% 60.2%; 22 | --destructive-foreground: 210 20% 98%; 23 | --border: 220 13% 91%; 24 | --input: 220 13% 91%; 25 | --ring: 262.1 83.3% 57.8%; 26 | --radius: 0.5rem; 27 | --chart-1: 12 76% 61%; 28 | --chart-2: 173 58% 39%; 29 | --chart-3: 197 37% 24%; 30 | --chart-4: 43 74% 66%; 31 | --chart-5: 27 87% 67%; 32 | } 33 | 34 | .dark { 35 | --background: 224 71.4% 4.1%; 36 | --foreground: 210 20% 98%; 37 | --card: 224 71.4% 4.1%; 38 | --card-foreground: 210 20% 98%; 39 | --popover: 224 71.4% 4.1%; 40 | --popover-foreground: 210 20% 98%; 41 | --primary: 263.4 70% 50.4%; 42 | --primary-foreground: 210 20% 98%; 43 | --secondary: 215 27.9% 16.9%; 44 | --secondary-foreground: 210 20% 98%; 45 | --muted: 215 27.9% 16.9%; 46 | --muted-foreground: 217.9 10.6% 64.9%; 47 | --accent: 215 27.9% 16.9%; 48 | --accent-foreground: 210 20% 98%; 49 | --destructive: 0 62.8% 30.6%; 50 | --destructive-foreground: 210 20% 98%; 51 | --border: 215 27.9% 16.9%; 52 | --input: 215 27.9% 16.9%; 53 | --ring: 263.4 70% 50.4%; 54 | --chart-1: 220 70% 50%; 55 | --chart-2: 160 60% 45%; 56 | --chart-3: 30 80% 55%; 57 | --chart-4: 280 65% 60%; 58 | --chart-5: 340 75% 55%; 59 | } 60 | } 61 | 62 | @layer base { 63 | * { 64 | @apply border-border; 65 | } 66 | body { 67 | @apply bg-background text-foreground; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /frontend/app/layout.tsx: -------------------------------------------------------------------------------- 1 | import type { Metadata } from "next"; 2 | import { Inter } from "next/font/google"; 3 | 4 | import "./globals.css"; 5 | 6 | // If loading a variable font, you don't need to specify the font weight 7 | const inter = Inter({ subsets: ["latin"] }); 8 | 9 | export const metadata: Metadata = { 10 | title: "Create Next App", 11 | description: "Generated by create next app", 12 | }; 13 | 14 | export default function RootLayout({ 15 | children, 16 | }: Readonly<{ 17 | children: React.ReactNode; 18 | }>) { 19 | return ( 20 | 21 | {children} 22 | 23 | ); 24 | } 25 | -------------------------------------------------------------------------------- /frontend/app/page.tsx: -------------------------------------------------------------------------------- 1 | import { DataTable } from "@/components/data-table"; 2 | import TopHeader from "@/components/data-table/Header"; 3 | 4 | import { projects } from "./data"; 5 | import { columns } from "./columns"; 6 | 7 | const IndexPage = () => { 8 | return ( 9 |
10 | 11 | 12 |
13 | ); 14 | }; 15 | 16 | export default IndexPage; 17 | -------------------------------------------------------------------------------- /frontend/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://ui.shadcn.com/schema.json", 3 | "style": "new-york", 4 | "rsc": true, 5 | "tsx": true, 6 | "tailwind": { 7 | "config": "tailwind.config.ts", 8 | "css": "app/globals.css", 9 | "baseColor": "zinc", 10 | "cssVariables": true, 11 | "prefix": "" 12 | }, 13 | "aliases": { 14 | "components": "@/components", 15 | "utils": "@/lib/utils", 16 | "ui": "@/components/ui", 17 | "lib": "@/lib", 18 | "hooks": "@/hooks" 19 | }, 20 | "iconLibrary": "lucide" 21 | } -------------------------------------------------------------------------------- /frontend/components/data-table/Footer/Pagination.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@/components/ui/button"; 2 | import { Table } from "@tanstack/react-table"; 3 | import { 4 | ChevronLeft, 5 | ChevronRight, 6 | ChevronsLeft, 7 | ChevronsRight, 8 | } from "lucide-react"; 9 | 10 | export function Pagination({ table }: { table: Table }) { 11 | return ( 12 |
13 | 22 | 31 | 40 | 49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /frontend/components/data-table/Footer/index.tsx: -------------------------------------------------------------------------------- 1 | import { Table } from "@tanstack/react-table"; 2 | 3 | import { 4 | Select, 5 | SelectContent, 6 | SelectItem, 7 | SelectTrigger, 8 | SelectValue, 9 | } from "@/components/ui/select"; 10 | 11 | export function TableFooter({ table }: { table: Table }) { 12 | return ( 13 |
14 |
15 | {table.getFilteredSelectedRowModel().rows.length} of{" "} 16 | {table.getFilteredRowModel().rows.length} row(s) selected. 17 |
18 |
19 |
20 |

Rows per page

21 | 38 |
39 |
40 | Page {table.getState().pagination.pageIndex + 1} of{" "} 41 | {table.getPageCount()} 42 |
43 |
44 |
45 | ); 46 | } 47 | -------------------------------------------------------------------------------- /frontend/components/data-table/Header/SearchBar.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | DropdownMenu, 3 | DropdownMenuContent, 4 | DropdownMenuItem, 5 | DropdownMenuLabel, 6 | DropdownMenuSeparator, 7 | DropdownMenuTrigger, 8 | } from "@/components/ui/dropdown-menu"; 9 | import { ChevronDown, FilterIcon, SearchIcon } from "lucide-react"; 10 | 11 | const SearchBar = () => { 12 | return ( 13 |
14 | 15 | 16 | 17 | All 18 | 19 | 20 | 21 | My Account 22 | 23 | Profile 24 | Billing 25 | Team 26 | Subscription 27 | 28 | 29 |
30 | 31 | 36 |
37 |
38 | ); 39 | }; 40 | 41 | export default SearchBar; 42 | -------------------------------------------------------------------------------- /frontend/components/data-table/Header/index.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@/components/ui/button"; 2 | import SearchBar from "./SearchBar"; 3 | import { PlusIcon } from "lucide-react"; 4 | 5 | const TopHeader = () => { 6 | return ( 7 |
8 | 9 | 10 | 14 |
15 | ); 16 | }; 17 | 18 | export default TopHeader; 19 | -------------------------------------------------------------------------------- /frontend/components/data-table/ProjectActions.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import { Row } from "@tanstack/react-table"; 4 | import { MoreVertical } from "lucide-react"; 5 | 6 | import { Button } from "@/components/ui/button"; 7 | import { 8 | DropdownMenu, 9 | DropdownMenuContent, 10 | DropdownMenuItem, 11 | DropdownMenuSeparator, 12 | DropdownMenuShortcut, 13 | DropdownMenuTrigger, 14 | } from "@/components/ui/dropdown-menu"; 15 | 16 | interface ProjectActionsProps { 17 | row: Row; 18 | } 19 | 20 | export function ProjectActions({ row }: ProjectActionsProps) { 21 | return ( 22 | 23 | 24 | 31 | 32 | 33 | Edit 34 | Make a copy 35 | Favorite 36 | 37 | 38 | Delete 39 | ⌘⌫ 40 | 41 | 42 | 43 | ); 44 | } 45 | -------------------------------------------------------------------------------- /frontend/components/data-table/ProjectLastUpdate.tsx: -------------------------------------------------------------------------------- 1 | import { NotepadText } from "lucide-react"; 2 | import { 3 | Tooltip, 4 | TooltipContent, 5 | TooltipProvider, 6 | TooltipTrigger, 7 | } from "@/components/ui/tooltip"; 8 | import { Arrow } from "@radix-ui/react-tooltip"; 9 | 10 | const ProjectLastUpdate = ({ date }: { date: string }) => { 11 | return ( 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | This is a note, user fills in while changing the status, which 21 | explains the current project status. 22 | 23 | 24 | 25 |

{date}

26 |
27 | ); 28 | }; 29 | 30 | export default ProjectLastUpdate; 31 | -------------------------------------------------------------------------------- /frontend/components/data-table/ProjectManager.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Tooltip, 3 | TooltipContent, 4 | TooltipProvider, 5 | TooltipTrigger, 6 | } from "@/components/ui/tooltip"; 7 | 8 | import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; 9 | import { Arrow } from "@radix-ui/react-tooltip"; 10 | 11 | const ProjectManager = ({ name }: { name: string }) => { 12 | return ( 13 | 14 | 15 | 16 | 17 | 18 | 19 | {name[0]} {name.split(" ")[1][0]} 20 | 21 | 22 | 23 | 24 | 25 | {name} 26 | 27 | 28 | 29 | ); 30 | }; 31 | 32 | export default ProjectManager; 33 | -------------------------------------------------------------------------------- /frontend/components/data-table/ProjectName.tsx: -------------------------------------------------------------------------------- 1 | import { ExternalLink } from "lucide-react"; 2 | 3 | const ProjectName = ({ name }: { name: string }) => { 4 | return ( 5 |
6 |

7 | {name} 8 |

9 | 10 |
11 | ); 12 | }; 13 | 14 | export default ProjectName; 15 | -------------------------------------------------------------------------------- /frontend/components/data-table/ProjectResources.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Tooltip, 3 | TooltipContent, 4 | TooltipProvider, 5 | TooltipTrigger, 6 | } from "@/components/ui/tooltip"; 7 | import { Arrow } from "@radix-ui/react-tooltip"; 8 | 9 | const ProjectResources = ({ resources }: { resources: string[] }) => { 10 | return ( 11 | 12 | 13 | 14 |
15 | {resources.length} 16 |
17 |
18 | 19 | 20 |
21 |

Resources

22 | {resources.map((resource) => ( 23 |

24 | {resource} 25 |

26 | ))} 27 |
28 |
29 |
30 |
31 | ); 32 | }; 33 | 34 | export default ProjectResources; 35 | -------------------------------------------------------------------------------- /frontend/components/data-table/ProjectStatus.tsx: -------------------------------------------------------------------------------- 1 | import { STATUS } from "@/enum"; 2 | 3 | const statusBackgroundColors = { 4 | [STATUS.RISK]: "bg-[#FFEDEF]", 5 | [STATUS.ON_TRACK]: "bg-[#E1FCEF]", 6 | [STATUS.ON_HOLD]: "bg-gray-50", 7 | [STATUS.POTENTIAL_RISK]: "bg-[#FCF2E6]", 8 | [STATUS.ARCHIVED]: "bg-gray-50", 9 | }; 10 | 11 | const statusTextColors = { 12 | [STATUS.RISK]: "text-[#D1293D]", 13 | [STATUS.ON_TRACK]: "text-[#14804A]", 14 | [STATUS.ON_HOLD]: "text-gray-600", 15 | [STATUS.POTENTIAL_RISK]: "text-[#AA5B00]", 16 | [STATUS.ARCHIVED]: "text-gray-600", 17 | }; 18 | 19 | const statusDotBackgroundColors = { 20 | [STATUS.RISK]: "bg-[#D1293D]", 21 | [STATUS.ON_TRACK]: "bg-[#14804A]", 22 | [STATUS.ON_HOLD]: "bg-gray-600", 23 | [STATUS.POTENTIAL_RISK]: "bg-[#AA5B00]", 24 | [STATUS.ARCHIVED]: "bg-gray-600", 25 | }; 26 | 27 | const statusLabels = { 28 | [STATUS.RISK]: "At Risk", 29 | [STATUS.ON_TRACK]: "On track", 30 | [STATUS.ON_HOLD]: "On hold", 31 | [STATUS.POTENTIAL_RISK]: "Potential risk", 32 | [STATUS.ARCHIVED]: "Archived", 33 | }; 34 | 35 | const ProjectStatus = ({ 36 | status, 37 | }: { 38 | status: 39 | | STATUS.RISK 40 | | STATUS.ON_TRACK 41 | | STATUS.ON_HOLD 42 | | STATUS.POTENTIAL_RISK 43 | | STATUS.ARCHIVED; 44 | }) => { 45 | return ( 46 |
49 |
52 | {statusLabels[status]} 53 |
54 | ); 55 | }; 56 | 57 | export default ProjectStatus; 58 | -------------------------------------------------------------------------------- /frontend/components/data-table/ProjectTimeLine.tsx: -------------------------------------------------------------------------------- 1 | import { ChevronRight } from "lucide-react"; 2 | 3 | const ProjectTimeLine = ({ 4 | startDate, 5 | endDate, 6 | }: { 7 | startDate: string; 8 | endDate: string; 9 | }) => { 10 | return ( 11 |
12 | 13 | {startDate} 14 | 15 | 16 | 17 | {endDate} 18 | 19 |
20 | ); 21 | }; 22 | 23 | export default ProjectTimeLine; 24 | -------------------------------------------------------------------------------- /frontend/components/data-table/SortableHeader.tsx: -------------------------------------------------------------------------------- 1 | import { Column } from "@tanstack/react-table"; 2 | import { ArrowDown, ArrowUp, ChevronsUpDown, X } from "lucide-react"; 3 | 4 | import { cn } from "@/lib/utils"; 5 | import { Button } from "@/components/ui/button"; 6 | import { 7 | DropdownMenu, 8 | DropdownMenuContent, 9 | DropdownMenuItem, 10 | DropdownMenuSeparator, 11 | DropdownMenuTrigger, 12 | } from "@/components/ui/dropdown-menu"; 13 | 14 | interface SortableHeaderProps 15 | extends React.HTMLAttributes { 16 | column: Column; 17 | title: string; 18 | } 19 | 20 | export function SortableHeader({ 21 | column, 22 | title, 23 | className, 24 | }: SortableHeaderProps) { 25 | if (!column.getCanSort()) { 26 | return
{title}
; 27 | } 28 | 29 | return ( 30 |
31 | 32 | 33 | 46 | 47 | 48 | column.toggleSorting(false)}> 49 | 50 | Asc 51 | 52 | column.toggleSorting(true)}> 53 | 54 | Desc 55 | 56 | column.clearSorting()}> 57 | 58 | Remove 59 | 60 | 61 | 62 |
63 | ); 64 | } 65 | -------------------------------------------------------------------------------- /frontend/components/data-table/TableHeader.tsx: -------------------------------------------------------------------------------- 1 | import { flexRender } from "@tanstack/react-table"; 2 | 3 | import { 4 | TableHead, 5 | TableHeader as ShadTableHeader, 6 | TableRow, 7 | } from "@/components/ui/table"; 8 | import { Table } from "@tanstack/react-table"; 9 | 10 | const TableHeader = ({ table }: { table: Table }) => { 11 | return ( 12 | 13 | {table.getHeaderGroups().map((headerGroup) => ( 14 | 15 | {headerGroup.headers.map((header) => { 16 | return ( 17 | 21 | {header.isPlaceholder 22 | ? null 23 | : flexRender( 24 | header.column.columnDef.header, 25 | header.getContext() 26 | )} 27 | 28 | ); 29 | })} 30 | 31 | ))} 32 | 33 | ); 34 | }; 35 | 36 | export default TableHeader; 37 | -------------------------------------------------------------------------------- /frontend/components/data-table/Tabs/TabItem.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useMemo } from "react"; 3 | import { Separator } from "@/components/ui/separator"; 4 | 5 | import { IItem } from "."; 6 | import { cn } from "@/lib/utils"; 7 | 8 | const TabItem = ({ 9 | item, 10 | activeTab, 11 | setActiveTab, 12 | }: { 13 | item: IItem; 14 | activeTab: IItem["value"]; 15 | setActiveTab: (value: IItem["value"]) => void; 16 | }) => { 17 | const { label, value, number } = item; 18 | 19 | const handleClick = () => { 20 | setActiveTab(value); 21 | }; 22 | 23 | const isActive = useMemo(() => activeTab === value, [activeTab, value]); 24 | 25 | return ( 26 | <> 27 | {value === "archived" && ( 28 | 29 | )} 30 |
40 |

46 | {label} 47 |

48 | 53 | {number} 54 | 55 |
56 | 57 | ); 58 | }; 59 | 60 | export default TabItem; 61 | -------------------------------------------------------------------------------- /frontend/components/data-table/Tabs/index.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import { useState } from "react"; 3 | import TabItem from "./TabItem"; 4 | 5 | export interface IItem { 6 | index: number; 7 | label: string; 8 | value: "all" | "risk" | "onHold" | "potentialRisk" | "onTrack" | "archived"; 9 | number: number; 10 | } 11 | 12 | const items: IItem[] = [ 13 | { 14 | index: 0, 15 | label: "All", 16 | value: "all", 17 | number: 27, 18 | }, 19 | { 20 | index: 1, 21 | label: "Risk", 22 | value: "risk", 23 | number: 4, 24 | }, 25 | { 26 | index: 2, 27 | label: "On Hold", 28 | value: "onHold", 29 | number: 6, 30 | }, 31 | { 32 | index: 3, 33 | label: "Potential Risk", 34 | value: "potentialRisk", 35 | number: 7, 36 | }, 37 | { 38 | index: 4, 39 | label: "On Track", 40 | value: "onTrack", 41 | number: 12, 42 | }, 43 | { 44 | index: 5, 45 | label: "Archived", 46 | value: "archived", 47 | number: 9, 48 | }, 49 | ]; 50 | 51 | const TableTabs = () => { 52 | const [activeTab, setActiveTab] = useState("all"); 53 | return ( 54 |
55 | {items.map((item) => ( 56 | 62 | ))} 63 |
64 | ); 65 | }; 66 | 67 | export default TableTabs; 68 | -------------------------------------------------------------------------------- /frontend/components/data-table/index.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | import React from "react"; 3 | import { 4 | ColumnDef, 5 | SortingState, 6 | flexRender, 7 | getCoreRowModel, 8 | useReactTable, 9 | getSortedRowModel, 10 | getPaginationRowModel, 11 | PaginationState, 12 | } from "@tanstack/react-table"; 13 | 14 | import { Table, TableBody, TableCell, TableRow } from "@/components/ui/table"; 15 | 16 | import TableTabs from "./Tabs"; 17 | import TableHeader from "./TableHeader"; 18 | import { TableFooter } from "./Footer"; 19 | 20 | export interface DataTableProps { 21 | columns: ColumnDef[]; 22 | data: TData[]; 23 | } 24 | 25 | export function DataTable({ 26 | columns, 27 | data, 28 | }: DataTableProps) { 29 | const [rowSelection, setRowSelection] = React.useState({}); 30 | const [sorting, setSorting] = React.useState([]); 31 | 32 | const table = useReactTable({ 33 | data, 34 | columns, 35 | getCoreRowModel: getCoreRowModel(), 36 | onRowSelectionChange: setRowSelection, 37 | onSortingChange: setSorting, 38 | getSortedRowModel: getSortedRowModel(), 39 | getPaginationRowModel: getPaginationRowModel(), 40 | state: { 41 | rowSelection, 42 | sorting, 43 | }, 44 | initialState: { 45 | pagination: { 46 | pageSize: 20, 47 | pageIndex: 0, 48 | }, 49 | }, 50 | }); 51 | 52 | console.log("sorting", table.getState().sorting); 53 | 54 | return ( 55 |
56 | 57 |
58 | 59 | 60 | 61 | {table.getRowModel().rows?.length ? ( 62 | table.getRowModel().rows.map((row) => ( 63 | 67 | {row.getVisibleCells().map((cell) => ( 68 | 69 | {flexRender( 70 | cell.column.columnDef.cell, 71 | cell.getContext() 72 | )} 73 | 74 | ))} 75 | 76 | )) 77 | ) : ( 78 | 79 | 83 | No results. 84 | 85 | 86 | )} 87 | 88 |
89 |
90 | 91 |
92 | ); 93 | } 94 | -------------------------------------------------------------------------------- /frontend/components/ui/avatar.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as AvatarPrimitive from "@radix-ui/react-avatar" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Avatar = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >(({ className, ...props }, ref) => ( 12 | 20 | )) 21 | Avatar.displayName = AvatarPrimitive.Root.displayName 22 | 23 | const AvatarImage = React.forwardRef< 24 | React.ElementRef, 25 | React.ComponentPropsWithoutRef 26 | >(({ className, ...props }, ref) => ( 27 | 32 | )) 33 | AvatarImage.displayName = AvatarPrimitive.Image.displayName 34 | 35 | const AvatarFallback = React.forwardRef< 36 | React.ElementRef, 37 | React.ComponentPropsWithoutRef 38 | >(({ className, ...props }, ref) => ( 39 | 47 | )) 48 | AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName 49 | 50 | export { Avatar, AvatarImage, AvatarFallback } 51 | -------------------------------------------------------------------------------- /frontend/components/ui/button.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { Slot } from "@radix-ui/react-slot"; 3 | import { cva, type VariantProps } from "class-variance-authority"; 4 | 5 | import { cn } from "@/lib/utils"; 6 | 7 | const buttonVariants = cva( 8 | "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", 9 | { 10 | variants: { 11 | variant: { 12 | default: 13 | "bg-primary text-primary-foreground shadow hover:bg-primary/90", 14 | destructive: 15 | "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", 16 | outline: 17 | "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", 18 | secondary: 19 | "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", 20 | ghost: "hover:bg-accent hover:text-accent-foreground", 21 | link: "text-primary underline-offset-4 hover:underline", 22 | }, 23 | size: { 24 | default: "h-9 px-4 py-2", 25 | sm: "h-8 rounded-md px-3 text-xs", 26 | lg: "h-10 rounded-md px-8", 27 | icon: "h-9 w-9", 28 | }, 29 | }, 30 | defaultVariants: { 31 | variant: "default", 32 | size: "default", 33 | }, 34 | } 35 | ); 36 | 37 | export interface ButtonProps 38 | extends React.ButtonHTMLAttributes, 39 | VariantProps { 40 | asChild?: boolean; 41 | } 42 | 43 | const Button = React.forwardRef( 44 | ({ className, variant, size, asChild = false, ...props }, ref) => { 45 | const Comp = asChild ? Slot : "button"; 46 | return ( 47 | 52 | ); 53 | } 54 | ); 55 | Button.displayName = "Button"; 56 | 57 | export { Button, buttonVariants }; 58 | -------------------------------------------------------------------------------- /frontend/components/ui/checkbox.tsx: -------------------------------------------------------------------------------- 1 | "use client"; 2 | 3 | import * as React from "react"; 4 | import * as CheckboxPrimitive from "@radix-ui/react-checkbox"; 5 | import { Check, Minus } from "lucide-react"; 6 | 7 | import { cn } from "@/lib/utils"; 8 | 9 | const Checkbox = React.forwardRef< 10 | React.ElementRef, 11 | React.ComponentPropsWithoutRef 12 | >(({ className, ...props }, ref) => ( 13 | 21 | 24 | {props.checked === "indeterminate" ? ( 25 | 26 | ) : ( 27 | 28 | )} 29 | 30 | 31 | )); 32 | Checkbox.displayName = CheckboxPrimitive.Root.displayName; 33 | 34 | export { Checkbox }; 35 | -------------------------------------------------------------------------------- /frontend/components/ui/dropdown-menu.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu" 5 | import { Check, ChevronRight, Circle } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const DropdownMenu = DropdownMenuPrimitive.Root 10 | 11 | const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger 12 | 13 | const DropdownMenuGroup = DropdownMenuPrimitive.Group 14 | 15 | const DropdownMenuPortal = DropdownMenuPrimitive.Portal 16 | 17 | const DropdownMenuSub = DropdownMenuPrimitive.Sub 18 | 19 | const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup 20 | 21 | const DropdownMenuSubTrigger = React.forwardRef< 22 | React.ElementRef, 23 | React.ComponentPropsWithoutRef & { 24 | inset?: boolean 25 | } 26 | >(({ className, inset, children, ...props }, ref) => ( 27 | 36 | {children} 37 | 38 | 39 | )) 40 | DropdownMenuSubTrigger.displayName = 41 | DropdownMenuPrimitive.SubTrigger.displayName 42 | 43 | const DropdownMenuSubContent = React.forwardRef< 44 | React.ElementRef, 45 | React.ComponentPropsWithoutRef 46 | >(({ className, ...props }, ref) => ( 47 | 55 | )) 56 | DropdownMenuSubContent.displayName = 57 | DropdownMenuPrimitive.SubContent.displayName 58 | 59 | const DropdownMenuContent = React.forwardRef< 60 | React.ElementRef, 61 | React.ComponentPropsWithoutRef 62 | >(({ className, sideOffset = 4, ...props }, ref) => ( 63 | 64 | 74 | 75 | )) 76 | DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName 77 | 78 | const DropdownMenuItem = React.forwardRef< 79 | React.ElementRef, 80 | React.ComponentPropsWithoutRef & { 81 | inset?: boolean 82 | } 83 | >(({ className, inset, ...props }, ref) => ( 84 | svg]:size-4 [&>svg]:shrink-0", 88 | inset && "pl-8", 89 | className 90 | )} 91 | {...props} 92 | /> 93 | )) 94 | DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName 95 | 96 | const DropdownMenuCheckboxItem = React.forwardRef< 97 | React.ElementRef, 98 | React.ComponentPropsWithoutRef 99 | >(({ className, children, checked, ...props }, ref) => ( 100 | 109 | 110 | 111 | 112 | 113 | 114 | {children} 115 | 116 | )) 117 | DropdownMenuCheckboxItem.displayName = 118 | DropdownMenuPrimitive.CheckboxItem.displayName 119 | 120 | const DropdownMenuRadioItem = React.forwardRef< 121 | React.ElementRef, 122 | React.ComponentPropsWithoutRef 123 | >(({ className, children, ...props }, ref) => ( 124 | 132 | 133 | 134 | 135 | 136 | 137 | {children} 138 | 139 | )) 140 | DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName 141 | 142 | const DropdownMenuLabel = React.forwardRef< 143 | React.ElementRef, 144 | React.ComponentPropsWithoutRef & { 145 | inset?: boolean 146 | } 147 | >(({ className, inset, ...props }, ref) => ( 148 | 157 | )) 158 | DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName 159 | 160 | const DropdownMenuSeparator = React.forwardRef< 161 | React.ElementRef, 162 | React.ComponentPropsWithoutRef 163 | >(({ className, ...props }, ref) => ( 164 | 169 | )) 170 | DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName 171 | 172 | const DropdownMenuShortcut = ({ 173 | className, 174 | ...props 175 | }: React.HTMLAttributes) => { 176 | return ( 177 | 181 | ) 182 | } 183 | DropdownMenuShortcut.displayName = "DropdownMenuShortcut" 184 | 185 | export { 186 | DropdownMenu, 187 | DropdownMenuTrigger, 188 | DropdownMenuContent, 189 | DropdownMenuItem, 190 | DropdownMenuCheckboxItem, 191 | DropdownMenuRadioItem, 192 | DropdownMenuLabel, 193 | DropdownMenuSeparator, 194 | DropdownMenuShortcut, 195 | DropdownMenuGroup, 196 | DropdownMenuPortal, 197 | DropdownMenuSub, 198 | DropdownMenuSubContent, 199 | DropdownMenuSubTrigger, 200 | DropdownMenuRadioGroup, 201 | } 202 | -------------------------------------------------------------------------------- /frontend/components/ui/select.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SelectPrimitive from "@radix-ui/react-select" 5 | import { Check, ChevronDown, ChevronUp } from "lucide-react" 6 | 7 | import { cn } from "@/lib/utils" 8 | 9 | const Select = SelectPrimitive.Root 10 | 11 | const SelectGroup = SelectPrimitive.Group 12 | 13 | const SelectValue = SelectPrimitive.Value 14 | 15 | const SelectTrigger = React.forwardRef< 16 | React.ElementRef, 17 | React.ComponentPropsWithoutRef 18 | >(({ className, children, ...props }, ref) => ( 19 | span]:line-clamp-1", 23 | className 24 | )} 25 | {...props} 26 | > 27 | {children} 28 | 29 | 30 | 31 | 32 | )) 33 | SelectTrigger.displayName = SelectPrimitive.Trigger.displayName 34 | 35 | const SelectScrollUpButton = React.forwardRef< 36 | React.ElementRef, 37 | React.ComponentPropsWithoutRef 38 | >(({ className, ...props }, ref) => ( 39 | 47 | 48 | 49 | )) 50 | SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName 51 | 52 | const SelectScrollDownButton = React.forwardRef< 53 | React.ElementRef, 54 | React.ComponentPropsWithoutRef 55 | >(({ className, ...props }, ref) => ( 56 | 64 | 65 | 66 | )) 67 | SelectScrollDownButton.displayName = 68 | SelectPrimitive.ScrollDownButton.displayName 69 | 70 | const SelectContent = React.forwardRef< 71 | React.ElementRef, 72 | React.ComponentPropsWithoutRef 73 | >(({ className, children, position = "popper", ...props }, ref) => ( 74 | 75 | 86 | 87 | 94 | {children} 95 | 96 | 97 | 98 | 99 | )) 100 | SelectContent.displayName = SelectPrimitive.Content.displayName 101 | 102 | const SelectLabel = React.forwardRef< 103 | React.ElementRef, 104 | React.ComponentPropsWithoutRef 105 | >(({ className, ...props }, ref) => ( 106 | 111 | )) 112 | SelectLabel.displayName = SelectPrimitive.Label.displayName 113 | 114 | const SelectItem = React.forwardRef< 115 | React.ElementRef, 116 | React.ComponentPropsWithoutRef 117 | >(({ className, children, ...props }, ref) => ( 118 | 126 | 127 | 128 | 129 | 130 | 131 | {children} 132 | 133 | )) 134 | SelectItem.displayName = SelectPrimitive.Item.displayName 135 | 136 | const SelectSeparator = React.forwardRef< 137 | React.ElementRef, 138 | React.ComponentPropsWithoutRef 139 | >(({ className, ...props }, ref) => ( 140 | 145 | )) 146 | SelectSeparator.displayName = SelectPrimitive.Separator.displayName 147 | 148 | export { 149 | Select, 150 | SelectGroup, 151 | SelectValue, 152 | SelectTrigger, 153 | SelectContent, 154 | SelectLabel, 155 | SelectItem, 156 | SelectSeparator, 157 | SelectScrollUpButton, 158 | SelectScrollDownButton, 159 | } 160 | -------------------------------------------------------------------------------- /frontend/components/ui/separator.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as SeparatorPrimitive from "@radix-ui/react-separator" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const Separator = React.forwardRef< 9 | React.ElementRef, 10 | React.ComponentPropsWithoutRef 11 | >( 12 | ( 13 | { className, orientation = "horizontal", decorative = true, ...props }, 14 | ref 15 | ) => ( 16 | 27 | ) 28 | ) 29 | Separator.displayName = SeparatorPrimitive.Root.displayName 30 | 31 | export { Separator } 32 | -------------------------------------------------------------------------------- /frontend/components/ui/table.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | 3 | import { cn } from "@/lib/utils"; 4 | 5 | const Table = React.forwardRef< 6 | HTMLTableElement, 7 | React.HTMLAttributes 8 | >(({ className, ...props }, ref) => ( 9 | 14 | )); 15 | Table.displayName = "Table"; 16 | 17 | const TableHeader = React.forwardRef< 18 | HTMLTableSectionElement, 19 | React.HTMLAttributes 20 | >(({ className, ...props }, ref) => ( 21 | 22 | )); 23 | TableHeader.displayName = "TableHeader"; 24 | 25 | const TableBody = React.forwardRef< 26 | HTMLTableSectionElement, 27 | React.HTMLAttributes 28 | >(({ className, ...props }, ref) => ( 29 | 34 | )); 35 | TableBody.displayName = "TableBody"; 36 | 37 | const TableFooter = React.forwardRef< 38 | HTMLTableSectionElement, 39 | React.HTMLAttributes 40 | >(({ className, ...props }, ref) => ( 41 | tr]:last:border-b-0", 45 | className 46 | )} 47 | {...props} 48 | /> 49 | )); 50 | TableFooter.displayName = "TableFooter"; 51 | 52 | const TableRow = React.forwardRef< 53 | HTMLTableRowElement, 54 | React.HTMLAttributes 55 | >(({ className, ...props }, ref) => ( 56 | 64 | )); 65 | TableRow.displayName = "TableRow"; 66 | 67 | const TableHead = React.forwardRef< 68 | HTMLTableCellElement, 69 | React.ThHTMLAttributes 70 | >(({ className, ...props }, ref) => ( 71 |
[role=checkbox]]:translate-y-[2px]", 75 | className 76 | )} 77 | {...props} 78 | /> 79 | )); 80 | TableHead.displayName = "TableHead"; 81 | 82 | const TableCell = React.forwardRef< 83 | HTMLTableCellElement, 84 | React.TdHTMLAttributes 85 | >(({ className, ...props }, ref) => ( 86 | [role=checkbox]]:translate-y-[2px]", 90 | className 91 | )} 92 | {...props} 93 | /> 94 | )); 95 | TableCell.displayName = "TableCell"; 96 | 97 | const TableCaption = React.forwardRef< 98 | HTMLTableCaptionElement, 99 | React.HTMLAttributes 100 | >(({ className, ...props }, ref) => ( 101 |
106 | )); 107 | TableCaption.displayName = "TableCaption"; 108 | 109 | export { 110 | Table, 111 | TableHeader, 112 | TableBody, 113 | TableFooter, 114 | TableHead, 115 | TableRow, 116 | TableCell, 117 | TableCaption, 118 | }; 119 | -------------------------------------------------------------------------------- /frontend/components/ui/tooltip.tsx: -------------------------------------------------------------------------------- 1 | "use client" 2 | 3 | import * as React from "react" 4 | import * as TooltipPrimitive from "@radix-ui/react-tooltip" 5 | 6 | import { cn } from "@/lib/utils" 7 | 8 | const TooltipProvider = TooltipPrimitive.Provider 9 | 10 | const Tooltip = TooltipPrimitive.Root 11 | 12 | const TooltipTrigger = TooltipPrimitive.Trigger 13 | 14 | const TooltipContent = React.forwardRef< 15 | React.ElementRef, 16 | React.ComponentPropsWithoutRef 17 | >(({ className, sideOffset = 4, ...props }, ref) => ( 18 | 19 | 28 | 29 | )) 30 | TooltipContent.displayName = TooltipPrimitive.Content.displayName 31 | 32 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } 33 | -------------------------------------------------------------------------------- /frontend/enum/index.ts: -------------------------------------------------------------------------------- 1 | export const enum STATUS { 2 | ALL = "all", 3 | RISK = "risk", 4 | ON_TRACK = "onTrack", 5 | ON_HOLD = "onHold", 6 | POTENTIAL_RISK = "potentialRisk", 7 | ARCHIVED = "archived", 8 | } 9 | -------------------------------------------------------------------------------- /frontend/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { clsx, type ClassValue } from "clsx" 2 | import { twMerge } from "tailwind-merge" 3 | 4 | export function cn(...inputs: ClassValue[]) { 5 | return twMerge(clsx(inputs)) 6 | } 7 | -------------------------------------------------------------------------------- /frontend/next.config.ts: -------------------------------------------------------------------------------- 1 | import type { NextConfig } from "next"; 2 | 3 | const nextConfig: NextConfig = { 4 | /* config options here */ 5 | }; 6 | 7 | export default nextConfig; 8 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "data-table-v3", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "lint": "next lint" 10 | }, 11 | "dependencies": { 12 | "@radix-ui/react-avatar": "^1.1.1", 13 | "@radix-ui/react-checkbox": "^1.1.2", 14 | "@radix-ui/react-dropdown-menu": "^2.1.2", 15 | "@radix-ui/react-select": "^2.1.2", 16 | "@radix-ui/react-separator": "^1.1.0", 17 | "@radix-ui/react-slot": "^1.1.0", 18 | "@radix-ui/react-tooltip": "^1.1.4", 19 | "@tanstack/react-table": "^8.20.5", 20 | "class-variance-authority": "^0.7.0", 21 | "clsx": "^2.1.1", 22 | "lucide-react": "^0.460.0", 23 | "next": "15.0.3", 24 | "react": "19.0.0-rc-66855b96-20241106", 25 | "react-dom": "19.0.0-rc-66855b96-20241106", 26 | "tailwind-merge": "^2.5.5", 27 | "tailwindcss-animate": "^1.0.7" 28 | }, 29 | "devDependencies": { 30 | "@types/node": "^20", 31 | "@types/react": "^18", 32 | "@types/react-dom": "^18", 33 | "eslint": "^8", 34 | "eslint-config-next": "15.0.3", 35 | "postcss": "^8", 36 | "tailwindcss": "^3.4.1", 37 | "typescript": "^5" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /frontend/postcss.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('postcss-load-config').Config} */ 2 | const config = { 3 | plugins: { 4 | tailwindcss: {}, 5 | }, 6 | }; 7 | 8 | export default config; 9 | -------------------------------------------------------------------------------- /frontend/public/file.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/globe.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/next.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/vercel.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/public/window.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | 3 | export default { 4 | darkMode: ["class"], 5 | content: [ 6 | "./pages/**/*.{js,ts,jsx,tsx,mdx}", 7 | "./components/**/*.{js,ts,jsx,tsx,mdx}", 8 | "./app/**/*.{js,ts,jsx,tsx,mdx}", 9 | ], 10 | theme: { 11 | extend: { 12 | colors: { 13 | background: "hsl(var(--background))", 14 | foreground: "hsl(var(--foreground))", 15 | card: { 16 | DEFAULT: "hsl(var(--card))", 17 | foreground: "hsl(var(--card-foreground))", 18 | }, 19 | popover: { 20 | DEFAULT: "hsl(var(--popover))", 21 | foreground: "hsl(var(--popover-foreground))", 22 | }, 23 | primary: { 24 | DEFAULT: "hsl(var(--primary))", 25 | foreground: "hsl(var(--primary-foreground))", 26 | }, 27 | secondary: { 28 | DEFAULT: "hsl(var(--secondary))", 29 | foreground: "hsl(var(--secondary-foreground))", 30 | }, 31 | muted: { 32 | DEFAULT: "hsl(var(--muted))", 33 | foreground: "hsl(var(--muted-foreground))", 34 | }, 35 | accent: { 36 | DEFAULT: "hsl(var(--accent))", 37 | foreground: "hsl(var(--accent-foreground))", 38 | }, 39 | destructive: { 40 | DEFAULT: "hsl(var(--destructive))", 41 | foreground: "hsl(var(--destructive-foreground))", 42 | }, 43 | border: "hsl(var(--border))", 44 | input: "hsl(var(--input))", 45 | ring: "hsl(var(--ring))", 46 | chart: { 47 | "1": "hsl(var(--chart-1))", 48 | "2": "hsl(var(--chart-2))", 49 | "3": "hsl(var(--chart-3))", 50 | "4": "hsl(var(--chart-4))", 51 | "5": "hsl(var(--chart-5))", 52 | }, 53 | table: { 54 | headerBackground: "#F7F9FC", 55 | }, 56 | }, 57 | borderRadius: { 58 | lg: "var(--radius)", 59 | md: "calc(var(--radius) - 2px)", 60 | sm: "calc(var(--radius) - 4px)", 61 | }, 62 | }, 63 | }, 64 | plugins: [require("tailwindcss-animate")], 65 | } satisfies Config; 66 | -------------------------------------------------------------------------------- /frontend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2017", 4 | "lib": ["dom", "dom.iterable", "esnext"], 5 | "allowJs": true, 6 | "skipLibCheck": true, 7 | "strict": true, 8 | "noEmit": true, 9 | "esModuleInterop": true, 10 | "module": "esnext", 11 | "moduleResolution": "bundler", 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "jsx": "preserve", 15 | "incremental": true, 16 | "plugins": [ 17 | { 18 | "name": "next" 19 | } 20 | ], 21 | "paths": { 22 | "@/*": ["./*"] 23 | } 24 | }, 25 | "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], 26 | "exclude": ["node_modules"] 27 | } 28 | --------------------------------------------------------------------------------