From 718e81890fd2ce97c8eaf420310646f76c1cab74 Mon Sep 17 00:00:00 2001 From: Hitesh Date: Wed, 18 Mar 2026 16:53:37 +0000 Subject: [PATCH 1/3] fix: CI Python path issue for tests - Add backend directory to sys.path in conftest.py (Path(__file__).parent.parent) - Update CI workflow to set PYTHONPATH correctly for all test jobs --- .github/workflows/tests.yml | 2 ++ backend/tests/conftest.py | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 808056b..ce23cf7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -71,6 +71,7 @@ jobs: - name: Run tests run: | source .venv/bin/activate + export PYTHONPATH="$(pwd)/backend:$PYTHONPATH" export TEST_BASE_URL="http://localhost:8013" export TEST_COMPANY_ID="${{ secrets.TEST_COMPANY_ID }}" export TEST_API_KEY="${{ secrets.TEST_API_KEY }}" @@ -166,6 +167,7 @@ jobs: - name: Run ${{ matrix.feature }} tests run: | source .venv/bin/activate + export PYTHONPATH="$(pwd)/backend:$PYTHONPATH" export TEST_BASE_URL="http://localhost:8013" export TEST_COMPANY_ID="${{ secrets.TEST_COMPANY_ID }}" export TEST_API_KEY="${{ secrets.TEST_API_KEY }}" diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index b4c7033..10c6d6b 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -1,5 +1,12 @@ -import pytest +import sys import os +from pathlib import Path + +backend_dir = Path(__file__).parent.parent +if str(backend_dir) not in sys.path: + sys.path.insert(0, str(backend_dir)) + +import pytest os.environ.setdefault("SUPABASE_URL", "https://iudjozrtichetbdwzgzv.supabase.co") os.environ.setdefault( From 2e1aacc3f292faab18bcbf78945ffaca2ded203b Mon Sep 17 00:00:00 2001 From: Hitesh Date: Wed, 18 Mar 2026 17:00:41 +0000 Subject: [PATCH 2/3] fix: CI runs tests from backend directory - Use working-directory: backend for all CI steps - Run pytest from backend/tests/ directory - Simplify conftest.py (no sys.path manipulation needed) - Tests now run from backend directory where main.py exists --- .github/workflows/tests.yml | 48 ++++++++++++++++++------------------- backend/tests/conftest.py | 6 ----- 2 files changed, 24 insertions(+), 30 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index ce23cf7..50462d7 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,29 +30,28 @@ jobs: - name: Cache virtual environment uses: actions/cache@v4 with: - path: .venv - key: ${{ runner.os }}-venv-${{ hashFiles('**/requirements.txt') }} + path: backend/.venv + key: ${{ runner.os }}-venv-${{ hashFiles('backend/requirements.txt') }} restore-keys: | ${{ runner.os }}-venv- - name: Install dependencies + working-directory: backend run: | python -m venv .venv source .venv/bin/activate pip install --upgrade pip - pip install -r backend/requirements.txt + pip install -r requirements.txt pip install pytest pytest-asyncio pytest-cov httpx - name: Start backend server + working-directory: backend run: | source .venv/bin/activate - export PYTHONPATH="$(pwd):$PYTHONPATH" - export TEST_BASE_URL="http://localhost:8013" - export TEST_COMPANY_ID="${{ secrets.TEST_COMPANY_ID }}" - export TEST_API_KEY="${{ secrets.TEST_API_KEY }}" export SUPABASE_URL="${{ secrets.SUPABASE_URL }}" export SUPABASE_ANON_KEY="${{ secrets.SUPABASE_ANON_KEY }}" - nohup .venv/bin/python -m uvicorn backend.main:app --host 0.0.0.0 --port 8013 & + export GEMINI_API_KEY="${{ secrets.GEMINI_API_KEY }}" + nohup .venv/bin/python -m uvicorn main:app --host 0.0.0.0 --port 8013 & sleep 10 - name: Wait for backend to be ready @@ -69,21 +68,21 @@ jobs: exit 1 - name: Run tests + working-directory: backend run: | source .venv/bin/activate - export PYTHONPATH="$(pwd)/backend:$PYTHONPATH" - export TEST_BASE_URL="http://localhost:8013" - export TEST_COMPANY_ID="${{ secrets.TEST_COMPANY_ID }}" - export TEST_API_KEY="${{ secrets.TEST_API_KEY }}" export SUPABASE_URL="${{ secrets.SUPABASE_URL }}" export SUPABASE_ANON_KEY="${{ secrets.SUPABASE_ANON_KEY }}" - pytest backend/tests/ -v --cov=backend --cov-report=xml --cov-report=html --cov-report=term + export GEMINI_API_KEY="${{ secrets.GEMINI_API_KEY }}" + export TEST_COMPANY_ID="${{ secrets.TEST_COMPANY_ID }}" + export TEST_API_KEY="${{ secrets.TEST_API_KEY }}" + pytest tests/ -v --cov=. --cov-report=xml --cov-report=html --cov-report=term - name: Upload coverage to Codecov uses: codecov/codecov-action@v4 if: success() with: - files: ./coverage.xml + files: ./backend/coverage.xml flags: unittests name: AI-Agent-Marketplace fail_ci_if_error: false @@ -93,7 +92,7 @@ jobs: if: always() with: name: coverage-report - path: htmlcov/ + path: backend/htmlcov/ retention-days: 14 # ───────────────────────────────────────────── @@ -148,29 +147,30 @@ jobs: python-version: '3.12' - name: Install dependencies + working-directory: backend run: | python -m venv .venv source .venv/bin/activate - pip install -r backend/requirements.txt + pip install -r requirements.txt pip install pytest pytest-asyncio httpx - name: Start backend + working-directory: backend run: | source .venv/bin/activate - export PYTHONPATH="$(pwd):$PYTHONPATH" - export TEST_BASE_URL="http://localhost:8013" export SUPABASE_URL="${{ secrets.SUPABASE_URL }}" export SUPABASE_ANON_KEY="${{ secrets.SUPABASE_ANON_KEY }}" - nohup .venv/bin/python -m uvicorn backend.main:app --host 0.0.0.0 --port 8013 & + export GEMINI_API_KEY="${{ secrets.GEMINI_API_KEY }}" + nohup .venv/bin/python -m uvicorn main:app --host 0.0.0.0 --port 8013 & sleep 10 - name: Run ${{ matrix.feature }} tests + working-directory: backend run: | source .venv/bin/activate - export PYTHONPATH="$(pwd)/backend:$PYTHONPATH" - export TEST_BASE_URL="http://localhost:8013" - export TEST_COMPANY_ID="${{ secrets.TEST_COMPANY_ID }}" - export TEST_API_KEY="${{ secrets.TEST_API_KEY }}" export SUPABASE_URL="${{ secrets.SUPABASE_URL }}" export SUPABASE_ANON_KEY="${{ secrets.SUPABASE_ANON_KEY }}" - pytest backend/tests/ -v -m ${{ matrix.feature }} + export GEMINI_API_KEY="${{ secrets.GEMINI_API_KEY }}" + export TEST_COMPANY_ID="${{ secrets.TEST_COMPANY_ID }}" + export TEST_API_KEY="${{ secrets.TEST_API_KEY }}" + pytest tests/ -v -m ${{ matrix.feature }} diff --git a/backend/tests/conftest.py b/backend/tests/conftest.py index 10c6d6b..2e7b0a3 100644 --- a/backend/tests/conftest.py +++ b/backend/tests/conftest.py @@ -1,10 +1,4 @@ -import sys import os -from pathlib import Path - -backend_dir = Path(__file__).parent.parent -if str(backend_dir) not in sys.path: - sys.path.insert(0, str(backend_dir)) import pytest From 5780e7f370a6b0167aecad1bb04cecfaed71659c Mon Sep 17 00:00:00 2001 From: Hitesh Date: Sat, 4 Apr 2026 13:07:26 +0000 Subject: [PATCH 3/3] feat: add premium UI components and fix TypeScript errors - Add PremiumNav, PremiumLayout, PremiumHero, PremiumFooter components - Add PremiumFeatureCard, PremiumCTA, PremiumStats components - Add PremiumLayout with navProps support - Add new pages: MarketplacePage, DocsPage - Add EnhancedMarketplace, EnhancedMetricsDashboard, EnhancedOnboardingWizard - Add MetricsDashboard, ExecutionResultsViewer components - Add shared components: EmptyState, LoadingAnimation - Add logo.svg - Fix Dashboard and DeveloperPortal to include header with logout - Fix RequestInterface PremiumLayout usage - Fix TypeScript errors in PremiumFeatureCard (ReactNode import) - Update App.tsx routing and imports --- backend/api/company.py | 107 +++ backend/engine/intent_parser.py | 5 +- frontend/index.html | 4 +- frontend/package-lock.json | 686 ++++++++--------- frontend/public/logo.svg | 12 + frontend/src/App.tsx | 212 +++--- frontend/src/api/client.ts | 7 + frontend/src/components/PremiumCTA.tsx | 71 ++ .../src/components/PremiumFeatureCard.tsx | 108 +++ frontend/src/components/PremiumFooter.tsx | 165 ++++ frontend/src/components/PremiumHero.tsx | 97 +++ frontend/src/components/PremiumLayout.tsx | 46 ++ frontend/src/components/PremiumNav.tsx | 168 +++++ frontend/src/components/PremiumStats.tsx | 87 +++ frontend/src/components/admin/Dashboard.tsx | 211 ++++-- .../components/admin/EnhancedMarketplace.tsx | 187 +++++ .../admin/EnhancedMetricsDashboard.tsx | 273 +++++++ .../admin/EnhancedOnboardingWizard.tsx | 256 +++++++ .../src/components/admin/MetricsDashboard.tsx | 447 +++++++++++ .../developer/AgentRegistration.tsx | 272 ++++--- .../components/developer/DeveloperPortal.tsx | 13 +- frontend/src/components/layout/Navbar.tsx | 169 +++++ frontend/src/components/shared/EmptyState.tsx | 65 ++ .../components/shared/LoadingAnimation.tsx | 63 ++ .../user/ExecutionResultsViewer.tsx | 269 +++++++ .../src/components/user/RequestInterface.tsx | 245 +++--- frontend/src/index.css | 713 +++++++++++------- frontend/src/pages/DocsPage.tsx | 354 +++++++++ frontend/src/pages/Login.tsx | 147 ++-- frontend/src/pages/MarketplacePage.tsx | 38 + frontend/src/pages/Signup.tsx | 37 +- 31 files changed, 4404 insertions(+), 1130 deletions(-) create mode 100644 frontend/public/logo.svg create mode 100644 frontend/src/components/PremiumCTA.tsx create mode 100644 frontend/src/components/PremiumFeatureCard.tsx create mode 100644 frontend/src/components/PremiumFooter.tsx create mode 100644 frontend/src/components/PremiumHero.tsx create mode 100644 frontend/src/components/PremiumLayout.tsx create mode 100644 frontend/src/components/PremiumNav.tsx create mode 100644 frontend/src/components/PremiumStats.tsx create mode 100644 frontend/src/components/admin/EnhancedMarketplace.tsx create mode 100644 frontend/src/components/admin/EnhancedMetricsDashboard.tsx create mode 100644 frontend/src/components/admin/EnhancedOnboardingWizard.tsx create mode 100644 frontend/src/components/admin/MetricsDashboard.tsx create mode 100644 frontend/src/components/layout/Navbar.tsx create mode 100644 frontend/src/components/shared/EmptyState.tsx create mode 100644 frontend/src/components/shared/LoadingAnimation.tsx create mode 100644 frontend/src/components/user/ExecutionResultsViewer.tsx create mode 100644 frontend/src/pages/DocsPage.tsx create mode 100644 frontend/src/pages/MarketplacePage.tsx diff --git a/backend/api/company.py b/backend/api/company.py index a1f1e6b..6a7544a 100644 --- a/backend/api/company.py +++ b/backend/api/company.py @@ -616,3 +616,110 @@ async def get_api_key_status(company_id: str): "api_key_expiry_date": data.get("api_key_expiry_date"), "has_active_key": data.get("api_key_status") == "active", } + + +@router.get("/{company_id}/metrics", tags=["Company"]) +async def get_company_metrics( + company_id: str, + range: str = "7d" # 24h, 7d, 30d +): + """ + Get company-wide metrics with time range support. + """ + from datetime import datetime, timedelta + + db = get_supabase() + + # Determine date cutoff + now = datetime.utcnow() + if range == "24h": + cutoff = now - timedelta(hours=24) + elif range == "30d": + cutoff = now - timedelta(days=30) + else: # 7d + cutoff = now - timedelta(days=7) + + # Fetch execution history within range + hist = ( + db.table("execution_history") + .select("*") + .eq("company_id", company_id) + .gte("executed_at", cutoff.isoformat()) + .order("executed_at", desc=True) + .execute() + ) + + rows = hist.data or [] + + # Calculate overall metrics + total_requests = len(rows) + successful_requests = sum(1 for r in rows if r.get("success")) + failed_requests = total_requests - successful_requests + + success_rate = (successful_requests / max(total_requests, 1)) * 100 + avg_response_time = round( + sum(r.get("execution_time_seconds", 0) or 0 for r in rows) / max(total_requests, 1) * 1000, + 2 + ) # Convert to ms + + # Group by agent for breakdown + agent_metrics = {} + for row in rows: + agent_id = row.get("agent_id", "unknown") + if agent_id not in agent_metrics: + agent_metrics[agent_id] = { + "agent_id": agent_id, + "agent_name": row.get("agent_name", "Unknown Agent"), + "total_requests": 0, + "successful_requests": 0, + "failed_requests": 0, + "response_times": [] + } + + agent_metrics[agent_id]["total_requests"] += 1 + if row.get("success"): + agent_metrics[agent_id]["successful_requests"] += 1 + else: + agent_metrics[agent_id]["failed_requests"] += 1 + + if row.get("execution_time_seconds"): + agent_metrics[agent_id]["response_times"].append(row["execution_time_seconds"]) + + # Calculate per-agent stats + agents = [] + for agent_id, metrics in agent_metrics.items(): + success_rate_agent = (metrics["successful_requests"] / max(metrics["total_requests"], 1)) * 100 + avg_response = (sum(metrics["response_times"]) / len(metrics["response_times"])) if metrics["response_times"] else 0 + + agents.append({ + "agent_id": agent_id, + "agent_name": metrics["agent_name"], + "total_requests": metrics["total_requests"], + "successful_requests": metrics["successful_requests"], + "failed_requests": metrics["failed_requests"], + "success_rate": round(success_rate_agent, 1), + "avg_response_time": round(avg_response * 1000, 2) # Convert to ms + }) + + # Group by day for timeline + from collections import defaultdict + daily_requests = defaultdict(int) + for row in rows: + day = (row.get("executed_at") or "")[:10] + if day: + daily_requests[day] += 1 + + daily_requests_list = [ + {"date": day, "count": count} + for day, count in sorted(daily_requests.items()) + ] + + return { + "total_requests": total_requests, + "successful_requests": successful_requests, + "failed_requests": failed_requests, + "success_rate": round(success_rate, 1), + "avg_response_time": avg_response_time, + "agents": sorted(agents, key=lambda x: x["total_requests"], reverse=True), + "daily_requests": daily_requests_list + } diff --git a/backend/engine/intent_parser.py b/backend/engine/intent_parser.py index 90307a0..cd6f6fd 100644 --- a/backend/engine/intent_parser.py +++ b/backend/engine/intent_parser.py @@ -122,10 +122,11 @@ def parse_intent(request: str) -> dict: genai = _get_genai() model = genai.GenerativeModel( model_name=os.getenv("LLM_MODEL", "gemini-2.0-flash"), - system_instruction=_SYSTEM_PROMPT, ) - prompt = f"Parse this user request: {request}" + prompt = f"""{_SYSTEM_PROMPT} + +Parse this user request: {request}""" response = model.generate_content(prompt) raw = response.text.strip() diff --git a/frontend/index.html b/frontend/index.html index 4f0a458..d0f36b7 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -2,9 +2,9 @@ - + - frontend + AgentForge - AI Agent Marketplace
diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 588a10f..95f99ac 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -211,23 +211,23 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", "dev": true, "license": "MIT", "dependencies": { "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/types": "^7.29.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", - "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", "dev": true, "license": "MIT", "dependencies": { @@ -289,35 +289,38 @@ } }, "node_modules/@emnapi/core": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", - "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { - "@emnapi/wasi-threads": "1.2.0", + "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", - "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@emnapi/wasi-threads": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", - "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", "dev": true, "license": "MIT", "optional": true, + "peer": true, "dependencies": { "tslib": "^2.4.0" } @@ -582,20 +585,22 @@ } }, "node_modules/@napi-rs/wasm-runtime": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", - "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", "dev": true, "license": "MIT", "optional": true, "dependencies": { - "@emnapi/core": "^1.7.1", - "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" } }, "node_modules/@nodelib/fs.scandir": { @@ -636,20 +641,10 @@ "node": ">= 8" } }, - "node_modules/@oxc-project/runtime": { - "version": "0.115.0", - "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz", - "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@oxc-project/types": { - "version": "0.115.0", - "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", - "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", "dev": true, "license": "MIT", "funding": { @@ -657,9 +652,9 @@ } }, "node_modules/@rolldown/binding-android-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", "cpu": [ "arm64" ], @@ -674,9 +669,9 @@ } }, "node_modules/@rolldown/binding-darwin-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", "cpu": [ "arm64" ], @@ -691,9 +686,9 @@ } }, "node_modules/@rolldown/binding-darwin-x64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", - "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", "cpu": [ "x64" ], @@ -708,9 +703,9 @@ } }, "node_modules/@rolldown/binding-freebsd-x64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", - "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", "cpu": [ "x64" ], @@ -725,9 +720,9 @@ } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", - "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", "cpu": [ "arm" ], @@ -742,9 +737,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", "cpu": [ "arm64" ], @@ -759,9 +754,9 @@ } }, "node_modules/@rolldown/binding-linux-arm64-musl": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", - "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", "cpu": [ "arm64" ], @@ -776,9 +771,9 @@ } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", "cpu": [ "ppc64" ], @@ -793,9 +788,9 @@ } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", "cpu": [ "s390x" ], @@ -810,9 +805,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-gnu": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", - "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", "cpu": [ "x64" ], @@ -827,9 +822,9 @@ } }, "node_modules/@rolldown/binding-linux-x64-musl": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", - "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", "cpu": [ "x64" ], @@ -844,9 +839,9 @@ } }, "node_modules/@rolldown/binding-openharmony-arm64": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", - "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", "cpu": [ "arm64" ], @@ -861,9 +856,9 @@ } }, "node_modules/@rolldown/binding-wasm32-wasi": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", - "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", "cpu": [ "wasm32" ], @@ -878,9 +873,9 @@ } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", - "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", "cpu": [ "arm64" ], @@ -894,23 +889,6 @@ "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@rolldown/binding-win32-x64-msvc": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", - "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^20.19.0 || >=22.12.0" - } - }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.7", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", @@ -919,9 +897,9 @@ "license": "MIT" }, "node_modules/@supabase/auth-js": { - "version": "2.99.2", - "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.99.2.tgz", - "integrity": "sha512-uRGNXMKEw4VhwouNW7N0XDAGqJP9redHNDmWi17dTrcO1lvFfyRiXsqqfgnVC8aqtRn8kLkLPEzHjiRWsni+oQ==", + "version": "2.101.1", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.101.1.tgz", + "integrity": "sha512-Kd0Wey+RkFHgyVep7adS6UOE2pN6MJ3mZ32PAXSvfw6IjUkFRC7IQpdZZjUOcUe5pXr1ejufCRgF6lsGINe4Tw==", "license": "MIT", "dependencies": { "tslib": "2.8.1" @@ -931,9 +909,9 @@ } }, "node_modules/@supabase/functions-js": { - "version": "2.99.2", - "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.99.2.tgz", - "integrity": "sha512-xuXQARvjdfB1UPK1yUceZ5EGjOLkVz4rBAaloS9foXiAuseWEdgWBCxkIAFRxGBLGX8Uzo8kseq90jhPb+07Vg==", + "version": "2.101.1", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.101.1.tgz", + "integrity": "sha512-OZWU7YtaG+NNNFZK8p/FuJ6gpq7pFyrG2fLOopP73HAIDHDGpOttPJapvO8ADu3RkqfQfkwrB354vPkSBbZ20A==", "license": "MIT", "dependencies": { "tslib": "2.8.1" @@ -942,10 +920,16 @@ "node": ">=20.0.0" } }, + "node_modules/@supabase/phoenix": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@supabase/phoenix/-/phoenix-0.4.0.tgz", + "integrity": "sha512-RHSx8bHS02xwfHdAbX5Lpbo6PXbgyf7lTaXTlwtFDPwOIw64NnVRwFAXGojHhjtVYI+PEPNSWwkL90f4agN3bw==", + "license": "MIT" + }, "node_modules/@supabase/postgrest-js": { - "version": "2.99.2", - "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.99.2.tgz", - "integrity": "sha512-ueiOVkbkTQ7RskwVmjR8zxWYw3VKOMxo1+qep+Dx/SgApqyhWBGd92waQb45tbLc7ydB5x8El8utXOLQTuTojQ==", + "version": "2.101.1", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.101.1.tgz", + "integrity": "sha512-UW1RajH5jbZoK+ldAJ1I6VZ+HWwZ2oaKjEQ6Gn+AQ67CHQVxGl8wNQoLYyumbyaExm41I+wn7arulcY1eHeZJw==", "license": "MIT", "dependencies": { "tslib": "2.8.1" @@ -955,12 +939,12 @@ } }, "node_modules/@supabase/realtime-js": { - "version": "2.99.2", - "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.99.2.tgz", - "integrity": "sha512-J6Jm9601dkpZf3+EJ48ki2pM4sFtCNm/BI0l8iEnrczgg+JSEQkMoOW5VSpM54t0pNs69bsz5PTmYJahDZKiIQ==", + "version": "2.101.1", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.101.1.tgz", + "integrity": "sha512-Oa6dno0OB9I+hv5do5zsZHbFu41ViZnE9IWjmkeeF/8fPmB5fWoHGqeTYEC3/0DAgtpUoFJa4FpvzFH0SBHo1Q==", "license": "MIT", "dependencies": { - "@types/phoenix": "^1.6.6", + "@supabase/phoenix": "^0.4.0", "@types/ws": "^8.18.1", "tslib": "2.8.1", "ws": "^8.18.2" @@ -970,9 +954,9 @@ } }, "node_modules/@supabase/storage-js": { - "version": "2.99.2", - "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.99.2.tgz", - "integrity": "sha512-V/FF8kX8JGSefsVCG1spCLSrHdNR/JFeUMn1jS9KG/Eizjx+evtdKQKLJXFgIylY/bKTXKhc2SYDPIGrIhzsug==", + "version": "2.101.1", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.101.1.tgz", + "integrity": "sha512-WhTaUOBgeEvnKLy95Cdlp6+D5igSF/65yC727w1olxbet5nzUvMlajKUWyzNtQu2efrz2cQ7FcdVBdQqgT9YKQ==", "license": "MIT", "dependencies": { "iceberg-js": "^0.8.1", @@ -983,16 +967,16 @@ } }, "node_modules/@supabase/supabase-js": { - "version": "2.99.2", - "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.99.2.tgz", - "integrity": "sha512-179rn5wq0wBAqqGwAwR7TUGg2NOaP+fkd5FCVbYJXby85fsRNPFoNJN8YRBepqX2tN7JJcnTjqaAMXuNjiyisA==", + "version": "2.101.1", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.101.1.tgz", + "integrity": "sha512-Jnhm3LfuACwjIzvk2pfUbGQn7pa7hi6MFzfSyPrRYWVCCu69RPLCFyHSBl7HSBwadbQ3UZOznnD3gPca3ePrRA==", "license": "MIT", "dependencies": { - "@supabase/auth-js": "2.99.2", - "@supabase/functions-js": "2.99.2", - "@supabase/postgrest-js": "2.99.2", - "@supabase/realtime-js": "2.99.2", - "@supabase/storage-js": "2.99.2" + "@supabase/auth-js": "2.101.1", + "@supabase/functions-js": "2.101.1", + "@supabase/postgrest-js": "2.101.1", + "@supabase/realtime-js": "2.101.1", + "@supabase/storage-js": "2.101.1" }, "engines": { "node": ">=20.0.0" @@ -1024,20 +1008,14 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "24.12.0", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", - "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", + "version": "24.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.2.tgz", + "integrity": "sha512-A1sre26ke7HDIuY/M23nd9gfB+nrmhtYyMINbjI1zHJxYteKR6qSMX56FsmjMcDb3SMcjJg5BiRRgOCC/yBD0g==", "license": "MIT", "dependencies": { "undici-types": "~7.16.0" } }, - "node_modules/@types/phoenix": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", - "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==", - "license": "MIT" - }, "node_modules/@types/react": { "version": "19.2.14", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", @@ -1068,20 +1046,20 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.0.tgz", - "integrity": "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.57.0", - "@typescript-eslint/type-utils": "8.57.0", - "@typescript-eslint/utils": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1091,9 +1069,9 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.57.0", + "@typescript-eslint/parser": "^8.58.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { @@ -1107,16 +1085,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz", - "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.57.0", - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3" }, "engines": { @@ -1128,18 +1106,18 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", - "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.57.0", - "@typescript-eslint/types": "^8.57.0", + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", "debug": "^4.4.3" }, "engines": { @@ -1150,18 +1128,18 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", - "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0" + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1172,9 +1150,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", - "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", "dev": true, "license": "MIT", "engines": { @@ -1185,21 +1163,21 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz", - "integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0", - "@typescript-eslint/utils": "8.57.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", "debug": "^4.4.3", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1210,13 +1188,13 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/types": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", - "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", "dev": true, "license": "MIT", "engines": { @@ -1228,21 +1206,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", - "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.57.0", - "@typescript-eslint/tsconfig-utils": "8.57.0", - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/visitor-keys": "8.57.0", + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", - "ts-api-utils": "^2.4.0" + "ts-api-utils": "^2.5.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1252,7 +1230,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { @@ -1266,9 +1244,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", - "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", "dev": true, "license": "MIT", "dependencies": { @@ -1279,13 +1257,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "10.2.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", - "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^5.0.2" + "brace-expansion": "^5.0.5" }, "engines": { "node": "18 || 20 || >=22" @@ -1308,16 +1286,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", - "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.57.0", - "@typescript-eslint/types": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0" + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1328,17 +1306,17 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", - "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.57.0", + "@typescript-eslint/types": "8.58.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -1465,19 +1443,6 @@ "node": ">= 8" } }, - "node_modules/anymatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -1536,14 +1501,14 @@ } }, "node_modules/axios": { - "version": "1.13.6", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", - "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.14.0.tgz", + "integrity": "sha512-3Y8yrqLSwjuzpXuZ0oIYZ/XGgLwUIBU3uLvbcpb0pidD9ctpShJd43KSlEEkVQg6DS0G9NKyzOvBfUtDKEyHvQ==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", - "proxy-from-env": "^1.1.0" + "proxy-from-env": "^2.1.0" } }, "node_modules/balanced-match": { @@ -1554,9 +1519,9 @@ "license": "MIT" }, "node_modules/baseline-browser-mapping": { - "version": "2.10.8", - "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.8.tgz", - "integrity": "sha512-PCLz/LXGBsNTErbtB6i5u4eLpHeMfi93aUv5duMmj6caNu6IphS4q6UevDnL36sZQv9lrP11dbPKGMaXPwMKfQ==", + "version": "2.10.14", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.14.tgz", + "integrity": "sha512-fOVLPAsFTsQfuCkvahZkzq6nf8KvGWanlYoTh0SVA0A/PIUxQGU2AOZAoD95n2gFLVDW/jP6sbGLny95nmEuHA==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1580,9 +1545,9 @@ } }, "node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", "dev": true, "license": "MIT", "dependencies": { @@ -1604,9 +1569,9 @@ } }, "node_modules/browserslist": { - "version": "4.28.1", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", - "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", "dev": true, "funding": [ { @@ -1624,11 +1589,11 @@ ], "license": "MIT", "dependencies": { - "baseline-browser-mapping": "^2.9.0", - "caniuse-lite": "^1.0.30001759", - "electron-to-chromium": "^1.5.263", - "node-releases": "^2.0.27", - "update-browserslist-db": "^1.2.0" + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" }, "bin": { "browserslist": "cli.js" @@ -1671,9 +1636,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001779", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001779.tgz", - "integrity": "sha512-U5og2PN7V4DMgF50YPNtnZJGWVLFjjsN3zb6uMT5VGYIewieDj1upwfuVNXf4Kor+89c3iCRJnSzMD5LmTvsfA==", + "version": "1.0.30001785", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001785.tgz", + "integrity": "sha512-blhOL/WNR+Km1RI/LCVAvA73xplXA7ZbjzI4YkMK9pa6T/P3F2GxjNpEkyw5repTw9IvkyrjyHpwjnhZ5FOvYQ==", "dev": true, "funding": [ { @@ -1922,9 +1887,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.313", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.313.tgz", - "integrity": "sha512-QBMrTWEf00GXZmJyx2lbYD45jpI3TUFnNIzJ5BBc8piGUDwMPa1GV6HJWTZVvY/eiN3fSopl7NRbgGp9sZ9LTA==", + "version": "1.5.331", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", + "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", "dev": true, "license": "ISC" }, @@ -2241,24 +2206,6 @@ "reusify": "^1.0.4" } }, - "node_modules/fdir": { - "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" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -2317,9 +2264,9 @@ } }, "node_modules/flatted": { - "version": "3.4.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", - "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", "dev": true, "license": "ISC" }, @@ -2374,12 +2321,12 @@ } }, "node_modules/framer-motion": { - "version": "12.36.0", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.36.0.tgz", - "integrity": "sha512-4PqYHAT7gev0ke0wos+PyrcFxI0HScjm3asgU8nSYa8YzJFuwgIvdj3/s3ZaxLq0bUSboIn19A2WS/MHwLCvfw==", + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz", + "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==", "license": "MIT", "dependencies": { - "motion-dom": "^12.36.0", + "motion-dom": "^12.38.0", "motion-utils": "^12.36.0", "tslib": "^2.4.0" }, @@ -3156,19 +3103,6 @@ "node": ">=8.6" } }, - "node_modules/micromatch/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -3204,9 +3138,9 @@ } }, "node_modules/motion-dom": { - "version": "12.36.0", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.36.0.tgz", - "integrity": "sha512-Ep1pq8P88rGJ75om8lTCA13zqd7ywPGwCqwuWwin6BKc0hMLkVfcS6qKlRqEo2+t0DwoUcgGJfXwaiFn4AOcQA==", + "version": "12.38.0", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz", + "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==", "license": "MIT", "dependencies": { "motion-utils": "^12.36.0" @@ -3264,9 +3198,9 @@ "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.36.tgz", - "integrity": "sha512-TdC8FSgHz8Mwtw9g5L4gR/Sh9XhSP/0DEkQxfEFXOpiul5IiHgHan2VhYYb6agDSfp4KuvltmGApc8HMgUrIkA==", + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", "dev": true, "license": "MIT" }, @@ -3398,13 +3332,13 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "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": ">=12" + "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -3604,10 +3538,13 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/punycode": { "version": "2.3.1", @@ -3679,9 +3616,9 @@ } }, "node_modules/react-router": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.13.1.tgz", - "integrity": "sha512-td+xP4X2/6BJvZoX6xw++A2DdEi++YypA69bJUV5oVvqf6/9/9nNlD70YO1e9d3MyamJEBQFEzk6mbfDYbqrSA==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.14.0.tgz", + "integrity": "sha512-m/xR9N4LQLmAS0ZhkY2nkPA1N7gQ5TUVa5n8TgANuDTARbn1gt+zLPXEm7W0XDTbrQ2AJSJKhoa6yx1D8BcpxQ==", "license": "MIT", "dependencies": { "cookie": "^1.0.1", @@ -3701,12 +3638,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.13.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.13.1.tgz", - "integrity": "sha512-UJnV3Rxc5TgUPJt2KJpo1Jpy0OKQr0AjgbZzBFjaPJcFOb2Y8jA5H3LT8HUJAiRLlWrEXWHbF1Z4SCZaQjWDHw==", + "version": "7.14.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.14.0.tgz", + "integrity": "sha512-2G3ajSVSZMEtmTjIklRWlNvo8wICEpLihfD/0YMDxbWK2UyP5EGfnoIn9AIQGnF3G/FX0MRbHXdFcD+rL1ZreQ==", "license": "MIT", "dependencies": { - "react-router": "7.13.1" + "react-router": "7.14.0" }, "engines": { "node": ">=20.0.0" @@ -3739,19 +3676,6 @@ "node": ">=8.10.0" } }, - "node_modules/readdirp/node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", @@ -3795,14 +3719,14 @@ } }, "node_modules/rolldown": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz", - "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.115.0", - "@rolldown/pluginutils": "1.0.0-rc.9" + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" }, "bin": { "rolldown": "bin/cli.mjs" @@ -3811,27 +3735,44 @@ "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { - "@rolldown/binding-android-arm64": "1.0.0-rc.9", - "@rolldown/binding-darwin-arm64": "1.0.0-rc.9", - "@rolldown/binding-darwin-x64": "1.0.0-rc.9", - "@rolldown/binding-freebsd-x64": "1.0.0-rc.9", - "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9", - "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9", - "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9", - "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9", - "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9", - "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9", - "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9", - "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9" + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + } + }, + "node_modules/rolldown/node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, "node_modules/rolldown/node_modules/@rolldown/pluginutils": { - "version": "1.0.0-rc.9", - "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz", - "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==", + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", "dev": true, "license": "MIT" }, @@ -4054,6 +3995,37 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, + "node_modules/tinyglobby/node_modules/fdir": { + "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" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4068,9 +4040,9 @@ } }, "node_modules/ts-api-utils": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", - "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", "dev": true, "license": "MIT", "engines": { @@ -4121,16 +4093,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.0.tgz", - "integrity": "sha512-W8GcigEMEeB07xEZol8oJ26rigm3+bfPHxHvwbYUlu1fUDsGuQ7Hiskx5xGW/xM4USc9Ephe3jtv7ZYPQntHeA==", + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.57.0", - "@typescript-eslint/parser": "8.57.0", - "@typescript-eslint/typescript-estree": "8.57.0", - "@typescript-eslint/utils": "8.57.0" + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -4141,7 +4113,7 @@ }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", - "typescript": ">=4.8.4 <6.0.0" + "typescript": ">=4.8.4 <6.1.0" } }, "node_modules/undici-types": { @@ -4199,17 +4171,16 @@ "license": "MIT" }, "node_modules/vite": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz", - "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", + "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", "dev": true, "license": "MIT", "dependencies": { - "@oxc-project/runtime": "0.115.0", "lightningcss": "^1.32.0", - "picomatch": "^4.0.3", + "picomatch": "^4.0.4", "postcss": "^8.5.8", - "rolldown": "1.0.0-rc.9", + "rolldown": "1.0.0-rc.12", "tinyglobby": "^0.2.15" }, "bin": { @@ -4226,7 +4197,7 @@ }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", - "@vitejs/devtools": "^0.0.0-alpha.31", + "@vitejs/devtools": "^0.1.0", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", @@ -4277,6 +4248,19 @@ } } }, + "node_modules/vite/node_modules/picomatch": { + "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" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4304,9 +4288,9 @@ } }, "node_modules/ws": { - "version": "8.19.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", - "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/frontend/public/logo.svg b/frontend/public/logo.svg new file mode 100644 index 0000000..e665c5c --- /dev/null +++ b/frontend/public/logo.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 52c58c2..5507275 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -1,22 +1,75 @@ -import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom'; +import { BrowserRouter, Routes, Route, Navigate, useNavigate } from 'react-router-dom'; import { AuthProvider, useAuth } from './context/AuthContext'; import Login from './pages/Login'; import Signup from './pages/Signup'; import Dashboard from './components/admin/Dashboard'; import RequestInterface from './components/user/RequestInterface'; import DeveloperPortal from './components/developer/DeveloperPortal'; +import DocsPage from './pages/DocsPage'; +import MarketplacePage from './pages/MarketplacePage'; +import PremiumLayout from './components/PremiumLayout'; +import PremiumNav from './components/PremiumNav'; import { Toaster } from 'react-hot-toast'; -import { useState, useEffect } from 'react'; -import { getInviteCode } from './api/client'; -import toast from 'react-hot-toast'; + +// Page wrappers for direct route access +function AdminPage() { + const { user, company, logout } = useAuth(); + const navigate = useNavigate(); + + const handleLogout = () => { + logout(); + navigate('/login'); + }; + + if (!user || !company) return ; + return ; +} + +function WorkPage() { + const { user, company, logout } = useAuth(); + const navigate = useNavigate(); + + const handleLogout = () => { + logout(); + navigate('/login'); + }; + + if (!user) return ; + return ( +
+ + +
+ +
+
+
+ ); +} + +function DeveloperPage() { + const { user, logout } = useAuth(); + const navigate = useNavigate(); + + const handleLogout = () => { + logout(); + navigate('/login'); + }; + + if (!user) return ; + return ; +} function ProtectedRoute({ children }: { children: React.ReactNode }) { const { isAuthenticated, loading } = useAuth(); if (loading) { return ( -
-
Loading...
+
+
+
+

Loading your workspace...

+
); } @@ -30,120 +83,32 @@ function ProtectedRoute({ children }: { children: React.ReactNode }) { function AppLayout() { const { user, company, logout } = useAuth(); - - const [inviteCode, setInviteCode] = useState(''); - const [inviteLoading, setInviteLoading] = useState(false); - const [showInviteModal, setShowInviteModal] = useState(false); + const navigate = useNavigate(); - const fetchInviteCode = () => { - if (!company?.company_id) return; - setInviteLoading(true); - getInviteCode() - .then(data => { - setInviteCode(data.invite_code || 'N/A'); - }) - .catch(err => { - console.error('Failed to get invite code:', err); - setInviteCode('ERROR'); - }) - .finally(() => { - setInviteLoading(false); - }); + const handleLogout = () => { + logout(); + navigate('/login'); }; - useEffect(() => { - if (company?.company_id) { - fetchInviteCode(); - } - }, [company]); - - const copyInviteCode = () => { - navigator.clipboard.writeText(inviteCode); - toast.success('Invite code copied!'); - }; + // If no user, redirect to login + if (!user) { + return ; + } return ( -
-
-
-
- AM -
- Agent Marketplace -
- -
- {company && ( - - )} -
- {user?.full_name || user?.email} - - {company ? `Admin · ${company.company_name}` : 'Personal Account'} - -
- -
-
- - {/* Invite Modal */} - {showInviteModal && company && ( -
setShowInviteModal(false)}> -
e.stopPropagation()}> -
-

Invite Team Members

-

Share this invite code with your colleagues

- -
- {inviteLoading ? ( -

Loading...

- ) : ( -

{inviteCode}

- )} -
- - - -

- Company: {company.company_name} -

- - -
-
+
+ {/* Render appropriate component based on user role */} + {user.role === 'admin' && company ? ( + + ) : user.role === 'developer' ? ( + + ) : user.role === 'user' || user.role === 'end_user' ? ( + + ) : ( +
+

Invalid user role: {user.role}

)} - -
- {user?.role === 'admin' && company && ( - - )} - {(!company || user?.role !== 'admin') && ( - - )} -
); } @@ -153,8 +118,11 @@ function AppRoutes() { if (loading) { return ( -
-
Loading...
+
+
+
+

Loading your workspace...

+
); } @@ -163,6 +131,11 @@ function AppRoutes() { : } /> : } /> + } /> + } /> + } /> + } /> + } /> } /> ); @@ -172,7 +145,14 @@ export default function App() { return ( - + diff --git a/frontend/src/api/client.ts b/frontend/src/api/client.ts index fae79c9..7984222 100644 --- a/frontend/src/api/client.ts +++ b/frontend/src/api/client.ts @@ -120,6 +120,13 @@ export const getAdminMetrics = async (company_id: string, agent_id?: string) => return data; }; +export const getCompanyMetrics = async (company_id: string, timeRange: '24h' | '7d' | '30d' = '7d') => { + const { data } = await api.get(`/api/company/${company_id}/metrics`, { + params: { range: timeRange }, + }); + return data; +}; + // ── Credentials ─────────────────────────────────────────────────────────────── export const generateCredentials = async (company_id: string, agent_id: string) => { const { data } = await api.post( diff --git a/frontend/src/components/PremiumCTA.tsx b/frontend/src/components/PremiumCTA.tsx new file mode 100644 index 0000000..e7685ef --- /dev/null +++ b/frontend/src/components/PremiumCTA.tsx @@ -0,0 +1,71 @@ +interface PremiumCTAProps { + title: string; + subtitle?: string; + primaryCta: { label: string; action: () => void }; + secondaryCta?: { label: string; action: () => void }; + backgroundImage?: string; +} + +export default function PremiumCTA({ + title, + subtitle, + primaryCta, + secondaryCta, + backgroundImage, +}: PremiumCTAProps) { + return ( +
+ {/* Background */} + {backgroundImage ? ( +
+
+
+ ) : ( +
+ )} + + {/* Animated Elements */} +
+
+
+ + {/* Content */} +
+ {/* Title */} +

+ {title} +

+ + {/* Subtitle */} + {subtitle && ( +

+ {subtitle} +

+ )} + + {/* Buttons */} +
+ + + {secondaryCta && ( + + )} +
+
+
+ ); +} diff --git a/frontend/src/components/PremiumFeatureCard.tsx b/frontend/src/components/PremiumFeatureCard.tsx new file mode 100644 index 0000000..2d7150d --- /dev/null +++ b/frontend/src/components/PremiumFeatureCard.tsx @@ -0,0 +1,108 @@ +import { type ReactNode } from 'react'; + +interface FeatureCardProps { + icon?: ReactNode; + title: string; + description: string; + features?: string[]; + image?: string; + sizeLarge?: boolean; + gradient?: 'blue' | 'purple' | 'emerald' | 'amber' | 'gray'; + onClick?: () => void; +} + +export default function PremiumFeatureCard({ + icon, + title, + description, + features, + image, + sizeLarge = false, + gradient = 'gray', + onClick, +}: FeatureCardProps) { + const gradientColors = { + blue: 'from-blue-600/20 to-blue-400/10', + purple: 'from-purple-600/20 to-purple-400/10', + emerald: 'from-emerald-600/20 to-emerald-400/10', + amber: 'from-amber-600/20 to-amber-400/10', + gray: 'from-white/10 to-white/5', + }; + + return ( +
+ {/* Background Gradient */} +
+ + {/* Image Background */} + {image && ( +
+
+
+ )} + + {/* Animated Border Glow */} +
+ + {/* Content */} +
+ {/* Icon */} + {icon && ( +
+ {icon} +
+ )} + + {/* Title */} +
+

+ {title} +

+ + {/* Description */} +

+ {description} +

+ + {/* Features List */} + {features && features.length > 0 && ( +
    + {features.map((feature, idx) => ( +
  • + + {feature} +
  • + ))} +
+ )} +
+ + {/* Bottom Accent */} +
+ + Learn More + + + + +
+
+
+ ); +} diff --git a/frontend/src/components/PremiumFooter.tsx b/frontend/src/components/PremiumFooter.tsx new file mode 100644 index 0000000..4ab0997 --- /dev/null +++ b/frontend/src/components/PremiumFooter.tsx @@ -0,0 +1,165 @@ +import { useState } from 'react'; + +interface FooterLink { + label: string; + href: string; +} + +interface FooterSection { + title: string; + links: FooterLink[]; +} + +interface PremiumFooterProps { + sections?: FooterSection[]; + companyName?: string; + socialLinks?: { icon: string; href: string; label: string }[]; +} + +export default function PremiumFooter({ + sections, + companyName = 'AgentForge', + socialLinks, +}: PremiumFooterProps) { + const [email, setEmail] = useState(''); + const [subscribed, setSubscribed] = useState(false); + + const handleSubscribe = (e: React.FormEvent) => { + e.preventDefault(); + if (email) { + setSubscribed(true); + setTimeout(() => setSubscribed(false), 3000); + setEmail(''); + } + }; + + const defaultSections: FooterSection[] = [ + { + title: 'Product', + links: [ + { label: 'Marketplace', href: '#' }, + { label: 'Pricing', href: '#' }, + { label: 'Documentation', href: '#' }, + { label: 'API', href: '#' }, + ], + }, + { + title: 'Company', + links: [ + { label: 'About', href: '#' }, + { label: 'Blog', href: '#' }, + { label: 'Careers', href: '#' }, + { label: 'Contact', href: '#' }, + ], + }, + { + title: 'Legal', + links: [ + { label: 'Privacy', href: '#' }, + { label: 'Terms', href: '#' }, + { label: 'Security', href: '#' }, + { label: 'Compliance', href: '#' }, + ], + }, + ]; + + const footerSections = sections || defaultSections; + + return ( +
+
+ {/* Main Footer Content */} +
+ {/* Brand Section */} +
+
+ AgentForge Logo +

{companyName}

+
+ +

+ The intelligent AI agent marketplace. Connect, deploy, and scale autonomous AI workflows. +

+ + {/* Newsletter Signup */} +
+ +
+ setEmail(e.target.value)} + required + className="flex-1 bg-white/5 border border-white/10 text-white px-3 py-2 rounded text-sm placeholder-gray-500 focus:border-white/30 outline-none transition" + /> + +
+
+
+ + {/* Footer Links */} + {footerSections.map((section, idx) => ( +
+

+ {section.title} +

+ +
+ ))} +
+ + {/* Bottom Bar */} +
+ {/* Copyright */} +

+ © {new Date().getFullYear()} {companyName}. All rights reserved. +

+ + {/* Social Links */} + {socialLinks && socialLinks.length > 0 && ( +
+ {socialLinks.map((social) => ( + + {social.icon} + + ))} +
+ )} + + {/* Right Side Text */} +

+ Made with intention +

+
+
+
+ ); +} diff --git a/frontend/src/components/PremiumHero.tsx b/frontend/src/components/PremiumHero.tsx new file mode 100644 index 0000000..9309e95 --- /dev/null +++ b/frontend/src/components/PremiumHero.tsx @@ -0,0 +1,97 @@ +interface HeroProps { + title: string; + subtitle?: string; + cta?: { label: string; action: () => void }; + secondaryCta?: { label: string; action: () => void }; + backgroundImage?: string; +} + +export default function PremiumHero({ + title, + subtitle, + cta, + secondaryCta, + backgroundImage, +}: HeroProps) { + return ( +
+ {/* Background Elements */} + {backgroundImage ? ( +
+ {/* Dark Overlay */} +
+
+ ) : ( +
+ )} + + {/* Animated Gradient Background */} +
+
+
+
+ + {/* Content */} +
+ {/* Pre-Title Badge */} +
+ + + Welcome to AgentForge + +
+ + {/* Main Title - Dramatic Typography */} +

+ {title.split(' ')[0]} + {title.split(' ').length > 1 && ( + <> +
+ {title.split(' ').slice(1).join(' ')} + + )} +

+ + {/* Subtitle */} + {subtitle && ( +

+ {subtitle} +

+ )} + + {/* CTA Buttons */} +
+ {cta && ( + + )} + + {secondaryCta && ( + + )} +
+ + {/* Scroll Indicator */} +
+ Scroll to explore + + + +
+
+
+ ); +} diff --git a/frontend/src/components/PremiumLayout.tsx b/frontend/src/components/PremiumLayout.tsx new file mode 100644 index 0000000..c40506c --- /dev/null +++ b/frontend/src/components/PremiumLayout.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import PremiumNav from './PremiumNav'; +import PremiumFooter from './PremiumFooter'; + +interface PremiumLayoutProps { + children: React.ReactNode; + navProps?: { + companyName?: string; + userEmail?: string; + onLogout?: () => void; + }; + showFooter?: boolean; + footerProps?: { + companyName?: string; + }; +} + +export default function PremiumLayout({ + children, + navProps, + showFooter = true, + footerProps, +}: PremiumLayoutProps) { + return ( +
+ {/* Navigation */} + + + {/* Main Content */} +
+ {children} +
+ + {/* Footer */} + {showFooter && ( + + )} +
+ ); +} diff --git a/frontend/src/components/PremiumNav.tsx b/frontend/src/components/PremiumNav.tsx new file mode 100644 index 0000000..a41ad8e --- /dev/null +++ b/frontend/src/components/PremiumNav.tsx @@ -0,0 +1,168 @@ +import { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; + +interface NavProps { + companyName?: string; + userEmail?: string; + onLogout?: () => void; +} + +export default function PremiumNav({ userEmail, onLogout }: NavProps) { + const navigate = useNavigate(); + const [isScrolled, setIsScrolled] = useState(false); + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + + useEffect(() => { + const handleScroll = () => { + setIsScrolled(window.scrollY > 20); + }; + + window.addEventListener('scroll', handleScroll); + return () => window.removeEventListener('scroll', handleScroll); + }, []); + + const navItems = [ + { label: 'Work', href: '/work' }, + { label: 'Marketplace', href: '/marketplace' }, + { label: 'Admin', href: '/admin' }, + ]; + + return ( + <> + {/* Backdrop for mobile menu */} + {isMobileMenuOpen && ( +
setIsMobileMenuOpen(false)} + /> + )} + + {/* Premium Navigation */} + + + {/* Spacer */} +
+ + ); +} diff --git a/frontend/src/components/PremiumStats.tsx b/frontend/src/components/PremiumStats.tsx new file mode 100644 index 0000000..b282d49 --- /dev/null +++ b/frontend/src/components/PremiumStats.tsx @@ -0,0 +1,87 @@ +interface StatItem { + number: string | number; + label: string; + description?: string; + trend?: { value: number; isPositive: boolean }; +} + +interface PremiumStatsProps { + stats: StatItem[]; + className?: string; +} + +export default function PremiumStats({ stats, className = '' }: PremiumStatsProps) { + return ( +
+
+ {/* Section Title */} +
+

+ By The Numbers +

+

+ Our platform powers intelligent AI automation at scale +

+
+ + {/* Stats Grid */} +
+ {stats.map((stat, idx) => ( +
+ {/* Card Background */} +
+
+ + {/* Hover Glow */} +
+ + {/* Content */} +
+ {/* Number */} +
+ + {stat.number} + + {stat.trend && ( + + {stat.trend.isPositive ? '+' : '-'}{stat.trend.value}% + + )} +
+ + {/* Label */} +

+ {stat.label} +

+ + {/* Description */} + {stat.description && ( +

+ {stat.description} +

+ )} + + {/* Bottom Accent */} +
+
+
+
+
+ ))} +
+
+
+ ); +} diff --git a/frontend/src/components/admin/Dashboard.tsx b/frontend/src/components/admin/Dashboard.tsx index a5ac0c7..e79decd 100644 --- a/frontend/src/components/admin/Dashboard.tsx +++ b/frontend/src/components/admin/Dashboard.tsx @@ -6,20 +6,24 @@ import Marketplace from './Marketplace'; import OnboardingWizard from './OnboardingWizard'; import UpdateNotification from './UpdateNotification'; import CompanyAPISettings from './CompanyAPISettings'; +import MetricsDashboard from './MetricsDashboard'; +import PremiumNav from '../PremiumNav'; import toast from 'react-hot-toast'; interface Props { companyId: string; userEmail: string; + onLogout?: () => void; } -export default function Dashboard({ companyId, userEmail }: Props) { +type DashboardTab = 'portfolio' | 'marketplace' | 'metrics' | 'settings'; + +export default function Dashboard({ companyId, userEmail, onLogout }: Props) { const [agents, setAgents] = useState([]); const [loading, setLoading] = useState(true); - const [showMarketplace, setShowMarketplace] = useState(false); + const [activeTab, setActiveTab] = useState('portfolio'); const [manualUpdates, setManualUpdates] = useState([]); const [showUpdateModal, setShowUpdateModal] = useState(null); - const [showAPISettings, setShowAPISettings] = useState(false); const fetchDashboard = async () => { setLoading(true); @@ -49,7 +53,7 @@ export default function Dashboard({ companyId, userEmail }: Props) { if (auto.length > 0) { await applyCompanyUpdates(companyId); toast.success(`${auto.length} agent(s) were automatically updated!`); - fetchDashboard(); // Refresh to see new versions + fetchDashboard(); } } } catch (e) { @@ -62,83 +66,128 @@ export default function Dashboard({ companyId, userEmail }: Props) { }, [companyId]); if (loading) { - return
Loading Portfolio
; + return ( +
+ +
+
+
+

Loading portfolio...

+
+
+
+ ); } // Onboarding - if (agents.length === 0 && !showMarketplace) { - return ; + if (agents.length === 0 && activeTab === 'portfolio') { + return ( +
+ + +
+ ); } return ( -
- {/* Header Bar */} -
+
+ +
+ {/* Header */} +
-

- {showAPISettings ? 'API Settings' : showMarketplace ? 'Marketplace' : 'Agent Portfolio'} +

+ Admin Portal

-

- {showAPISettings - ? 'Manage your company API key and view available upgrades' - : showMarketplace - ? 'Browse and add new AI agents to your portfolio' - : 'Manage your company\'s active AI agents automatically.'} -

+

Manage your AI agent portfolio and settings

-
- {manualUpdates.length > 0 && ( - - )} - + {manualUpdates.length > 0 && ( - - -
+ )}
- {/* Main Content */} -
- {showAPISettings ? ( - - ) : showMarketplace ? ( - a.agent_id)} /> - ) : ( -
- {agents.map(agent => ( - - ))} + {/* Tab Navigation */} +
+ setActiveTab('portfolio')} + label="Portfolio" + icon="🤖" + badge={agents.length > 0 ? agents.length : undefined} + /> + setActiveTab('marketplace')} + label="Marketplace" + icon="🌐" + /> + setActiveTab('metrics')} + label="Metrics" + icon="📊" + /> + setActiveTab('settings')} + label="Settings" + icon="⚙️" + /> +
+ + {/* Content */} +
+ {activeTab === 'portfolio' && ( +
+
+

Your Agent Portfolio

+

Manage and monitor your active AI agents

+
+ + + +
+ {agents.map(agent => ( + + ))} +
)} + + {activeTab === 'marketplace' && ( + a.agent_id)} + /> + )} + + {activeTab === 'metrics' && ( + + )} + + {activeTab === 'settings' && ( + + )}
{/* Update Modal */} @@ -154,5 +203,39 @@ export default function Dashboard({ companyId, userEmail }: Props) { /> )}
+
+ ); +} + +function Tab({ + active, + onClick, + label, + icon, + badge +}: { + active: boolean + onClick: () => void + label: string + icon: string + badge?: number +}) { + return ( + ); } diff --git a/frontend/src/components/admin/EnhancedMarketplace.tsx b/frontend/src/components/admin/EnhancedMarketplace.tsx new file mode 100644 index 0000000..c7efa43 --- /dev/null +++ b/frontend/src/components/admin/EnhancedMarketplace.tsx @@ -0,0 +1,187 @@ +import { useState, useEffect } from 'react'; +import type { MarketplaceAgent } from '../../types'; +import { getMarketplaceAgents, getMarketplaceCapabilities, addAgentToCompany } from '../../api/client'; +import LoadingAnimation from '../shared/LoadingAnimation'; +import EmptyState from '../shared/EmptyState'; +import toast from 'react-hot-toast'; + +interface Props { + companyId: string; + onAgentAdded: () => void; + existingAgentIds?: string[]; +} + +export default function EnhancedMarketplace({ companyId, onAgentAdded, existingAgentIds = [] }: Props) { + const [agents, setAgents] = useState([]); + const [capabilities, setCapabilities] = useState([]); + const [filter, setFilter] = useState(''); + const [loading, setLoading] = useState(true); + const [addingId, setAddingId] = useState(null); + const [searchQuery, setSearchQuery] = useState(''); + + useEffect(() => { + const fetchData = async () => { + setLoading(true); + try { + const [agentsRes, capsRes] = await Promise.all([ + getMarketplaceAgents(filter), + getMarketplaceCapabilities() + ]); + setAgents(agentsRes.agents); + setCapabilities(capsRes.capabilities); + } catch (error) { + console.error(error); + toast.error('Failed to load marketplace'); + } finally { + setLoading(false); + } + }; + fetchData(); + }, [filter]); + + const handleAdd = async (agent: MarketplaceAgent) => { + setAddingId(agent.agent_id); + try { + await addAgentToCompany(companyId, agent.agent_id, true); + toast.success(`${agent.agent_name} added to portfolio`); + onAgentAdded(); + } catch { + toast.error('Failed to add agent'); + } finally { + setAddingId(null); + } + }; + + const filteredAgents = agents.filter(a => + a.agent_name.toLowerCase().includes(searchQuery.toLowerCase()) || + a.description.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + return ( +
+ {/* Header */} +
+
+

🌐 Global Marketplace

+

Discover and add AI agents to your portfolio

+
+ + {/* Search & Filter Bar */} +
+ setSearchQuery(e.target.value)} + className="px-4 py-3 bg-white/5 border border-white/10 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:border-transparent transition-all" + /> + + +
+
+ + {loading ? ( +
+ +
+ ) : filteredAgents.length === 0 ? ( + + ) : ( +
+ {filteredAgents.map((agent, idx) => { + const isFeatured = idx < 2; // First 2 are featured (larger) + const isAlreadyAdded = existingAgentIds.includes(agent.agent_id); + + return ( +
+ {/* Background with gradient */} +
+ {/* Top section */} +
+
+
+

+ {agent.agent_name} +

+
+ + v{agent.version} + + by {agent.provider} +
+
+
+ +

+ {agent.description} +

+ + {/* Capabilities */} +
+ {agent.capabilities.slice(0, isFeatured ? 6 : 3).map(cap => ( + + {cap.replace(/_/g, ' ')} + + ))} + {agent.capabilities.length > (isFeatured ? 6 : 3) && ( + + +{agent.capabilities.length - (isFeatured ? 6 : 3)} more + + )} +
+
+ + {/* Bottom section with pricing and button */} +
+
+ {agent.pricing_model === 'free' ? '✨ Free' : '💎 Premium'} +
+ + {isAlreadyAdded ? ( +
+ ✓ Added +
+ ) : ( + + )} +
+
+
+ ); + })} +
+ )} +
+ ); +} diff --git a/frontend/src/components/admin/EnhancedMetricsDashboard.tsx b/frontend/src/components/admin/EnhancedMetricsDashboard.tsx new file mode 100644 index 0000000..d8b87c4 --- /dev/null +++ b/frontend/src/components/admin/EnhancedMetricsDashboard.tsx @@ -0,0 +1,273 @@ +import { useState, useEffect } from 'react'; +import toast from 'react-hot-toast'; +import { getAdminMetrics, getCompanyMetrics } from '../../api/client'; +import LoadingAnimation from '../shared/LoadingAnimation'; +import EmptyState from '../shared/EmptyState'; + +interface MetricsData { + agent_name: string; + agent_id: string; + time_period: string; + metrics: { + total_executions: number; + success_rate: number; + average_quality_score: number; + average_execution_time: number; + total_tokens_used: number; + estimated_cost: number; + }; + quality_trend: Array<{ date: string; score: number }>; + recent_failures: Array<{ execution_id: string; error: string; timestamp: string }>; +} + +interface CompanyMetricsData { + total_requests: number; + successful_requests: number; + failed_requests: number; + success_rate: number; + avg_response_time: number; + agents: Array<{ + agent_id: string; + agent_name: string; + total_requests: number; + successful_requests: number; + failed_requests: number; + success_rate: number; + avg_response_time: number; + }>; + daily_requests: Array<{ date: string; count: number }>; +} + +interface Props { + companyId: string; + agentId?: string; + useCompanyMetrics?: boolean; +} + +interface StatCardProps { + label: string; + value: string | number; + icon: string; + trend?: number; + color?: 'indigo' | 'emerald' | 'blue' | 'red'; +} + +function StatCard({ label, value, icon, trend, color = 'indigo' }: StatCardProps) { + const colorMap: Record<'indigo' | 'emerald' | 'blue' | 'red', string> = { + indigo: 'from-indigo-500/20 to-indigo-600/20 border-indigo-500/30 text-indigo-200', + emerald: 'from-emerald-500/20 to-emerald-600/20 border-emerald-500/30 text-emerald-200', + blue: 'from-blue-500/20 to-blue-600/20 border-blue-500/30 text-blue-200', + red: 'from-red-500/20 to-red-600/20 border-red-500/30 text-red-200', + }; + + return ( +
+
+
{icon}
+ {trend && ( + 0 ? 'bg-emerald-500/20 text-emerald-300' : 'bg-red-500/20 text-red-300'}`}> + {trend > 0 ? '+' : ''}{trend}% + + )} +
+

{label}

+

{value}

+
+ ); +} + +export default function EnhancedMetricsDashboard({ companyId, agentId, useCompanyMetrics = false }: Props) { + const [agentMetrics, setAgentMetrics] = useState(null); + const [companyMetricsData, setCompanyMetricsData] = useState(null); + const [loading, setLoading] = useState(true); + const [timeRange, setTimeRange] = useState<'24h' | '7d' | '30d'>('7d'); + + useEffect(() => { + const fetchMetrics = async () => { + setLoading(true); + try { + if (useCompanyMetrics) { + const data = await getCompanyMetrics(companyId, timeRange); + setCompanyMetricsData(data); + } else { + const data = await getAdminMetrics(companyId, agentId || undefined); + setAgentMetrics(data); + } + } catch (error) { + console.error('Failed to load metrics:', error); + toast.error('Failed to load metrics'); + } finally { + setLoading(false); + } + }; + + fetchMetrics(); + }, [companyId, agentId, timeRange, useCompanyMetrics]); + + if (loading) { + return ( +
+ +
+ ); + } + + if (useCompanyMetrics) { + if (!companyMetricsData) { + return ; + } + + const m = companyMetricsData; + + return ( +
+ {/* Header with Time Range */} +
+
+

Performance Analytics

+

Track all agent requests and performance metrics

+
+ +
+ {(['24h', '7d', '30d'] as const).map(range => ( + + ))} +
+
+ + {/* Key Stats Grid */} +
+ + + + +
+ + {/* Agents Breakdown */} +
+

Agent Breakdown

+
+ {m.agents.map(agent => ( +
+
+

{agent.agent_name}

+
+ Requests: {agent.total_requests} + Success: {agent.successful_requests} + Failed: {agent.failed_requests} +
+
+
+

{agent.success_rate.toFixed(1)}%

+

success rate

+
+
+ ))} +
+
+
+ ); + } + + // Agent-specific metrics + if (!agentMetrics) { + return ; + } + + return ( +
+
+

{agentMetrics.agent_name} Metrics

+

Period: {agentMetrics.time_period}

+
+ + {/* Summary Stats */} +
+ + + + + + +
+ + {agentMetrics.recent_failures.length > 0 && ( +
+

⚠️ Recent Failures

+
+ {agentMetrics.recent_failures.map((failure, idx) => ( +
+

{failure.error}

+

{failure.timestamp}

+
+ ))} +
+
+ )} +
+ ); +} diff --git a/frontend/src/components/admin/EnhancedOnboardingWizard.tsx b/frontend/src/components/admin/EnhancedOnboardingWizard.tsx new file mode 100644 index 0000000..304174a --- /dev/null +++ b/frontend/src/components/admin/EnhancedOnboardingWizard.tsx @@ -0,0 +1,256 @@ +import { useState, useEffect } from 'react'; +import type { MarketplaceAgent } from '../../types'; +import { getMarketplaceAgents, addAgentToCompany, getCompanyAgents } from '../../api/client'; +import toast from 'react-hot-toast'; + +interface Props { + companyId: string; + onComplete: () => void; +} + +const TASKS = [ + { id: 'summarization', label: 'Summarizing documents', icon: '📝', color: 'blue' }, + { id: 'research', label: 'Research and analysis', icon: '🔍', color: 'purple' }, + { id: 'content_creation', label: 'Content writing', icon: '✍️', color: 'indigo' }, + { id: 'data_analysis', label: 'Data processing', icon: '📊', color: 'cyan' }, + { id: 'mathematics', label: 'Mathematical calculations', icon: '🧮', color: 'emerald' }, + { id: 'translation', label: 'Translation', icon: '🌍', color: 'pink' }, +]; + +export default function EnhancedOnboardingWizard({ companyId, onComplete }: Props) { + const [step, setStep] = useState(1); + const [selectedTasks, setSelectedTasks] = useState([]); + const [recommendedAgents, setRecommendedAgents] = useState([]); + const [autoUpdateEnabled, setAutoUpdateEnabled] = useState(true); + const [loading, setLoading] = useState(false); + + useEffect(() => { + getCompanyAgents(companyId).then(({ agents }) => { + if (agents.length > 0) onComplete(); + }); + }, [companyId, onComplete]); + + const handleTaskSelect = (id: string) => { + setSelectedTasks(prev => + prev.includes(id) ? prev.filter(t => t !== id) : [...prev, id] + ); + }; + + const loadRecommendations = async () => { + if (selectedTasks.length === 0) { + toast.error('Please select at least one task'); + return; + } + setLoading(true); + try { + const { agents } = await getMarketplaceAgents(); + const recs = agents.filter(a => a.capabilities.some(c => selectedTasks.includes(c))); + const seen = new Set(); + const finalRecs = []; + for (const a of recs) { + if (!seen.has(a.agent_name)) { + seen.add(a.agent_name); + finalRecs.push(a); + } + } + setRecommendedAgents(finalRecs.slice(0, 4)); + setStep(3); + } catch { + toast.error('Failed to load recommendations'); + } finally { + setLoading(false); + } + }; + + const handleFinishSetup = async () => { + setLoading(true); + try { + await Promise.all( + recommendedAgents.map(a => addAgentToCompany(companyId, a.agent_id, autoUpdateEnabled)) + ); + setStep(5); + setTimeout(onComplete, 1000); + } catch { + toast.error('Setup failed'); + setLoading(false); + } + }; + + return ( +
+ {/* Animated background elements */} +
+
+ +
+ {/* STEP 1: Welcome */} + {step === 1 && ( +
+
+ +
+
+

+ Welcome to AgentForge! +

+

+ Let's set up your first AI agents. We'll recommend the perfect agents based on your needs. +

+
+ +
+ )} + + {/* STEP 2: Select Tasks */} + {step === 2 && ( +
+
+

What do you need?

+

Select the tasks you'd like help with

+
+ +
+ {TASKS.map((task, idx) => ( + + ))} +
+ +
+ + +
+
+ )} + + {/* STEP 3: Recommendations */} + {step === 3 && ( +
+
+

Perfect agents for you

+

We've found {recommendedAgents.length} great agents matching your needs

+
+ +
+ {recommendedAgents.map((agent, idx) => ( +
+
+
+

{agent.agent_name}

+

{agent.description}

+
+ {agent.capabilities.slice(0, 4).map(cap => ( + + {cap.replace(/_/g, ' ')} + + ))} +
+
+
+ {agent.pricing_model === 'free' ? 'Free' : 'Premium'} +
+
+
+ ))} +
+ +
+ setAutoUpdateEnabled(e.target.checked)} + className="mt-1 cursor-pointer" + id="auto-update" + /> + +
+ +
+ + +
+
+ )} + + {/* STEP 5: Success */} + {step === 5 && ( +
+
+ 🎉 +
+
+

You're all set!

+

+ {recommendedAgents.length} {recommendedAgents.length === 1 ? 'agent' : 'agents'} have been added to your portfolio +

+
+ +
+ )} +
+
+ ); +} diff --git a/frontend/src/components/admin/MetricsDashboard.tsx b/frontend/src/components/admin/MetricsDashboard.tsx new file mode 100644 index 0000000..9145a7b --- /dev/null +++ b/frontend/src/components/admin/MetricsDashboard.tsx @@ -0,0 +1,447 @@ +import { useState, useEffect } from 'react'; +import { getAdminMetrics, getCompanyMetrics } from '../../api/client'; + +interface MetricsData { + agent_name: string; + agent_id: string; + time_period: string; + metrics: { + total_executions: number; + success_rate: number; + average_quality_score: number; + average_execution_time: number; + total_tokens_used: number; + estimated_cost: number; + }; + quality_trend: Array<{ date: string; score: number }>; + recent_failures: Array<{ execution_id: string; error: string; timestamp: string }>; +} + +interface CompanyMetricsData { + total_requests: number; + successful_requests: number; + failed_requests: number; + success_rate: number; + avg_response_time: number; + agents: Array<{ + agent_id: string; + agent_name: string; + total_requests: number; + successful_requests: number; + failed_requests: number; + success_rate: number; + avg_response_time: number; + }>; + daily_requests: Array<{ date: string; count: number }>; +} + +interface Props { + companyId: string; + agentId?: string; + useCompanyMetrics?: boolean; // If true, fetches company-wide metrics, else agent-specific +} + +export default function MetricsDashboard({ companyId, agentId, useCompanyMetrics = false }: Props) { + const [agentMetrics, setAgentMetrics] = useState(null); + const [companyMetricsData, setCompanyMetricsData] = useState(null); + const [loading, setLoading] = useState(true); + const [timeRange, setTimeRange] = useState<'24h' | '7d' | '30d'>('7d'); + + useEffect(() => { + const fetchMetrics = async () => { + setLoading(true); + try { + if (useCompanyMetrics) { + const data = await getCompanyMetrics(companyId, timeRange); + setCompanyMetricsData(data); + } else { + const data = await getAdminMetrics(companyId, agentId || undefined); + setAgentMetrics(data); + } + } catch (error) { + console.error('Failed to load metrics:', error); + // Don't show error toast - metrics are optional and data can be populated when agents are used + // Metrics will become available once agents start being executed + } finally { + setLoading(false); + } + }; + + fetchMetrics(); + }, [companyId, agentId, timeRange, useCompanyMetrics]); + + if (loading) { + return ( +
+
+
+
+

Loading metrics...

+
+
+
+ ); + } + + if (useCompanyMetrics) { + // Company-wide metrics view + if (!companyMetricsData) { + return ( +
+
+
+
📊
+

No metrics available yet

+

Metrics will appear here once you start executing agents. Run your first agent request to see performance data.

+
+
+
+ ); + } + + const m = companyMetricsData; + + return ( +
+ {/* Header with Time Range */} +
+
+

Performance Metrics

+

Track agent requests and performance

+
+ +
+ {(['24h', '7d', '30d'] as const).map(range => ( + + ))} +
+
+ + {/* Key Stats Grid */} +
+ + + + +
+ + {/* Charts Section */} +
+ {/* Request Timeline */} +
+

Request Timeline

+
+ {m.daily_requests.map((day, idx) => { + const maxCount = Math.max(...m.daily_requests.map(d => d.count), 1); + const height = (day.count / maxCount) * 100; + return ( +
+
+ + {new Date(day.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })} + + + {day.count} + +
+ ); + })} +
+
+ + {/* Request Distribution */} +
+

Request Status

+
+
+
+ Successful + {m.successful_requests} +
+
+
+
+
+
+
+ Failed + {m.failed_requests} +
+
+
+
+
+
+
+
+ + {/* Agent-wise Breakdown */} + {m.agents.length > 0 && ( +
+

Agent Performance

+
+ + + + + + + + + + + + {m.agents.map(agent => ( + + + + + + + + ))} + +
AgentRequestsSuccess RateAvg ResponseStatus
+
{agent.agent_name}
+
{agent.agent_id}
+
+ {agent.total_requests.toLocaleString()} + +
+
+
= 90 + ? 'bg-emerald-500' + : agent.success_rate >= 70 + ? 'bg-amber-500' + : 'bg-red-500' + }`} + style={{ width: `${agent.success_rate}%` }} + /> +
+ + {agent.success_rate.toFixed(1)}% + +
+
+ {agent.avg_response_time.toFixed(0)}ms + + = 90 + ? 'bg-emerald-500/20 text-emerald-400' + : agent.success_rate >= 70 + ? 'bg-amber-500/20 text-amber-400' + : 'bg-red-500/20 text-red-400' + }`} + > + {agent.success_rate >= 90 ? 'Healthy' : agent.success_rate >= 70 ? 'Warning' : 'Critical'} + +
+
+
+ )} +
+ ); + } + + // Agent-specific metrics view (original) + if (!agentMetrics) { + return ( +
+
+
+
📈
+

No metrics available yet

+

Metrics for this agent will appear here once it starts being executed.

+
+
+
+ ); + } + + const m = agentMetrics.metrics; + + return ( +
+ {/* Metrics Grid */} +
+
+
Success Rate
+
{(m.success_rate * 100).toFixed(1)}%
+
{m.total_executions} total executions
+
+ +
+
Quality Score
+
{(m.average_quality_score * 100).toFixed(0)}%
+
+
+
+
+ +
+
Avg Execution Time
+
{m.average_execution_time.toFixed(2)}s
+
Across all runs
+
+ +
+
Tokens Used
+
{(m.total_tokens_used / 1000).toFixed(1)}K
+
${m.estimated_cost.toFixed(4)} estimated cost
+
+ +
+
Estimated Cost
+
${m.estimated_cost.toFixed(6)}
+
Based on current pricing
+
+ +
+
Period
+
{agentMetrics.time_period.replace('_', ' ')}
+
All-time data
+
+
+ + {/* Quality Trend Chart (Simple) */} + {agentMetrics.quality_trend.length > 0 && ( +
+

+ 📈 Quality Trend Over Time +

+ +
+ {agentMetrics.quality_trend.map((point, idx) => ( +
+
{point.date}
+
+
+ {point.score > 0.3 && (point.score * 100).toFixed(0)}% +
+
+
{(point.score * 100).toFixed(0)}%
+
+ ))} +
+
+ )} + + {/* Recent Failures */} + {agentMetrics.recent_failures.length > 0 && ( +
+

+ ⚠️ Recent Failures ({agentMetrics.recent_failures.length}) +

+ +
+ {agentMetrics.recent_failures.map((failure, idx) => ( +
+
+

{failure.execution_id}

+ + {new Date(failure.timestamp).toLocaleDateString()} + +
+

{failure.error}

+
+ ))} +
+
+ )} + + {agentMetrics.recent_failures.length === 0 && ( +
+

✨ No recent failures - great job!

+
+ )} +
+ ); +} + +function StatCard({ + label, + value, + icon, + color = 'indigo', + trend, + trendUp +}: { + label: string; + value: string | number; + icon: string; + color?: 'indigo' | 'emerald' | 'blue' | 'red'; + trend?: string; + trendUp?: boolean; +}) { + const colorClasses = { + indigo: 'from-indigo-600/20 to-indigo-500/10 border-indigo-500/30', + emerald: 'from-emerald-600/20 to-emerald-500/10 border-emerald-500/30', + blue: 'from-blue-600/20 to-blue-500/10 border-blue-500/30', + red: 'from-red-600/20 to-red-500/10 border-red-500/30' + }; + + return ( +
+
+
{icon}
+ {trend && ( + + {trend} + + )} +
+

{label}

+

{value}

+
+ ); +} diff --git a/frontend/src/components/developer/AgentRegistration.tsx b/frontend/src/components/developer/AgentRegistration.tsx index e7535ce..ffb11f3 100644 --- a/frontend/src/components/developer/AgentRegistration.tsx +++ b/frontend/src/components/developer/AgentRegistration.tsx @@ -1,5 +1,4 @@ import { useState } from 'react'; -import { motion, AnimatePresence } from 'framer-motion'; import { registerAgent } from '../../api/client'; import toast from 'react-hot-toast'; @@ -27,7 +26,6 @@ export default function AgentRegistration({ onClose }: Props) { e.preventDefault(); setLoading(true); - // Parse capabilities const caps = formData.capabilities.split(',').map(c => c.trim()).filter(Boolean); if (caps.length === 0) { toast.error('Please enter at least one capability'); @@ -56,169 +54,153 @@ export default function AgentRegistration({ onClose }: Props) { }; return ( - - - - {/* Header */} -
-
- -
-

Register New Agent

-

Publish your AI model to the global marketplace

-
- - +
+
+
+
+ +
+

Register New Agent

+

Publish your AI model to the global marketplace

+ + +
-
- -
-
- - setFormData({...formData, agent_name: e.target.value})} - className="w-full bg-black/40 border border-white/10 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-1 focus:ring-emerald-500 transition-colors" - placeholder="e.g., SentimentPulse" - /> -
-
- - setFormData({...formData, version: e.target.value})} - className="w-full bg-black/40 border border-white/10 rounded-lg px-4 py-2 text-white focus:outline-none focus:ring-1 focus:ring-emerald-500 transition-colors font-mono" - placeholder="1.0.0" - /> -
-
- + +
- -