diff --git a/README.md b/README.md index 1eb0d71..ce959fe 100644 --- a/README.md +++ b/README.md @@ -7,9 +7,15 @@ QueryMindAI is an open-source AI data workspace that lets users connect a read-o QueryMindAI is an early production-minded release. Implemented today: encrypted saved PostgreSQL connections, public-host SSRF checks, SSL connections, catalog snapshots, structured SQL generation, explicit approval drafts, parser/table validation, read-only execution, verified examples, history, audit events, Docker Compose, and Render configuration. Signed anonymous workspace sessions provide ownership isolation for the public demo; account login and enterprise identity integration remain roadmap work. -## Screenshots +## Product workflow -![AI Query Assistant placeholder](docs/screenshots/ai-query-assistant-placeholder.svg) +The web app contains only three API-backed workspaces: + +1. **Connections** — test and save an encrypted, read-only PostgreSQL connection; inspect and refresh its live schema catalog. +2. **Query** — ask a business question, generate structured SQL, and review the SQL, explanation, assumptions, and warnings before explicitly running it. +3. **History** — review real approved executions and their metadata. Query result rows are not persisted. + +No dashboard metrics, schemas, connections, query results, or history records are fabricated in the UI. ## Core capabilities and differentiators @@ -49,7 +55,7 @@ FastAPI, SQLAlchemy, Alembic, PostgreSQL, sqlglot, OpenAI-compatible provider AP ```text apps/api FastAPI service, migrations, scripts, tests apps/web Next.js application -docs Architecture, deployment, and security guidance +docs Architecture, local setup, deployment, and security guidance evaluations Small deterministic dataset and runner scripts Operator helpers .github CI and issue templates @@ -80,6 +86,8 @@ npm run dev Open `http://localhost:4028`. +For secret generation, read-only role SQL, local-database connectivity, and troubleshooting, follow the complete [local open-source setup guide](docs/local-development.md). + ## Docker quickstart The default stack runs PostgreSQL, API, and web. Because no LLM is bundled by default, configure a reachable remote provider or use the Ollama profile. diff --git a/apps/web/README.md b/apps/web/README.md index b4c0b6c..536afff 100644 --- a/apps/web/README.md +++ b/apps/web/README.md @@ -2,9 +2,17 @@ Next.js 15, React 19, TypeScript, and Tailwind UI for QueryMindAI. -## Architecture and implemented pages +## Product routes -`src/app` contains routes, `src/lib/api.ts` is the typed authenticated fetch boundary, and `src/types` contains contracts. Data Connections creates/tests/deletes encrypted PostgreSQL connections; Schema Explorer renders normalized live catalog snapshots; AI Query Workspace separates generation from explicit execution; Query History lists approved executions without stored rows. The client stores a signed anonymous workspace token in browser local storage; this is ownership isolation, not account login. +The frontend intentionally has three product routes: + +- `/connections` creates, tests, lists, refreshes, and deletes encrypted PostgreSQL connections. Its expandable Schema Catalog renders live introspection data. +- `/query` generates validated SQL and requires a separate **Run query** approval before execution. It renders real results and can save a successful execution as a verified example. +- `/history` lists real approved executions and their metadata. Result rows are not persisted. + +Old generated dashboard, login, browser, editor, and history URLs redirect to these routes. There are no fake metrics, login forms, connection records, schemas, results, or history entries. + +`src/features` owns workflow UI, `src/lib/api.ts` is the typed authenticated fetch boundary, and `src/types` contains API contracts. The client stores a signed anonymous workspace token in browser local storage; this is ownership isolation, not account login. ## Configuration and API integration @@ -29,4 +37,6 @@ The multi-stage Dockerfile builds a standalone, non-root image. Pass `NEXT_PUBLI ## UI conventions -Use AI Query Assistant, Generated SQL, Verified Example, Schema Explorer, Query History, and Data Connections. Label every non-API data source `Demo data` or `Coming soon`; never describe stored examples as model training. +Use Connections, AI Query Assistant, Generated SQL, Verified Example, Schema Catalog, and Query History. Product data must come from the API. Do not add sample metrics or fabricated operational records. Never describe stored examples as model training. + +For the complete repository setup, database role, local-database caveat, and troubleshooting steps, read [Run QueryMindAI locally](../../docs/local-development.md). diff --git a/apps/web/image-hosts.config.mjs b/apps/web/image-hosts.config.mjs deleted file mode 100644 index 3a3b026..0000000 --- a/apps/web/image-hosts.config.mjs +++ /dev/null @@ -1,22 +0,0 @@ -/** - * Image Hosts Configuration (add your image hosts here) - */ - -export const imageHosts = [ - { - protocol: 'https', - hostname: 'images.unsplash.com', - }, - { - protocol: 'https', - hostname: 'images.pexels.com', - }, - { - protocol: 'https', - hostname: 'images.pixabay.com', - }, - { - protocol: 'https', - hostname: 'img.rocket.new', - }, -]; diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs index 3993820..bd4ac77 100644 --- a/apps/web/next.config.mjs +++ b/apps/web/next.config.mjs @@ -1,11 +1,15 @@ -import { imageHosts } from './image-hosts.config.mjs'; - /** @type {import('next').NextConfig} */ const nextConfig = { output: 'standalone', - images: { remotePatterns: imageHosts }, async redirects() { - return [{ source: '/', destination: '/sql-editor-and-ai-assistant', permanent: false }]; + return [ + { source: '/', destination: '/connections', permanent: false }, + { source: '/dashboard-and-connection', destination: '/connections', permanent: true }, + { source: '/database-browser', destination: '/connections', permanent: true }, + { source: '/sql-editor-and-ai-assistant', destination: '/query', permanent: true }, + { source: '/query-history', destination: '/history', permanent: true }, + { source: '/homepage-login', destination: '/connections', permanent: true }, + ]; }, }; diff --git a/apps/web/package-lock.json b/apps/web/package-lock.json index e78c251..864dcf2 100644 --- a/apps/web/package-lock.json +++ b/apps/web/package-lock.json @@ -8,16 +8,10 @@ "name": "querymindai", "version": "0.1.0", "dependencies": { - "@heroicons/react": "^2.2.0", - "@tailwindcss/forms": "^0.5.10", - "@tailwindcss/typography": "^0.5.16", "lucide-react": "^1.7.0", - "next": "^15.5.14", + "next": "15.5.23", "react": "19.0.3", - "react-dom": "19.0.3", - "react-hook-form": "^7.72.0", - "recharts": "^2.15.2", - "sonner": "^1.7.1" + "react-dom": "19.0.3" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -28,13 +22,12 @@ "@typescript-eslint/parser": "^8.29.0", "autoprefixer": "10.4.2", "eslint": "^9", - "eslint-config-next": "^15.2.4", + "eslint-config-next": "15.5.23", "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^5.2.6", - "postcss": "^8.5.8", + "postcss": "8.5.26", "prettier": "^3.5.3", "tailwindcss": "3.4.6", - "tailwindcss-animate": "^1.0.7", "typescript": "^5" } }, @@ -42,6 +35,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -50,15 +44,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@babel/runtime": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", - "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, "node_modules/@emnapi/core": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.1.tgz", @@ -223,15 +208,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@heroicons/react": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz", - "integrity": "sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==", - "license": "MIT", - "peerDependencies": { - "react": ">= 16 || ^19.0.0-rc" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -754,6 +730,7 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -764,6 +741,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -773,12 +751,14 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -799,15 +779,15 @@ } }, "node_modules/@next/env": { - "version": "15.5.20", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.20.tgz", - "integrity": "sha512-dXh51Wvddf8daEyBXryZZEe1FdVxEWx9lgaTseLZUtC1XP/W8Wri+Z+VPOElHlByk23CyqHdc2oVByX7wsTWsw==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.23.tgz", + "integrity": "sha512-Mv3Z9hVbFcPnoLevsZ6rnX1TBtyHb5E17yN7HTPDXSXxeNsGBjUFrdbjRXKKXIOhfth7/cg6Ay7PZ2UFawaWsQ==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.14.tgz", - "integrity": "sha512-ogBjgsFrPPz19abP3VwcYSahbkUOMMvJjxCOYWYndw+PydeMuLuB4XrvNkNutFrTjC9St2KFULRdKID8Sd/CMQ==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.23.tgz", + "integrity": "sha512-0KnCFpiWVIsbwBhByZ0uIcjYM5xqGrFzN2eOPbwru/wuy5Z1dmA+3gP+PRbi4gl1Ny7an66BqAM/NHkX/50rbw==", "dev": true, "license": "MIT", "dependencies": { @@ -845,9 +825,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.20", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.20.tgz", - "integrity": "sha512-in0yXG7/pRBVjWeEl7f7ZZETpletSMFKXVS4GJgHENTPVrJFNJKPrYewa9rpZcvdjwFece5fZP0CK34G4PxowA==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.23.tgz", + "integrity": "sha512-SrEwOROH/rhA03F59hHtdhgtfZMWGzr5duDBWgRQt2rS3mJhqMKOcnNx6txOd0/i3E3D3uFKYFvyHsEiwQxzag==", "cpu": [ "arm64" ], @@ -861,9 +841,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.20", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.20.tgz", - "integrity": "sha512-0hsFshdPnTzGJdDTHeHJ+XPUShOpnyp9pUFDwDhqctsA0Cd8NcIVGRPtptYhgYY9DjkKgCDRkXxmgRc+CgT5Wg==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.23.tgz", + "integrity": "sha512-f0FpFbG2EhDCuptBGcfrLcYMDuQAhe6m1QA4VVfXFrIBoFXvXt/olGbBkYkloKlXQtmhuzvtdYyuu/6zf07GIg==", "cpu": [ "x64" ], @@ -877,9 +857,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.20", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.20.tgz", - "integrity": "sha512-DMvkoBtAABOzE6pMZRW/xNm7sKqql3wzzzZJ1R/d/rp4BCxv6LykouD3tHjGY8WdQqGpZs11t+R9AtjPxvvljw==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.23.tgz", + "integrity": "sha512-WlNtfepUXKX2u2ZsJZ8c3c8+tJSRZqsYzoMwLOY72A8ucKCCgxgNhiePA3qzFYahVWrwcQd8jOeJmBinc+VFVQ==", "cpu": [ "arm64" ], @@ -896,9 +876,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.20", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.20.tgz", - "integrity": "sha512-RQmDfeYBtXV2FSId7dfA1hE6M/T6+g7wdbYnFQ47tw/gUBwV+CccLVejNmCGa9yLDitk83foeg8hl/3DjfYQ5g==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.23.tgz", + "integrity": "sha512-W/6qKk7UG93mg14PmQC+2urt69MIdwTBLNQ6MJyeC4wOCIHCjz+VfgssvS1pK7mgYBtLC1g6VKNoHD9xB0WWGg==", "cpu": [ "arm64" ], @@ -915,9 +895,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.20", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.20.tgz", - "integrity": "sha512-DkWLEdKajJwdGt27M3i1VEO2kelTvZrK6Pcb7JvW2BY+nofWm7FBsBNDj7g7Pr1NuQ5PLJvqEqYa20GTsBDnKQ==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.23.tgz", + "integrity": "sha512-vzefI32mi6VMk96RaTAyxApgfGbiFzQBXVsekEjsDv1fr48mlABTWx0sUYhaYCBHWqCalxmz3DxbxFcbFvzNtw==", "cpu": [ "x64" ], @@ -934,9 +914,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.20", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.20.tgz", - "integrity": "sha512-rAO5b7pKHvX+ExdmJskusDXTNbiNZfptifIPZItbUx+AOXxxTydVBsPt7Oz84DRd5mY8e0DcE8kvLj3AIfjE6w==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.23.tgz", + "integrity": "sha512-qppK/3dTGOTI+aoWWBZc3DshFIhrzgL8guATlaN9V6M1QJxbkP/rhEZ22tdICsQ/2WWXopMZ2Jokzj2u3uKY3Q==", "cpu": [ "x64" ], @@ -953,9 +933,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.20", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.20.tgz", - "integrity": "sha512-Hp3zFsN8N8Kj9+vY6L4vnZ9EtA9eXyATu0q4EfGbZTiocgPUNSfz8NWhym6xvaOmHpJ8EuoypuU1WejCPsTFtg==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.23.tgz", + "integrity": "sha512-Wc29KFOdT7XBcII3Vtmw7aoU8Uk3Mes/FNJfhFeSHdYBFJWMcR/DsI8U9BCPUhq/uycsUVuqSKGthW15tLsigA==", "cpu": [ "arm64" ], @@ -969,9 +949,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.20", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.20.tgz", - "integrity": "sha512-T/L7CXpR1M0wij/xbF3rT1+7KvSkfOLr7C+ToHHWZTG2eKmb52C5WvsyGCBNtkVvDEUESWkRUbbqSH4rSbOCYQ==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.23.tgz", + "integrity": "sha512-/C7wRW4fa9s/PKA18zGPPpVmx8ycgVpP8yOxro4gzGTzjPJdscbAP3ODeFvgiIovxD176Z2J/SXO9t8PJKHLeQ==", "cpu": [ "x64" ], @@ -988,6 +968,7 @@ "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -1001,6 +982,7 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -1010,6 +992,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -1065,30 +1048,6 @@ "tslib": "^2.8.0" } }, - "node_modules/@tailwindcss/forms": { - "version": "0.5.11", - "resolved": "https://registry.npmjs.org/@tailwindcss/forms/-/forms-0.5.11.tgz", - "integrity": "sha512-h9wegbZDPurxG22xZSoWtdzc41/OlNEUQERNqI/0fOwa2aVlWGu7C35E/x6LDyD3lgtztFSSjKZyuVM0hxhbgA==", - "license": "MIT", - "dependencies": { - "mini-svg-data-uri": "^1.2.3" - }, - "peerDependencies": { - "tailwindcss": ">=3.0.0 || >= 3.0.0-alpha.1 || >= 4.0.0-alpha.20 || >= 4.0.0-beta.1" - } - }, - "node_modules/@tailwindcss/typography": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", - "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "6.0.10" - }, - "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" - } - }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", @@ -1100,69 +1059,6 @@ "tslib": "^2.4.0" } }, - "node_modules/@types/d3-array": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz", - "integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==", - "license": "MIT" - }, - "node_modules/@types/d3-color": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz", - "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==", - "license": "MIT" - }, - "node_modules/@types/d3-ease": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz", - "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==", - "license": "MIT" - }, - "node_modules/@types/d3-interpolate": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz", - "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==", - "license": "MIT", - "dependencies": { - "@types/d3-color": "*" - } - }, - "node_modules/@types/d3-path": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", - "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", - "license": "MIT" - }, - "node_modules/@types/d3-scale": { - "version": "4.0.9", - "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz", - "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==", - "license": "MIT", - "dependencies": { - "@types/d3-time": "*" - } - }, - "node_modules/@types/d3-shape": { - "version": "3.1.8", - "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz", - "integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==", - "license": "MIT", - "dependencies": { - "@types/d3-path": "*" - } - }, - "node_modules/@types/d3-time": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz", - "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==", - "license": "MIT" - }, - "node_modules/@types/d3-timer": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz", - "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", - "license": "MIT" - }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -1838,12 +1734,14 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -1857,6 +1755,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -1869,6 +1768,7 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, "license": "MIT" }, "node_modules/argparse": { @@ -2153,6 +2053,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -2176,6 +2077,7 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -2282,6 +2184,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -2311,6 +2214,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -2335,6 +2239,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -2349,15 +2254,6 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, - "node_modules/clsx": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", - "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2382,6 +2278,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -2413,6 +2310,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, "license": "MIT", "bin": { "cssesc": "bin/cssesc" @@ -2425,129 +2323,9 @@ "version": "3.2.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, "license": "MIT" }, - "node_modules/d3-array": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", - "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", - "license": "ISC", - "dependencies": { - "internmap": "1 - 2" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-color": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", - "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-ease": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", - "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", - "license": "BSD-3-Clause", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-format": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz", - "integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-interpolate": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", - "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", - "license": "ISC", - "dependencies": { - "d3-color": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-path": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", - "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-scale": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", - "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", - "license": "ISC", - "dependencies": { - "d3-array": "2.10.0 - 3", - "d3-format": "1 - 3", - "d3-interpolate": "1.2.0 - 3", - "d3-time": "2.1.1 - 3", - "d3-time-format": "2 - 4" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-shape": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", - "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", - "license": "ISC", - "dependencies": { - "d3-path": "^3.1.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", - "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", - "license": "ISC", - "dependencies": { - "d3-array": "2 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-time-format": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", - "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", - "license": "ISC", - "dependencies": { - "d3-time": "1 - 3" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/d3-timer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", - "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -2627,12 +2405,6 @@ } } }, - "node_modules/decimal.js-light": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", - "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", - "license": "MIT" - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2690,12 +2462,14 @@ "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, "license": "Apache-2.0" }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, "license": "MIT" }, "node_modules/doctrine": { @@ -2711,16 +2485,6 @@ "node": ">=0.10.0" } }, - "node_modules/dom-helpers": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", - "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" - } - }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -3012,13 +2776,13 @@ } }, "node_modules/eslint-config-next": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.14.tgz", - "integrity": "sha512-lmJ5F8ZgOYogq0qtH4L5SpxuASY2SPdOzqUprN2/56+P3GPsIpXaUWIJC66kYIH+yZdsM4nkHE5MIBP6s1NiBw==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.23.tgz", + "integrity": "sha512-z4WcTXNqFHwMG4V8WHb2xrlEPJvwarZa+H/6CR28vxr53icRnQzGXviO11p748BwrMZGl52itdPRzZfzYo0SKw==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.5.14", + "@next/eslint-plugin-next": "15.5.23", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", @@ -3492,12 +3256,6 @@ "node": ">=0.10.0" } }, - "node_modules/eventemitter3": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", - "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", - "license": "MIT" - }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -3512,19 +3270,11 @@ "dev": true, "license": "Apache-2.0" }, - "node_modules/fast-equals": { - "version": "5.4.0", - "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", - "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -3541,6 +3291,7 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -3567,6 +3318,7 @@ "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -3576,6 +3328,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -3593,6 +3346,7 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -3659,6 +3413,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -3673,6 +3428,7 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3793,6 +3549,7 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -3929,6 +3686,7 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3989,15 +3747,6 @@ "node": ">= 0.4" } }, - "node_modules/internmap": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", - "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -4056,6 +3805,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -4121,6 +3871,7 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -4171,6 +3922,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4216,6 +3968,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -4254,6 +4007,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -4457,6 +4211,7 @@ "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, "license": "MIT", "bin": { "jiti": "bin/jiti.js" @@ -4466,6 +4221,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -4576,6 +4332,7 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -4585,6 +4342,7 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, "license": "MIT" }, "node_modules/locate-path": { @@ -4603,12 +4361,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash": { - "version": "4.18.1", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", - "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -4620,6 +4372,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -4651,6 +4404,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -4660,6 +4414,7 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -4673,6 +4428,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -4681,15 +4437,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/mini-svg-data-uri": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/mini-svg-data-uri/-/mini-svg-data-uri-1.4.4.tgz", - "integrity": "sha512-r9deDe9p5FJUPZAk3A59wGH7Ii9YrjjWw0jmw/liSbHl2CHiyXj6FcDXDu2K3TjVAXqiJdaw3xxwlZZr9E6nHg==", - "license": "MIT", - "bin": { - "mini-svg-data-uri": "cli.js" - } - }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", @@ -4724,6 +4471,7 @@ "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0", @@ -4732,9 +4480,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.15", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz", - "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "funding": [ { "type": "github", @@ -4773,12 +4521,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.5.20", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.20.tgz", - "integrity": "sha512-cvyS3/geydan1xLtE3FA8VCgdoQ/Gg/dlOldFkFCbB5VcVYJV7090hQLBnvTW2PwT76Z/dHdzDZCsVhZpoOlUA==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.23.tgz", + "integrity": "sha512-Gvd2WKgvxIXCGotxcI1im/Uf3rS3J3oZGw0g/uskg6AVBZhyE3aAbujkYWzS3xLmEPEtTLfkaVQUKK0KMTSIkA==", "license": "MIT", "dependencies": { - "@next/env": "15.5.20", + "@next/env": "15.5.23", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -4791,14 +4539,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.20", - "@next/swc-darwin-x64": "15.5.20", - "@next/swc-linux-arm64-gnu": "15.5.20", - "@next/swc-linux-arm64-musl": "15.5.20", - "@next/swc-linux-x64-gnu": "15.5.20", - "@next/swc-linux-x64-musl": "15.5.20", - "@next/swc-win32-arm64-msvc": "15.5.20", - "@next/swc-win32-x64-msvc": "15.5.20", + "@next/swc-darwin-arm64": "15.5.23", + "@next/swc-darwin-x64": "15.5.23", + "@next/swc-linux-arm64-gnu": "15.5.23", + "@next/swc-linux-arm64-musl": "15.5.23", + "@next/swc-linux-x64-gnu": "15.5.23", + "@next/swc-linux-x64-musl": "15.5.23", + "@next/swc-win32-arm64-msvc": "15.5.23", + "@next/swc-win32-x64-msvc": "15.5.23", "sharp": "^0.34.3" }, "peerDependencies": { @@ -4882,6 +4630,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4901,6 +4650,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4910,6 +4660,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -5133,6 +4884,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, "license": "MIT" }, "node_modules/picocolors": { @@ -5145,6 +4897,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -5157,6 +4910,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -5166,6 +4920,7 @@ "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -5182,9 +4937,10 @@ } }, "node_modules/postcss": { - "version": "8.5.16", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz", - "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -5201,7 +4957,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.12", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -5213,6 +4969,7 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, "license": "MIT", "dependencies": { "postcss-value-parser": "^4.0.0", @@ -5230,6 +4987,7 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -5255,19 +5013,7 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.0.10", - "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", - "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -5281,6 +5027,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, "license": "MIT" }, "node_modules/prelude-ls": { @@ -5326,6 +5073,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -5347,6 +5095,7 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, "funding": [ { "type": "github", @@ -5384,63 +5133,18 @@ "react": "^19.0.3" } }, - "node_modules/react-hook-form": { - "version": "7.72.0", - "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.72.0.tgz", - "integrity": "sha512-V4v6jubaf6JAurEaVnT9aUPKFbNtDgohj5CIgVGyPHvT9wRx5OZHVjz31GsxnPNI278XMu+ruFz+wGOscHaLKw==", - "license": "MIT", - "engines": { - "node": ">=18.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/react-hook-form" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17 || ^18 || ^19" - } - }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "dev": true, "license": "MIT" }, - "node_modules/react-smooth": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/react-smooth/-/react-smooth-4.0.4.tgz", - "integrity": "sha512-gnGKTpYwqL0Iii09gHobNolvX4Kiq4PKx6eWBCYYix+8cdw+cGo3do906l1NBPKkSWx1DghC1dlWG9L2uGd61Q==", - "license": "MIT", - "dependencies": { - "fast-equals": "^5.0.1", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-transition-group": { - "version": "4.4.5", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", - "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", - "license": "BSD-3-Clause", - "dependencies": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" - }, - "peerDependencies": { - "react": ">=16.6.0", - "react-dom": ">=16.6.0" - } - }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, "license": "MIT", "dependencies": { "pify": "^2.3.0" @@ -5450,6 +5154,7 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -5462,6 +5167,7 @@ "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -5470,44 +5176,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/recharts": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.15.4.tgz", - "integrity": "sha512-UT/q6fwS3c1dHbXv2uFgYJ9BMFHu3fwnd7AYZaEQhXuYQ4hgsxLvsUXzGdKeZrW5xopzDCvuA2N41WJ88I7zIw==", - "license": "MIT", - "dependencies": { - "clsx": "^2.0.0", - "eventemitter3": "^4.0.1", - "lodash": "^4.17.21", - "react-is": "^18.3.1", - "react-smooth": "^4.0.4", - "recharts-scale": "^0.4.4", - "tiny-invariant": "^1.3.1", - "victory-vendor": "^36.6.8" - }, - "engines": { - "node": ">=14" - }, - "peerDependencies": { - "react": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/recharts-scale": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/recharts-scale/-/recharts-scale-0.4.5.tgz", - "integrity": "sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==", - "license": "MIT", - "dependencies": { - "decimal.js-light": "^2.4.1" - } - }, - "node_modules/recharts/node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -5556,6 +5224,7 @@ "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.1", @@ -5596,6 +5265,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", @@ -5606,6 +5276,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, "funding": [ { "type": "github", @@ -5902,16 +5573,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sonner": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.4.tgz", - "integrity": "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==", - "license": "MIT", - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", - "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" - } - }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -6105,6 +5766,7 @@ "version": "3.35.1", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", @@ -6127,6 +5789,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -6155,6 +5818,7 @@ "version": "3.4.6", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.6.tgz", "integrity": "sha512-1uRHzPB+Vzu57ocybfZ4jh5Q3SdlH7XW23J5sQoM9LhE9eIOlzxer/3XPSsycvih3rboRsvt0QCmzSrqyOYUIA==", + "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -6188,20 +5852,11 @@ "node": ">=14.0.0" } }, - "node_modules/tailwindcss-animate": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", - "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "tailwindcss": ">=3.0.0 || insiders" - } - }, "node_modules/tailwindcss/node_modules/postcss-js": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, "funding": [ { "type": "opencollective", @@ -6227,6 +5882,7 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", + "dev": true, "funding": [ { "type": "opencollective", @@ -6262,6 +5918,7 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, "license": "MIT", "engines": { "node": ">=14" @@ -6274,6 +5931,7 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -6287,6 +5945,7 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0" @@ -6296,6 +5955,7 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" @@ -6304,16 +5964,11 @@ "node": ">=0.8" } }, - "node_modules/tiny-invariant": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", - "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", - "license": "MIT" - }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -6330,6 +5985,7 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -6355,6 +6011,7 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, "license": "Apache-2.0" }, "node_modules/tsconfig-paths": { @@ -6600,30 +6257,9 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, "license": "MIT" }, - "node_modules/victory-vendor": { - "version": "36.9.2", - "resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-36.9.2.tgz", - "integrity": "sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==", - "license": "MIT AND ISC", - "dependencies": { - "@types/d3-array": "^3.0.3", - "@types/d3-ease": "^3.0.0", - "@types/d3-interpolate": "^3.0.1", - "@types/d3-scale": "^4.0.2", - "@types/d3-shape": "^3.1.0", - "@types/d3-time": "^3.0.0", - "@types/d3-timer": "^3.0.0", - "d3-array": "^3.1.6", - "d3-ease": "^3.0.1", - "d3-interpolate": "^3.0.1", - "d3-scale": "^4.0.2", - "d3-shape": "^3.1.0", - "d3-time": "^3.0.0", - "d3-timer": "^3.0.1" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6743,6 +6379,7 @@ "version": "2.8.3", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/apps/web/package.json b/apps/web/package.json index 657a5ea..f0b3be3 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -11,16 +11,10 @@ "type-check": "tsc --noEmit" }, "dependencies": { - "@heroicons/react": "^2.2.0", - "@tailwindcss/forms": "^0.5.10", - "@tailwindcss/typography": "^0.5.16", "lucide-react": "^1.7.0", - "next": "^15.5.14", + "next": "15.5.23", "react": "19.0.3", - "react-dom": "19.0.3", - "react-hook-form": "^7.72.0", - "recharts": "^2.15.2", - "sonner": "^1.7.1" + "react-dom": "19.0.3" }, "devDependencies": { "@eslint/eslintrc": "^3", @@ -31,13 +25,12 @@ "@typescript-eslint/parser": "^8.29.0", "autoprefixer": "10.4.2", "eslint": "^9", - "eslint-config-next": "^15.2.4", + "eslint-config-next": "15.5.23", "eslint-config-prettier": "^10.1.1", "eslint-plugin-prettier": "^5.2.6", - "postcss": "^8.5.8", + "postcss": "8.5.26", "prettier": "^3.5.3", "tailwindcss": "3.4.6", - "tailwindcss-animate": "^1.0.7", "typescript": "^5" } } diff --git a/apps/web/src/app/components/AppLayout.tsx b/apps/web/src/app/components/AppLayout.tsx index 5e5e76e..7915290 100644 --- a/apps/web/src/app/components/AppLayout.tsx +++ b/apps/web/src/app/components/AppLayout.tsx @@ -1,29 +1,75 @@ -'use client'; +import type { ReactNode } from 'react'; +import Link from 'next/link'; +import { DatabaseZap, GitFork, History, PlugZap, Sparkles } from 'lucide-react'; -import React, { useState } from 'react'; -import Sidebar from './Sidebar'; +const navigation = [ + { label: 'Connections', href: '/connections', icon: PlugZap }, + { label: 'Query', href: '/query', icon: Sparkles }, + { label: 'History', href: '/history', icon: History }, +]; interface AppLayoutProps { - children: React.ReactNode; - activeRoute?: string; + children: ReactNode; + activeRoute: '/connections' | '/query' | '/history'; } export default function AppLayout({ children, activeRoute }: AppLayoutProps) { - const [sidebarCollapsed, setSidebarCollapsed] = useState(false); - return ( -
- setSidebarCollapsed(!sidebarCollapsed)} - activeRoute={activeRoute} - /> -
- {children} -
+
+
+
+ + + + + + QueryMindAI + + Open source + + + + + + + + + GitHub + +
+
+
{children}
); -} \ No newline at end of file +} diff --git a/apps/web/src/app/components/InlineAlert.tsx b/apps/web/src/app/components/InlineAlert.tsx new file mode 100644 index 0000000..af407ee --- /dev/null +++ b/apps/web/src/app/components/InlineAlert.tsx @@ -0,0 +1,28 @@ +import { AlertCircle, CheckCircle2, Info } from 'lucide-react'; + +type AlertKind = 'error' | 'success' | 'info'; + +const styles: Record = { + error: 'border-red-200 bg-red-50 text-red-800', + success: 'border-emerald-200 bg-emerald-50 text-emerald-800', + info: 'border-indigo-200 bg-indigo-50 text-indigo-900', +}; + +export default function InlineAlert({ + kind = 'info', + children, +}: { + kind?: AlertKind; + children: React.ReactNode; +}) { + const Icon = kind === 'error' ? AlertCircle : kind === 'success' ? CheckCircle2 : Info; + return ( +
+ +
{children}
+
+ ); +} diff --git a/apps/web/src/app/components/PageHeader.tsx b/apps/web/src/app/components/PageHeader.tsx new file mode 100644 index 0000000..036477d --- /dev/null +++ b/apps/web/src/app/components/PageHeader.tsx @@ -0,0 +1,23 @@ +import type { ReactNode } from 'react'; + +interface PageHeaderProps { + eyebrow: string; + title: string; + description: string; + action?: ReactNode; +} + +export default function PageHeader({ eyebrow, title, description, action }: PageHeaderProps) { + return ( +
+
+

{eyebrow}

+

+ {title} +

+

{description}

+
+ {action} +
+ ); +} diff --git a/apps/web/src/app/components/Sidebar.tsx b/apps/web/src/app/components/Sidebar.tsx deleted file mode 100644 index e565dce..0000000 --- a/apps/web/src/app/components/Sidebar.tsx +++ /dev/null @@ -1,143 +0,0 @@ -'use client'; - -import React from 'react'; -import Link from 'next/link'; -import { - LayoutDashboard, - Code2, - Database, - History, - Settings, - HelpCircle, - Plus, -} from 'lucide-react'; - -interface SidebarProps { - collapsed: boolean; - onToggle: () => void; - activeRoute?: string; -} - -const navItems = [ - { label: 'Data Connections', icon: LayoutDashboard, route: '/dashboard-and-connection' }, - { label: 'AI Query Assistant', icon: Code2, route: '/sql-editor-and-ai-assistant' }, - { label: 'Schema Explorer', icon: Database, route: '/database-browser' }, - { label: 'Query History', icon: History, route: '/query-history' }, -]; - -const bottomNavItems = [ - { label: 'Settings', icon: Settings, route: '/dashboard-and-connection' }, - { label: 'Support', icon: HelpCircle, route: '/dashboard-and-connection' }, -]; - -export default function Sidebar({ collapsed, onToggle: _onToggle, activeRoute }: SidebarProps) { - return ( - - ); -} diff --git a/apps/web/src/app/components/styles/index.css b/apps/web/src/app/components/styles/index.css deleted file mode 100644 index 7db3ed1..0000000 --- a/apps/web/src/app/components/styles/index.css +++ /dev/null @@ -1 +0,0 @@ -@import './tailwind.css'; \ No newline at end of file diff --git a/apps/web/src/app/components/styles/tailwind.css b/apps/web/src/app/components/styles/tailwind.css index 064d7c3..4b3a3d2 100644 --- a/apps/web/src/app/components/styles/tailwind.css +++ b/apps/web/src/app/components/styles/tailwind.css @@ -2,8 +2,6 @@ @tailwind components; @tailwind utilities; -@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,300;0,9..40,400;0,9..40,500;0,9..40,600;0,9..40,700;1,9..40,400&family=JetBrains+Mono:wght@400;500;600&display=swap'); - @layer base { :root { --background: 222 47% 6%; @@ -27,13 +25,43 @@ box-sizing: border-box; } body { - font-family: 'DM Sans', sans-serif; - background-color: hsl(var(--background)); - color: hsl(var(--foreground)); + min-height: 100vh; + margin: 0; + font-family: + Inter, + ui-sans-serif, + system-ui, + -apple-system, + BlinkMacSystemFont, + 'Segoe UI', + sans-serif; + background-color: #f6f7f9; + color: #0f172a; -webkit-font-smoothing: antialiased; } - code, pre, .font-mono { - font-family: 'JetBrains Mono', monospace; + code, + pre, + .font-mono { + font-family: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, monospace; + } + button, + input, + select, + textarea { + font: inherit; + } + ::selection { + background: #c7d2fe; + color: #1e1b4b; + } +} + +@layer components { + .input { + @apply w-full rounded-xl border border-slate-200 bg-white px-3.5 py-3 text-sm text-slate-900 outline-none transition placeholder:text-slate-400 focus:border-indigo-400 focus:ring-4 focus:ring-indigo-100; + } + .button-secondary { + @apply inline-flex items-center justify-center gap-2 rounded-xl border border-slate-200 bg-white px-3.5 py-2.5 text-sm font-bold text-slate-700 shadow-sm transition hover:border-slate-300 hover:bg-slate-50 hover:text-slate-950 disabled:cursor-not-allowed disabled:opacity-50; } } @@ -56,4 +84,4 @@ background-color: hsl(var(--border)); border-radius: 2px; } -} \ No newline at end of file +} diff --git a/apps/web/src/app/components/ui/AppIcon.tsx b/apps/web/src/app/components/ui/AppIcon.tsx deleted file mode 100644 index bd7d5c0..0000000 --- a/apps/web/src/app/components/ui/AppIcon.tsx +++ /dev/null @@ -1,55 +0,0 @@ -'use client'; - -import React from 'react'; -import * as HeroIcons from '@heroicons/react/24/outline'; -import * as HeroIconsSolid from '@heroicons/react/24/solid'; -import { QuestionMarkCircleIcon } from '@heroicons/react/24/outline'; - -type IconVariant = 'outline' | 'solid'; - -interface IconProps { - name: string; // Changed to string to accept dynamic values - variant?: IconVariant; - size?: number; - className?: string; - onClick?: () => void; - disabled?: boolean; - [key: string]: any; -} - -function Icon({ - name, - variant = 'outline', - size = 24, - className = '', - onClick, - disabled = false, - ...props -}: IconProps) { - const iconSet = variant === 'solid' ? HeroIconsSolid : HeroIcons; - const IconComponent = iconSet[name as keyof typeof iconSet] as React.ComponentType; - - if (!IconComponent) { - return ( - - ); - } - - return ( - - ); -} - -export default Icon; \ No newline at end of file diff --git a/apps/web/src/app/components/ui/AppImage.tsx b/apps/web/src/app/components/ui/AppImage.tsx deleted file mode 100644 index 4932ae1..0000000 --- a/apps/web/src/app/components/ui/AppImage.tsx +++ /dev/null @@ -1,123 +0,0 @@ -'use client'; - -import React, { useState, useCallback, useMemo, memo } from 'react'; -import Image from 'next/image'; - -interface AppImageProps { - src: string; - alt: string; - width?: number; - height?: number; - className?: string; - priority?: boolean; - quality?: number; - placeholder?: 'blur' | 'empty'; - blurDataURL?: string; - fill?: boolean; - sizes?: string; - onClick?: () => void; - fallbackSrc?: string; - loading?: 'lazy' | 'eager'; - unoptimized?: boolean; - [key: string]: any; -} - -const AppImage = memo(function AppImage({ - src, - alt, - width, - height, - className = '', - priority = false, - quality = 85, - placeholder = 'empty', - blurDataURL, - fill = false, - sizes, - onClick, - fallbackSrc = '/assets/images/no_image.png', - loading = 'lazy', - unoptimized = false, - ...props -}: AppImageProps) { - const [imageSrc, setImageSrc] = useState(src); - const [isLoading, setIsLoading] = useState(true); - const [hasError, setHasError] = useState(false); - - const isExternalUrl = useMemo(() => typeof imageSrc === 'string' && imageSrc.startsWith('http'), [imageSrc]); - const resolvedUnoptimized = unoptimized || isExternalUrl; - - const handleError = useCallback(() => { - if (!hasError && imageSrc !== fallbackSrc) { - setImageSrc(fallbackSrc); - setHasError(true); - } - setIsLoading(false); - }, [hasError, imageSrc, fallbackSrc]); - - const handleLoad = useCallback(() => { - setIsLoading(false); - setHasError(false); - }, []); - - const imageClassName = useMemo(() => { - const classes = [className]; - if (isLoading) classes.push('bg-gray-200'); - if (onClick) classes.push('cursor-pointer hover:opacity-90 transition-opacity duration-200'); - return classes.filter(Boolean).join(' '); - }, [className, isLoading, onClick]); - - const imageProps = useMemo(() => { - const baseProps: any = { - src: imageSrc, - alt, - className: imageClassName, - quality, - placeholder, - unoptimized: resolvedUnoptimized, - onError: handleError, - onLoad: handleLoad, - onClick, - }; - - if (priority) { - baseProps.priority = true; - } else { - baseProps.loading = loading; - } - - if (blurDataURL && placeholder === 'blur') { - baseProps.blurDataURL = blurDataURL; - } - - return baseProps; - }, [imageSrc, alt, imageClassName, quality, placeholder, blurDataURL, resolvedUnoptimized, priority, loading, handleError, handleLoad, onClick]); - - if (fill) { - return ( -
- -
- ); - } - - return ( - - ); -}); - -AppImage.displayName = 'AppImage'; - -export default AppImage; \ No newline at end of file diff --git a/apps/web/src/app/components/ui/AppLogo.tsx b/apps/web/src/app/components/ui/AppLogo.tsx deleted file mode 100644 index 7667e7e..0000000 --- a/apps/web/src/app/components/ui/AppLogo.tsx +++ /dev/null @@ -1,50 +0,0 @@ -'use client'; - -import React, { memo, useMemo } from 'react'; -import AppIcon from './AppIcon'; -import AppImage from './AppImage'; - -interface AppLogoProps { - src?: string; // Image source (optional) - iconName?: string; // Icon name when no image - size?: number; // Size for icon/image - className?: string; // Additional classes - onClick?: () => void; // Click handler -} - -const AppLogo = memo(function AppLogo({ - src = '/assets/images/app_logo.png', - iconName = 'SparklesIcon', - size = 64, - className = '', - onClick, -}: AppLogoProps) { - // Memoize className calculation - const containerClassName = useMemo(() => { - const classes = ['flex items-center']; - if (onClick) classes.push('cursor-pointer hover:opacity-80 transition-opacity'); - if (className) classes.push(className); - return classes.join(' '); - }, [onClick, className]); - - return ( -
- {/* Show image if src provided, otherwise show icon */} - {src ? ( - - ) : ( - - )} -
- ); -}); - -export default AppLogo; diff --git a/apps/web/src/app/connections/page.tsx b/apps/web/src/app/connections/page.tsx new file mode 100644 index 0000000..d92f5fd --- /dev/null +++ b/apps/web/src/app/connections/page.tsx @@ -0,0 +1,10 @@ +import AppLayout from '@/app/components/AppLayout'; +import ConnectionsPage from '@/features/connections/ConnectionsPage'; + +export default function ConnectionsRoute() { + return ( + + + + ); +} diff --git a/apps/web/src/app/dashboard-and-connection/components/ActivityFeed.tsx b/apps/web/src/app/dashboard-and-connection/components/ActivityFeed.tsx deleted file mode 100644 index 21ad367..0000000 --- a/apps/web/src/app/dashboard-and-connection/components/ActivityFeed.tsx +++ /dev/null @@ -1,124 +0,0 @@ -import React from 'react'; -import { CheckCircle, XCircle, AlertTriangle, Plug, RefreshCw } from 'lucide-react'; - -const activities = [ - { - id: 'act-001', - type: 'query_success', - icon: CheckCircle, - iconColor: 'text-green-400', - text: 'Query completed on', - entity: 'prod-postgres-01', - meta: '2,841 rows · 218ms', - time: '2m ago', - user: 'AK', - }, - { - id: 'act-002', - type: 'query_error', - icon: XCircle, - iconColor: 'text-red-400', - text: 'Query failed on', - entity: 'analytics-redshift', - meta: 'syntax error near "WHER"', - time: '7m ago', - user: 'SR', - }, - { - id: 'act-003', - type: 'connection_added', - icon: Plug, - iconColor: 'text-blue-400', - text: 'New connection added', - entity: 'staging-mysql-02', - meta: 'MySQL 8.0 · Staging', - time: '14m ago', - user: 'PM', - }, - { - id: 'act-004', - type: 'slow_query', - icon: AlertTriangle, - iconColor: 'text-amber-400', - text: 'Slow query detected on', - entity: 'prod-postgres-01', - meta: 'P95 exceeded · 4.2s', - time: '31m ago', - user: 'AK', - }, - { - id: 'act-005', - type: 'schema_refresh', - icon: RefreshCw, - iconColor: 'text-slate-400', - text: 'Schema refreshed for', - entity: 'analytics-redshift', - meta: '47 tables updated', - time: '1h ago', - user: 'SYS', - }, - { - id: 'act-006', - type: 'query_success', - icon: CheckCircle, - iconColor: 'text-green-400', - text: 'Bulk export completed on', - entity: 'prod-bigquery-01', - meta: '1.2M rows · 8.4s', - time: '2h ago', - user: 'NP', - }, - { - id: 'act-007', - type: 'query_error', - icon: XCircle, - iconColor: 'text-red-400', - text: 'Query timeout on', - entity: 'prod-postgres-01', - meta: 'Exceeded 30s limit', - time: '3h ago', - user: 'SR', - }, -]; - -const userColors: Record = { - AK: 'from-blue-500 to-blue-700', - SR: 'from-purple-500 to-purple-700', - PM: 'from-emerald-500 to-emerald-700', - NP: 'from-amber-500 to-amber-700', - SYS: 'from-slate-500 to-slate-700', -}; - -export default function ActivityFeed() { - return ( -
-
-

Recent Activity

- Demo data -
-
- {activities.map((item) => ( -
- -
-

- {item.text}{' '} - {item.entity} -

-

{item.meta}

-
-
- {item.time} -
- {item.user} -
-
-
- ))} -
-
- ); -} diff --git a/apps/web/src/app/dashboard-and-connection/components/ConnectionsTable.tsx b/apps/web/src/app/dashboard-and-connection/components/ConnectionsTable.tsx deleted file mode 100644 index 73a97dc..0000000 --- a/apps/web/src/app/dashboard-and-connection/components/ConnectionsTable.tsx +++ /dev/null @@ -1,332 +0,0 @@ -'use client'; - -import React, { useState } from 'react'; -import { - Search, - Plus, - RefreshCw, - MoreHorizontal, - CheckCircle, - XCircle, - AlertTriangle, - Trash2, - Edit2, - Plug, - ChevronUp, - ChevronDown, -} from 'lucide-react'; - -interface Connection { - id: string; - name: string; - type: string; - host: string; - database: string; - env: 'Production' | 'Staging' | 'Development'; - status: 'Connected' | 'Failed' | 'Degraded'; - latency: number; - queriesDay: number; - lastUsed: string; - owner: string; - version: string; -} - -const connections: Connection[] = [ - { - id: 'conn-001', name: 'prod-postgres-01', type: 'PostgreSQL', host: 'db-prod-01.internal:5432', - database: 'analytics_core', env: 'Production', status: 'Connected', latency: 12, queriesDay: 847, - lastUsed: '2m ago', owner: 'Arjun Kumar', version: '15.4', - }, - { - id: 'conn-002', name: 'analytics-redshift', type: 'Redshift', host: 'cluster.us-east-1.redshift.amazonaws.com', - database: 'dw_prod', env: 'Production', status: 'Degraded', latency: 340, queriesDay: 213, - lastUsed: '7m ago', owner: 'Sneha Reddy', version: '1.0.50780', - }, - { - id: 'conn-003', name: 'staging-mysql-02', type: 'MySQL', host: 'mysql-staging.internal:3306', - database: 'app_staging', env: 'Staging', status: 'Connected', latency: 8, queriesDay: 124, - lastUsed: '14m ago', owner: 'Priya Menon', version: '8.0.33', - }, - { - id: 'conn-004', name: 'prod-bigquery-01', type: 'BigQuery', host: 'bigquery.googleapis.com', - database: 'analytics-prod-gcp', env: 'Production', status: 'Connected', latency: 180, queriesDay: 312, - lastUsed: '1h ago', owner: 'Nikhil Patel', version: 'v2', - }, - { - id: 'conn-005', name: 'dev-sqlite-local', type: 'SQLite', host: 'localhost', - database: 'dev_sandbox.db', env: 'Development', status: 'Connected', latency: 1, queriesDay: 56, - lastUsed: '3h ago', owner: 'Arjun Kumar', version: '3.43.2', - }, - { - id: 'conn-006', name: 'staging-snowflake', type: 'Snowflake', host: 'xy12345.snowflakecomputing.com', - database: 'STAGING_DB', env: 'Staging', status: 'Failed', latency: 0, queriesDay: 0, - lastUsed: '6h ago', owner: 'Sneha Reddy', version: '7.44', - }, - { - id: 'conn-007', name: 'prod-mongodb-01', type: 'MongoDB', host: 'mongo-prod.cluster.internal:27017', - database: 'events_store', env: 'Production', status: 'Connected', latency: 24, queriesDay: 189, - lastUsed: '22m ago', owner: 'Priya Menon', version: '6.0.8', - }, -]; - -const statusConfig = { - Connected: { icon: CheckCircle, color: 'text-green-400', bg: 'bg-green-400/10', label: 'Connected' }, - Failed: { icon: XCircle, color: 'text-red-400', bg: 'bg-red-400/10', label: 'Failed' }, - Degraded: { icon: AlertTriangle, color: 'text-amber-400', bg: 'bg-amber-400/10', label: 'Degraded' }, -}; - -const envConfig = { - Production: 'bg-blue-600/15 text-blue-400', - Staging: 'bg-amber-600/15 text-amber-400', - Development: 'bg-slate-600/20 text-slate-400', -}; - -const dbTypeColors: Record = { - PostgreSQL: 'bg-sky-600/15 text-sky-400', - Redshift: 'bg-red-600/15 text-red-400', - MySQL: 'bg-orange-600/15 text-orange-400', - BigQuery: 'bg-blue-600/15 text-blue-400', - SQLite: 'bg-slate-600/15 text-slate-400', - Snowflake: 'bg-cyan-600/15 text-cyan-400', - MongoDB: 'bg-green-600/15 text-green-400', -}; - -export default function ConnectionsTable() { - const [search, setSearch] = useState(''); - const [sortCol, setSortCol] = useState('name'); - const [sortDir, setSortDir] = useState<'asc' | 'desc'>('asc'); - const [hoveredRow, setHoveredRow] = useState(null); - const [envFilter, setEnvFilter] = useState('All'); - - const handleSort = (col: keyof Connection) => { - if (sortCol === col) setSortDir(sortDir === 'asc' ? 'desc' : 'asc'); - else { setSortCol(col); setSortDir('asc'); } - }; - - const filtered = connections - .filter((c) => { - const matchSearch = c.name.toLowerCase().includes(search.toLowerCase()) || - c.type.toLowerCase().includes(search.toLowerCase()) || - c.database.toLowerCase().includes(search.toLowerCase()); - const matchEnv = envFilter === 'All' || c.env === envFilter; - return matchSearch && matchEnv; - }) - .sort((a, b) => { - const av = a[sortCol]; - const bv = b[sortCol]; - const cmp = String(av).localeCompare(String(bv), undefined, { numeric: true }); - return sortDir === 'asc' ? cmp : -cmp; - }); - - const SortIcon = ({ col }: { col: keyof Connection }) => ( - - - - - ); - - return ( -
- {/* Table header */} -
-
- -

Database Connections

- - {connections.length} - -
-
- {/* Env filter */} -
- {['All', 'Production', 'Staging', 'Development'].map((env) => ( - - ))} -
- {/* Search */} -
- - setSearch(e.target.value)} - className="pl-8 pr-3 py-1.5 text-xs rounded-lg border bg-transparent text-slate-300 placeholder-slate-600 focus:outline-none focus:border-blue-500/50 transition-colors w-44" - style={{ borderColor: 'hsl(222 30% 20%)' }} - /> -
- - -
-
- - {/* Table */} -
- - - - {[ - { label: 'Connection', col: 'name' as keyof Connection }, - { label: 'Type', col: 'type' as keyof Connection }, - { label: 'Environment', col: 'env' as keyof Connection }, - { label: 'Status', col: 'status' as keyof Connection }, - { label: 'Latency', col: 'latency' as keyof Connection }, - { label: 'Queries / Day', col: 'queriesDay' as keyof Connection }, - { label: 'Database', col: 'database' as keyof Connection }, - { label: 'Last Used', col: 'lastUsed' as keyof Connection }, - { label: 'Owner', col: 'owner' as keyof Connection }, - ].map(({ label, col }) => ( - - ))} - - - - - {filtered.length === 0 ? ( - - - - ) : ( - filtered.map((conn, idx) => { - const sc = statusConfig[conn.status]; - const isHovered = hoveredRow === conn.id; - return ( - setHoveredRow(conn.id)} - onMouseLeave={() => setHoveredRow(null)} - className="transition-colors" - style={{ - background: isHovered ? 'rgba(255,255,255,0.025)' : idx % 2 === 1 ? 'rgba(255,255,255,0.01)' : 'transparent', - borderBottom: '1px solid hsl(222 30% 13%)', - }} - > - {/* Name */} - - {/* Type */} - - {/* Env */} - - {/* Status */} - - {/* Latency */} - - {/* Queries */} - - {/* Database */} - - {/* Last used */} - - {/* Owner */} - - {/* Actions */} - - - ); - }) - )} - -
handleSort(col)} - className="px-4 py-3 text-left text-[11px] font-semibold tracking-wide uppercase cursor-pointer select-none whitespace-nowrap" - style={{ color: 'hsl(215 20% 45%)' }} - > - - {label} - - - - Actions -
- -

