diff --git a/README.md b/README.md index d5a42a595..4b4e1ff08 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,10 @@ -# [Shadcn Table](https://table.sadmn.com) +# [Resizable Shadcn Table](https://resizable-shadcn-table.vercel.app) + +Forked from [Shadcn Table](https://table.sadmn.com) This is a shadcn table with server-side sorting, filtering, and pagination. It is bootstrapped with `create-t3-app`. -[![Shadcn Table](./public/images/screenshot.png)](https://table.sadmn.com) +[![Resizable Shadcn Table](./public/images/screenshot.png)](https://table.sadmn.com) > **Warning** > This project is still in development and is not ready for production use. @@ -51,15 +53,35 @@ This is a shadcn table with server-side sorting, filtering, and pagination. It i 4. Start the development server ```bash - pnpm run dev + pnpm dev ``` 5. Push the database schema ```bash - pnpm run db:push + pnpm db:push + ``` + +6. Use drizzle studio to import CSV data + + ```bash + pnpm db:studio + ``` + +7. To import CSV data without deleting existing data, use the following command: + + ```bash + pnpm db:import-csv ``` +8. To import CSV data and delete existing data, use the following command: + + ```bash + pnpm db:import-csv:clean + ``` + +Both the above commands will automatically revalidate the next cache. If it fails to do so, restart the development server. By default, the data is imported from tasks.csv file in the root directory. (maybe I'll change it later to accept a file name as an argument) + ## Build your own Table 1. Copy the following folders and files into your project (configured with ) at the exact specific locations diff --git a/package.json b/package.json index 2637d17cb..f13bfb0f4 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "scripts": { "clean": "rimraf --glob **/node_modules **/dist **/.next pnpm-lock.yaml **/.tsbuildinfo", "build": "next build", - "dev": "next dev", + "dev": "next dev --turbopack", "start": "next start", "lint": "biome check .", "lint:fix": "biome check . --write", @@ -19,7 +19,9 @@ "db:migrate": "dotenv tsx src/db/migrate.ts", "db:drop-migration": "drizzle-kit drop", "db:seed": "dotenv tsx src/db/seed.ts", - "db:studio": "dotenv drizzle-kit studio" + "db:studio": "dotenv drizzle-kit studio", + "db:import-csv": "dotenv tsx src/scripts/import-csv.ts", + "db:import-csv:clean": "dotenv tsx src/scripts/import-csv.ts --delete-existing" }, "dependencies": { "@dnd-kit/core": "^6.3.1", @@ -44,6 +46,7 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "cmdk": "^1.0.4", + "csv-parse": "^5.6.0", "date-fns": "^4.1.0", "drizzle-orm": "^0.40.0", "export-to-csv": "^1.4.0", @@ -52,6 +55,7 @@ "nanoid": "^5.0.9", "next": "^15.1.6", "next-themes": "^0.4.4", + "node-fetch": "^3.3.2", "nuqs": "^2.3.1", "postgres": "^3.4.5", "react": "^19.0.0", @@ -84,6 +88,5 @@ }, "ct3aMetadata": { "initVersion": "7.23.1" - }, - "packageManager": "pnpm@9.15.4" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c114298ba..1eaa4f85c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -74,12 +74,15 @@ importers: cmdk: specifier: ^1.0.4 version: 1.0.4(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + csv-parse: + specifier: ^5.6.0 + version: 5.6.0 date-fns: specifier: ^4.1.0 version: 4.1.0 drizzle-orm: specifier: ^0.40.0 - version: 0.40.0(@libsql/client-wasm@0.14.0)(gel@2.0.0)(pg@8.13.3)(postgres@3.4.5) + version: 0.40.0(gel@2.0.0)(pg@8.13.3)(postgres@3.4.5) export-to-csv: specifier: ^1.4.0 version: 1.4.0 @@ -98,9 +101,12 @@ importers: next-themes: specifier: ^0.4.4 version: 0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + node-fetch: + specifier: ^3.3.2 + version: 3.3.2 nuqs: specifier: ^2.3.1 - version: 2.4.0(@remix-run/react@2.13.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.8.2))(next@15.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-router-dom@6.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-router@6.27.0(react@19.0.0))(react@19.0.0) + version: 2.4.0(next@15.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0) postgres: specifier: ^3.4.5 version: 3.4.5 @@ -146,7 +152,7 @@ importers: version: 0.6.1 '@types/node': specifier: ^22.12.0 - version: 22.13.7 + version: 22.13.8 '@types/react': specifier: ^19.0.8 version: 19.0.10 @@ -853,14 +859,6 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} - '@libsql/client-wasm@0.14.0': - resolution: {integrity: sha512-gB/jtz0xuwrqAHApBv9e9JSew2030Fhj2edyZ83InZ4yPj/Q2LTUlEhaspEYT0T0xsAGqPy38uGrmq/OGS+DdQ==} - bundledDependencies: - - '@libsql/libsql-wasm-experimental' - - '@libsql/core@0.14.0': - resolution: {integrity: sha512-nhbuXf7GP3PSZgdCY2Ecj8vz187ptHlZQ0VRc751oB2C1W8jQUXKKklvt7t1LJiUTQBVJuadF628eUk+3cRi4Q==} - '@next/env@15.2.0': resolution: {integrity: sha512-eMgJu1RBXxxqqnuRJQh5RozhskoNUDHBFybvi+Z+yK9qzKeG7dadhv/Vp1YooSZmCnegf7JxWuapV77necLZNA==} @@ -1151,19 +1149,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.0.1': - resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - '@radix-ui/react-primitive@2.0.2': resolution: {integrity: sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==} peerDependencies: @@ -1216,15 +1201,6 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slot@1.1.1': - resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@radix-ui/react-slot@1.1.2': resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==} peerDependencies: @@ -1352,30 +1328,6 @@ packages: '@radix-ui/rect@1.1.0': resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} - '@remix-run/react@2.13.1': - resolution: {integrity: sha512-kZevCoKMz0ZDOOzTnG95yfM7M9ju38FkWNY1wtxCy+NnUJYrmTerGQtiBsJgMzYD6i29+w4EwoQsdqys7DmMSg==} - engines: {node: '>=18.0.0'} - peerDependencies: - react: ^18.0.0 - react-dom: ^18.0.0 - typescript: ^5.1.0 - peerDependenciesMeta: - typescript: - optional: true - - '@remix-run/router@1.20.0': - resolution: {integrity: sha512-mUnk8rPJBI9loFDZ+YzPGdeniYK+FTmRD1TMCz7ev2SNIozyKKpnGgsxO34u6Z4z/t0ITuu7voi/AshfsGsgFg==} - engines: {node: '>=14.0.0'} - - '@remix-run/server-runtime@2.13.1': - resolution: {integrity: sha512-2DfBPRcHKVzE4bCNsNkKB50BhCCKF73x+jiS836OyxSIAL+x0tguV2AEjmGXefEXc5AGGzoxkus0AUUEYa29Vg==} - engines: {node: '>=18.0.0'} - peerDependencies: - typescript: ^5.1.0 - peerDependenciesMeta: - typescript: - optional: true - '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} @@ -1427,11 +1379,8 @@ packages: '@total-typescript/ts-reset@0.6.1': resolution: {integrity: sha512-cka47fVSo6lfQDIATYqb/vO1nvFfbPw7uWLayIXIhGETj0wcOOlrlkobOMDNQOFr9QOafegUPq13V2+6vtD7yg==} - '@types/cookie@0.6.0': - resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} - - '@types/node@22.13.7': - resolution: {integrity: sha512-oU2q+BsQldB9lYxHNp/5aZO+/Bs0Usa74Abo9mAKulz4ahQyXRHK6UVKYIN8KSC8HXwhWSi7b49JnX+txuac0w==} + '@types/node@22.13.8': + resolution: {integrity: sha512-G3EfaZS+iOGYWLLRCEAXdWK9my08oHNZ+FHluRiggIYJPOXzhOiDgpVCUHaUvyIC5/fj7C/p637jdzC666AOKQ==} '@types/react-dom@19.0.4': resolution: {integrity: sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==} @@ -1441,9 +1390,6 @@ packages: '@types/react@19.0.10': resolution: {integrity: sha512-JuRQ9KXLEjaUNjTWpzuR231Z2WpIwczOkBEIvbHNCzQefFIT0L8IqE6NV6ULLyC1SI/i234JnDoMkfg+RjQj2g==} - '@web3-storage/multipart-parser@1.0.0': - resolution: {integrity: sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==} - ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} @@ -1511,9 +1457,6 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001696: - resolution: {integrity: sha512-pDCPkvzfa39ehJtJ+OwGT/2yvT2SbjfHhiIW2LWOAcMQ7BzwxT/XuyUp4OTOd0XFWA6BKw0JalnBHgSi5DGJBQ==} - caniuse-lite@1.0.30001701: resolution: {integrity: sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==} @@ -1555,10 +1498,6 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - cookie@0.6.0: - resolution: {integrity: sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==} - engines: {node: '>= 0.6'} - cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1571,6 +1510,13 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csv-parse@5.6.0: + resolution: {integrity: sha512-l3nz3euub2QMg5ouu5U09Ew9Wf6/wQ8I++ch1loQ0ljmzhmfZYrH9fflS22i/PQEvsPvxCwxgz5q7UB8K1JO4Q==} + + data-uri-to-buffer@4.0.1: + resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} + engines: {node: '>= 12'} + date-fns@4.1.0: resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} @@ -1704,8 +1650,8 @@ packages: eastasianwidth@0.2.0: resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - electron-to-chromium@1.5.90: - resolution: {integrity: sha512-C3PN4aydfW91Natdyd449Kw+BzhLmof6tzy5W1pFC5SpQxVXT+oyiyOG9AgYYSN9OdA/ik3YkCrpwqI8ug5Tug==} + electron-to-chromium@1.5.109: + resolution: {integrity: sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} @@ -1749,17 +1695,25 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} - fastq@1.19.0: - resolution: {integrity: sha512-7SFSRCNjBQIZH/xZR3iy5iQYR8aGBE0h3VG6/cwlbrpdciNYBMotQav8c1XI3HjHH+NikUpP53nPdlZSdWmFzA==} + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fetch-blob@3.2.0: + resolution: {integrity: sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==} + engines: {node: ^12.20 || >= 14.13} fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} - foreground-child@3.3.0: - resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} engines: {node: '>=14'} + formdata-polyfill@4.0.10: + resolution: {integrity: sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==} + engines: {node: '>=12.20.0'} + fraction.js@4.3.7: resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} @@ -1846,17 +1800,14 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jackspeak@4.0.2: - resolution: {integrity: sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==} + jackspeak@4.1.0: + resolution: {integrity: sha512-9DDdhb5j6cpeitCbvLO7n7J4IxnbM6hoF6O1g4HQ5TfhvvKN8ywDM7668ZhMHRqVmxqhps/F6syWK2KcPxYlkw==} engines: {node: 20 || >=22} jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true - js-base64@3.7.7: - resolution: {integrity: sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==} - lilconfig@3.1.3: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} @@ -1945,6 +1896,14 @@ packages: sass: optional: true + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + + node-fetch@3.3.2: + resolution: {integrity: sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + node-releases@2.0.19: resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} @@ -2154,19 +2113,6 @@ packages: '@types/react': optional: true - react-router-dom@6.27.0: - resolution: {integrity: sha512-+bvtFWMC0DgAFrfKXKG9Fc+BcXWRUO1aJIihbB79xaeq0v5UzfvnM5houGUm1Y461WVRcgAQ+Clh5rdb1eCx4g==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' - react-dom: '>=16.8' - - react-router@6.27.0: - resolution: {integrity: sha512-YA+HGZXz4jaAkVoYBE98VQl+nVzI+cVI2Oj/06F5ZM+0u3TgedN9Y9kmMRo2mnkSK2nCpNQn0DVob4HCsY/WLw==} - engines: {node: '>=14.0.0'} - peerDependencies: - react: '>=16.8' - react-style-singleton@2.2.3: resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} engines: {node: '>=10'} @@ -2196,8 +2142,8 @@ packages: engines: {node: '>= 0.4'} hasBin: true - reusify@1.0.4: - resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rimraf@6.0.1: @@ -2219,9 +2165,6 @@ packages: server-only@0.0.1: resolution: {integrity: sha512-qepMx2JxAa5jjfzxG79yPPq+8BuFToHd1hm7kI+Z4zAq1ftQiP7HcxMhDDItrbtwVeLg/cY2JnKnrcFkmiswNA==} - set-cookie-parser@2.7.1: - resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} - sharp@0.33.5: resolution: {integrity: sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} @@ -2262,10 +2205,6 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - source-map@0.7.4: - resolution: {integrity: sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==} - engines: {node: '>= 8'} - split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} @@ -2347,9 +2286,6 @@ packages: engines: {node: '>=18.0.0'} hasBin: true - turbo-stream@2.4.0: - resolution: {integrity: sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==} - typescript@5.8.2: resolution: {integrity: sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==} engines: {node: '>=14.17'} @@ -2358,8 +2294,8 @@ packages: undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} - update-browserslist-db@1.1.2: - resolution: {integrity: sha512-PPypAm5qvlD7XMZC3BujecnaOxwhrtoFR+Dqkk5Aa/6DssiH0ibKoketaj9w8LP7Bont1rYeoV5plxD7RTEPRg==} + update-browserslist-db@1.1.3: + resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -2398,6 +2334,10 @@ packages: react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + web-streams-polyfill@3.3.3: + resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} + engines: {node: '>= 8'} + which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -2851,17 +2791,6 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 - '@libsql/client-wasm@0.14.0': - dependencies: - '@libsql/core': 0.14.0 - js-base64: 3.7.7 - optional: true - - '@libsql/core@0.14.0': - dependencies: - js-base64: 3.7.7 - optional: true - '@next/env@15.2.0': {} '@next/swc-darwin-arm64@15.2.0': @@ -2898,7 +2827,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.0 + fastq: 1.19.1 '@petamoriken/float16@3.9.1': {} @@ -3134,15 +3063,6 @@ snapshots: '@types/react': 19.0.10 '@types/react-dom': 19.0.4(@types/react@19.0.10) - '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.10 - '@types/react-dom': 19.0.4(@types/react@19.0.10) - '@radix-ui/react-primitive@2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': dependencies: '@radix-ui/react-slot': 1.1.2(@types/react@19.0.10)(react@19.0.0) @@ -3207,13 +3127,6 @@ snapshots: '@types/react': 19.0.10 '@types/react-dom': 19.0.4(@types/react@19.0.10) - '@radix-ui/react-slot@1.1.1(@types/react@19.0.10)(react@19.0.0)': - dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.10 - '@radix-ui/react-slot@1.1.2(@types/react@19.0.10)(react@19.0.0)': dependencies: '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.10)(react@19.0.0) @@ -3324,35 +3237,6 @@ snapshots: '@radix-ui/rect@1.1.0': {} - '@remix-run/react@2.13.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.8.2)': - dependencies: - '@remix-run/router': 1.20.0 - '@remix-run/server-runtime': 2.13.1(typescript@5.8.2) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-router: 6.27.0(react@19.0.0) - react-router-dom: 6.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - turbo-stream: 2.4.0 - optionalDependencies: - typescript: 5.8.2 - optional: true - - '@remix-run/router@1.20.0': - optional: true - - '@remix-run/server-runtime@2.13.1(typescript@5.8.2)': - dependencies: - '@remix-run/router': 1.20.0 - '@types/cookie': 0.6.0 - '@web3-storage/multipart-parser': 1.0.0 - cookie: 0.6.0 - set-cookie-parser: 2.7.1 - source-map: 0.7.4 - turbo-stream: 2.4.0 - optionalDependencies: - typescript: 5.8.2 - optional: true - '@standard-schema/utils@0.3.0': {} '@swc/counter@0.1.3': {} @@ -3383,10 +3267,7 @@ snapshots: '@total-typescript/ts-reset@0.6.1': {} - '@types/cookie@0.6.0': - optional: true - - '@types/node@22.13.7': + '@types/node@22.13.8': dependencies: undici-types: 6.20.0 @@ -3398,9 +3279,6 @@ snapshots: dependencies: csstype: 3.1.3 - '@web3-storage/multipart-parser@1.0.0': - optional: true - ansi-regex@5.0.1: {} ansi-regex@6.1.0: {} @@ -3427,7 +3305,7 @@ snapshots: autoprefixer@10.4.20(postcss@8.5.3): dependencies: browserslist: 4.24.4 - caniuse-lite: 1.0.30001696 + caniuse-lite: 1.0.30001701 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -3448,10 +3326,10 @@ snapshots: browserslist@4.24.4: dependencies: - caniuse-lite: 1.0.30001696 - electron-to-chromium: 1.5.90 + caniuse-lite: 1.0.30001701 + electron-to-chromium: 1.5.109 node-releases: 2.0.19 - update-browserslist-db: 1.1.2(browserslist@4.24.4) + update-browserslist-db: 1.1.3(browserslist@4.24.4) buffer-from@1.1.2: {} @@ -3461,8 +3339,6 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001696: {} - caniuse-lite@1.0.30001701: {} chokidar@3.6.0: @@ -3489,7 +3365,7 @@ snapshots: dependencies: '@radix-ui/react-dialog': 1.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) '@radix-ui/react-id': 1.1.0(@types/react@19.0.10)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) react: 19.0.0 react-dom: 19.0.0(react@19.0.0) use-sync-external-store: 1.4.0(react@19.0.0) @@ -3517,9 +3393,6 @@ snapshots: commander@4.1.1: {} - cookie@0.6.0: - optional: true - cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -3530,6 +3403,10 @@ snapshots: csstype@3.1.3: {} + csv-parse@5.6.0: {} + + data-uri-to-buffer@4.0.1: {} + date-fns@4.1.0: {} debug@4.4.0: @@ -3566,16 +3443,15 @@ snapshots: transitivePeerDependencies: - supports-color - drizzle-orm@0.40.0(@libsql/client-wasm@0.14.0)(gel@2.0.0)(pg@8.13.3)(postgres@3.4.5): + drizzle-orm@0.40.0(gel@2.0.0)(pg@8.13.3)(postgres@3.4.5): optionalDependencies: - '@libsql/client-wasm': 0.14.0 gel: 2.0.0 pg: 8.13.3 postgres: 3.4.5 eastasianwidth@0.2.0: {} - electron-to-chromium@1.5.90: {} + electron-to-chromium@1.5.109: {} emoji-regex@8.0.0: {} @@ -3681,19 +3557,28 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fastq@1.19.0: + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fetch-blob@3.2.0: dependencies: - reusify: 1.0.4 + node-domexception: 1.0.0 + web-streams-polyfill: 3.3.3 fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 - foreground-child@3.3.0: + foreground-child@3.3.1: dependencies: cross-spawn: 7.0.6 signal-exit: 4.1.0 + formdata-polyfill@4.0.10: + dependencies: + fetch-blob: 3.2.0 + fraction.js@4.3.7: {} fsevents@2.3.3: @@ -3732,7 +3617,7 @@ snapshots: glob@10.4.5: dependencies: - foreground-child: 3.3.0 + foreground-child: 3.3.1 jackspeak: 3.4.3 minimatch: 9.0.5 minipass: 7.1.2 @@ -3741,8 +3626,8 @@ snapshots: glob@11.0.1: dependencies: - foreground-child: 3.3.0 - jackspeak: 4.0.2 + foreground-child: 3.3.1 + jackspeak: 4.1.0 minimatch: 10.0.1 minipass: 7.1.2 package-json-from-dist: 1.0.1 @@ -3783,15 +3668,12 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jackspeak@4.0.2: + jackspeak@4.1.0: dependencies: '@isaacs/cliui': 8.0.2 jiti@1.21.7: {} - js-base64@3.7.7: - optional: true - lilconfig@3.1.3: {} lines-and-columns@1.2.4: {} @@ -3867,21 +3749,26 @@ snapshots: - '@babel/core' - babel-plugin-macros + node-domexception@1.0.0: {} + + node-fetch@3.3.2: + dependencies: + data-uri-to-buffer: 4.0.1 + fetch-blob: 3.2.0 + formdata-polyfill: 4.0.10 + node-releases@2.0.19: {} normalize-path@3.0.0: {} normalize-range@0.1.2: {} - nuqs@2.4.0(@remix-run/react@2.13.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.8.2))(next@15.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-router-dom@6.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react-router@6.27.0(react@19.0.0))(react@19.0.0): + nuqs@2.4.0(next@15.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(react@19.0.0): dependencies: mitt: 3.0.1 react: 19.0.0 optionalDependencies: - '@remix-run/react': 2.13.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(typescript@5.8.2) next: 15.2.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react-router: 6.27.0(react@19.0.0) - react-router-dom: 6.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) object-assign@4.1.1: {} @@ -4036,20 +3923,6 @@ snapshots: optionalDependencies: '@types/react': 19.0.10 - react-router-dom@6.27.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - '@remix-run/router': 1.20.0 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-router: 6.27.0(react@19.0.0) - optional: true - - react-router@6.27.0(react@19.0.0): - dependencies: - '@remix-run/router': 1.20.0 - react: 19.0.0 - optional: true - react-style-singleton@2.2.3(@types/react@19.0.10)(react@19.0.0): dependencies: get-nonce: 1.0.1 @@ -4076,7 +3949,7 @@ snapshots: path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - reusify@1.0.4: {} + reusify@1.1.0: {} rimraf@6.0.1: dependencies: @@ -4093,9 +3966,6 @@ snapshots: server-only@0.0.1: {} - set-cookie-parser@2.7.1: - optional: true - sharp@0.33.5: dependencies: color: 4.2.3 @@ -4152,9 +4022,6 @@ snapshots: source-map@0.6.1: {} - source-map@0.7.4: - optional: true - split2@4.2.0: {} streamsearch@1.1.0: {} @@ -4252,14 +4119,11 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - turbo-stream@2.4.0: - optional: true - typescript@5.8.2: {} undici-types@6.20.0: {} - update-browserslist-db@1.1.2(browserslist@4.24.4): + update-browserslist-db@1.1.3(browserslist@4.24.4): dependencies: browserslist: 4.24.4 escalade: 3.2.0 @@ -4295,6 +4159,8 @@ snapshots: - '@types/react' - '@types/react-dom' + web-streams-polyfill@3.3.3: {} + which@2.0.2: dependencies: isexe: 2.0.0 diff --git a/src/app/_components/tasks-table-columns.tsx b/src/app/_components/tasks-table-columns.tsx index 05a427e80..87f7e58a6 100644 --- a/src/app/_components/tasks-table-columns.tsx +++ b/src/app/_components/tasks-table-columns.tsx @@ -42,51 +42,67 @@ export function getColumns({ return [ { id: "select", + size: 60, + minSize: 40, + maxSize: 80, header: ({ table }) => ( - table.toggleAllPageRowsSelected(!!value)} - aria-label="Select all" - className="translate-y-0.5" - /> +
+ + table.toggleAllPageRowsSelected(!!value) + } + aria-label="Select all" + className="translate-y-0.5" + /> +
), cell: ({ row }) => ( - row.toggleSelected(!!value)} - aria-label="Select row" - className="translate-y-0.5" - /> +
+ row.toggleSelected(!!value)} + aria-label="Select row" + className="translate-y-0.5" + /> +
), enableSorting: false, enableHiding: false, + enableResizing: false, }, { accessorKey: "code", + size: 100, + minSize: 68, + maxSize: 1600, header: ({ column }) => ( ), - cell: ({ row }) =>
{row.getValue("code")}
, + cell: ({ row }) =>
{row.getValue("code")}
, enableSorting: false, enableHiding: false, }, { accessorKey: "title", + size: 380, + minSize: 240, + maxSize: 2400, header: ({ column }) => ( ), cell: ({ row }) => { const label = tasks.label.enumValues.find( - (label) => label === row.original.label, + (label) => label === row.original.label ); return (
{label && {label}} - + {row.getValue("title")}
@@ -95,12 +111,15 @@ export function getColumns({ }, { accessorKey: "status", + size: 106, + minSize: 106, + maxSize: 400, header: ({ column }) => ( ), cell: ({ row }) => { const status = tasks.status.enumValues.find( - (status) => status === row.original.status, + (status) => status === row.original.status ); if (!status) return null; @@ -108,12 +127,12 @@ export function getColumns({ const Icon = getStatusIcon(status); return ( -
+
); }, @@ -123,12 +142,15 @@ export function getColumns({ }, { accessorKey: "priority", + size: 111, + minSize: 111, + maxSize: 400, header: ({ column }) => ( ), cell: ({ row }) => { const priority = tasks.priority.enumValues.find( - (priority) => priority === row.original.priority, + (priority) => priority === row.original.priority ); if (!priority) return null; @@ -138,10 +160,10 @@ export function getColumns({ return (
); }, @@ -151,22 +173,35 @@ export function getColumns({ }, { accessorKey: "archived", + size: 119, + minSize: 119, + maxSize: 400, header: ({ column }) => ( ), cell: ({ row }) => ( - {row.original.archived ? "Yes" : "No"} + + {row.original.archived ? "Yes" : "No"} + ), }, { accessorKey: "createdAt", + size: 130, + minSize: 130, + maxSize: 800, header: ({ column }) => ( ), - cell: ({ cell }) => formatDate(cell.getValue() as Date), + cell: ({ cell }) => ( +
{formatDate(cell.getValue() as Date)}
+ ), }, { id: "actions", + size: 60, + minSize: 40, + maxSize: 1200, cell: function Cell({ row }) { const [isUpdatePending, startUpdateTransition] = React.useTransition(); @@ -203,7 +238,7 @@ export function getColumns({ loading: "Updating...", success: "Label updated", error: (err) => getErrorMessage(err), - }, + } ); }); }} @@ -232,7 +267,7 @@ export function getColumns({ ); }, - size: 40, + enableResizing: false, }, ]; } diff --git a/src/app/_components/tasks-table.tsx b/src/app/_components/tasks-table.tsx index 0eb0289ea..b1ba18b60 100644 --- a/src/app/_components/tasks-table.tsx +++ b/src/app/_components/tasks-table.tsx @@ -26,6 +26,7 @@ import { getColumns } from "./tasks-table-columns"; import { TasksTableFloatingBar } from "./tasks-table-floating-bar"; import { TasksTableToolbarActions } from "./tasks-table-toolbar-actions"; import { UpdateTaskSheet } from "./update-task-sheet"; +import DebugPanel from "@/components/debug-panel"; interface TasksTableProps { promises: Promise< @@ -134,16 +135,25 @@ export function TasksTable({ promises }: TasksTableProps) { const enableAdvancedTable = featureFlags.includes("advancedTable"); const enableFloatingBar = featureFlags.includes("floatingBar"); + // Enable resizing if the "resizableColumns" feature flag is set + const enableResizing = featureFlags.includes("resizableColumns"); - const { table } = useDataTable({ + const { table, tableContainerRef } = useDataTable({ data, columns, pageCount, filterFields, enableAdvancedFilter: enableAdvancedTable, + // Enable column resizing + enableResizing, + // Default column sizing constraints + defaultColumn: { + minSize: 60, + maxSize: 800, + }, initialState: { sorting: [{ id: "createdAt", desc: true }], - columnPinning: { right: ["actions"] }, + // columnPinning: { left: ["actions"] }, }, getRowId: (originalRow) => originalRow.id, shallow: false, @@ -154,6 +164,9 @@ export function TasksTable({ promises }: TasksTableProps) { <> : null } diff --git a/src/app/api/revalidate/route.ts b/src/app/api/revalidate/route.ts new file mode 100644 index 000000000..bfbb58209 --- /dev/null +++ b/src/app/api/revalidate/route.ts @@ -0,0 +1,27 @@ +import { revalidateTag } from "next/cache"; +import { NextRequest, NextResponse } from "next/server"; + +export async function POST(request: NextRequest) { + const tag = request.nextUrl.searchParams.get("tag"); + + if (!tag) { + return NextResponse.json( + { message: "Missing tag parameter" }, + { status: 400 } + ); + } + + try { + revalidateTag(tag); + return NextResponse.json({ + revalidated: true, + now: Date.now(), + tag, + }); + } catch (error) { + return NextResponse.json( + { message: "Error revalidating", error }, + { status: 500 } + ); + } +} diff --git a/src/components/data-table/data-table-column-header.tsx b/src/components/data-table/data-table-column-header.tsx index ba5564d17..1d734633b 100644 --- a/src/components/data-table/data-table-column-header.tsx +++ b/src/components/data-table/data-table-column-header.tsx @@ -32,7 +32,7 @@ export function DataTableColumnHeader({ const hideValue = `${column.id}-hide`; return ( -
+