No connections match your search

-

Try adjusting the filter or search term

-
-
-
- {conn.name} -
-
- - {conn.type} - - - - {conn.env} - - - - - {sc.label} - - - 200 ? 'text-amber-400' : 'text-green-400'}`}> - {conn.latency === 0 ? '—' : `${conn.latency}ms`} - - - - {conn.queriesDay.toLocaleString()} - - - {conn.database} - - {conn.lastUsed} - - {conn.owner} - -
- - - -
-
-
- - {/* Pagination */} -
-

- Showing {filtered.length} of {connections.length} connections -

-
- {[1].map((page) => ( - - ))} -
-
-
- ); -} \ No newline at end of file diff --git a/apps/web/src/app/dashboard-and-connection/components/DashboardContent.tsx b/apps/web/src/app/dashboard-and-connection/components/DashboardContent.tsx deleted file mode 100644 index 5b3dd01..0000000 --- a/apps/web/src/app/dashboard-and-connection/components/DashboardContent.tsx +++ /dev/null @@ -1,28 +0,0 @@ -'use client'; - -import { FormEvent, useCallback, useEffect, useState } from 'react'; -import { Database, Plus, RefreshCw, ShieldCheck, Trash2 } from 'lucide-react'; -import { api, ApiError } from '@/lib/api'; -import type { SavedConnection } from '@/types/api'; - -export default function DashboardContent() { - const [items,setItems]=useState([]); const [loading,setLoading]=useState(true); - const [error,setError]=useState(''); const [mode,setMode]=useState<'fields'|'url'>('fields'); const [saving,setSaving]=useState(false); - const load=useCallback(async()=>{setLoading(true);try{setItems(await api.listConnections());setError('')}catch(e){setError(e instanceof ApiError?e.message:'Could not load connections')}finally{setLoading(false)}},[]); - useEffect(()=>{void load()},[load]); - async function submit(event:FormEvent){event.preventDefault();setSaving(true);setError('');const data=new FormData(event.currentTarget); - const body:Record={name:data.get('name'),ssl_mode:data.get('ssl_mode')}; - if(mode==='url')body.connection_string=data.get('connection_string'); else Object.assign(body,{host:data.get('host'),port:Number(data.get('port')),database:data.get('database'),username:data.get('username'),password:data.get('password')}); - try{await api.createConnection(body);event.currentTarget.reset();await load()}catch(e){setError(e instanceof ApiError?e.message:'Connection failed')}finally{setSaving(false)}} - async function remove(id:string){if(!confirm('Delete this connection and its schema, drafts, and query history?'))return;await api.deleteConnection(id);await load()} - return
-

Data Connections

Connect a publicly reachable PostgreSQL database with SSL and dedicated read-only credentials.

-
QueryMindAI does not need write access. Credentials are encrypted in the API; connection details and row data are never sent to the LLM.
- {error&&
{error}
} -

Add PostgreSQL connection

-
- {mode==='url'?:<>} -
-
-

Saved connections

{loading?

Loading connections…

:items.length===0?

No saved connections yet.

:items.map(item=>

{item.name}

{item.username}@{item.host}/{item.database} · {item.ssl_mode}

{item.status}

)}
-} diff --git a/apps/web/src/app/dashboard-and-connection/components/KPIGrid.tsx b/apps/web/src/app/dashboard-and-connection/components/KPIGrid.tsx deleted file mode 100644 index f617120..0000000 --- a/apps/web/src/app/dashboard-and-connection/components/KPIGrid.tsx +++ /dev/null @@ -1,119 +0,0 @@ -'use client'; - -import React from 'react'; -import { TrendingUp, TrendingDown, Plug, Zap, AlertTriangle, Database, BarChart2 } from 'lucide-react'; - -const metrics = [ - { - id: 'kpi-active-connections', - label: 'Active Connections', - value: '7', - sub: '2 staging, 5 production', - trend: '+1 since yesterday', - trendUp: true, - icon: Plug, - color: 'blue', - span: 'col-span-1', - }, - { - id: 'kpi-queries-today', - label: 'Queries Run Today', - value: '1,284', - sub: 'across all connections', - trend: '+18% vs yesterday', - trendUp: true, - icon: Zap, - color: 'green', - span: 'col-span-1', - }, - { - id: 'kpi-avg-exec-time', - label: 'Avg Execution Time', - value: '342ms', - sub: 'P95: 1.8s', - trend: '+12% slower vs last week', - trendUp: false, - icon: BarChart2, - color: 'amber', - span: 'col-span-1', - }, - { - id: 'kpi-rows-scanned', - label: 'Rows Scanned (24h)', - value: '2.4B', - sub: '~890 GB data processed', - trend: '+34% vs yesterday', - trendUp: true, - icon: Database, - color: 'blue', - span: 'col-span-1', - }, - { - id: 'kpi-failed-queries', - label: 'Failed Queries (24h)', - value: '23', - sub: '1.8% error rate', - trend: '+8 since yesterday', - trendUp: false, - icon: AlertTriangle, - color: 'red', - span: 'col-span-1', - alert: true, - }, -]; - -const colorMap: Record = { - blue: { bg: 'rgba(37,99,235,0.08)', icon: 'text-blue-400', badge: 'bg-blue-600/15 text-blue-400' }, - green: { bg: 'rgba(34,197,94,0.08)', icon: 'text-green-400', badge: 'bg-green-600/15 text-green-400' }, - amber: { bg: 'rgba(245,158,11,0.08)', icon: 'text-amber-400', badge: 'bg-amber-600/15 text-amber-400' }, - red: { bg: 'rgba(239,68,68,0.10)', icon: 'text-red-400', badge: 'bg-red-600/15 text-red-400' }, -}; - -export default function KPIGrid() { - return ( -
- {metrics.map((m) => { - const colors = colorMap[m.color]; - return ( -
-
-
- -
- {m.alert && ( - - Alert - - )} -
-
-

- {m.label} -

-

{m.value}

-

{m.sub}

-
-
- {m.trendUp ? ( - - ) : ( - - )} - {m.trend} -
-
- ); - })} -
- ); -} \ No newline at end of file diff --git a/apps/web/src/app/dashboard-and-connection/components/QueryVolumeChart.tsx b/apps/web/src/app/dashboard-and-connection/components/QueryVolumeChart.tsx deleted file mode 100644 index b038c9f..0000000 --- a/apps/web/src/app/dashboard-and-connection/components/QueryVolumeChart.tsx +++ /dev/null @@ -1,106 +0,0 @@ -'use client'; - -import React from 'react'; -import { AreaChart, Area, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, } from 'recharts'; - -const data = [ - { date: 'Mar 13', production: 820, staging: 210, failed: 14 }, - { date: 'Mar 14', production: 1140, staging: 340, failed: 8 }, - { date: 'Mar 15', production: 980, staging: 290, failed: 22 }, - { date: 'Mar 16', production: 1320, staging: 410, failed: 11 }, - { date: 'Mar 17', production: 760, staging: 180, failed: 6 }, - { date: 'Mar 18', production: 640, staging: 120, failed: 4 }, - { date: 'Mar 19', production: 1580, staging: 520, failed: 31 }, - { date: 'Mar 20', production: 1720, staging: 480, failed: 19 }, - { date: 'Mar 21', production: 1450, staging: 390, failed: 15 }, - { date: 'Mar 22', production: 1890, staging: 560, failed: 28 }, - { date: 'Mar 23', production: 1240, staging: 320, failed: 9 }, - { date: 'Mar 24', production: 2100, staging: 640, failed: 37 }, - { date: 'Mar 25', production: 1960, staging: 590, failed: 21 }, - { date: 'Mar 26', production: 1284, staging: 312, failed: 23 }, -]; - -const CustomTooltip = ({ active, payload, label }: { active?: boolean; payload?: Array<{ name: string; value: number; color: string }>; label?: string }) => { - if (!active || !payload?.length) return null; - return ( -
-

{label}

- {payload.map((entry) => ( -
- - {entry.name}: - {entry.value.toLocaleString()} -
- ))} -
- ); -}; - -export default function QueryVolumeChart() { - return ( -
-
-
-

Query Volume

-

- Production vs staging executions — last 14 days -

-
-
-
-
- Production -
-
-
- Staging -
-
-
- Failed -
-
-
- - - - - - - - - - - - - - - - - - - - } /> - - - - - -
- ); -} \ No newline at end of file diff --git a/apps/web/src/app/dashboard-and-connection/page.tsx b/apps/web/src/app/dashboard-and-connection/page.tsx deleted file mode 100644 index 73bab69..0000000 --- a/apps/web/src/app/dashboard-and-connection/page.tsx +++ /dev/null @@ -1,10 +0,0 @@ -import AppLayout from '../components/AppLayout'; -import DashboardContent from './components/DashboardContent'; - -export default function Page() { - return ( - - - - ); -} \ No newline at end of file diff --git a/apps/web/src/app/database-browser/components/DatabaseBrowserContent.tsx b/apps/web/src/app/database-browser/components/DatabaseBrowserContent.tsx deleted file mode 100644 index 05a363d..0000000 --- a/apps/web/src/app/database-browser/components/DatabaseBrowserContent.tsx +++ /dev/null @@ -1,13 +0,0 @@ -'use client'; - -import { useCallback, useEffect, useState } from 'react'; -import { KeyRound, RefreshCw } from 'lucide-react'; -import { api, ApiError } from '@/lib/api'; -import type { CatalogResponse, SavedConnection } from '@/types/api'; - -export default function DatabaseBrowserContent(){const [connections,setConnections]=useState([]);const [selected,setSelected]=useState('');const [catalog,setCatalog]=useState(null);const [error,setError]=useState('');const [loading,setLoading]=useState(false); - const loadConnections=useCallback(async()=>{try{const list=await api.listConnections();setConnections(list);if(list[0])setSelected(v=>v||list[0].id)}catch(e){setError(e instanceof ApiError?e.message:'Could not load connections')}},[]);useEffect(()=>{void loadConnections()},[loadConnections]); - const load=useCallback(async()=>{if(!selected)return;setLoading(true);try{setCatalog(await api.connectionSchema(selected));setError('')}catch(e){setError(e instanceof ApiError?e.message:'Could not load schema')}finally{setLoading(false)}},[selected]);useEffect(()=>{void load()},[load]); - async function refresh(){if(!selected)return;setLoading(true);try{setCatalog(await api.refreshSchema(selected));setError('')}catch(e){setError(e instanceof ApiError?e.message:'Refresh failed')}finally{setLoading(false)}} - return

Schema Explorer

Live API data from the latest encrypted connection snapshot.

- {error&&
{error}
}{loading?

Introspecting schema…

:!catalog?

Create and select a connection first.

:
{catalog.metadata.schemas.map(schema=>

{schema.name}

{schema.objects.map(obj=>

{obj.name}

{obj.type}
{obj.columns.map(col=>
{obj.primary_key.includes(col.name)&&}{col.name}{col.type}{col.nullable?' · nullable':''}
)}
{obj.foreign_keys.length>0&&
{obj.foreign_keys.map((fk,i)=>

FK {fk.columns.join(', ')} → {fk.referred_schema}.{fk.referred_table}({fk.referred_columns.join(', ')})

)}
}
)}
)}
}
} diff --git a/apps/web/src/app/database-browser/page.tsx b/apps/web/src/app/database-browser/page.tsx deleted file mode 100644 index ea0d8e0..0000000 --- a/apps/web/src/app/database-browser/page.tsx +++ /dev/null @@ -1,13 +0,0 @@ -'use client'; - -import React from 'react'; -import AppLayout from '../components/AppLayout'; -import DatabaseBrowserContent from './components/DatabaseBrowserContent'; - -export default function DatabaseBrowserPage() { - return ( - - - - ); -} diff --git a/apps/web/src/app/history/page.tsx b/apps/web/src/app/history/page.tsx new file mode 100644 index 0000000..f16bb06 --- /dev/null +++ b/apps/web/src/app/history/page.tsx @@ -0,0 +1,10 @@ +import AppLayout from '@/app/components/AppLayout'; +import QueryHistoryPage from '@/features/history/QueryHistoryPage'; + +export default function HistoryRoute() { + return ( + + + + ); +} diff --git a/apps/web/src/app/homepage-login/components/LoginPage.tsx b/apps/web/src/app/homepage-login/components/LoginPage.tsx deleted file mode 100644 index b63a80d..0000000 --- a/apps/web/src/app/homepage-login/components/LoginPage.tsx +++ /dev/null @@ -1,328 +0,0 @@ -'use client'; - -import React, { useState } from 'react'; -import { useForm } from 'react-hook-form'; -import { useRouter } from 'next/navigation'; -import { Eye, EyeOff, Database, Zap, Info } from 'lucide-react'; - -const GithubIcon = ({ className }: { className?: string }) => ( - - - -); - - -interface LoginFormData { - email: string; - password: string; - rememberMe: boolean; -} - -const MOCK_EMAIL = 'architect@querymind.ai'; -const MOCK_PASSWORD = 'Schema#Engine2026'; - -export default function LoginPage() { - const router = useRouter(); - const [showPassword, setShowPassword] = useState(false); - const [isLoading, setIsLoading] = useState(false); - const [oauthLoading, setOauthLoading] = useState<'google' | 'github' | null>(null); - const [authError, setAuthError] = useState(''); - - const { - register, - handleSubmit, - formState: { errors }, - } = useForm({ defaultValues: { rememberMe: false } }); - - const onSubmit = async (data: LoginFormData) => { - setIsLoading(true); - setAuthError(''); - await new Promise((r) => setTimeout(r, 1400)); - // Backend integration point: POST /api/auth/login - if (data.email === MOCK_EMAIL && data.password === MOCK_PASSWORD) { - router.push('/dashboard-and-connection'); - } else { - setAuthError( - `Invalid credentials. Try: ${MOCK_EMAIL} / ${MOCK_PASSWORD}` - ); - setIsLoading(false); - } - }; - - const handleOAuth = async (provider: 'google' | 'github') => { - setOauthLoading(provider); - await new Promise((r) => setTimeout(r, 1200)); - // Backend integration point: /api/auth/oauth/{provider} - router.push('/dashboard-and-connection'); - }; - - return ( -
- {/* Main split layout */} -
- {/* LEFT PANEL — Dark brand */} -
- {/* Subtle grid overlay */} -
- - {/* Logo */} -
-
-
- -
- QueryMindAI -
-
- - {/* Hero content */} -
-
-
-

- Ask questions,{' '} - not SQL. -

-

- Open-source natural-language analytics that produces safe, explainable SQL over structured data. -

-
- - {/* 3D Geometric shapes */} -
-
-
-
-
-
- - {/* Bottom bar */} -
-
- - - Schema Engine V4.02 // Enterprise - -
-
-
-
-
-
-
-
- - {/* RIGHT PANEL — Light form */} -
-
-
-

Welcome Back

-

- Access your managed data environments and SQL pipelines. -

-
- - {/* OAuth buttons */} -
- - -
- - {/* Divider */} -
-
- Or use email -
-
- - {/* Auth error */} - {authError && ( -
-

{authError}

-
- )} - - {/* Email/password form */} -
-
- - - {errors.email && ( -

{errors.email.message}

- )} -
- -
-
- - -
-
- - -
- {errors.password && ( -

{errors.password.message}

- )} -
- -
- - -
- - -
- - {/* Sign up link */} -

- Don't have an account yet?{' '} - -

- - {/* Demo credentials */} -
- -
-

Demo Credentials

-

- {MOCK_EMAIL} · {MOCK_PASSWORD} -

-
-
-
-
-
- - {/* Footer */} -
-

- © 2026 QueryMind AI. Architectural Precision in Data. -

-
- {['Privacy Policy', 'Terms of Service', 'Security Architecture', 'Trust Center'].map((item) => ( - - ))} -
-
-
- ); -} diff --git a/apps/web/src/app/homepage-login/page.tsx b/apps/web/src/app/homepage-login/page.tsx deleted file mode 100644 index 0fe099f..0000000 --- a/apps/web/src/app/homepage-login/page.tsx +++ /dev/null @@ -1,5 +0,0 @@ -import LoginPage from './components/LoginPage'; - -export default function Page() { - return ; -} \ No newline at end of file diff --git a/apps/web/src/app/layout.tsx b/apps/web/src/app/layout.tsx index fbfa43f..227e6d1 100644 --- a/apps/web/src/app/layout.tsx +++ b/apps/web/src/app/layout.tsx @@ -1,21 +1,16 @@ -import type { Metadata } from 'next' -import './components/styles/tailwind.css' +import type { Metadata } from 'next'; +import './components/styles/tailwind.css'; export const metadata: Metadata = { title: 'QueryMindAI — Ask questions, not SQL', - description: 'SQL Editor and AI Assistant', -} + description: + 'Connect a read-only PostgreSQL database and query it safely using natural language.', +}; -export default function RootLayout({ - children, -}: { - children: React.ReactNode -}) { +export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - - {children} - + {children} - ) + ); } diff --git a/apps/web/src/app/not-found.tsx b/apps/web/src/app/not-found.tsx index f827baf..763ebdd 100644 --- a/apps/web/src/app/not-found.tsx +++ b/apps/web/src/app/not-found.tsx @@ -1,10 +1,22 @@ +import Link from 'next/link'; +import { ArrowLeft } from 'lucide-react'; + export default function NotFound() { return ( -
+
-

404 - Page Not Found

-

The page you're looking for doesn't exist.

+

404

+

This page does not exist.

+

+ QueryMindAI has three workspaces: Connections, Query, and History. +

+ + Back to connections +
- ) -} \ No newline at end of file + ); +} diff --git a/apps/web/src/app/query-history/components/QueryLibraryContent.tsx b/apps/web/src/app/query-history/components/QueryLibraryContent.tsx deleted file mode 100644 index 9f97afc..0000000 --- a/apps/web/src/app/query-history/components/QueryLibraryContent.tsx +++ /dev/null @@ -1,6 +0,0 @@ -'use client'; - -import { useEffect, useState } from 'react'; -import { api, ApiError } from '@/lib/api'; - -export default function QueryLibraryContent(){const [items,setItems]=useState>>([]);const [error,setError]=useState('');useEffect(()=>{api.history().then(r=>setItems(r.items)).catch(e=>setError(e instanceof ApiError?e.message:'Could not load history'))},[]);return

Query History

Successful approved executions. Result row data is not stored.

{error&&
{error}
}
{items.length===0?

No approved queries yet.

:items.map(item=>

{String(item.question)}

{String(item.row_count)} rows · {String(item.duration_ms)}ms
{String(item.sql)}

{new Date(String(item.created_at)).toLocaleString()}

)}
} diff --git a/apps/web/src/app/query-history/page.tsx b/apps/web/src/app/query-history/page.tsx deleted file mode 100644 index 39c41f7..0000000 --- a/apps/web/src/app/query-history/page.tsx +++ /dev/null @@ -1,13 +0,0 @@ -'use client'; - -import React from 'react'; -import AppLayout from '../components/AppLayout'; -import QueryLibraryContent from './components/QueryLibraryContent'; - -export default function QueryHistoryPage() { - return ( - - - - ); -} diff --git a/apps/web/src/app/query/page.tsx b/apps/web/src/app/query/page.tsx new file mode 100644 index 0000000..ff63b53 --- /dev/null +++ b/apps/web/src/app/query/page.tsx @@ -0,0 +1,10 @@ +import AppLayout from '@/app/components/AppLayout'; +import QueryWorkspace from '@/features/query/QueryWorkspace'; + +export default function QueryRoute() { + return ( + + + + ); +} diff --git a/apps/web/src/app/sql-editor-and-ai-assistant/components/AIAssistant.tsx b/apps/web/src/app/sql-editor-and-ai-assistant/components/AIAssistant.tsx deleted file mode 100644 index 5923902..0000000 --- a/apps/web/src/app/sql-editor-and-ai-assistant/components/AIAssistant.tsx +++ /dev/null @@ -1,318 +0,0 @@ -'use client'; - -import React, { useState, useRef, useEffect } from 'react'; -import { Sparkles, Send, Copy, RefreshCw, ChevronDown, Lightbulb, Code2, ThumbsUp, ThumbsDown, } from 'lucide-react'; - -interface Message { - id: string; - role: 'user' | 'assistant'; - content: string; - sql?: string; - type?: 'suggestion' | 'explanation' | 'error' | 'optimization'; - timestamp: string; -} - -const initialMessages: Message[] = [ - { - id: 'msg-001', - role: 'assistant', - content: 'Hello! I\'m your QueryMind AI assistant. I can help you write SQL, optimize queries, explain schemas, and debug errors. I\'m connected to **prod-postgres-01**.', - timestamp: '03:50', - }, - { - id: 'msg-002', - role: 'user', - content: 'Can you optimize the retention query? It\'s taking too long on large date ranges.', - timestamp: '03:52', - }, - { - id: 'msg-003', - role: 'assistant', - type: 'optimization', - content: 'I found two performance issues in your retention query:\n\n1. **Missing index** on `events.user_id` + `events.occurred_at` — the join is doing a sequential scan on 89M rows.\n2. **NULLIF in SELECT** is fine, but the `BETWEEN` on `occurred_at` can be rewritten as a range condition for better index use.\n\nHere\'s the optimized version:', - sql: `-- Optimized: uses composite index on (user_id, occurred_at) --- Add index: CREATE INDEX CONCURRENTLY idx_events_user_time --- ON events(user_id, occurred_at) WHERE occurred_at > NOW() - INTERVAL '90 days'; - -SELECT - DATE_TRUNC('week', u.created_at) AS cohort_week, - COUNT(DISTINCT u.id) AS total_users, - COUNT(DISTINCT e.user_id) AS retained_users, - ROUND(COUNT(DISTINCT e.user_id)::numeric / - NULLIF(COUNT(DISTINCT u.id), 0) * 100, 2) AS retention_rate_pct -FROM users u -LEFT JOIN events e ON e.user_id = u.id - AND e.event_type = 'app_opened' - AND e.occurred_at >= u.created_at - AND e.occurred_at < u.created_at + INTERVAL '7 days' -WHERE u.created_at >= NOW() - INTERVAL '90 days' - AND u.is_active = true -GROUP BY 1 -ORDER BY 1 DESC -LIMIT 12;`, - timestamp: '03:52', - }, - { - id: 'msg-004', - role: 'user', - content: 'What indexes exist on the events table?', - timestamp: '03:53', - }, - { - id: 'msg-005', - role: 'assistant', - type: 'explanation', - content: 'Running schema introspection on `public.events`...\n\nCurrent indexes on **events**:\n- `events_pkey` — PRIMARY KEY on `event_id` (btree)\n- `idx_events_user_id` — btree on `user_id` only\n- `idx_events_type_time` — btree on `(event_type, occurred_at)`\n\n**Missing:** A composite index on `(user_id, occurred_at)` — this is exactly what your retention query needs. Estimated query speedup: **8–12×** for 90-day windows.', - timestamp: '03:53', - }, -]; - -const suggestions = [ - 'Explain the EXPLAIN ANALYZE output', - 'Find slow queries in pg_stat_statements', - 'Generate a cohort retention heatmap query', - 'Check for table bloat and dead tuples', -]; - -export default function AIAssistant() { - const [messages, setMessages] = useState(initialMessages); - const [input, setInput] = useState(''); - const [isTyping, setIsTyping] = useState(false); - const [copiedId, setCopiedId] = useState(null); - const messagesEndRef = useRef(null); - - useEffect(() => { - messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); - }, [messages, isTyping]); - - const handleSend = async () => { - if (!input.trim() || isTyping) return; - const userMsg: Message = { - id: `msg-user-${Date.now()}`, - role: 'user', - content: input.trim(), - timestamp: new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false }), - }; - setMessages((prev) => [...prev, userMsg]); - setInput(''); - setIsTyping(true); - // Backend integration point: POST /api/ai/chat with {message, context: {connection, schema, currentSql}} - await new Promise((r) => setTimeout(r, 1800)); - const assistantMsg: Message = { - id: `msg-ai-${Date.now()}`, - role: 'assistant', - content: 'I\'ve analyzed your query and the current schema context. The `organizations` table has a foreign key relationship with `users.org_id`. For better join performance, consider adding a partial index on `users.org_id` where `is_active = true` — this will reduce the index size by ~40% based on your current data distribution.', - timestamp: new Date().toLocaleTimeString('en-US', { hour: '2-digit', minute: '2-digit', hour12: false }), - }; - setMessages((prev) => [...prev, assistantMsg]); - setIsTyping(false); - }; - - const handleCopySQL = (msgId: string) => { - setCopiedId(msgId); - setTimeout(() => setCopiedId(null), 2000); - }; - - const handleSuggestion = (s: string) => setInput(s); - - return ( -
- {/* Header */} -
-
- -
-
-

AI Assistant

-

Schema Engine · GPT-4o

-
- - -
- - {/* Context bar */} -
- - - Context: prod-postgres-01 · public.users, events - -
- - {/* Messages */} -
- {messages.map((msg) => ( -
- {msg.role === 'assistant' && ( -
-
- -
- {msg.type && ( - - {msg.type} - - )} -
- )} -
-

- {msg.content.replace(/\*\*([^*]+)\*\*/g, '$1')} -

-
- - {/* SQL block */} - {msg.sql && ( -
-
-
- - Optimized SQL -
- -
-
-
-                    {msg.sql.slice(0, 300)}{msg.sql.length > 300 ? '...' : ''}
-                  
-
-
- -
-
- )} - - {/* Feedback for assistant messages */} - {msg.role === 'assistant' && !isTyping && ( -
- {msg.timestamp} - - -
- )} - - {msg.role === 'user' && ( - {msg.timestamp} - )} -
- ))} - - {/* Typing indicator */} - {isTyping && ( -
-
- -
-
-
- {[0, 1, 2].map((i) => ( -
- ))} -
-
-
- )} -
-
- - {/* Suggestions */} -
-

- Suggestions -

-
- {suggestions.map((s) => ( - - ))} -
-
- - {/* Input */} -
-
-